Files
SVSimServer/SVSim.UnitTests/Importers/PackImporterStubsTests.cs
gamer147 869727cc06 feat(pack-draw): per-class card pools for rotation-starter packs
Add a nullable ClassId axis to PackDrawCardWeightEntry. Null means the
row applies to any draw (the existing behavior for non-RS packs); 1..8
restricts the row to a specific class draw on a RotationStarterCardPack.
Slot rates are class-invariant (verified against per-klan captures), so
PackDrawSlotRateEntry is unchanged.

PackOpenService.Draw now takes an optional classId parameter. With a
class supplied, the card pool is filtered to (ClassId == classId ||
ClassId == null); without one, class-tagged rows are excluded so a
mistaken non-RS call against a mixed table can't leak class-specific
cards.

PackController.Open lifts the 501 guard on RotationStarterCardPack and
plumbs class_id through. Class_id is now required (and validated 1..8)
for RS packs and rejected as 400 BadRequest on non-RS packs. The skin
overload (target_card_id) remains 501 — out of scope for this work.

Seed JSON regenerated end-to-end: 32 RS packs now carry 30,160 class-
tagged weight rows; 247 non-RS packs keep their existing ~87k null-class
rows. Slot rate fix from the upstream extractor change replaces the
~99.99% conditional-rate sums on 97xxx/93xxx packs with the page's
authoritative 1.5/6/25/67.5 distribution.

Migration AddPackDrawCardWeightClassId. End-to-end bootstrap verified
against fresh Postgres; pack 93025 reads back exactly 122/119/120/120/
120/121/118/119 weights for classes 1-8.

Tests: 3 new PackOpenServiceTests (class filter happy path, disjoint
pools, defensive null-class isolation); existing class_id-on-non-RS
controller test updated 501 → 400 BadRequest; one stub-importer test
corrected to match extractor-authored gacha_detail.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-27 19:35:08 -04:00

46 lines
1.8 KiB
C#

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using SVSim.Bootstrap.Importers;
using SVSim.Database;
using SVSim.UnitTests.Infrastructure;
namespace SVSim.UnitTests.Importers;
public class PackImporterStubsTests
{
private static string SeedDir => Path.Combine(AppContext.BaseDirectory, "Data", "seeds");
[Test]
public async Task Live_capture_overrides_stub_on_conflict()
{
using var factory = new SVSimTestFactory();
using var scope = factory.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
await new PackImporter().ImportAsync(db, SeedDir);
// 10001 is in both packs.json (no is_enabled -> defaults true) and pack-stubs.json
// (is_enabled=false). Live capture wins -> IsEnabled stays true and gacha_detail
// is the packs.json value, not "STUB CLC".
var live = await db.Packs.FirstAsync(p => p.Id == 10001);
Assert.That(live.IsEnabled, Is.True);
Assert.That(live.GachaDetail, Does.Not.Contain("STUB"));
}
[Test]
public async Task Stub_only_packs_are_inserted_with_IsEnabled_false()
{
using var factory = new SVSimTestFactory();
using var scope = factory.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
await new PackImporter().ImportAsync(db, SeedDir);
// 95001 is stub-only -> inserted with IsEnabled=false and the stub's gacha_detail
// (the pack's short_code from cardsetnametext.json, plumbed through by the extractor).
var stub = await db.Packs.FirstAsync(p => p.Id == 95001);
Assert.That(stub.IsEnabled, Is.False);
Assert.That(stub.GachaDetail, Is.EqualTo("7th"));
}
}