Skip to content

Commit 36345b0

Browse files
Merge pull request #49 from nullinside-development-group/feature/Poc2Prod
Unit testing fundamentals
2 parents 4e0605a + f4a9126 commit 36345b0

File tree

9 files changed

+114
-37
lines changed

9 files changed

+114
-37
lines changed

src/Nullinside.Api.Common/Twitch/Support/TwitchBotLoginErrors.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Nullinside.Api.Shared.Support;
1+
namespace Nullinside.Api.Common.Twitch.Support;
22

33
/// <summary>
44
/// Enumerates the types of errors when authenticating with twitch.

src/Nullinside.Api.Model/Migrations/NullinsideContextModelSnapshot.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
1616
{
1717
#pragma warning disable 612, 618
1818
modelBuilder
19-
.HasAnnotation("ProductVersion", "8.0.3")
19+
.HasAnnotation("ProductVersion", "8.0.8")
2020
.HasAnnotation("Relational:MaxIdentifierLength", 64);
2121

2222
modelBuilder.Entity("Nullinside.Api.Model.Ddl.DockerDeployments", b =>

src/Nullinside.Api.Model/NullinsideContext.cs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -66,22 +66,6 @@ public NullinsideContext(DbContextOptions<NullinsideContext> options) : base(opt
6666
/// </summary>
6767
public DbSet<TwitchUserChatLogs> TwitchUserChatLogs { get; set; } = null!;
6868

69-
/// <summary>
70-
/// Configures the default database connection.
71-
/// </summary>
72-
/// <param name="optionsBuilder">The database configuration options.</param>
73-
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
74-
string? server = Environment.GetEnvironmentVariable("MYSQL_SERVER");
75-
string? username = Environment.GetEnvironmentVariable("MYSQL_USERNAME");
76-
string? password = Environment.GetEnvironmentVariable("MYSQL_PASSWORD");
77-
optionsBuilder.UseMySQL(
78-
$"server={server};database=nullinside;user={username};password={password};AllowUserVariables=true;",
79-
builder => {
80-
builder.CommandTimeout(60 * 5);
81-
builder.EnableRetryOnFailure(3);
82-
});
83-
}
84-
8569
/// <summary>
8670
/// Dynamically finds all <seealso cref="ITableModel" /> classes and generates tables from their definitions.
8771
/// </summary>

src/Nullinside.Api.Model/NullinsideContextFactory.cs renamed to src/Nullinside.Api.Model/NullinsideDesignTimeDbContextFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Nullinside.Api.Model;
99
/// references more than once solution with a DbContext in it. This factory is lazy loaded by the CLI automatically
1010
/// simply by implementing the IDesignTimeDbContextFactory interface.
1111
/// </summary>
12-
public class NullinsideContextFactory : IDesignTimeDbContextFactory<NullinsideContext> {
12+
public class NullinsideDesignTimeDbContextFactory : IDesignTimeDbContextFactory<NullinsideContext> {
1313
/// <summary>
1414
/// Creates a new database context.
1515
/// </summary>

src/Nullinside.Api.Model/Shared/UserHelpers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public static class UserHelpers {
2222
/// <param name="twitchUsername">The username of the user on twitch.</param>
2323
/// <param name="twitchId">The id of the user on twitch.</param>
2424
/// <returns>The bearer token if successful, null otherwise.</returns>
25-
public static async Task<string?> GetTokenAndSaveToDatabase(INullinsideContext dbContext, string email,
25+
public static async Task<string?> GenerateTokenAndSaveToDatabase(INullinsideContext dbContext, string email,
2626
CancellationToken token = new(), string? authToken = null, string? refreshToken = null, DateTime? expires = null,
2727
string? twitchUsername = null, string? twitchId = null) {
2828
string bearerToken = AuthUtils.GenerateBearerToken();
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
using Microsoft.EntityFrameworkCore;
2+
using Microsoft.EntityFrameworkCore.Diagnostics;
3+
4+
using Nullinside.Api.Model;
5+
using Nullinside.Api.Model.Ddl;
6+
using Nullinside.Api.Model.Shared;
7+
8+
namespace Nullinside.Api.Tests.Nullinside.Api.Model.Shared;
9+
10+
public class UserHelpersTests {
11+
private INullinsideContext _db;
12+
13+
[SetUp]
14+
public void Setup() {
15+
// Create an in-memory database to fake the SQL queries. Note that we generate a random GUID for the name
16+
// here. If you use the same name more than once you'll get collisions between tests.
17+
DbContextOptions<NullinsideContext> contextOptions = new DbContextOptionsBuilder<NullinsideContext>()
18+
.UseInMemoryDatabase(Guid.NewGuid().ToString())
19+
.ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning))
20+
.Options;
21+
_db = new NullinsideContext(contextOptions);
22+
}
23+
24+
[TearDown]
25+
public async Task TearDown() {
26+
await _db.DisposeAsync();
27+
}
28+
29+
/// <summary>
30+
/// The case where a user is generating a new token to replace their existing one.
31+
/// </summary>
32+
[Test]
33+
public async Task GenerateTokenForExistingUser() {
34+
_db.Users.Add(
35+
new User {
36+
Email = "email"
37+
}
38+
);
39+
await _db.SaveChangesAsync();
40+
41+
// Verify there is only one user
42+
Assert.That(_db.Users.Count(), Is.EqualTo(1));
43+
44+
// Generate a new token
45+
string? token = await UserHelpers.GenerateTokenAndSaveToDatabase(_db, "email");
46+
Assert.That(token, Is.Not.Null);
47+
48+
// Verify we still only have one user
49+
Assert.That(_db.Users.Count(), Is.EqualTo(1));
50+
Assert.That(_db.Users.First().Token, Is.EqualTo(token));
51+
}
52+
53+
/// <summary>
54+
/// The case where a user is getting a token for the first time. A new user should be created. The existing user
55+
/// should be untouched.
56+
/// </summary>
57+
[Test]
58+
public async Task GenerateTokenForNewUser() {
59+
_db.Users.Add(
60+
new User {
61+
Email = "email2"
62+
}
63+
);
64+
await _db.SaveChangesAsync();
65+
66+
// Verify there is only one user
67+
Assert.That(_db.Users.Count(), Is.EqualTo(1));
68+
69+
// Generate a new token
70+
string? token = await UserHelpers.GenerateTokenAndSaveToDatabase(_db, "email");
71+
Assert.That(token, Is.Not.Null);
72+
73+
// Verify we have a new user
74+
Assert.That(_db.Users.Count(), Is.EqualTo(2));
75+
Assert.That(_db.Users.FirstOrDefault(u => u.Email == "email")?.Token, Is.EqualTo(token));
76+
77+
// Verfy the old user is untouched
78+
Assert.That(_db.Users.FirstOrDefault(u => u.Email == "email2")?.Token, Is.Null);
79+
}
80+
81+
/// <summary>
82+
/// Unexpected database errors should result in a null being returned.
83+
/// </summary>
84+
[Test]
85+
public async Task HandleUnexpectedErrors() {
86+
// Force an error to occur.
87+
string? token = await UserHelpers.GenerateTokenAndSaveToDatabase(null!, "email");
88+
Assert.That(token, Is.Null);
89+
}
90+
}

src/Nullinside.Api.Tests/Nullinside.Api.Tests.csproj

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414
</PropertyGroup>
1515

1616
<ItemGroup>
17-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
18-
<PackageReference Include="NUnit" Version="4.2.2" />
19-
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
17+
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.8"/>
18+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1"/>
19+
<PackageReference Include="Moq" Version="4.20.72"/>
20+
<PackageReference Include="NUnit" Version="4.2.2"/>
21+
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0"/>
2022
<PackageReference Include="NUnit.Analyzers" Version="4.3.0">
2123
<PrivateAssets>all</PrivateAssets>
2224
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -27,4 +29,17 @@
2729
</PackageReference>
2830
</ItemGroup>
2931

32+
<ItemGroup>
33+
<Folder Include="Nullinside.Api.Common.AspNetCore\"/>
34+
<Folder Include="Nullinside.Api.Common\"/>
35+
<Folder Include="Nullinside.Api\"/>
36+
</ItemGroup>
37+
38+
<ItemGroup>
39+
<ProjectReference Include="..\Nullinside.Api.Common.AspNetCore\Nullinside.Api.Common.AspNetCore.csproj"/>
40+
<ProjectReference Include="..\Nullinside.Api.Common\Nullinside.Api.Common.csproj"/>
41+
<ProjectReference Include="..\Nullinside.Api.Model\Nullinside.Api.Model.csproj"/>
42+
<ProjectReference Include="..\Nullinside.Api\Nullinside.Api.csproj"/>
43+
</ItemGroup>
44+
3045
</Project>

src/Nullinside.Api.Tests/UnitTest1.cs

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/Nullinside.Api/Controllers/UserController.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public async Task<IActionResult> Login([FromForm] GoogleOpenIdToken creds, Cance
6565
return Redirect($"{siteUrl}/user/login?error=1");
6666
}
6767

68-
string? bearerToken = await UserHelpers.GetTokenAndSaveToDatabase(_dbContext, credentials.Email, token);
68+
string? bearerToken = await UserHelpers.GenerateTokenAndSaveToDatabase(_dbContext, credentials.Email, token);
6969
if (string.IsNullOrWhiteSpace(bearerToken)) {
7070
return Redirect($"{siteUrl}/user/login?error=2");
7171
}
@@ -107,7 +107,7 @@ public async Task<IActionResult> TwitchLogin([FromQuery] string code, [FromServi
107107
return Redirect($"{siteUrl}/user/login?error=4");
108108
}
109109

110-
string? bearerToken = await UserHelpers.GetTokenAndSaveToDatabase(_dbContext, email, token);
110+
string? bearerToken = await UserHelpers.GenerateTokenAndSaveToDatabase(_dbContext, email, token);
111111
if (string.IsNullOrWhiteSpace(bearerToken)) {
112112
return Redirect($"{siteUrl}/user/login?error=2");
113113
}

0 commit comments

Comments
 (0)