using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using RpgRoller.Hosting; using RpgRoller.Data; namespace RpgRoller.Tests; public sealed class HostingCoverageTests { [Fact] public void AddRpgRollerCore_WithInMemoryConnectionString_RegistersCoreServices() { var services = new ServiceCollection(); var configuration = new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary { ["ConnectionStrings:RpgRoller"] = "Data Source=:memory:" }) .Build(); var environment = new TestWebHostEnvironment { ContentRootPath = Path.GetTempPath() }; services.AddRpgRollerCore(configuration, environment); Assert.Contains(services, d => d.ServiceType == typeof(RpgRoller.Services.IGameService)); Assert.Contains(services, d => d.ServiceType == typeof(RpgRoller.Services.IDiceRoller)); } [Fact] public void SqliteSchemaUpgrader_MigratesLegacySkillsSchema() { var dbPath = Path.Combine(Path.GetTempPath(), $"rpgroller-legacy-upgrade-{Guid.NewGuid():N}.db"); var connectionString = $"Data Source={dbPath}"; using (var connection = new SqliteConnection(connectionString)) { connection.Open(); using var command = connection.CreateCommand(); command.CommandText = """ CREATE TABLE "Users" ( "Id" TEXT NOT NULL CONSTRAINT "PK_Users" PRIMARY KEY, "Username" TEXT NOT NULL, "UsernameNormalized" TEXT NOT NULL, "PasswordHash" TEXT NOT NULL, "DisplayName" TEXT NOT NULL, "ActiveCharacterId" TEXT NULL ); CREATE TABLE "Sessions" ( "Token" TEXT NOT NULL CONSTRAINT "PK_Sessions" PRIMARY KEY, "UserId" TEXT NOT NULL, "CreatedAtUtc" TEXT NOT NULL ); CREATE TABLE "Campaigns" ( "Id" TEXT NOT NULL CONSTRAINT "PK_Campaigns" PRIMARY KEY, "GmUserId" TEXT NOT NULL, "Name" TEXT NOT NULL, "Ruleset" TEXT NOT NULL, "Version" INTEGER NOT NULL ); CREATE TABLE "Characters" ( "Id" TEXT NOT NULL CONSTRAINT "PK_Characters" PRIMARY KEY, "OwnerUserId" TEXT NOT NULL, "CampaignId" TEXT NOT NULL, "Name" TEXT NOT NULL ); CREATE TABLE "Skills" ( "Id" TEXT NOT NULL CONSTRAINT "PK_Skills" PRIMARY KEY, "CharacterId" TEXT NOT NULL, "Name" TEXT NOT NULL, "DiceRollDefinition" TEXT NOT NULL ); CREATE TABLE "RollLogEntries" ( "Id" TEXT NOT NULL CONSTRAINT "PK_RollLogEntries" PRIMARY KEY, "CampaignId" TEXT NOT NULL, "CharacterId" TEXT NOT NULL, "SkillId" TEXT NOT NULL, "RollerUserId" TEXT NOT NULL, "Visibility" TEXT NOT NULL, "Result" INTEGER NOT NULL, "Breakdown" TEXT NOT NULL, "TimestampUtc" TEXT NOT NULL ); """; _ = command.ExecuteNonQuery(); } var options = new DbContextOptionsBuilder() .UseSqlite(connectionString) .Options; using (var db = new RpgRollerDbContext(options)) { SqliteSchemaUpgrader.ApplyPendingChanges(db); } using var verifyConnection = new SqliteConnection(connectionString); verifyConnection.Open(); using var tableInfoCommand = verifyConnection.CreateCommand(); tableInfoCommand.CommandText = "PRAGMA table_info('Skills');"; using var tableInfoReader = tableInfoCommand.ExecuteReader(); var columns = new HashSet(StringComparer.OrdinalIgnoreCase); while (tableInfoReader.Read()) { columns.Add(tableInfoReader.GetString(1)); } Assert.Contains("WildDice", columns); Assert.Contains("AllowFumble", columns); using var historyCommand = verifyConnection.CreateCommand(); historyCommand.CommandText = "SELECT COUNT(*) FROM \"__EFMigrationsHistory\" WHERE \"MigrationId\" = '20260226084000_InitialSchema';"; var historyCount = Convert.ToInt32(historyCommand.ExecuteScalar()); Assert.Equal(1, historyCount); using var modelSyncHistoryCommand = verifyConnection.CreateCommand(); modelSyncHistoryCommand.CommandText = "SELECT COUNT(*) FROM \"__EFMigrationsHistory\" WHERE \"MigrationId\" = '20260226090000_ModelSync';"; var modelSyncHistoryCount = Convert.ToInt32(modelSyncHistoryCommand.ExecuteScalar()); Assert.Equal(1, modelSyncHistoryCount); } }