Skip to content

Commit b51003c

Browse files
committed
refactor, write red test
1 parent 5a02aa5 commit b51003c

File tree

10 files changed

+109
-40
lines changed

10 files changed

+109
-40
lines changed

Rubberduck.Core/Properties/Settings.Designer.cs

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Rubberduck.Core/Rubberduck.Core.csproj

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,17 @@
9393
<Version>2.0.20525</Version>
9494
</PackageReference>
9595
</ItemGroup>
96+
<ItemGroup>
97+
<Compile Update="Properties\Settings.Designer.cs">
98+
<DesignTimeSharedInput>True</DesignTimeSharedInput>
99+
<AutoGen>True</AutoGen>
100+
<DependentUpon>Settings.settings</DependentUpon>
101+
</Compile>
102+
</ItemGroup>
103+
<ItemGroup>
104+
<None Update="Properties\Settings.settings">
105+
<Generator>SettingsSingleFileGenerator</Generator>
106+
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
107+
</None>
108+
</ItemGroup>
96109
</Project>

Rubberduck.Core/Settings/GeneralSettings.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public interface IGeneralSettings
1313
DisplayLanguageSetting Language { get; set; }
1414
bool CanShowSplash { get; set; }
1515
bool CanCheckVersion { get; set; }
16+
string ApiBaseUrl { get; set; }
1617
bool IncludePreRelease { get; set; }
1718
bool CompileBeforeParse { get; set; }
1819
bool IsSmartIndenterPrompted { get; set; }
@@ -45,6 +46,7 @@ public DisplayLanguageSetting Language
4546

4647
public bool CanShowSplash { get; set; }
4748
public bool CanCheckVersion { get; set; }
49+
public string ApiBaseUrl { get; set; }
4850
public bool IncludePreRelease { get; set; }
4951
public bool CompileBeforeParse { get; set; }
5052
public bool IsSmartIndenterPrompted { get; set; }
@@ -103,7 +105,8 @@ public bool Equals(GeneralSettings other)
103105
EnableExperimentalFeatures.Count == other.EnableExperimentalFeatures.Count &&
104106
EnableExperimentalFeatures.All(other.EnableExperimentalFeatures.Contains) &&
105107
SetDpiUnaware == other.SetDpiUnaware &&
106-
EnableFolderDragAndDrop == other.EnableFolderDragAndDrop;
108+
EnableFolderDragAndDrop == other.EnableFolderDragAndDrop &&
109+
ApiBaseUrl == other.ApiBaseUrl;
107110
}
108111
}
109112
}

Rubberduck.Core/UI/Splash2021.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System.Drawing;
22
using System.Windows.Forms;
3-
using Rubberduck.VersionCheck;
43

54
namespace Rubberduck.UI
65
{
@@ -11,9 +10,9 @@ public Splash2021()
1110
InitializeComponent();
1211
}
1312

14-
public Splash2021(IVersionCheckService versionCheck) : this()
13+
public Splash2021(string versionString) : this()
1514
{
16-
VersionLabel.Text = string.Format(Resources.RubberduckUI.Rubberduck_AboutBuild, versionCheck.VersionString);
15+
VersionLabel.Text = string.Format(Resources.RubberduckUI.Rubberduck_AboutBuild, versionString);
1716
VersionLabel.Parent = pictureBox1;
1817
VersionLabel.BackColor = Color.Transparent;
1918
}

Rubberduck.Core/VersionCheck/ApiClientBase.cs

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Newtonsoft.Json;
2+
using Rubberduck.Settings;
23
using System;
34
using System.Net;
45
using System.Net.Http;
@@ -9,40 +10,69 @@
910

