refactor(bootstrap): finalize load-index migration; GlobalsImporter is now a stub

Stage 9C of the bootstrap-seed-refactor:

- Add 6 seed DTOs for the card-id-keyed load-index tables (SpotCard,
  ReprintedCard, UnlimitedRestriction, LoadingExclusionCard, MaintenanceCard,
  FeatureMaintenance).
- Add CardListsImporter: idempotent upsert of the 6 tables, sharing one
  Cards FK set for orphan-warning. FeatureMaintenances clear-and-rewrites
  (synthetic ordinal Id; no natural key).
- Add RotationFlagUpdater: reads RotationConfig.RotationCardSetIds from the
  GameConfigs section (populated by RotationConfigImporter) and flips
  CardSet.IsInRotation to match.
- Add RotationConfig.RotationCardSetIds list property + wire it through
  RotationConfigImporter. No migration needed (sections are JSON blobs).
- RotationConfigImporter: use legacy local-kind DateTime parse for schedule
  windows so the JSON round-trip stays byte-equivalent to GlobalsImporter.
- Strip GlobalsImporter down to a no-op stub (Task 10 will delete it).
- Wire all 9 new importers into Program.cs and SVSimTestFactory.SeedGlobalsAsync,
  in the order RotationConfigImporter -> ... -> CardListsImporter -> RotationFlagUpdater.
- Delete prod-captures/load-index-2026-05-23.json.
- Add CardListsImporterTests covering each sub-table, idempotency,
  empty-seed handling, orphan-warning, and the clear-and-rewrite path.

Tests: 391 passing (382 baseline + 9 new).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-05-26 15:46:36 -04:00
parent 87d0001569
commit d14a0be2c8
15 changed files with 550 additions and 15057 deletions

View File

@@ -189,9 +189,21 @@ internal sealed class SVSimTestFactory : WebApplicationFactory<Program>
using var scope = Services.CreateScope();
var ctx = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
await new GlobalsImporter().ImportAllAsync(ctx, capturesDir);
// Per-importer seed pipeline runs alongside GlobalsImporter during the migration.
// Wired here so SeedGlobalsAsync callers (e.g. PracticeControllerTests) still see
// practice-opponent rows after the corresponding block was lifted out of GlobalsImporter.
// Load-index seed pipeline (Stage 9C). Mirrors the wiring in SVSim.Bootstrap.Program.cs:
// RotationConfigImporter must precede RotationFlagUpdater; CardListsImporter is
// ordered after the GameConfig importers for tidiness (no FK dependency).
await new RotationConfigImporter().ImportAsync(ctx, seedDir);
await new MyRotationImporter().ImportAsync(ctx, seedDir);
await new AvatarAbilityImporter().ImportAsync(ctx, seedDir);
await new ArenaSeasonImporter().ImportAsync(ctx, seedDir);
await new BattlePassImporter().ImportAsync(ctx, seedDir);
await new DailyLoginBonusImporter().ImportAsync(ctx, seedDir);
await new PreReleaseInfoImporter().ImportAsync(ctx, seedDir);
await new CardListsImporter().ImportAsync(ctx, seedDir);
await new RotationFlagUpdater().UpdateAsync(ctx);
// Per-importer seed pipeline for the rest of the load-index split.
await new PracticeOpponentImporter().ImportAsync(ctx, seedDir);
await new PaymentItemImporter().ImportAsync(ctx, seedDir);
var puzzleImporter = new PuzzleImporter();