Test
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
using Microsoft.Playwright;
|
||||
using Microsoft.Playwright.NUnit;
|
||||
|
||||
namespace Tests;
|
||||
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
[TestFixture]
|
||||
public class CardsPageTests : PageTest
|
||||
{
|
||||
private const string BaseUrl = "http://localhost:5256";
|
||||
|
||||
[Test]
|
||||
public async Task CardsPage_InspectRendering()
|
||||
{
|
||||
var consoleMessages = new List<string>();
|
||||
var pageErrors = new List<string>();
|
||||
|
||||
Page.Console += (_, msg) => Console.WriteLine($"[BROWSER {msg.Type}] {msg.Text}");
|
||||
Page.PageError += (_, err) => Console.WriteLine($"[BROWSER PAGE ERROR] {err}");
|
||||
|
||||
await Page.GotoAsync($"{BaseUrl}/cards");
|
||||
await Page.WaitForSelectorAsync("h1", new() { Timeout = 30000 });
|
||||
|
||||
// Give Blazor WASM a moment to render
|
||||
await Page.WaitForTimeoutAsync(3000);
|
||||
|
||||
var cardCells = Page.Locator(".card-cell");
|
||||
var count = await cardCells.CountAsync();
|
||||
Console.WriteLine($"Found {count} card cells");
|
||||
|
||||
var html = await Page.ContentAsync();
|
||||
File.WriteAllText("cards_page_dump.html", html);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
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
|
||||
{
|
||||
private IDatabaseConfigService _configService = null!;
|
||||
private IDatabaseService _dbService = null!;
|
||||
private string _testDbName = null!;
|
||||
|
||||
[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<DatabaseService>.Instance);
|
||||
|
||||
await _dbService.EnsureDatabaseCreatedAsync();
|
||||
}
|
||||
|
||||
[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 CardNotes_FullCrudLifecycle()
|
||||
{
|
||||
var cardName = "Test Hero " + Guid.NewGuid().ToString("N");
|
||||
var initialNote = new CardNote { CardName = cardName, Note = "Initial note text" };
|
||||
|
||||
// 1. Create
|
||||
var createResult = await _dbService.SaveCardNoteAsync(initialNote, isNew: true);
|
||||
Assert.That(createResult.Success, Is.True);
|
||||
|
||||
// 2. Read
|
||||
var fetched = await _dbService.GetCardNoteAsync(cardName);
|
||||
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, isNew: false);
|
||||
Assert.That(updateResult.Success, Is.True);
|
||||
|
||||
var updated = await _dbService.GetCardNoteAsync(cardName);
|
||||
Assert.That(updated!.Note, Is.EqualTo("Updated note text"));
|
||||
|
||||
// 4. Delete
|
||||
var deleteResult = await _dbService.DeleteCardNoteAsync(cardName);
|
||||
Assert.That(deleteResult.Success, Is.True);
|
||||
|
||||
var deleted = await _dbService.GetCardNoteAsync(cardName);
|
||||
Assert.That(deleted, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task DuplicateCardNote_ReturnsFailureResult()
|
||||
{
|
||||
var cardName = "Unique Card " + Guid.NewGuid().ToString("N");
|
||||
var note1 = new CardNote { CardName = cardName, Note = "First" };
|
||||
var note2 = new CardNote { CardName = cardName, Note = "Second" };
|
||||
|
||||
var res1 = await _dbService.SaveCardNoteAsync(note1, isNew: true);
|
||||
Assert.That(res1.Success, Is.True);
|
||||
|
||||
var res2 = await _dbService.SaveCardNoteAsync(note2, isNew: 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<string> { "Chronos, Time Shifter", "Temporal Rift", "Chronos, Time Shifter" },
|
||||
Divers = new List<string> { "Chronos, Time Shifter" }
|
||||
};
|
||||
|
||||
// 1. Create
|
||||
var createResult = await _dbService.SaveUserDeckAsync(deck, isNew: 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, isNew: 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, isNew: 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, isNew: 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.CardNotesSeeded, Is.GreaterThan(0));
|
||||
Assert.That(seedResult.UserDecksSeeded, Is.GreaterThan(0));
|
||||
Assert.That(seedResult.PasskeysSeeded, Is.GreaterThan(0));
|
||||
|
||||
var notes = await _dbService.GetCardNotesAsync();
|
||||
var decks = await _dbService.GetUserDecksAsync();
|
||||
var passkeys = await _dbService.GetPasskeysAsync();
|
||||
|
||||
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 notesAfterReset = await _dbService.GetCardNotesAsync();
|
||||
var decksAfterReset = await _dbService.GetUserDecksAsync();
|
||||
var passkeysAfterReset = await _dbService.GetPasskeysAsync();
|
||||
|
||||
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(6));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
namespace Tests;
|
||||
|
||||
using Microsoft.JSInterop;
|
||||
using Web.Services;
|
||||
|
||||
[TestFixture]
|
||||
public class NetworkStatusTests
|
||||
{
|
||||
private class MockJSRuntime : IJSRuntime
|
||||
{
|
||||
public bool ReturnOnlineValue { get; set; } = true;
|
||||
public bool InitializeCalled { get; private set; }
|
||||
public bool DisposeCalled { get; private set; }
|
||||
|
||||
public ValueTask<TValue> InvokeAsync<TValue>(string identifier, object?[]? args)
|
||||
{
|
||||
if (identifier == "networkStatus.initialize")
|
||||
{
|
||||
InitializeCalled = true;
|
||||
return new ValueTask<TValue>((TValue)(object)ReturnOnlineValue);
|
||||
}
|
||||
|
||||
return new ValueTask<TValue>(default(TValue)!);
|
||||
}
|
||||
|
||||
public ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, object?[]? args)
|
||||
{
|
||||
return InvokeAsync<TValue>(identifier, args);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NetworkStatusService_InitialState_IsOnlineByDefault()
|
||||
{
|
||||
var jsRuntime = new MockJSRuntime();
|
||||
using var service = new NetworkStatusService(jsRuntime);
|
||||
|
||||
Assert.That(service.IsOnline, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task NetworkStatusService_InitializeAsync_SetsStatusFromBrowser()
|
||||
{
|
||||
var jsRuntime = new MockJSRuntime { ReturnOnlineValue = false };
|
||||
var service = new NetworkStatusService(jsRuntime);
|
||||
|
||||
var eventFired = false;
|
||||
bool? reportedStatus = null;
|
||||
service.StatusChanged += status =>
|
||||
{
|
||||
eventFired = true;
|
||||
reportedStatus = status;
|
||||
};
|
||||
|
||||
await service.InitializeAsync();
|
||||
|
||||
Assert.That(jsRuntime.InitializeCalled, Is.True);
|
||||
Assert.That(service.IsOnline, Is.False);
|
||||
Assert.That(eventFired, Is.True);
|
||||
Assert.That(reportedStatus, Is.False);
|
||||
|
||||
await service.DisposeAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NetworkStatusService_SetOnlineStatus_FiresEventWhenChanged()
|
||||
{
|
||||
var jsRuntime = new MockJSRuntime();
|
||||
using var service = new NetworkStatusService(jsRuntime);
|
||||
|
||||
var eventCount = 0;
|
||||
bool? currentStatus = null;
|
||||
service.StatusChanged += status =>
|
||||
{
|
||||
eventCount++;
|
||||
currentStatus = status;
|
||||
};
|
||||
|
||||
// Transition to offline
|
||||
service.SetOnlineStatus(false);
|
||||
Assert.That(service.IsOnline, Is.False);
|
||||
Assert.That(eventCount, Is.EqualTo(1));
|
||||
Assert.That(currentStatus, Is.False);
|
||||
|
||||
// Setting same status again should not re-trigger event
|
||||
service.SetOnlineStatus(false);
|
||||
Assert.That(eventCount, Is.EqualTo(1));
|
||||
|
||||
// Transition back to online
|
||||
service.SetOnlineStatus(true);
|
||||
Assert.That(service.IsOnline, Is.True);
|
||||
Assert.That(eventCount, Is.EqualTo(2));
|
||||
Assert.That(currentStatus, Is.True);
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ public class TestSetup
|
||||
[OneTimeSetUp]
|
||||
public async Task BeforeAllTests()
|
||||
{
|
||||
Console.WriteLine("[DEBUG_LOG] Starting Cloud...");
|
||||
Console.WriteLine("[DEBUG_LOG] Starting Web server...");
|
||||
|
||||
// Find solution root
|
||||
var currentDir = AppContext.BaseDirectory;
|
||||
@@ -20,12 +20,12 @@ public class TestSetup
|
||||
|
||||
if (currentDir == null) throw new Exception("Could not find Chrono.sln to determine project path.");
|
||||
|
||||
var projectPath = Path.Combine(currentDir, "Cloud", "Cloud.csproj");
|
||||
var projectPath = Path.Combine(currentDir, "Web", "Web.csproj");
|
||||
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "dotnet",
|
||||
Arguments = $"run --project \"{projectPath}\" --no-build",
|
||||
Arguments = $"run --project \"{projectPath}\" --urls \"{ServerUrl}\" --no-build",
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
@@ -71,10 +71,10 @@ public class TestSetup
|
||||
if (!isReady)
|
||||
{
|
||||
_serverProcess.Kill(true);
|
||||
throw new Exception($"Cloud did not start within {timeout.TotalSeconds} seconds.");
|
||||
throw new Exception($"Web server did not start within {timeout.TotalSeconds} seconds.");
|
||||
}
|
||||
|
||||
Console.WriteLine("[DEBUG_LOG] Cloud is ready.");
|
||||
Console.WriteLine("[DEBUG_LOG] Web server is ready.");
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
@@ -82,7 +82,7 @@ public class TestSetup
|
||||
{
|
||||
if (_serverProcess != null && !_serverProcess.HasExited)
|
||||
{
|
||||
Console.WriteLine("[DEBUG_LOG] Stopping Cloud...");
|
||||
Console.WriteLine("[DEBUG_LOG] Stopping Web server...");
|
||||
_serverProcess.Kill(true);
|
||||
_serverProcess.Dispose();
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Model\Model.csproj"/>
|
||||
<ProjectReference Include="..\DatabaseTest\DatabaseTest.csproj"/>
|
||||
<ProjectReference Include="..\Web\Web.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user