Adding service layer testing (#151)

This commit is contained in:
Aaron Po
2026-02-14 21:17:39 -05:00
committed by GitHub
parent 6b66f5680f
commit 0d52c937ce
23 changed files with 820 additions and 92 deletions

View File

@@ -11,6 +11,6 @@ WORKDIR "/src/Infrastructure/Infrastructure.Repository.Tests"
RUN dotnet build "./Infrastructure.Repository.Tests.csproj" -c $BUILD_CONFIGURATION -o /app/build
FROM build AS final
RUN mkdir -p /app/test-results
RUN mkdir -p /app/test-results/repository-tests
WORKDIR /src/Infrastructure/Infrastructure.Repository.Tests
ENTRYPOINT ["dotnet", "test", "./Infrastructure.Repository.Tests.csproj", "-c", "Release", "--logger", "trx;LogFileName=/app/test-results/repository-tests.trx"]
ENTRYPOINT ["dotnet", "test", "./Infrastructure.Repository.Tests.csproj", "-c", "Release", "--logger", "trx;LogFileName=/app/test-results/repository-tests/results.trx"]

View File

@@ -6,28 +6,14 @@
<IsPackable>false</IsPackable>
<RootNamespace>Infrastructure.Repository.Tests</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
<PackageReference Include="FluentAssertions" Version="6.9.0" />
<PackageReference Include="DbMocker" Version="1.26.0" />
<PackageReference
Include="Microsoft.Extensions.Configuration"
Version="9.0.0"
/>
<PackageReference
Include="Microsoft.Extensions.Configuration.Abstractions"
Version="9.0.0"
/>
<PackageReference
Include="Microsoft.Extensions.Configuration.Binder"
Version="9.0.0"
/>
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.3" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.4" />
</ItemGroup>
<ItemGroup>

View File

@@ -88,9 +88,7 @@ public class AuthRepository(ISqlConnectionFactory connectionFactory)
AddParameter(command, "@UserAccountId", userAccountId);
await using var reader = await command.ExecuteReaderAsync();
return await reader.ReadAsync()
? MapToCredentialEntity(reader)
: null;
return await reader.ReadAsync() ? MapToCredentialEntity(reader) : null;
}
public async Task RotateCredentialAsync(
@@ -118,9 +116,7 @@ public class AuthRepository(ISqlConnectionFactory connectionFactory)
{
return new Domain.Entities.UserAccount
{
UserAccountId = reader.GetGuid(
reader.GetOrdinal("UserAccountId")
),
UserAccountId = reader.GetGuid(reader.GetOrdinal("UserAccountId")),
Username = reader.GetString(reader.GetOrdinal("Username")),
FirstName = reader.GetString(reader.GetOrdinal("FirstName")),
LastName = reader.GetString(reader.GetOrdinal("LastName")),
@@ -129,9 +125,7 @@ public class AuthRepository(ISqlConnectionFactory connectionFactory)
UpdatedAt = reader.IsDBNull(reader.GetOrdinal("UpdatedAt"))
? null
: reader.GetDateTime(reader.GetOrdinal("UpdatedAt")),
DateOfBirth = reader.GetDateTime(
reader.GetOrdinal("DateOfBirth")
),
DateOfBirth = reader.GetDateTime(reader.GetOrdinal("DateOfBirth")),
Timer = reader.IsDBNull(reader.GetOrdinal("Timer"))
? null
: (byte[])reader["Timer"],
@@ -148,9 +142,7 @@ public class AuthRepository(ISqlConnectionFactory connectionFactory)
UserCredentialId = reader.GetGuid(
reader.GetOrdinal("UserCredentialId")
),
UserAccountId = reader.GetGuid(
reader.GetOrdinal("UserAccountId")
),
UserAccountId = reader.GetGuid(reader.GetOrdinal("UserAccountId")),
Hash = reader.GetString(reader.GetOrdinal("Hash")),
CreatedAt = reader.GetDateTime(reader.GetOrdinal("CreatedAt")),
};
@@ -192,4 +184,4 @@ public class AuthRepository(ISqlConnectionFactory connectionFactory)
p.Value = value ?? DBNull.Value;
command.Parameters.Add(p);
}
}
}

View File

@@ -33,9 +33,7 @@ public interface IAuthRepository
/// </summary>
/// <param name="email">Email address to search for</param>
/// <returns>UserAccount if found, null otherwise</returns>
Task<Domain.Entities.UserAccount?> GetUserByEmailAsync(
string email
);
Task<Domain.Entities.UserAccount?> GetUserByEmailAsync(string email);
/// <summary>
/// Retrieves a user account by username (typically used for login).
@@ -43,9 +41,7 @@ public interface IAuthRepository
/// </summary>
/// <param name="username">Username to search for</param>
/// <returns>UserAccount if found, null otherwise</returns>
Task<Domain.Entities.UserAccount?> GetUserByUsernameAsync(
string username
);
Task<Domain.Entities.UserAccount?> GetUserByUsernameAsync(string username);
/// <summary>
/// Retrieves the active (non-revoked) credential for a user account.
@@ -64,4 +60,4 @@ public interface IAuthRepository
/// <param name="userAccountId">ID of the user account</param>
/// <param name="newPasswordHash">New hashed password</param>
Task RotateCredentialAsync(Guid userAccountId, string newPasswordHash);
}
}

