using Cloud.Models; using DatabaseTest.Models; using DatabaseTest.Services; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging.Abstractions; using Model; namespace Tests; [TestFixture] public class DatabaseServiceTests { [SetUp] public async Task Setup() { _testDbName = $"chrono_test_{Guid.NewGuid():N}"; var config = new ConfigurationBuilder().Build(); var configService = new DatabaseConfigService(config); configService.SetProvider(DatabaseProviderType.InMemory); configService.SetInMemoryDbName(_testDbName); _configService = configService; _dbService = new DatabaseService(configService, NullLogger.Instance); await _dbService.EnsureDatabaseCreatedAsync(); } private IDatabaseConfigService _configService = null!; private IDatabaseService _dbService = null!; private string _testDbName = null!; [Test] public async Task CheckHealth_InMemory_ReturnsConnected() { var health = await _dbService.CheckHealthAsync(); Assert.That(health.IsConnected, Is.True); Assert.That(health.ProviderType, Is.EqualTo(DatabaseProviderType.InMemory)); } [Test] public async Task Users_FullCrudLifecycle() { var username = "player_" + Guid.NewGuid().ToString("N")[..8]; var initialUser = new User { Username = username, Password = "password123", CreatedAt = DateTime.UtcNow }; // 1. Create var createResult = await _dbService.SaveUserAsync(initialUser, true); Assert.That(createResult.Success, Is.True); // 2. Read var fetched = await _dbService.GetUserAsync(username); Assert.That(fetched, Is.Not.Null); Assert.That(fetched!.Password, Is.EqualTo("password123")); // 3. Update fetched.Password = "newpassword456"; var updateResult = await _dbService.SaveUserAsync(fetched, false); Assert.That(updateResult.Success, Is.True); var updated = await _dbService.GetUserAsync(username); Assert.That(updated!.Password, Is.EqualTo("newpassword456")); // 4. Delete var deleteResult = await _dbService.DeleteUserAsync(username); Assert.That(deleteResult.Success, Is.True); var deleted = await _dbService.GetUserAsync(username); Assert.That(deleted, Is.Null); } [Test] public async Task CardNotes_FullCrudLifecycle() { var cardName = "Test Hero " + Guid.NewGuid().ToString("N"); var username = "user1"; var initialNote = new CardNote { Username = username, CardName = cardName, Note = "Initial note text" }; // 1. Create var createResult = await _dbService.SaveCardNoteAsync(initialNote, true); Assert.That(createResult.Success, Is.True); // 2. Read var fetched = await _dbService.GetCardNoteAsync(cardName, username); Assert.That(fetched, Is.Not.Null); Assert.That(fetched!.Note, Is.EqualTo("Initial note text")); // 3. Update fetched.Note = "Updated note text"; var updateResult = await _dbService.SaveCardNoteAsync(fetched, false); Assert.That(updateResult.Success, Is.True); var updated = await _dbService.GetCardNoteAsync(cardName, username); Assert.That(updated!.Note, Is.EqualTo("Updated note text")); // 4. Delete var deleteResult = await _dbService.DeleteCardNoteAsync(cardName, username); Assert.That(deleteResult.Success, Is.True); var deleted = await _dbService.GetCardNoteAsync(cardName, username); Assert.That(deleted, Is.Null); } [Test] public async Task DuplicateCardNote_ReturnsFailureResult() { var cardName = "Unique Card " + Guid.NewGuid().ToString("N"); var username = "user1"; var note1 = new CardNote { Username = username, CardName = cardName, Note = "First" }; var note2 = new CardNote { Username = username, CardName = cardName, Note = "Second" }; var res1 = await _dbService.SaveCardNoteAsync(note1, true); Assert.That(res1.Success, Is.True); var res2 = await _dbService.SaveCardNoteAsync(note2, true); Assert.That(res2.Success, Is.False); Assert.That(res2.Message, Does.Contain("already exists")); } [Test] public async Task UserDecks_FullCrudLifecycle_WithJsonCollections() { var deckId = Guid.NewGuid(); var deck = new UserDeck { Id = deckId, Name = "Control Deck", Season = "Season 1", Notes = "Strategic combo control", Cards = new List { "Chronos, Time Shifter", "Temporal Rift", "Chronos, Time Shifter" }, Divers = new List { "Chronos, Time Shifter" } }; // 1. Create var createResult = await _dbService.SaveUserDeckAsync(deck, true); Assert.That(createResult.Success, Is.True); // 2. Read var fetched = await _dbService.GetUserDeckAsync(deckId); Assert.That(fetched, Is.Not.Null); Assert.That(fetched!.Name, Is.EqualTo("Control Deck")); Assert.That(fetched.Cards.Count, Is.EqualTo(3)); Assert.That(fetched.Divers.Count, Is.EqualTo(1)); // 3. Update fetched.Cards.Add("Added Card"); fetched.Name = "Control Deck v2"; var updateResult = await _dbService.SaveUserDeckAsync(fetched, false); Assert.That(updateResult.Success, Is.True); var updated = await _dbService.GetUserDeckAsync(deckId); Assert.That(updated!.Name, Is.EqualTo("Control Deck v2")); Assert.That(updated.Cards.Count, Is.EqualTo(4)); // 4. Delete var deleteResult = await _dbService.DeleteUserDeckAsync(deckId); Assert.That(deleteResult.Success, Is.True); var deleted = await _dbService.GetUserDeckAsync(deckId); Assert.That(deleted, Is.Null); } [Test] public async Task PasskeyCredentials_FullCrudLifecycle_WithBinaryData() { var credId = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0xAA, 0xBB, 0xCC }; var pubKey = new byte[] { 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80 }; var userHandle = new byte[] { 0xDE, 0xAD, 0xBE, 0xEF }; var passkey = new PasskeyCredential { CredentialId = credId, PublicKey = pubKey, UserHandle = userHandle, AaGuid = Guid.NewGuid().ToString(), SignCount = 5 }; // 1. Create var createResult = await _dbService.SavePasskeyAsync(passkey, true); Assert.That(createResult.Success, Is.True); Assert.That(passkey.Id, Is.GreaterThan(0)); // 2. Read & Binary Verification var fetched = await _dbService.GetPasskeyAsync(passkey.Id); Assert.That(fetched, Is.Not.Null); Assert.That(fetched!.CredentialId, Is.EqualTo(credId)); Assert.That(fetched.PublicKey, Is.EqualTo(pubKey)); Assert.That(fetched.UserHandle, Is.EqualTo(userHandle)); Assert.That(fetched.SignCount, Is.EqualTo(5)); // 3. Update fetched.SignCount = 10; var updateResult = await _dbService.SavePasskeyAsync(fetched, false); Assert.That(updateResult.Success, Is.True); var updated = await _dbService.GetPasskeyAsync(passkey.Id); Assert.That(updated!.SignCount, Is.EqualTo(10)); // 4. Delete var deleteResult = await _dbService.DeletePasskeyAsync(passkey.Id); Assert.That(deleteResult.Success, Is.True); var deleted = await _dbService.GetPasskeyAsync(passkey.Id); Assert.That(deleted, Is.Null); } [Test] public async Task SeedAndResetDatabase_WorksCorrectly() { // 1. Seed var seedResult = await _dbService.SeedSampleDataAsync(); Assert.That(seedResult.Success, Is.True); Assert.That(seedResult.UsersSeeded, Is.GreaterThan(0)); Assert.That(seedResult.CardNotesSeeded, Is.GreaterThan(0)); Assert.That(seedResult.UserDecksSeeded, Is.GreaterThan(0)); Assert.That(seedResult.PasskeysSeeded, Is.GreaterThan(0)); var users = await _dbService.GetUsersAsync(); var notes = await _dbService.GetCardNotesAsync(); var decks = await _dbService.GetUserDecksAsync(); var passkeys = await _dbService.GetPasskeysAsync(); Assert.That(users.Count, Is.GreaterThan(0)); Assert.That(notes.Count, Is.GreaterThan(0)); Assert.That(decks.Count, Is.GreaterThan(0)); Assert.That(passkeys.Count, Is.GreaterThan(0)); // 2. Reset var resetResult = await _dbService.ResetDatabaseAsync(); Assert.That(resetResult.Success, Is.True); var usersAfterReset = await _dbService.GetUsersAsync(); var notesAfterReset = await _dbService.GetCardNotesAsync(); var decksAfterReset = await _dbService.GetUserDecksAsync(); var passkeysAfterReset = await _dbService.GetPasskeysAsync(); Assert.That(usersAfterReset, Is.Empty); Assert.That(notesAfterReset, Is.Empty); Assert.That(decksAfterReset, Is.Empty); Assert.That(passkeysAfterReset, Is.Empty); } [Test] public async Task RunAllTestsAsync_ExecutesAndPassesCoreTests() { var testResults = await _dbService.RunAllTestsAsync(); Assert.That(testResults, Is.Not.Empty); var failed = testResults.Where(t => t.Status == TestStatus.Failed).ToList(); Assert.That(failed, Is.Empty, () => $"Failed tests: {string.Join(", ", failed.Select(f => $"{f.Name}: {f.ErrorMessage}"))}"); var passedCount = testResults.Count(t => t.Status == TestStatus.Passed); Assert.That(passedCount, Is.GreaterThanOrEqualTo(7)); } [Test] public void DatabaseConfigService_WithPostgreSQL_Password_EnvironmentVariable_AppliesPassword() { var testPassword = "test_custom_env_password_123"; Environment.SetEnvironmentVariable("PostgreSQL_Password", testPassword); try { var config = new ConfigurationBuilder().Build(); var service = new DatabaseConfigService(config); Assert.That(service.ConnectionString, Does.Contain(testPassword)); } finally { Environment.SetEnvironmentVariable("PostgreSQL_Password", null); } } [Test] public void DatabaseConfigService_MasksPassword_InDisplay() { var config = new ConfigurationBuilder().Build(); var service = new DatabaseConfigService(config); service.SetConnectionString("Host=localhost;Database=chrono;Username=postgres;Password=secret_password;"); var description = service.GetActiveProviderDescription(); Assert.That(description, Does.Contain("Password=******")); Assert.That(description, Does.Not.Contain("secret_password")); } }