Commit Graph

2 Commits

Author SHA1 Message Date
gamer147
0f44a3482c fix(shops): smoke-test fallout from today's shop-cluster ship
Two issues caught in a real-client smoke run against the freshly
bootstrapped DB:

1. NRE in ShadowverseTranslationMiddleware for parameterless actions.
   Five new actions (Sleeve.Info, LeaderSkin.{Ids,Products},
   ItemPurchase.Info, SpotCardExchange.Top) took no parameters, but
   the middleware does
   `endpointDescriptor.Parameters.FirstOrDefault().ParameterType`
   to discover the request DTO — `FirstOrDefault` returns null on a
   zero-param action and `.ParameterType` NREs. Tests didn't catch it
   because the test client POSTs plain JSON, bypassing this path.
   Fix: each action now takes `BaseRequest _` matching the codebase
   convention (PuzzleController.Info, BattlePassController.Info, etc.),
   plus the middleware throws an actionable
   InvalidOperationException pointing at the convention so the next
   contributor doesn't repeat the mistake.

2. Leader-skin set sale showed up as "FREE / Claim" with empty
   Includes panel after the viewer bought every skin in a series
   with no configured bonus items. Root cause: ComputeRewardStatus
   emitted status=1 (not_got) when set_sales_status != 0 regardless
   of whether rewards.items was empty, and SkinPurchaseInfoTask.
   CreateSetSaleInfo flags `is_free=true` on (is_completed &&
   not_got). Prod ships status=0 when items is empty even with
   set_sales_status==1 — we now mirror that.

504 tests still pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 23:57:12 -04:00
gamer147
f237851e42 feat(sleeve): shop catalog + /sleeve/{info,buy} endpoints
Schema: SleeveShopSeries -> SleeveShopProducts -> Rewards (owned).
Migration AddSleeveShop creates 3 tables with FK cascade.

Importer mirrors BuildDeck pattern: find-or-create per series/product,
rewards replaced wholesale on rerun (owned collection). 10 series,
270 products imported from seeds/sleeve-shop.json.

Controller:
- /sleeve/info returns wire-faithful dict-keyed shape
  ({sleeve_list: {<series_id>: {product_info: {<product_id>: ...}}}}).
  is_purchased_product derived from viewer.Sleeves.Contains(sleeve_id).
- /sleeve/buy: sales_type 0=free / 1=crystal / 2=rupy / 3=ticket(501).
  Validates series_product mismatch, currency, already-purchased.
  Currency debited with post-state-total reward_list entry; cosmetic
  grants dispatched through RewardGrantService.ApplyAsync (covers
  sleeve + emblem bundled grants per product).

476 tests pass (was 466; +10 sleeve tests).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 22:09:45 -04:00