View File

@@ -6,7 +6,7 @@
<RootNamespace>Infrastructure.Repository</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.3" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.4" />
<PackageReference
Include="Microsoft.SqlServer.Types"
Version="160.1000.6"
@@ -14,7 +14,7 @@
<PackageReference Include="System.Data.SqlClient" Version="4.9.0" />
<PackageReference
Include="Microsoft.Extensions.Configuration.Abstractions"
Version="8.0.0"
Version="9.0.0"
/>
</ItemGroup>
<ItemGroup>

View File

@@ -14,4 +14,4 @@ public abstract class Repository<T>(ISqlConnectionFactory connectionFactory)
}
protected abstract T MapToEntity(DbDataReader reader);
}
}

View File

@@ -46,4 +46,4 @@ public class DefaultSqlConnectionFactory(IConfiguration configuration)
{
return new SqlConnection(_connectionString);
}
}
}

View File

@@ -5,4 +5,4 @@ namespace Infrastructure.Repository.Sql;
public interface ISqlConnectionFactory
{
DbConnection CreateConnection();
}
}

View File

@@ -58,4 +58,4 @@ public static class SqlConnectionStringHelper
{
return BuildConnectionString("master");
}
}
}

View File

@@ -9,8 +9,6 @@ public interface IUserAccountRepository
);
Task UpdateAsync(Domain.Entities.UserAccount userAccount);
Task DeleteAsync(Guid id);
Task<Domain.Entities.UserAccount?> GetByUsernameAsync(
string username
);
Task<Domain.Entities.UserAccount?> GetByUsernameAsync(string username);
Task<Domain.Entities.UserAccount?> GetByEmailAsync(string email);
}
}

View File

@@ -8,9 +8,7 @@ public class UserAccountRepository(ISqlConnectionFactory connectionFactory)
: Repository<Domain.Entities.UserAccount>(connectionFactory),
IUserAccountRepository
{
public async Task<Domain.Entities.UserAccount?> GetByIdAsync(
Guid id
)
public async Task<Domain.Entities.UserAccount?> GetByIdAsync(Guid id)
{
await using var connection = await CreateConnection();
await using var command = connection.CreateCommand();
@@ -23,9 +21,10 @@ public class UserAccountRepository(ISqlConnectionFactory connectionFactory)
return await reader.ReadAsync() ? MapToEntity(reader) : null;
}
public async Task<
IEnumerable<Domain.Entities.UserAccount>
> GetAllAsync(int? limit, int? offset)
public async Task<IEnumerable<Domain.Entities.UserAccount>> GetAllAsync(
int? limit,
int? offset
)
{
await using var connection = await CreateConnection();
await using var command = connection.CreateCommand();
@@ -49,9 +48,7 @@ public class UserAccountRepository(ISqlConnectionFactory connectionFactory)
return users;
}
public async Task UpdateAsync(
Domain.Entities.UserAccount userAccount
)
public async Task UpdateAsync(Domain.Entities.UserAccount userAccount)
{
await using var connection = await CreateConnection();
await using var command = connection.CreateCommand();
@@ -115,9 +112,7 @@ public class UserAccountRepository(ISqlConnectionFactory connectionFactory)
{
return new Domain.Entities.UserAccount
{
UserAccountId = reader.GetGuid(
reader.GetOrdinal("UserAccountId")
),
UserAccountId = reader.GetGuid(reader.GetOrdinal("UserAccountId")),
Username = reader.GetString(reader.GetOrdinal("Username")),
FirstName = reader.GetString(reader.GetOrdinal("FirstName")),
LastName = reader.GetString(reader.GetOrdinal("LastName")),
@@ -126,9 +121,7 @@ public class UserAccountRepository(ISqlConnectionFactory connectionFactory)
UpdatedAt = reader.IsDBNull(reader.GetOrdinal("UpdatedAt"))
? null
: reader.GetDateTime(reader.GetOrdinal("UpdatedAt")),
DateOfBirth = reader.GetDateTime(
reader.GetOrdinal("DateOfBirth")
),
DateOfBirth = reader.GetDateTime(reader.GetOrdinal("DateOfBirth")),
Timer = reader.IsDBNull(reader.GetOrdinal("Timer"))
? null
: (byte[])reader["Timer"],
@@ -146,4 +139,4 @@ public class UserAccountRepository(ISqlConnectionFactory connectionFactory)
p.Value = value ?? DBNull.Value;
command.Parameters.Add(p);
}
}
}