Skip to content

Commit

Permalink
feat: improve the overall unit test rate (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
BoBoBaSs84 authored Jun 29, 2024
2 parents 35ec0e5 + 88289c7 commit 0970fd7
Show file tree
Hide file tree
Showing 44 changed files with 1,203 additions and 106 deletions.
8 changes: 3 additions & 5 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,9 @@
</ItemGroup>

<ItemGroup Condition="$(MSBuildProjectName.EndsWith('Tests'))">
<PackageReference Include="coverlet.collector" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Moq" />
<PackageReference Include="MSTest.TestAdapter" />
<PackageReference Include="MSTest.TestFramework" />
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
<Using Include="System.Diagnostics" />
<Using Include="System.Diagnostics.CodeAnalysis" />
</ItemGroup>

<ItemGroup>
Expand Down
5 changes: 4 additions & 1 deletion src/BB84.SAU.Application/Provider/DateTimeProvider.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
using BB84.SAU.Application.Interfaces.Application.Provider;
using System.Diagnostics.CodeAnalysis;

using BB84.SAU.Application.Interfaces.Application.Provider;

namespace BB84.SAU.Application.Provider;

/// <summary>
/// The date time provider class.
/// </summary>
[ExcludeFromCodeCoverage(Justification = "Wrapper for DateTime")]
internal sealed class DateTimeProvider : IDateTimeProvider
{
public DateTime Now => DateTime.Now;
Expand Down
5 changes: 4 additions & 1 deletion src/BB84.SAU.Application/Services/NotificationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ internal sealed class NotificationService : INotificationService
public event NotificationEventHandler? NotificationReceived;

public void Send(string message)
=> NotificationReceived?.Invoke(this, message);
{
if (NotificationReceived is not null)
NotificationReceived.Invoke(this, message);
}

public async Task SendAsync(string message)
{
Expand Down
61 changes: 32 additions & 29 deletions src/BB84.SAU.Application/Services/SteamWebService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@
using System.Text.Json;

using BB84.Extensions;

using Microsoft.Extensions.Logging;

using BB84.SAU.Application.Interfaces.Application.Provider;
using BB84.SAU.Application.Interfaces.Application.Services;
using BB84.SAU.Application.Interfaces.Infrastructure.Services;
using BB84.SAU.Domain.Models;

using Microsoft.Extensions.Logging;

using HC = BB84.SAU.Application.Common.ApplicationConstants.HttpClients;

namespace BB84.SAU.Application.Services;
Expand All @@ -23,10 +22,6 @@ namespace BB84.SAU.Application.Services;
/// <param name="httpClientFactory">The http client factory instance to use.</param>
internal sealed class SteamWebService(ILoggerService<SteamWebService> loggerService, INotificationService notificationService, IDateTimeProvider dateTimeProvider, IHttpClientFactory httpClientFactory) : ISteamWebService
{
private readonly ILoggerService<SteamWebService> _loggerService = loggerService;
private readonly INotificationService _notificationService = notificationService;
private readonly IHttpClientFactory _httpClientFactory = httpClientFactory;

private static readonly Action<ILogger, Exception?> LogException =
LoggerMessage.Define(LogLevel.Error, 0, "Exception occured.");

Expand All @@ -38,13 +33,14 @@ public async Task<IEnumerable<AchievementModel>> GetAchievements(int appId, stri
List<AchievementModel> achievements = [];
try
{
HttpClient httpClient = _httpClientFactory.CreateClient(nameof(HC.SteamPowered));
HttpClient httpClient = httpClientFactory.CreateClient(nameof(HC.SteamPowered));
string requestUri = HC.SteamPowered.GetSchemaForGame.FormatInvariant(apiKey, appId);
using HttpResponseMessage responseMessage = await httpClient.GetAsync(requestUri, cancellationToken);

if (!responseMessage.IsSuccessStatusCode)
{
_loggerService.Log(LogExceptionWithParams, responseMessage.StatusCode, null);
loggerService.Log(LogExceptionWithParams, responseMessage.StatusCode, null);
notificationService.Send($"{responseMessage.StatusCode} - {responseMessage.ReasonPhrase}");
return achievements;
}

Expand Down Expand Up @@ -73,14 +69,14 @@ public async Task<IEnumerable<AchievementModel>> GetAchievements(int appId, stri
achievements.Add(new(id, name, description, hidden, icon, iconGray));
}

_notificationService.Send($"Loaded {achievements.Count} achievements.");
notificationService.Send($"Loaded {achievements.Count} achievements.");

return achievements;
}
catch (Exception ex)
{
_loggerService.Log(LogExceptionWithParams, appId, ex);
_notificationService.Send(ex.Message);
loggerService.Log(LogExceptionWithParams, appId, ex);
notificationService.Send(ex.Message);
return achievements;
}
}
Expand All @@ -90,40 +86,41 @@ public async Task<IEnumerable<GameModel>> GetGames(long steamId, string apiKey,
List<GameModel> games = [];
try
{
HttpClient httpClient = _httpClientFactory.CreateClient(nameof(HC.SteamPowered));
HttpClient httpClient = httpClientFactory.CreateClient(nameof(HC.SteamPowered));
string requestUri = HC.SteamPowered.GetOwnedGames.FormatInvariant(apiKey, steamId);
using HttpResponseMessage gamesResponseMessage = await httpClient.GetAsync(requestUri, cancellationToken);
using HttpResponseMessage responseMessage = await httpClient.GetAsync(requestUri, cancellationToken);

if (!gamesResponseMessage.IsSuccessStatusCode)
if (!responseMessage.IsSuccessStatusCode)
{
_loggerService.Log(LogExceptionWithParams, gamesResponseMessage.StatusCode, null);
loggerService.Log(LogExceptionWithParams, responseMessage.StatusCode, null);
notificationService.Send($"{responseMessage.StatusCode} - {responseMessage.ReasonPhrase}");
return games;
}

string jsonContent = await gamesResponseMessage.Content.ReadAsStringAsync(cancellationToken)
string jsonContent = await responseMessage.Content.ReadAsStringAsync(cancellationToken)
.ConfigureAwait(false);

JsonElement.ArrayEnumerator jsonArray = JsonSerializer.Deserialize<JsonElement>(jsonContent)
.GetProperty("response")
.GetProperty("games")
.EnumerateArray();

foreach (var jsonElement in jsonArray)
foreach (JsonElement jsonElement in jsonArray)
{
int id = jsonElement.GetProperty("appid").GetInt32();
string title = jsonElement.GetProperty("name").ToString();

games.Add(new(id, title));
}

_notificationService.Send($"Loaded {games.Count} games.");
notificationService.Send($"Loaded {games.Count} games.");

return games;
}
catch (Exception ex)
{
_loggerService.Log(LogException, ex);
_notificationService.Send(ex.Message);
loggerService.Log(LogException, ex);
notificationService.Send(ex.Message);
return games;
}
}
Expand All @@ -132,13 +129,14 @@ public async Task<IEnumerable<GameModel>> GetGames(long steamId, string apiKey,
{
try
{
HttpClient httpClient = _httpClientFactory.CreateClient(nameof(HC.SteamStore));
HttpClient httpClient = httpClientFactory.CreateClient(nameof(HC.SteamStore));
string requestUri = HC.SteamStore.GetAppDetails.FormatInvariant(appId);
using HttpResponseMessage responseMessage = await httpClient.GetAsync(requestUri, cancellationToken);

if (!responseMessage.IsSuccessStatusCode)
{
_loggerService.Log(LogExceptionWithParams, responseMessage.StatusCode, null);
loggerService.Log(LogExceptionWithParams, responseMessage.StatusCode, null);
notificationService.Send($"{responseMessage.StatusCode} - {responseMessage.ReasonPhrase}");
return default;
}

Expand All @@ -160,12 +158,14 @@ public async Task<IEnumerable<GameModel>> GetGames(long steamId, string apiKey,
if (jsonElement.TryGetProperty("header_image", out value))
imageUrl = value.GetString();

notificationService.Send($"Details for {title} loaded.");

return new(id, title, description, imageUrl, dateTimeProvider.Now);
}
catch (Exception ex)
{
_loggerService.Log(LogExceptionWithParams, appId, ex);
_notificationService.Send(ex.Message);
loggerService.Log(LogExceptionWithParams, appId, ex);
notificationService.Send(ex.Message);
return default;
}
}
Expand All @@ -174,13 +174,14 @@ public async Task<IEnumerable<GameModel>> GetGames(long steamId, string apiKey,
{
try
{
HttpClient httpClient = _httpClientFactory.CreateClient(nameof(HC.SteamPowered));
HttpClient httpClient = httpClientFactory.CreateClient(nameof(HC.SteamPowered));
string requestUri = HC.SteamPowered.GetPlayerSummaries.FormatInvariant(apiKey, steamId);
using HttpResponseMessage responseMessage = await httpClient.GetAsync(requestUri, cancellationToken);

if (!responseMessage.IsSuccessStatusCode)
{
_loggerService.Log(LogExceptionWithParams, responseMessage.StatusCode, null);
loggerService.Log(LogExceptionWithParams, responseMessage.StatusCode, null);
notificationService.Send($"{responseMessage.StatusCode} - {responseMessage.ReasonPhrase}");
return default;
}

Expand All @@ -201,12 +202,14 @@ public async Task<IEnumerable<GameModel>> GetGames(long steamId, string apiKey,
DateTime created = DateTimeOffset.FromUnixTimeMilliseconds(jsonElement.GetProperty("timecreated").GetInt64()).LocalDateTime;
DateTime lastLogOff = DateTimeOffset.FromUnixTimeMilliseconds(jsonElement.GetProperty("lastlogoff").GetInt64()).LocalDateTime;

notificationService.Send($"User data for {name} loaded.");

return new(name, imageUrl, profileUrl, created, lastLogOff, dateTimeProvider.Now);
}
catch (Exception ex)
{
_loggerService.Log(LogException, ex);
_notificationService.Send(ex.Message);
loggerService.Log(LogException, ex);
notificationService.Send(ex.Message);
return default;
}
}
Expand Down
20 changes: 8 additions & 12 deletions src/BB84.SAU.Application/ViewModels/GamesViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@
using BB84.Notifications.Attributes;
using BB84.Notifications.Commands;
using BB84.Notifications.Interfaces.Commands;

using Microsoft.Extensions.Options;

using BB84.SAU.Application.Interfaces.Application.Services;
using BB84.SAU.Application.ViewModels.Base;
using BB84.SAU.Domain.Models;
using BB84.SAU.Domain.Settings;

using Microsoft.Extensions.Options;

namespace BB84.SAU.Application.ViewModels;

/// <summary>
Expand Down Expand Up @@ -94,8 +93,8 @@ public IAsyncActionCommand LoadGamesCommand
/// <summary>
/// The command to select the game.
/// </summary>
public IActionCommand<GameDetailModel?> SelectGameCommand
=> new ActionCommand<GameDetailModel?>(SelectGame, CanSelectGame);
public IActionCommand<GameDetailModel> SelectGameCommand
=> new ActionCommand<GameDetailModel>(SelectGame, CanSelectGame);

/// <summary>
/// Indicates if the select game button is visible.
Expand All @@ -106,16 +105,13 @@ public bool IsSelectButtonVisible
private bool CanLoadGames()
=> Model.LastUpdate is not null && GamesAreLoading.IsFalse();

private bool CanSelectGame(GameDetailModel? model)
private bool CanSelectGame(GameDetailModel model)
=> model is not null && model.LastUpdate is not null;

private void SelectGame(GameDetailModel? model)
private void SelectGame(GameDetailModel model)
{
if (model is not null)
{
_achievementsViewModel.Model = model;
_navigationService.NavigateTo<AchievementsViewModel>();
}
_achievementsViewModel.Model = model;
_navigationService.NavigateTo<AchievementsViewModel>();
}

private async Task LoadGames()
Expand Down
3 changes: 2 additions & 1 deletion src/BB84.SAU.Infrastructure/Services/SteamApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,10 @@ public bool RequestStats()
throw new SteamSdkException("Stats have not been requested!");

bool result = steamWorksProvider.GetAchievementAndUnlockTime(name, out bool achieved, out uint unlockTime);

DateTime? dateTime = achieved
? DateTimeOffset.FromUnixTimeSeconds(unlockTime).LocalDateTime
: default;
: null;

return (achieved, dateTime);
}
Expand Down
24 changes: 24 additions & 0 deletions tests/BB84.SAU.Application.Tests/ApplicationTestBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace BB84.SAU.Application.Tests;

[TestClass]
public abstract class ApplicationTestBase
{
public sealed class ViewModelTestAttribute : TestMethodAttribute
{
public override TestResult[] Execute(ITestMethod testMethod)
{
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
return Invoke(testMethod);

TestResult[] result = [];
Thread thread = new(() => result = Invoke(testMethod));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
return result;
}

private static TestResult[] Invoke(ITestMethod testMethod)
=> [testMethod.Invoke(null)];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
<ProjectReference Include="..\..\src\BB84.SAU.Application\BB84.SAU.Application.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="coverlet.collector">
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Moq" />
<PackageReference Include="MSTest.TestAdapter" />
<PackageReference Include="MSTest.TestFramework" />
<PackageReference Include="coverlet.collector">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using BB84.SAU.Application.Installer;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using BB84.SAU.Application.Installer;

namespace BB84.SAU.Application.Tests.Installer;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;

using BB84.SAU.Application.Services;
using BB84.SAU.Application.Services;
using BB84.SAU.Application.ViewModels;

namespace BB84.SAU.Application.Tests.Services;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using BB84.SAU.Application.Services;

namespace BB84.SAU.Application.Tests.Services;

[TestClass]
public sealed class NotificationServiceTests
{
[TestMethod]
[TestCategory("Methods")]
public void SendShouldRaiseNotificationReceivedEventHandler()
{
bool notificationReceived = false;
NotificationService service = new();
service.NotificationReceived += (s, e) => notificationReceived = true;

service.Send(string.Empty);

Assert.IsTrue(notificationReceived);
}

[TestMethod]
[TestCategory("Methods")]
public async Task SendAsyncShouldNotRaiseAsyncNotificationReceivedEventHandler()
{
bool notificationReceived = false;
NotificationService service = new();
service.AsyncNotificationReceived += (s, e) => Task.Run(() => notificationReceived = true);

await service.SendAsync(string.Empty)
.ConfigureAwait(true);

Assert.IsTrue(notificationReceived);
}
}
Loading

0 comments on commit 0970fd7

Please sign in to comment.