Final shop family. Schema additions:
- ViewerCurrency.SpotPoints (ulong) — new currency column on Viewers.
- SpotCardExchangeEntry — catalog (distinct from the pre-existing
SpotCardEntry, which is the /load/index rental-cost concept).
- ViewerSpotCardExchange — standalone composite-PK table tracking
(viewer, card, exchanged_at, is_pre_release_snapshot). Standalone
avoids cartesian-explode on viewer-graph reads.
RewardGrantService gains a SpotCardPoint=12 currency case mirroring
the RedEther/Crystal pattern. Doc comment refreshed; SpotCard=11 and
SpotCardOnlyLatestCardPack=13 remain unimplemented with explanatory
NotSupportedException — captures show emitters always use Card=5 with
the spot-card-specific id.
Controller:
- /top: emits exactly 9 clan buckets [{"1": [cards]}, ...] matching
prod's arbitrary single-key shape. exchange_status per-card (0=
available, 1=already-exchanged, 2=LimitOver after pre-release cap).
pre_relase_info WIRE TYPO PRESERVED ("relase" not "release").
- /exchange: server-authoritative price (client-supplied
exchange_point ignored); debits SpotPoints with post-state-total
reward_list entry; grants card via RewardGrantService.ApplyAsync
(cosmetic cascade included); persists ViewerSpotCardExchange row.
Insufficient points / already-exchanged / pre-release-limit all
return 400 without partial state.
LoadController now populates /load/index spot_point from
viewer.Currency.SpotPoints (was always 0).
PreReleaseLimit hardcoded to 2 matching capture; promote to GameConfig
when captures show variance.
504 tests pass (was 496; +8 spot-card-exchange tests).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
71 lines
2.6 KiB
C#
71 lines
2.6 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using SVSim.Bootstrap.Importers;
|
|
using SVSim.Database;
|
|
using SVSim.Database.Models;
|
|
using SVSim.UnitTests.Infrastructure;
|
|
|
|
namespace SVSim.UnitTests.Importers;
|
|
|
|
public class SpotCardExchangeImporterTests
|
|
{
|
|
private static string SeedDir => Path.Combine(AppContext.BaseDirectory, "Data", "seeds");
|
|
|
|
[Test]
|
|
public async Task Imports_catalog_from_seed_file()
|
|
{
|
|
using var factory = new SVSimTestFactory();
|
|
using var scope = factory.Services.CreateScope();
|
|
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
|
|
|
await new SpotCardExchangeImporter().ImportAsync(db, SeedDir);
|
|
|
|
var entries = await db.SpotCardExchangeCatalog.ToListAsync();
|
|
Assert.That(entries.Count, Is.GreaterThan(0));
|
|
|
|
// Spot-check: card 113041010 (class 0, exchange_point 3500, ts_rotation_id 10013)
|
|
var c = entries.FirstOrDefault(e => e.Id == 113041010);
|
|
Assert.That(c, Is.Not.Null);
|
|
Assert.That(c!.ClassId, Is.EqualTo(0));
|
|
Assert.That(c.ExchangePoint, Is.EqualTo(3500));
|
|
Assert.That(c.TsRotationId, Is.EqualTo(10013));
|
|
Assert.That(c.IsPreRelease, Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public async Task Is_idempotent_on_rerun()
|
|
{
|
|
using var factory = new SVSimTestFactory();
|
|
using var scope = factory.Services.CreateScope();
|
|
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
|
|
|
await new SpotCardExchangeImporter().ImportAsync(db, SeedDir);
|
|
int before = await db.SpotCardExchangeCatalog.CountAsync();
|
|
await new SpotCardExchangeImporter().ImportAsync(db, SeedDir);
|
|
int after = await db.SpotCardExchangeCatalog.CountAsync();
|
|
|
|
Assert.That(after, Is.EqualTo(before));
|
|
}
|
|
|
|
[Test]
|
|
public async Task Leaves_existing_rows_untouched_when_missing_from_seed()
|
|
{
|
|
using var factory = new SVSimTestFactory();
|
|
using var scope = factory.Services.CreateScope();
|
|
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
|
|
|
const long legacyId = 999_999_999L;
|
|
db.SpotCardExchangeCatalog.Add(new SpotCardExchangeEntry
|
|
{
|
|
Id = legacyId, ClassId = 9, ExchangePoint = 99999, TsRotationId = 1, IsEnabled = true,
|
|
});
|
|
await db.SaveChangesAsync();
|
|
|
|
await new SpotCardExchangeImporter().ImportAsync(db, SeedDir);
|
|
|
|
var legacy = await db.SpotCardExchangeCatalog.FindAsync(legacyId);
|
|
Assert.That(legacy, Is.Not.Null);
|
|
Assert.That(legacy!.ExchangePoint, Is.EqualTo(99999));
|
|
}
|
|
}
|