using System.Text.Json; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using SVSim.Database; using SVSim.Database.Enums; using SVSim.Database.Models; using SVSim.Database.Models.Config; using SVSim.Database.Repositories.Card; using SVSim.Database.Repositories.Collectibles; using SVSim.Database.Services; using SVSim.EmulatedEntrypoint.Services; using SVSim.UnitTests.Infrastructure; namespace SVSim.UnitTests.Services; public class ViewerEntitlementsTests { /// /// FreeplayConfig is in SVSim.Database so EnsureSeedDataAsync seeds a DB row with /// Enabled=false (ShippedDefaults). Since tier 1 (DB) wins, we mutate the seeded row /// to activate freeplay rather than relying on an IConfiguration override. /// private static void SetFreeplayEnabled(SVSimDbContext db, bool enabled, ulong currencyAmount = 99999, int cardCopies = 3) { var row = db.GameConfigs.First(s => s.SectionName == "Freeplay"); var cfg = JsonSerializer.Deserialize(row.ValueJson)!; cfg.Enabled = enabled; cfg.CurrencyAmount = currencyAmount; cfg.CardCopies = cardCopies; row.ValueJson = JsonSerializer.Serialize(cfg); db.SaveChanges(); } private static ViewerEntitlements Build(IServiceScope scope) { var db = scope.ServiceProvider.GetRequiredService(); return new ViewerEntitlements( new GameConfigService(db, new ConfigurationBuilder().Build()), new CardRepository(db), new CollectionRepository(db)); } [Test] public async Task Freeplay_off_reflects_real_balance_and_ownership() { using var factory = new SVSimTestFactory(); long viewerId = await factory.SeedViewerAsync(); using var scope = factory.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); // Freeplay is seeded as Enabled=false by default — no mutation needed. var viewer = await db.Viewers.Include(v => v.Cards).ThenInclude(c => c.Card).FirstAsync(v => v.Id == viewerId); viewer.Currency.Crystals = 7; var ent = Build(scope); Assert.That(ent.IsFreeplay, Is.False); Assert.That(ent.EffectiveBalance(viewer, SpendCurrency.Crystal), Is.EqualTo(7)); Assert.That(ent.OwnsCard(viewer, 99_999_999L), Is.False); Assert.That(ent.OwnsCosmetic(viewer, CosmeticType.Skin, 99_999), Is.False); } [Test] public async Task Freeplay_on_inflates_main_currencies_but_not_spot_points() { using var factory = new SVSimTestFactory(); long viewerId = await factory.SeedViewerAsync(); using var scope = factory.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); SetFreeplayEnabled(db, enabled: true, currencyAmount: 99999); var viewer = await db.Viewers.FirstAsync(v => v.Id == viewerId); viewer.Currency.SpotPoints = 5; var ent = Build(scope); Assert.That(ent.IsFreeplay, Is.True); Assert.That(ent.EffectiveBalance(viewer, SpendCurrency.Crystal), Is.EqualTo(99999)); Assert.That(ent.EffectiveBalance(viewer, SpendCurrency.Rupee), Is.EqualTo(99999)); Assert.That(ent.EffectiveBalance(viewer, SpendCurrency.RedEther), Is.EqualTo(99999)); Assert.That(ent.EffectiveBalance(viewer, SpendCurrency.SpotPoint), Is.EqualTo(5), "spot points are not a freeplay-inflated currency"); } [Test] public async Task Freeplay_on_treats_all_cards_and_cosmetics_as_owned() { using var factory = new SVSimTestFactory(); long viewerId = await factory.SeedViewerAsync(); using var scope = factory.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); SetFreeplayEnabled(db, enabled: true); var viewer = await db.Viewers.Include(v => v.Cards).ThenInclude(c => c.Card).FirstAsync(v => v.Id == viewerId); var ent = Build(scope); Assert.That(ent.OwnsCard(viewer, 99_999_999L), Is.True); Assert.That(ent.OwnsCosmetic(viewer, CosmeticType.Skin, 99_999), Is.True); } }