feat(pack): /pack/open supports type_detail=10 FREE_PACKS with per-campaign daily quota

This commit is contained in:
gamer147
2026-06-08 21:43:04 -04:00
parent 6c7e8ae8ad
commit 57d231cd56
2 changed files with 142 additions and 2 deletions

View File

@@ -777,4 +777,107 @@ public class PackControllerOpenTests
.FirstAsync(x => x.Id == viewerId);
Assert.That(v.GachaPointBalances, Is.Empty, "tutorial path must not accrue gacha points");
}
// ---------------- Free pack (type_detail=10) ----------------
/// <summary>
/// Seeds parent pack 80032 with a single type_detail=10 free child (gacha_id=780032)
/// for free-pack scenarios. Mirrors the shape from traffic_event_crate_free_pack.ndjson.
/// </summary>
private static async Task SeedFreePack(SVSimTestFactory f)
{
long[] cardIds;
using (var scope = f.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
int baseId = await db.CardSets.Where(s => s.Cards.Count > 0).Select(s => s.Id).FirstAsync();
cardIds = await db.CardSets.Where(s => s.Id == baseId)
.SelectMany(s => s.Cards).Select(c => c.Id).ToArrayAsync();
db.Packs.Add(new PackConfigEntry
{
Id = 80032, BasePackId = baseId, PackCategory = PackCategory.LegendCardPack,
CommenceDate = DateTime.UtcNow.AddDays(-1), CompleteDate = DateTime.UtcNow.AddDays(30),
GachaType = 1, GachaDetail = "free pack test", SleeveId = 5090001,
ChildGachas =
{
new PackChildGachaEntry
{
GachaId = 780032, TypeDetail = 10, Cost = 1, CardCount = 8,
DailyFreeGachaCount = 1, PurchaseLimitCount = 1,
FreeGachaCampaignId = 49, CampaignName = "Season Bonus",
},
},
});
await db.SaveChangesAsync();
}
await f.SeedPackDrawTableAsync(80032, cardIds);
}
private const string FreePackOpenBody =
"""{"viewer_id":"0","steam_id":0,"steam_session_ticket":"","parent_gacha_id":80032,"gacha_id":780032,"gacha_type":1,"pack_number":1,"exclude_card_ids":[]}""";
[Test]
public async Task Open_free_pack_returns_8_cards_and_spends_no_currency()
{
using var factory = new SVSimTestFactory();
long viewerId = await factory.SeedViewerAsync();
await SeedFreePack(factory);
using (var scope = factory.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
var v = await db.Viewers.FirstAsync(x => x.Id == viewerId);
v.Currency.Crystals = 0;
v.Currency.Rupees = 0;
await db.SaveChangesAsync();
}
using var client = factory.CreateAuthenticatedClient(viewerId);
var response = await client.PostAsync("/pack/open", JsonBody(FreePackOpenBody));
var body = await response.Content.ReadAsStringAsync();
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK), body);
using var doc = JsonDocument.Parse(body);
Assert.That(doc.RootElement.GetProperty("pack_list").GetArrayLength(), Is.EqualTo(8));
using var scope2 = factory.Services.CreateScope();
var db2 = scope2.ServiceProvider.GetRequiredService<SVSimDbContext>();
var v2 = await db2.Viewers.FirstAsync(x => x.Id == viewerId);
Assert.That(v2.Currency.Crystals, Is.EqualTo(0UL), "free pack must not debit crystals");
Assert.That(v2.Currency.Rupees, Is.EqualTo(0UL), "free pack must not debit rupees");
}
[Test]
public async Task Open_free_pack_rejects_second_claim_same_day()
{
using var factory = new SVSimTestFactory();
long viewerId = await factory.SeedViewerAsync();
await SeedFreePack(factory);
using var client = factory.CreateAuthenticatedClient(viewerId);
var first = await client.PostAsync("/pack/open", JsonBody(FreePackOpenBody));
Assert.That(first.StatusCode, Is.EqualTo(HttpStatusCode.OK), await first.Content.ReadAsStringAsync());
var second = await client.PostAsync("/pack/open", JsonBody(FreePackOpenBody));
Assert.That(second.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest),
"Second free-pack open same UTC day must be rejected.");
}
[Test]
public async Task Open_free_pack_records_free_pack_claim()
{
using var factory = new SVSimTestFactory();
long viewerId = await factory.SeedViewerAsync();
await SeedFreePack(factory);
using var client = factory.CreateAuthenticatedClient(viewerId);
var response = await client.PostAsync("/pack/open", JsonBody(FreePackOpenBody));
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
using var scope = factory.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
var v = await db.Viewers.Include(x => x.FreePackClaims).FirstAsync(x => x.Id == viewerId);
var claim = v.FreePackClaims.Single(c => c.FreeGachaCampaignId == 49);
Assert.That(claim.ClaimCount, Is.EqualTo(1));
Assert.That(claim.LastClaimedAt.Date, Is.EqualTo(DateTime.UtcNow.Date));
}
}