using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using RpgRoller.Data; namespace RpgRoller.Tests; internal static class ServiceTestSupport { internal sealed class ServiceHarness : IDisposable { internal ServiceHarness(GameService service, SqliteDbContextFactory factory, string dbPath) { Service = service; m_Factory = factory; DbPath = dbPath; } public void Dispose() { m_Factory.Dispose(); } public RpgRollerDbContext CreateDbContext() { return m_Factory.CreateDbContext(); } public GameService Service { get; } public string DbPath { get; } private readonly SqliteDbContextFactory m_Factory; } internal sealed class RehashingPasswordHasher : IPasswordHasher { public string HashPassword(UserAccount user, string password) { HashCalls += 1; return $"hash:{HashCalls}:{password}"; } public PasswordVerificationResult VerifyHashedPassword(UserAccount user, string hashedPassword, string providedPassword) { return providedPassword == "Password123" ? PasswordVerificationResult.SuccessRehashNeeded : PasswordVerificationResult.Failed; } public int HashCalls { get; private set; } } private sealed class FixedDiceRoller(IEnumerable values) : IDiceRoller { public int Roll(int sides) { var next = m_Values.Count > 0 ? m_Values.Dequeue() : 1; return Math.Clamp(next, 1, sides); } private readonly Queue m_Values = new(values); } internal sealed class SqliteDbContextFactory(string dbPath) : IDbContextFactory, IDisposable { public RpgRollerDbContext CreateDbContext() { return new(m_Options); } public void Dispose() { } private readonly DbContextOptions m_Options = new DbContextOptionsBuilder().UseSqlite($"Data Source={dbPath}").Options; } internal static ServiceHarness CreateHarness(params int[] rollValues) { return CreateHarness(new PasswordHasher(), rollValues); } internal static ServiceHarness CreateHarness(IPasswordHasher passwordHasher, params int[] rollValues) { var dbPath = Path.Combine(Path.GetTempPath(), $"rpgroller-servicetests-{Guid.NewGuid():N}.db"); return CreateHarnessFromPath(dbPath, passwordHasher, rollValues); } internal static ServiceHarness CreateHarnessFromPath(string dbPath, params int[] rollValues) { return CreateHarnessFromPath(dbPath, new PasswordHasher(), rollValues); } internal static ServiceHarness CreateHarnessFromPath(string dbPath, IPasswordHasher passwordHasher, params int[] rollValues) { var options = new DbContextOptionsBuilder().UseSqlite($"Data Source={dbPath}").Options; using (var db = new RpgRollerDbContext(options)) { db.Database.EnsureCreated(); } var factory = new SqliteDbContextFactory(dbPath); var service = new GameService(factory, passwordHasher, new FixedDiceRoller(rollValues)); return new(service, factory, dbPath); } internal static T GetValue(ServiceResult result) { Assert.True(result.Succeeded); Assert.NotNull(result.Value); return result.Value!; } }