1011
namespace Rubberduck.Client.Abstract
1112
{
12-
public abstract class ApiClientBase : IDisposable
13+
public interface IHttpClientProvider
14+
{
15+
HttpClient GetClient();
16+
}
17+
18+
public sealed class ApiHttpClientProvider : IHttpClientProvider, IDisposable
19+
{
20+
private readonly Lazy<HttpClient> _client;
21+
22+
public ApiHttpClientProvider(Func<HttpClient> getClient)
23+
{
24+
_client = new Lazy<HttpClient>(getClient);
25+
}
26+
27+
public HttpClient GetClient()
28+
{
29+
return _client.Value;
30+
}
31+
32+
public void Dispose()
33+
{
34+
if (_client.IsValueCreated)
35+
{
36+
_client.Value.Dispose();
37+
}
38+
}
39+
}
40+
41+
public abstract class ApiClientBase
1342
{
1443
protected static readonly string UserAgentName = "Rubberduck";
15-
protected static readonly string BaseUrl = "https://api.rubberduckvba.com/api/v1/";
1644
protected static readonly string ContentTypeApplicationJson = "application/json";
1745
protected static readonly int MaxAttempts = 3;
1846
protected static readonly TimeSpan RetryCooldownDelay = TimeSpan.FromSeconds(1);
1947

20-
protected readonly Lazy<HttpClient> _client;
48+
protected readonly IHttpClientProvider _clientProvider;
49+
protected readonly string _baseUrl;
2150

22-
protected ApiClientBase()
51+
protected ApiClientBase(IGeneralSettings settings, IHttpClientProvider clientProvider)
2352
{
24-
_client = new Lazy<HttpClient>(() => GetClient());
53+
_clientProvider = clientProvider;
54+
_baseUrl = string.IsNullOrWhiteSpace(settings.ApiBaseUrl) ? "https://api.rubberduckvba.com" : settings.ApiBaseUrl;
2555
}
2656

2757
protected HttpClient GetClient()
2858
{
2959
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
30-
var client = new HttpClient();
31-
return ConfigureClient(client);
60+
var client = _clientProvider.GetClient();
61+
ConfigureClient(client);
62+
return client;
3263
}
3364

34-
protected virtual HttpClient ConfigureClient(HttpClient client)
65+
protected virtual void ConfigureClient(HttpClient client)
3566
{
3667
var userAgentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(3);
3768
var userAgentHeader = new ProductInfoHeaderValue(UserAgentName, userAgentVersion);
3869

3970
client.DefaultRequestHeaders.UserAgent.Add(userAgentHeader);
40-
return client;
4171
}
4272

4373
protected virtual async Task<TResult> GetResponse<TResult>(string route, CancellationToken? cancellationToken = null)
4474
{
45-
var uri = new Uri($"{BaseUrl}{route}");
75+
var uri = new Uri($"{_baseUrl}{route}");
4676

4777
var attempt = 0;
4878
var token = cancellationToken ?? CancellationToken.None;
@@ -102,7 +132,7 @@ protected virtual async Task<TResult> GetResponse<TResult>(string route, Cancell
102132

103133
protected virtual async Task<TResult> Post<TArgs, TResult>(string route, TArgs args, CancellationToken? cancellationToken = null)
104134
{
105-
var uri = new Uri($"{BaseUrl}{route}");
135+
var uri = new Uri($"{_baseUrl}{route}");
106136
string json;
107137
try
108138
{
@@ -167,13 +197,5 @@ protected virtual async Task<TResult> Post<TArgs, TResult>(string route, TArgs a
167197
return default;
168198
}
169199
}
170-
171-
public void Dispose()
172-
{
173-
if (_client.IsValueCreated)
174-
{
175-
_client.Value.Dispose();
176-
}
177-
}
178200
}
179201
}

Rubberduck.Core/VersionCheck/PublicApiClient.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Linq;
43
using System.Threading;
54
using System.Threading.Tasks;
65
using Rubberduck.Client.Abstract;
6+
using Rubberduck.Settings;
77

88
namespace Rubberduck.VersionCheck
99
{
10-
public class PublicApiClient : ApiClientBase
10+
public interface IPublicApiClient
11+
{
12+
Task<IEnumerable<Tag>> GetLatestTagsAsync(CancellationToken token);
13+
}
14+
15+
public class PublicApiClient : ApiClientBase, IPublicApiClient
1116
{
1217
private static readonly string PublicTagsEndPoint = "public/tags";
1318

19+
public PublicApiClient(IGeneralSettings settings, IHttpClientProvider clientProvider)
20+
: base(settings, clientProvider)
21+
{
22+
}
23+
1424
public async Task<IEnumerable<Tag>> GetLatestTagsAsync(CancellationToken token)
1525
{
1626
return await GetResponse<Tag[]>(PublicTagsEndPoint, token);

Rubberduck.Core/VersionCheck/VersionCheckService.cs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,15 @@ namespace Rubberduck.VersionCheck
1111
public class VersionCheckService : IVersionCheckService
1212
{
1313
private static readonly ILogger _logger = LogManager.GetCurrentClassLogger();
14+
private readonly IPublicApiClient _client;
1415

1516
/// <param name="version">That would be the version of the assembly for the <c>_Extension</c> class.</param>
16-
public VersionCheckService(Version version)
17+
/// <param name="client"></param>
18+
public VersionCheckService(Version version, IPublicApiClient client)
1719
{
1820
CurrentVersion = version;
21+
_client = client;
22+
1923
#if DEBUG
2024
IsDebugBuild = true;
2125
#endif
@@ -32,19 +36,16 @@ public async Task<Version> GetLatestVersionAsync(GeneralSettings settings, Cance
3236
return _latestVersion;
3337
}
3438

35-
using (var client = new PublicApiClient())
36-
{
37-
var tags = await client.GetLatestTagsAsync(token);
39+
var tags = await _client.GetLatestTagsAsync(token);
3840

39-
var next = tags.Single(e => e.IsPreRelease);
40-
var main = tags.Single(e => !e.IsPreRelease);
41-
_logger.Info($"Main: v{main.Version.ToString(3)}; Next: v{next.Version.ToString(4)}");
41+
var next = tags.Single(e => e.IsPreRelease);
42+
var main = tags.Single(e => !e.IsPreRelease);
43+
_logger.Info($"Main: v{main.Version.ToString(3)}; Next: v{next.Version.ToString(4)}");
4244

43-
_latestVersion = settings.IncludePreRelease ? next.Version : main.Version;
44-
_logger.Info($"Check prerelease: {settings.IncludePreRelease}; latest: v{_latestVersion.ToString(4)}");
45+
_latestVersion = settings.IncludePreRelease ? next.Version : main.Version;
46+
_logger.Info($"Check prerelease: {settings.IncludePreRelease}; latest: v{_latestVersion.ToString(4)}");
4547

46-
return _latestVersion;
47-
}
48+
return _latestVersion;
4849
}
4950

5051
public Version CurrentVersion { get; }

Rubberduck.Core/app.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@
221221
<MinimumLogLevel>0</MinimumLogLevel>
222222
<SetDpiUnaware>false</SetDpiUnaware>
223223
<EnableExperimentalFeatures />
224+
<ApiBaseUrl>https://api.rubberduckvba.com/api/v1/</ApiBaseUrl>
224225
</GeneralSettings>
225226
</value>
226227
</setting>

Rubberduck.Main/Extension.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ private void InitializeAddIn()
194194

195195
if (_initialSettings?.CanShowSplash ?? false)
196196
{
197-
splash = new Splash2021(new VersionCheckService(typeof(Splash2021).Assembly.GetName().Version));
197+
splash = new Splash2021(string.Format(RubberduckUI.Rubberduck_AboutBuild, Assembly.GetExecutingAssembly().GetName().Version.ToString(3)));
198198
splash.Show();
199199
splash.Refresh();
200200
}

RubberduckTests/VersionCheckTests.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
22
using System.Threading;
3+
using System.Threading.Tasks;
4+
using System.Web;
35
using Moq;
46
using NUnit.Framework;
57
using Rubberduck.Interaction;
@@ -10,6 +12,24 @@
1012

1113
namespace RubberduckTests
1214
{
15+
[TestFixture]
16+
public class VersionCheckServiceTests
17+
{
18+
[Test]
19+
public async Task GetLatestVersionThrowsHttpException_IsHandled()
20+
{
21+
var appVersion = new Version();
22+
var apiClient = new Mock<IPublicApiClient>();
23+
apiClient.Setup(m => m.GetLatestTagsAsync(It.IsAny<CancellationToken>())).Throws<HttpException>();
24+
25+
var sut = new VersionCheckService(appVersion, apiClient.Object);
26+
27+
await sut.GetLatestVersionAsync(new GeneralSettings(), CancellationToken.None);
28+
29+
Assert.IsTrue(true); // if we make it here, service isn't throwing.
30+
}
31+
}
32+
1333
[TestFixture]
1434
public class VersionCheckTests
1535
{
@@ -34,12 +54,12 @@ private VersionCheckCommand CreateSUT(Configuration config, Version currentVersi
3454
mockConfig.Setup(m => m.Read()).Returns(() => config);
3555

3656
mockService = new Mock<IVersionCheckService>();
37-
57+
3858
mockService.Setup(m => m.CurrentVersion)
3959
.Returns(() => currentVersion);
4060

4161
mockService.Setup(m => m.GetLatestVersionAsync(It.IsAny<GeneralSettings>(), It.IsAny<CancellationToken>()))
42-
.ReturnsAsync(() => latestVersion);
62+
.ReturnsAsync(() => latestVersion);
4363

4464
return new VersionCheckCommand(mockService.Object, mockPrompt.Object, mockProcess.Object, mockConfig.Object);
4565
}

0 commit comments

Comments
 (0)