using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using SVSim.Database; using SVSim.Database.Models; using SVSim.UnitTests.Infrastructure; namespace SVSim.UnitTests.Models; public class GameConfigurationJsonbTests { [Test] public async Task DefaultSeed_populates_canonical_GameConfigRoot_defaults() { using var factory = new SVSimTestFactory(); using var scope = factory.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); var cfg = await db.GameConfigurations.FirstOrDefaultAsync(c => c.Id == "default"); Assert.That(cfg, Is.Not.Null, "default GameConfiguration row must exist (seeded via EnsureSeedDataAsync)"); Assert.That(cfg!.Config, Is.Not.Null, "Config must round-trip to non-null GameConfigRoot"); Assert.That(cfg.Config.DefaultGrants.Crystals, Is.EqualTo(50000UL), "pre-refactor default"); Assert.That(cfg.Config.DefaultGrants.Rupees, Is.EqualTo(50000UL), "pre-refactor default"); Assert.That(cfg.Config.DefaultGrants.Ether, Is.EqualTo(50000UL), "pre-refactor default"); Assert.That(cfg.Config.Player.MaxFriends, Is.EqualTo(20), "pre-refactor default"); Assert.That(cfg.Config.DefaultLoadout.SleeveId, Is.EqualTo(3000011), "pre-refactor default sleeve"); Assert.That(cfg.Config.PackRates.AnimatedRate, Is.EqualTo(0.08).Within(1e-9), "SV Classic default"); Assert.That(cfg.Config.PackRates.Default.Bronze, Is.EqualTo(0.6744).Within(1e-9)); // PerSlot is now a List keyed by Slot string (see Task 5 deviation note). var slot8 = cfg.Config.PackRates.PerSlot.FirstOrDefault(s => s.Slot == "8"); Assert.That(slot8, Is.Not.Null, "slot-8 default entry must be present"); Assert.That(slot8!.Silver, Is.EqualTo(0.7692).Within(1e-9)); } [Test] public async Task Mutation_then_save_then_reload_round_trips_through_jsonb() { using var factory = new SVSimTestFactory(); using (var scope = factory.Services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService(); var cfg = await db.GameConfigurations.FirstAsync(c => c.Id == "default"); cfg.Config.Rotation.TsRotationId = "99999"; cfg.Config.PackRates.AnimatedRate = 0.42; db.Entry(cfg).Property(c => c.Config).IsModified = true; await db.SaveChangesAsync(); } using (var scope = factory.Services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService(); var cfg = await db.GameConfigurations.FirstAsync(c => c.Id == "default"); Assert.That(cfg.Config.Rotation.TsRotationId, Is.EqualTo("99999")); Assert.That(cfg.Config.PackRates.AnimatedRate, Is.EqualTo(0.42).Within(1e-9)); } } [Test] public async Task GlobalsImporter_updates_Rotation_without_clobbering_other_subconfigs() { using var factory = new SVSimTestFactory(); await factory.SeedGlobalsAsync(); // imports load-index which has ts_rotation_id="10015" using var scope = factory.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); var cfg = await db.GameConfigurations.FirstAsync(c => c.Id == "default"); Assert.That(cfg.Config.Rotation.TsRotationId, Is.EqualTo("10015"), "GlobalsImporter should set Rotation.TsRotationId from the prod capture."); // PackRates is NOT in the load-index capture; must keep the seeded default unchanged. Assert.That(cfg.Config.PackRates.AnimatedRate, Is.EqualTo(0.08).Within(1e-9), "GlobalsImporter must not clobber PackRates while updating Rotation."); } }