using Ludarium.Application; using Ludarium.Domain; using Ludarium.Infrastructure; using Npgsql; using Testcontainers.PostgreSql; namespace Ludarium.IntegrationTests; /// /// The title a scan derives from a file name. /// /// /// The extensions stripped from a derived title were listed separately in SQL from the extensions /// the identifier recognises, and the two drifted apart: a Mega Drive ROM was identified correctly /// and then presented as "Sonic Adventure md". Both now read one vocabulary, and a catalog that /// already carries the old spelling keeps its game rather than gaining a second one. /// public sealed class DiscoveredGameTitleTests { [Fact] [Trait("Category", "Container")] public async Task ACartridgeExtensionDoesNotSurviveIntoTheTitle() { 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_title_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("Title fixture", Path.GetFullPath(Path.Combine(Path.GetTempPath(), "ludarium-title-library")), LibraryKind.Rom) with { IsReadOnly = true, IsAvailable = true }; await store.UpsertLibraryAsync(library, CancellationToken.None); // The catalog already holds this game under the spelling the old query produced, discovered by // a scan rather than named by hand: a manually named game keeps the operator's title. await using (var legacy = dataSource.CreateCommand(""" INSERT INTO games(id,title,version,data,origin) VALUES(md5('ludarium:game:sonic adventure.md')::uuid, 'Sonic Adventure md', 1, jsonb_build_object('id', md5('ludarium:game:sonic adventure.md')::uuid, 'title', 'Sonic Adventure md', 'createdAt', now(), 'version', 1, 'origin', 0), 0) """)) await legacy.ExecuteNonQueryAsync(CancellationToken.None); foreach (var (path, platform) in new[] { ("roms/genesis/Sonic Adventure.md", "genesis"), ("roms/mastersystem/Alex Quest.sms", "mastersystem"), ("roms/gamegear/Pocket Racer.gg", "gamegear"), }) await store.UpsertArtifactAsync( new(Guid.NewGuid(), library.Id, path, 65_536, DateTimeOffset.UtcNow.AddDays(-1), ArtifactState.Present, MediaType.Rom, Confidence.Deterministic, null, DateTimeOffset.UtcNow.AddDays(-1), DateTimeOffset.UtcNow.AddDays(-1), null, platform), null, [], CancellationToken.None); await store.SynchronizeDiscoveredGamesAsync(library.Id, CancellationToken.None); var catalog = await store.SearchGamesAsync(new GameQuery { PageSize = 50 }, CancellationToken.None); var titles = catalog.Items.Select(game => game.Title).OrderBy(title => title, StringComparer.Ordinal).ToArray(); Assert.Equal(["Alex Quest", "Pocket Racer", "Sonic Adventure"], titles); // The pre-existing row was recognised as the same game, so no duplicate was left behind. Assert.Equal(3, catalog.Total); } }