diff --git a/src/Core/Infrastructure/Infrastructure.Jwt/IJwtService.cs b/src/Core/Infrastructure/Infrastructure.Jwt/IJwtService.cs index 730a17c..969954a 100644 --- a/src/Core/Infrastructure/Infrastructure.Jwt/IJwtService.cs +++ b/src/Core/Infrastructure/Infrastructure.Jwt/IJwtService.cs @@ -3,4 +3,4 @@ namespace Service.Core.Jwt; public interface IJwtService { string GenerateJwt(Guid userId, string username, DateTime expiry); -} \ No newline at end of file +} diff --git a/src/Core/Infrastructure/Infrastructure.Jwt/Infrastructure.Jwt.csproj b/src/Core/Infrastructure/Infrastructure.Jwt/Infrastructure.Jwt.csproj index c7fc3ad..5461560 100644 --- a/src/Core/Infrastructure/Infrastructure.Jwt/Infrastructure.Jwt.csproj +++ b/src/Core/Infrastructure/Infrastructure.Jwt/Infrastructure.Jwt.csproj @@ -1,13 +1,19 @@ - - net10.0 - enable - enable - Service.Core.Jwt - + + net10.0 + enable + enable + Service.Core.Jwt + - - - - + + + + diff --git a/src/Core/Infrastructure/Infrastructure.Jwt/JwtService.cs b/src/Core/Infrastructure/Infrastructure.Jwt/JwtService.cs index 5e91144..41b2ed1 100644 --- a/src/Core/Infrastructure/Infrastructure.Jwt/JwtService.cs +++ b/src/Core/Infrastructure/Infrastructure.Jwt/JwtService.cs @@ -5,21 +5,27 @@ using Microsoft.IdentityModel.Tokens; using JwtRegisteredClaimNames = System.IdentityModel.Tokens.Jwt.JwtRegisteredClaimNames; namespace Service.Core.Jwt; + public class JwtService : IJwtService { - private readonly string? _secret = Environment.GetEnvironmentVariable("JWT_SECRET"); + private readonly string? _secret = Environment.GetEnvironmentVariable( + "JWT_SECRET" + ); + public string GenerateJwt(Guid userId, string username, DateTime expiry) { var handler = new JsonWebTokenHandler(); - var key = Encoding.UTF8.GetBytes(_secret ?? throw new InvalidOperationException("secret not set")); + var key = Encoding.UTF8.GetBytes( + _secret ?? throw new InvalidOperationException("secret not set") + ); // Base claims (always present) var claims = new List { new(JwtRegisteredClaimNames.Sub, userId.ToString()), new(JwtRegisteredClaimNames.UniqueName, username), - new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) + new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), }; var tokenDescriptor = new SecurityTokenDescriptor @@ -28,7 +34,8 @@ public class JwtService : IJwtService Expires = expiry, SigningCredentials = new SigningCredentials( new SymmetricSecurityKey(key), - SecurityAlgorithms.HmacSha256) + SecurityAlgorithms.HmacSha256 + ), }; return handler.CreateToken(tokenDescriptor); diff --git a/src/Core/Infrastructure/Infrastructure.PasswordHashing/IPasswordService.cs b/src/Core/Infrastructure/Infrastructure.PasswordHashing/IPasswordService.cs index 809fd8b..a5adb4a 100644 --- a/src/Core/Infrastructure/Infrastructure.PasswordHashing/IPasswordService.cs +++ b/src/Core/Infrastructure/Infrastructure.PasswordHashing/IPasswordService.cs @@ -4,4 +4,4 @@ public interface IPasswordService { public string Hash(string password); public bool Verify(string password, string stored); -} \ No newline at end of file +} diff --git a/src/Core/Infrastructure/Infrastructure.PasswordHashing/Infrastructure.PasswordHashing.csproj b/src/Core/Infrastructure/Infrastructure.PasswordHashing/Infrastructure.PasswordHashing.csproj index acf2e50..23b3af3 100644 --- a/src/Core/Infrastructure/Infrastructure.PasswordHashing/Infrastructure.PasswordHashing.csproj +++ b/src/Core/Infrastructure/Infrastructure.PasswordHashing/Infrastructure.PasswordHashing.csproj @@ -1,12 +1,15 @@ - - net10.0 - enable - enable - Service.Core.Password - + + net10.0 + enable + enable + Service.Core.Password + - - - + + + diff --git a/src/Core/Infrastructure/Infrastructure.PasswordHashing/PasswordService.cs b/src/Core/Infrastructure/Infrastructure.PasswordHashing/PasswordService.cs index c66b47c..5d024de 100644 --- a/src/Core/Infrastructure/Infrastructure.PasswordHashing/PasswordService.cs +++ b/src/Core/Infrastructure/Infrastructure.PasswordHashing/PasswordService.cs @@ -19,7 +19,7 @@ public class PasswordService : IPasswordService Salt = salt, DegreeOfParallelism = Math.Max(Environment.ProcessorCount, 1), MemorySize = ArgonMemoryKb, - Iterations = ArgonIterations + Iterations = ArgonIterations, }; var hash = argon2.GetBytes(HashSize); @@ -30,8 +30,12 @@ public class PasswordService : IPasswordService { try { - var parts = stored.Split(':', StringSplitOptions.RemoveEmptyEntries); - if (parts.Length != 2) return false; + var parts = stored.Split( + ':', + StringSplitOptions.RemoveEmptyEntries + ); + if (parts.Length != 2) + return false; var salt = Convert.FromBase64String(parts[0]); var expected = Convert.FromBase64String(parts[1]); @@ -41,7 +45,7 @@ public class PasswordService : IPasswordService Salt = salt, DegreeOfParallelism = Math.Max(Environment.ProcessorCount, 1), MemorySize = ArgonMemoryKb, - Iterations = ArgonIterations + Iterations = ArgonIterations, }; var actual = argon2.GetBytes(expected.Length); @@ -52,4 +56,4 @@ public class PasswordService : IPasswordService return false; } } -} \ No newline at end of file +} diff --git a/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Repositories/Auth/AuthRepository.cs b/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Repositories/Auth/AuthRepository.cs index efc3d8a..3b7c536 100644 --- a/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Repositories/Auth/AuthRepository.cs +++ b/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Repositories/Auth/AuthRepository.cs @@ -5,13 +5,12 @@ using Repository.Core.Sql; namespace Repository.Core.Repositories.Auth { - public class AuthRepository : Repository, IAuthRepository + public class AuthRepository + : Repository, + IAuthRepository { public AuthRepository(ISqlConnectionFactory connectionFactory) - : base(connectionFactory) - { - } - + : base(connectionFactory) { } public async Task RegisterUserAsync( string username, @@ -19,7 +18,8 @@ namespace Repository.Core.Repositories.Auth string lastName, string email, DateTime dateOfBirth, - string passwordHash) + string passwordHash + ) { await using var connection = await CreateConnection(); await using var command = connection.CreateCommand(); @@ -45,12 +45,13 @@ namespace Repository.Core.Repositories.Auth LastName = lastName, Email = email, DateOfBirth = dateOfBirth, - CreatedAt = DateTime.UtcNow + CreatedAt = DateTime.UtcNow, }; } - - public async Task GetUserByEmailAsync(string email) + public async Task GetUserByEmailAsync( + string email + ) { await using var connection = await CreateConnection(); await using var command = connection.CreateCommand(); @@ -63,8 +64,9 @@ namespace Repository.Core.Repositories.Auth return await reader.ReadAsync() ? MapToEntity(reader) : null; } - - public async Task GetUserByUsernameAsync(string username) + public async Task GetUserByUsernameAsync( + string username + ) { await using var connection = await CreateConnection(); await using var command = connection.CreateCommand(); @@ -77,7 +79,9 @@ namespace Repository.Core.Repositories.Auth return await reader.ReadAsync() ? MapToEntity(reader) : null; } - public async Task GetActiveCredentialByUserAccountIdAsync(Guid userAccountId) + public async Task GetActiveCredentialByUserAccountIdAsync( + Guid userAccountId + ) { await using var connection = await CreateConnection(); await using var command = connection.CreateCommand(); @@ -87,10 +91,15 @@ namespace Repository.Core.Repositories.Auth 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(Guid userAccountId, string newPasswordHash) + public async Task RotateCredentialAsync( + Guid userAccountId, + string newPasswordHash + ) { await using var connection = await CreateConnection(); await using var command = connection.CreateCommand(); @@ -106,11 +115,15 @@ namespace Repository.Core.Repositories.Auth /// /// Maps a data reader row to a UserAccount entity. /// - protected override Domain.Core.Entities.UserAccount MapToEntity(DbDataReader reader) + protected override Domain.Core.Entities.UserAccount MapToEntity( + DbDataReader reader + ) { return new Domain.Core.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")), @@ -119,10 +132,12 @@ namespace Repository.Core.Repositories.Auth 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"] + : (byte[])reader["Timer"], }; } @@ -133,22 +148,34 @@ namespace Repository.Core.Repositories.Auth { var entity = new UserCredential { - UserCredentialId = reader.GetGuid(reader.GetOrdinal("UserCredentialId")), - UserAccountId = reader.GetGuid(reader.GetOrdinal("UserAccountId")), + UserCredentialId = reader.GetGuid( + reader.GetOrdinal("UserCredentialId") + ), + UserAccountId = reader.GetGuid( + reader.GetOrdinal("UserAccountId") + ), Hash = reader.GetString(reader.GetOrdinal("Hash")), - CreatedAt = reader.GetDateTime(reader.GetOrdinal("CreatedAt")) + CreatedAt = reader.GetDateTime(reader.GetOrdinal("CreatedAt")), }; // Optional columns - var hasTimer = reader.GetSchemaTable()?.Rows - .Cast() - .Any(r => string.Equals(r["ColumnName"]?.ToString(), "Timer", - StringComparison.OrdinalIgnoreCase)) ?? - false; + var hasTimer = + reader + .GetSchemaTable() + ?.Rows.Cast() + .Any(r => + string.Equals( + r["ColumnName"]?.ToString(), + "Timer", + StringComparison.OrdinalIgnoreCase + ) + ) ?? false; if (hasTimer) { - entity.Timer = reader.IsDBNull(reader.GetOrdinal("Timer")) ? null : (byte[])reader["Timer"]; + entity.Timer = reader.IsDBNull(reader.GetOrdinal("Timer")) + ? null + : (byte[])reader["Timer"]; } return entity; @@ -157,7 +184,11 @@ namespace Repository.Core.Repositories.Auth /// /// Helper method to add a parameter to a database command. /// - private static void AddParameter(DbCommand command, string name, object? value) + private static void AddParameter( + DbCommand command, + string name, + object? value + ) { var p = command.CreateParameter(); p.ParameterName = name; diff --git a/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Repositories/Auth/IAuthRepository.cs b/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Repositories/Auth/IAuthRepository.cs index 2f96e96..2ebd49d 100644 --- a/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Repositories/Auth/IAuthRepository.cs +++ b/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Repositories/Auth/IAuthRepository.cs @@ -24,7 +24,8 @@ namespace Repository.Core.Repositories.Auth string lastName, string email, DateTime dateOfBirth, - string passwordHash); + string passwordHash + ); /// /// Retrieves a user account by email address (typically used for login). @@ -32,7 +33,9 @@ namespace Repository.Core.Repositories.Auth /// /// Email address to search for /// UserAccount if found, null otherwise - Task GetUserByEmailAsync(string email); + Task GetUserByEmailAsync( + string email + ); /// /// Retrieves a user account by username (typically used for login). @@ -40,7 +43,9 @@ namespace Repository.Core.Repositories.Auth /// /// Username to search for /// UserAccount if found, null otherwise - Task GetUserByUsernameAsync(string username); + Task GetUserByUsernameAsync( + string username + ); /// /// Retrieves the active (non-revoked) credential for a user account. @@ -48,7 +53,9 @@ namespace Repository.Core.Repositories.Auth /// /// ID of the user account /// Active UserCredential if found, null otherwise - Task GetActiveCredentialByUserAccountIdAsync(Guid userAccountId); + Task GetActiveCredentialByUserAccountIdAsync( + Guid userAccountId + ); /// /// Rotates a user's credential by invalidating all existing credentials and creating a new one. diff --git a/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Repositories/UserAccount/IUserAccountRepository.cs b/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Repositories/UserAccount/IUserAccountRepository.cs index d0bed2c..d578bb3 100644 --- a/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Repositories/UserAccount/IUserAccountRepository.cs +++ b/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Repositories/UserAccount/IUserAccountRepository.cs @@ -1,15 +1,19 @@ using Domain.Core.Entities; - namespace Repository.Core.Repositories.UserAccount { public interface IUserAccountRepository { Task GetByIdAsync(Guid id); - Task> GetAllAsync(int? limit, int? offset); + Task> GetAllAsync( + int? limit, + int? offset + ); Task UpdateAsync(Domain.Core.Entities.UserAccount userAccount); Task DeleteAsync(Guid id); - Task GetByUsernameAsync(string username); + Task GetByUsernameAsync( + string username + ); Task GetByEmailAsync(string email); } } diff --git a/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Repositories/UserAccount/UserAccountRepository.cs b/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Repositories/UserAccount/UserAccountRepository.cs index 548522b..7b4eb19 100644 --- a/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Repositories/UserAccount/UserAccountRepository.cs +++ b/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Repositories/UserAccount/UserAccountRepository.cs @@ -6,9 +6,12 @@ using Repository.Core.Sql; namespace Repository.Core.Repositories.UserAccount { public class UserAccountRepository(ISqlConnectionFactory connectionFactory) - : Repository(connectionFactory), IUserAccountRepository + : Repository(connectionFactory), + IUserAccountRepository { - public async Task GetByIdAsync(Guid id) + public async Task GetByIdAsync( + Guid id + ) { await using var connection = await CreateConnection(); await using var command = connection.CreateCommand(); @@ -21,7 +24,9 @@ namespace Repository.Core.Repositories.UserAccount return await reader.ReadAsync() ? MapToEntity(reader) : null; } - public async Task> GetAllAsync(int? limit, int? offset) + public async Task< + IEnumerable + > GetAllAsync(int? limit, int? offset) { await using var connection = await CreateConnection(); await using var command = connection.CreateCommand(); @@ -45,7 +50,9 @@ namespace Repository.Core.Repositories.UserAccount return users; } - public async Task UpdateAsync(Domain.Core.Entities.UserAccount userAccount) + public async Task UpdateAsync( + Domain.Core.Entities.UserAccount userAccount + ) { await using var connection = await CreateConnection(); await using var command = connection.CreateCommand(); @@ -73,7 +80,9 @@ namespace Repository.Core.Repositories.UserAccount await command.ExecuteNonQueryAsync(); } - public async Task GetByUsernameAsync(string username) + public async Task GetByUsernameAsync( + string username + ) { await using var connection = await CreateConnection(); await using var command = connection.CreateCommand(); @@ -86,7 +95,9 @@ namespace Repository.Core.Repositories.UserAccount return await reader.ReadAsync() ? MapToEntity(reader) : null; } - public async Task GetByEmailAsync(string email) + public async Task GetByEmailAsync( + string email + ) { await using var connection = await CreateConnection(); await using var command = connection.CreateCommand(); @@ -99,11 +110,15 @@ namespace Repository.Core.Repositories.UserAccount return await reader.ReadAsync() ? MapToEntity(reader) : null; } - protected override Domain.Core.Entities.UserAccount MapToEntity(DbDataReader reader) + protected override Domain.Core.Entities.UserAccount MapToEntity( + DbDataReader reader + ) { return new Domain.Core.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")), @@ -112,14 +127,20 @@ namespace Repository.Core.Repositories.UserAccount 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"] + : (byte[])reader["Timer"], }; } - private static void AddParameter(DbCommand command, string name, object? value) + private static void AddParameter( + DbCommand command, + string name, + object? value + ) { var p = command.CreateParameter(); p.ParameterName = name; diff --git a/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Repository.Core.csproj b/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Repository.Core.csproj index 1a3107a..9268fc3 100644 --- a/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Repository.Core.csproj +++ b/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Repository.Core.csproj @@ -12,7 +12,10 @@ Version="160.1000.6" /> - + diff --git a/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Sql/DefaultSqlConnectionFactory.cs b/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Sql/DefaultSqlConnectionFactory.cs index b8cdec6..e12e1b1 100644 --- a/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Sql/DefaultSqlConnectionFactory.cs +++ b/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Sql/DefaultSqlConnectionFactory.cs @@ -2,17 +2,21 @@ using System.Data.Common; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Configuration; - namespace Repository.Core.Sql { - public class DefaultSqlConnectionFactory(IConfiguration configuration) : ISqlConnectionFactory + public class DefaultSqlConnectionFactory(IConfiguration configuration) + : ISqlConnectionFactory { - private readonly string _connectionString = GetConnectionString(configuration); + private readonly string _connectionString = GetConnectionString( + configuration + ); private static string GetConnectionString(IConfiguration configuration) { // Check for full connection string first - var fullConnectionString = Environment.GetEnvironmentVariable("DB_CONNECTION_STRING"); + var fullConnectionString = Environment.GetEnvironmentVariable( + "DB_CONNECTION_STRING" + ); if (!string.IsNullOrEmpty(fullConnectionString)) { return fullConnectionString; diff --git a/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Sql/SqlConnectionStringHelper.cs b/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Sql/SqlConnectionStringHelper.cs index 7e50707..42a991c 100644 --- a/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Sql/SqlConnectionStringHelper.cs +++ b/src/Core/Infrastructure/Infrastructure.Repository/Repository.Core/Sql/SqlConnectionStringHelper.cs @@ -12,18 +12,30 @@ namespace Repository.Core.Sql /// A properly formatted SQL Server connection string. public static string BuildConnectionString(string? databaseName = null) { - var server = Environment.GetEnvironmentVariable("DB_SERVER") - ?? throw new InvalidOperationException("DB_SERVER environment variable is not set"); + var server = + Environment.GetEnvironmentVariable("DB_SERVER") + ?? throw new InvalidOperationException( + "DB_SERVER environment variable is not set" + ); - var dbName = databaseName + var dbName = + databaseName ?? Environment.GetEnvironmentVariable("DB_NAME") - ?? throw new InvalidOperationException("DB_NAME environment variable is not set"); + ?? throw new InvalidOperationException( + "DB_NAME environment variable is not set" + ); - var user = Environment.GetEnvironmentVariable("DB_USER") - ?? throw new InvalidOperationException("DB_USER environment variable is not set"); + var user = + Environment.GetEnvironmentVariable("DB_USER") + ?? throw new InvalidOperationException( + "DB_USER environment variable is not set" + ); - var password = Environment.GetEnvironmentVariable("DB_PASSWORD") - ?? throw new InvalidOperationException("DB_PASSWORD environment variable is not set"); + var password = + Environment.GetEnvironmentVariable("DB_PASSWORD") + ?? throw new InvalidOperationException( + "DB_PASSWORD environment variable is not set" + ); var builder = new SqlConnectionStringBuilder { @@ -32,7 +44,7 @@ namespace Repository.Core.Sql UserID = user, Password = password, TrustServerCertificate = true, - Encrypt = true + Encrypt = true, }; return builder.ConnectionString; diff --git a/src/Core/Infrastructure/Infrastructure.Repository/Repository.Tests/Auth/AuthRepository.test.cs b/src/Core/Infrastructure/Infrastructure.Repository/Repository.Tests/Auth/AuthRepository.test.cs index 91b6d4a..efd8eac 100644 --- a/src/Core/Infrastructure/Infrastructure.Repository/Repository.Tests/Auth/AuthRepository.test.cs +++ b/src/Core/Infrastructure/Infrastructure.Repository/Repository.Tests/Auth/AuthRepository.test.cs @@ -1,15 +1,15 @@ -using Apps72.Dev.Data.DbMocker; -using Repository.Core.Repositories.Auth; -using FluentAssertions; -using Repository.Tests.Database; using System.Data; +using Apps72.Dev.Data.DbMocker; +using FluentAssertions; +using Repository.Core.Repositories.Auth; +using Repository.Tests.Database; namespace Repository.Tests.Auth; public class AuthRepositoryTest { - private static AuthRepository CreateRepo(MockDbConnection conn) - => new(new TestConnectionFactory(conn)); + private static AuthRepository CreateRepo(MockDbConnection conn) => + new(new TestConnectionFactory(conn)); [Fact] public async Task RegisterUserAsync_CreatesUserWithCredential_ReturnsUserAccount() @@ -17,10 +17,12 @@ public class AuthRepositoryTest var expectedUserId = Guid.NewGuid(); var conn = new MockDbConnection(); - conn.Mocks - .When(cmd => cmd.CommandText == "USP_RegisterUser") - .ReturnsTable(MockTable.WithColumns(("UserAccountId", typeof(Guid))) - .AddRow(expectedUserId)); + conn.Mocks.When(cmd => cmd.CommandText == "USP_RegisterUser") + .ReturnsTable( + MockTable + .WithColumns(("UserAccountId", typeof(Guid))) + .AddRow(expectedUserId) + ); var repo = CreateRepo(conn); var result = await repo.RegisterUserAsync( @@ -47,29 +49,32 @@ public class AuthRepositoryTest var userId = Guid.NewGuid(); var conn = new MockDbConnection(); - conn.Mocks - .When(cmd => cmd.CommandText == "usp_GetUserAccountByEmail") - .ReturnsTable(MockTable.WithColumns( - ("UserAccountId", typeof(Guid)), - ("Username", typeof(string)), - ("FirstName", typeof(string)), - ("LastName", typeof(string)), - ("Email", typeof(string)), - ("CreatedAt", typeof(DateTime)), - ("UpdatedAt", typeof(DateTime?)), - ("DateOfBirth", typeof(DateTime)), - ("Timer", typeof(byte[])) - ).AddRow( - userId, - "emailuser", - "Email", - "User", - "emailuser@example.com", - DateTime.UtcNow, - null, - new DateTime(1990, 5, 15), - null - )); + conn.Mocks.When(cmd => cmd.CommandText == "usp_GetUserAccountByEmail") + .ReturnsTable( + MockTable + .WithColumns( + ("UserAccountId", typeof(Guid)), + ("Username", typeof(string)), + ("FirstName", typeof(string)), + ("LastName", typeof(string)), + ("Email", typeof(string)), + ("CreatedAt", typeof(DateTime)), + ("UpdatedAt", typeof(DateTime?)), + ("DateOfBirth", typeof(DateTime)), + ("Timer", typeof(byte[])) + ) + .AddRow( + userId, + "emailuser", + "Email", + "User", + "emailuser@example.com", + DateTime.UtcNow, + null, + new DateTime(1990, 5, 15), + null + ) + ); var repo = CreateRepo(conn); var result = await repo.GetUserByEmailAsync("emailuser@example.com"); @@ -87,8 +92,7 @@ public class AuthRepositoryTest { var conn = new MockDbConnection(); - conn.Mocks - .When(cmd => cmd.CommandText == "usp_GetUserAccountByEmail") + conn.Mocks.When(cmd => cmd.CommandText == "usp_GetUserAccountByEmail") .ReturnsTable(MockTable.Empty()); var repo = CreateRepo(conn); @@ -103,29 +107,34 @@ public class AuthRepositoryTest var userId = Guid.NewGuid(); var conn = new MockDbConnection(); - conn.Mocks - .When(cmd => cmd.CommandText == "usp_GetUserAccountByUsername") - .ReturnsTable(MockTable.WithColumns( - ("UserAccountId", typeof(Guid)), - ("Username", typeof(string)), - ("FirstName", typeof(string)), - ("LastName", typeof(string)), - ("Email", typeof(string)), - ("CreatedAt", typeof(DateTime)), - ("UpdatedAt", typeof(DateTime?)), - ("DateOfBirth", typeof(DateTime)), - ("Timer", typeof(byte[])) - ).AddRow( - userId, - "usernameuser", - "Username", - "User", - "username@example.com", - DateTime.UtcNow, - null, - new DateTime(1985, 8, 20), - null - )); + conn.Mocks.When(cmd => + cmd.CommandText == "usp_GetUserAccountByUsername" + ) + .ReturnsTable( + MockTable + .WithColumns( + ("UserAccountId", typeof(Guid)), + ("Username", typeof(string)), + ("FirstName", typeof(string)), + ("LastName", typeof(string)), + ("Email", typeof(string)), + ("CreatedAt", typeof(DateTime)), + ("UpdatedAt", typeof(DateTime?)), + ("DateOfBirth", typeof(DateTime)), + ("Timer", typeof(byte[])) + ) + .AddRow( + userId, + "usernameuser", + "Username", + "User", + "username@example.com", + DateTime.UtcNow, + null, + new DateTime(1985, 8, 20), + null + ) + ); var repo = CreateRepo(conn); var result = await repo.GetUserByUsernameAsync("usernameuser"); @@ -141,8 +150,9 @@ public class AuthRepositoryTest { var conn = new MockDbConnection(); - conn.Mocks - .When(cmd => cmd.CommandText == "usp_GetUserAccountByUsername") + conn.Mocks.When(cmd => + cmd.CommandText == "usp_GetUserAccountByUsername" + ) .ReturnsTable(MockTable.Empty()); var repo = CreateRepo(conn); @@ -158,21 +168,26 @@ public class AuthRepositoryTest var credentialId = Guid.NewGuid(); var conn = new MockDbConnection(); - conn.Mocks - .When(cmd => cmd.CommandText == "USP_GetActiveUserCredentialByUserAccountId") - .ReturnsTable(MockTable.WithColumns( - ("UserCredentialId", typeof(Guid)), - ("UserAccountId", typeof(Guid)), - ("Hash", typeof(string)), - ("CreatedAt", typeof(DateTime)), - ("Timer", typeof(byte[])) - ).AddRow( - credentialId, - userId, - "hashed_password_value", - DateTime.UtcNow, - null - )); + conn.Mocks.When(cmd => + cmd.CommandText == "USP_GetActiveUserCredentialByUserAccountId" + ) + .ReturnsTable( + MockTable + .WithColumns( + ("UserCredentialId", typeof(Guid)), + ("UserAccountId", typeof(Guid)), + ("Hash", typeof(string)), + ("CreatedAt", typeof(DateTime)), + ("Timer", typeof(byte[])) + ) + .AddRow( + credentialId, + userId, + "hashed_password_value", + DateTime.UtcNow, + null + ) + ); var repo = CreateRepo(conn); var result = await repo.GetActiveCredentialByUserAccountIdAsync(userId); @@ -189,8 +204,9 @@ public class AuthRepositoryTest var userId = Guid.NewGuid(); var conn = new MockDbConnection(); - conn.Mocks - .When(cmd => cmd.CommandText == "USP_GetActiveUserCredentialByUserAccountId") + conn.Mocks.When(cmd => + cmd.CommandText == "USP_GetActiveUserCredentialByUserAccountId" + ) .ReturnsTable(MockTable.Empty()); var repo = CreateRepo(conn); @@ -206,14 +222,14 @@ public class AuthRepositoryTest var newPasswordHash = "new_hashed_password"; var conn = new MockDbConnection(); - conn.Mocks - .When(cmd => cmd.CommandText == "USP_RotateUserCredential") + conn.Mocks.When(cmd => cmd.CommandText == "USP_RotateUserCredential") .ReturnsScalar(1); var repo = CreateRepo(conn); // Should not throw - var act = async () => await repo.RotateCredentialAsync(userId, newPasswordHash); + var act = async () => + await repo.RotateCredentialAsync(userId, newPasswordHash); await act.Should().NotThrowAsync(); } } diff --git a/src/Core/Infrastructure/Infrastructure.Repository/Repository.Tests/Repository.Tests.csproj b/src/Core/Infrastructure/Infrastructure.Repository/Repository.Tests/Repository.Tests.csproj index a1d64e0..d3755e2 100644 --- a/src/Core/Infrastructure/Infrastructure.Repository/Repository.Tests/Repository.Tests.csproj +++ b/src/Core/Infrastructure/Infrastructure.Repository/Repository.Tests/Repository.Tests.csproj @@ -15,9 +15,18 @@ - - - + + + @@ -28,4 +37,4 @@ - \ No newline at end of file + diff --git a/src/Core/Infrastructure/Infrastructure.Repository/Repository.Tests/UserAccount/UserAccountRepository.test.cs b/src/Core/Infrastructure/Infrastructure.Repository/Repository.Tests/UserAccount/UserAccountRepository.test.cs index 5cf9a64..65bdf6c 100644 --- a/src/Core/Infrastructure/Infrastructure.Repository/Repository.Tests/UserAccount/UserAccountRepository.test.cs +++ b/src/Core/Infrastructure/Infrastructure.Repository/Repository.Tests/UserAccount/UserAccountRepository.test.cs @@ -1,38 +1,50 @@ using Apps72.Dev.Data.DbMocker; -using Repository.Core.Repositories.UserAccount; using FluentAssertions; +using Repository.Core.Repositories.UserAccount; using Repository.Tests.Database; namespace Repository.Tests.UserAccount; public class UserAccountRepositoryTest { - private static UserAccountRepository CreateRepo(MockDbConnection conn) - => new(new TestConnectionFactory(conn)); + private static UserAccountRepository CreateRepo(MockDbConnection conn) => + new(new TestConnectionFactory(conn)); [Fact] public async Task GetByIdAsync_ReturnsRow_Mapped() { var conn = new MockDbConnection(); - conn.Mocks - .When(cmd => cmd.CommandText == "usp_GetUserAccountById") - .ReturnsTable(MockTable.WithColumns( - ("UserAccountId", typeof(Guid)), - ("Username", typeof(string)), - ("FirstName", typeof(string)), - ("LastName", typeof(string)), - ("Email", typeof(string)), - ("CreatedAt", typeof(DateTime)), - ("UpdatedAt", typeof(DateTime?)), - ("DateOfBirth", typeof(DateTime)), - ("Timer", typeof(byte[])) - ).AddRow(Guid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"), - "yerb", "Aaron", "Po", "aaronpo@example.com", - new DateTime(2020, 1, 1), null, - new DateTime(1990, 1, 1), null)); + conn.Mocks.When(cmd => cmd.CommandText == "usp_GetUserAccountById") + .ReturnsTable( + MockTable + .WithColumns( + ("UserAccountId", typeof(Guid)), + ("Username", typeof(string)), + ("FirstName", typeof(string)), + ("LastName", typeof(string)), + ("Email", typeof(string)), + ("CreatedAt", typeof(DateTime)), + ("UpdatedAt", typeof(DateTime?)), + ("DateOfBirth", typeof(DateTime)), + ("Timer", typeof(byte[])) + ) + .AddRow( + Guid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"), + "yerb", + "Aaron", + "Po", + "aaronpo@example.com", + new DateTime(2020, 1, 1), + null, + new DateTime(1990, 1, 1), + null + ) + ); var repo = CreateRepo(conn); - var result = await repo.GetByIdAsync(Guid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")); + var result = await repo.GetByIdAsync( + Guid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa") + ); result.Should().NotBeNull(); result!.Username.Should().Be("yerb"); @@ -43,48 +55,85 @@ public class UserAccountRepositoryTest public async Task GetAllAsync_ReturnsMultipleRows() { var conn = new MockDbConnection(); - conn.Mocks - .When(cmd => cmd.CommandText == "usp_GetAllUserAccounts") - .ReturnsTable(MockTable.WithColumns( - ("UserAccountId", typeof(Guid)), - ("Username", typeof(string)), - ("FirstName", typeof(string)), - ("LastName", typeof(string)), - ("Email", typeof(string)), - ("CreatedAt", typeof(DateTime)), - ("UpdatedAt", typeof(DateTime?)), - ("DateOfBirth", typeof(DateTime)), - ("Timer", typeof(byte[])) - ).AddRow(Guid.NewGuid(), "a", "A", "A", "a@example.com", DateTime.UtcNow, null, DateTime.UtcNow.Date, - null) - .AddRow(Guid.NewGuid(), "b", "B", "B", "b@example.com", DateTime.UtcNow, null, DateTime.UtcNow.Date, - null)); + conn.Mocks.When(cmd => cmd.CommandText == "usp_GetAllUserAccounts") + .ReturnsTable( + MockTable + .WithColumns( + ("UserAccountId", typeof(Guid)), + ("Username", typeof(string)), + ("FirstName", typeof(string)), + ("LastName", typeof(string)), + ("Email", typeof(string)), + ("CreatedAt", typeof(DateTime)), + ("UpdatedAt", typeof(DateTime?)), + ("DateOfBirth", typeof(DateTime)), + ("Timer", typeof(byte[])) + ) + .AddRow( + Guid.NewGuid(), + "a", + "A", + "A", + "a@example.com", + DateTime.UtcNow, + null, + DateTime.UtcNow.Date, + null + ) + .AddRow( + Guid.NewGuid(), + "b", + "B", + "B", + "b@example.com", + DateTime.UtcNow, + null, + DateTime.UtcNow.Date, + null + ) + ); var repo = CreateRepo(conn); var results = (await repo.GetAllAsync(null, null)).ToList(); results.Should().HaveCount(2); - results.Select(r => r.Username).Should().BeEquivalentTo(new[] { "a", "b" }); + results + .Select(r => r.Username) + .Should() + .BeEquivalentTo(new[] { "a", "b" }); } - [Fact] public async Task GetByUsername_ReturnsRow() { var conn = new MockDbConnection(); - conn.Mocks - .When(cmd => cmd.CommandText == "usp_GetUserAccountByUsername") - .ReturnsTable(MockTable.WithColumns( - ("UserAccountId", typeof(Guid)), - ("Username", typeof(string)), - ("FirstName", typeof(string)), - ("LastName", typeof(string)), - ("Email", typeof(string)), - ("CreatedAt", typeof(DateTime)), - ("UpdatedAt", typeof(DateTime?)), - ("DateOfBirth", typeof(DateTime)), - ("Timer", typeof(byte[])) - ).AddRow(Guid.NewGuid(), "lookupuser", "L", "U", "lookup@example.com", DateTime.UtcNow, null, - DateTime.UtcNow.Date, null)); + conn.Mocks.When(cmd => + cmd.CommandText == "usp_GetUserAccountByUsername" + ) + .ReturnsTable( + MockTable + .WithColumns( + ("UserAccountId", typeof(Guid)), + ("Username", typeof(string)), + ("FirstName", typeof(string)), + ("LastName", typeof(string)), + ("Email", typeof(string)), + ("CreatedAt", typeof(DateTime)), + ("UpdatedAt", typeof(DateTime?)), + ("DateOfBirth", typeof(DateTime)), + ("Timer", typeof(byte[])) + ) + .AddRow( + Guid.NewGuid(), + "lookupuser", + "L", + "U", + "lookup@example.com", + DateTime.UtcNow, + null, + DateTime.UtcNow.Date, + null + ) + ); var repo = CreateRepo(conn); var result = await repo.GetByUsernameAsync("lookupuser"); @@ -96,20 +145,32 @@ public class UserAccountRepositoryTest public async Task GetByEmail_ReturnsRow() { var conn = new MockDbConnection(); - conn.Mocks - .When(cmd => cmd.CommandText == "usp_GetUserAccountByEmail") - .ReturnsTable(MockTable.WithColumns( - ("UserAccountId", typeof(Guid)), - ("Username", typeof(string)), - ("FirstName", typeof(string)), - ("LastName", typeof(string)), - ("Email", typeof(string)), - ("CreatedAt", typeof(DateTime)), - ("UpdatedAt", typeof(DateTime?)), - ("DateOfBirth", typeof(DateTime)), - ("Timer", typeof(byte[])) - ).AddRow(Guid.NewGuid(), "byemail", "B", "E", "byemail@example.com", DateTime.UtcNow, null, - DateTime.UtcNow.Date, null)); + conn.Mocks.When(cmd => cmd.CommandText == "usp_GetUserAccountByEmail") + .ReturnsTable( + MockTable + .WithColumns( + ("UserAccountId", typeof(Guid)), + ("Username", typeof(string)), + ("FirstName", typeof(string)), + ("LastName", typeof(string)), + ("Email", typeof(string)), + ("CreatedAt", typeof(DateTime)), + ("UpdatedAt", typeof(DateTime?)), + ("DateOfBirth", typeof(DateTime)), + ("Timer", typeof(byte[])) + ) + .AddRow( + Guid.NewGuid(), + "byemail", + "B", + "E", + "byemail@example.com", + DateTime.UtcNow, + null, + DateTime.UtcNow.Date, + null + ) + ); var repo = CreateRepo(conn); var result = await repo.GetByEmailAsync("byemail@example.com");