using Ludarium.Application; using Ludarium.Domain; using Ludarium.Infrastructure; using Npgsql; using Testcontainers.PostgreSql; namespace Ludarium.IntegrationTests; /// /// What resolving a review leaves behind in the record. /// /// /// Ignoring non-game content wrote the state's name into the record, while the record stores the /// enum's ordinal. Every row it touched became undeserialisable, and the review list reads them all, /// so a single ignored file was enough to fail the whole Attention workspace from then on. /// public sealed class ReviewResolutionTests { [Fact] [Trait("Category", "Container")] public async Task AResolvedReviewCanStillBeRead() { if (!string.Equals(Environment.GetEnvironmentVariable("LUDARIUM_RUN_CONTAINER_TESTS"), "1", StringComparison.Ordinal)) return; await using var postgres = new PostgreSqlBuilder("postgres:16.15-bookworm") .WithDatabase("ludarium_review_test") .WithUsername("ludarium") .WithPassword("synthetic-test-password") .Build(); await postgres.StartAsync(); await using var dataSource = NpgsqlDataSource.Create(postgres.GetConnectionString()); var store = new PostgresStore(dataSource); await store.InitializeAsync(CancellationToken.None); var library = LibraryRoot.Create("Review fixture", Path.GetFullPath(Path.Combine(Path.GetTempPath(), "ludarium-review-library")), LibraryKind.Rom) with { IsReadOnly = true, IsAvailable = true }; await store.UpsertLibraryAsync(library, CancellationToken.None); var artifact = new Artifact(Guid.NewGuid(), library.Id, "roms/nes/readme.txt", 12, DateTimeOffset.UtcNow.AddDays(-1), ArtifactState.Present, MediaType.Unknown, Confidence.Low, null, DateTimeOffset.UtcNow.AddDays(-1), DateTimeOffset.UtcNow.AddDays(-1), null, null); await store.UpsertArtifactAsync(artifact, null, [], CancellationToken.None); await store.AddReviewAsync(new(Guid.NewGuid(), "Unknown content", Severity.Warning, ReviewState.Open, $"{{\"artifactId\":\"{artifact.Id}\"}}", DateTimeOffset.UtcNow), CancellationToken.None); await store.MarkArtifactIgnoredAsync(artifact.Id, artifact.Version + 1, new("content.role", "operator", "review", "1", Confidence.Deterministic, DateTimeOffset.UtcNow), CancellationToken.None); // Reading the list is the part that used to throw, for every review the catalog holds. var reviews = await store.ListReviewsAsync(CancellationToken.None); var resolved = Assert.Single(reviews); Assert.Equal(ReviewState.Resolved, resolved.State); Assert.Equal("Ignored non-game content", resolved.Resolution); } }