Implements POST /account/update_name — writes Viewer.DisplayName and
returns an empty array per the prod capture. Includes TDD test covering
the persist side-effect.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
POST /tutorial/update echoes tutorial_step back and saves it to
Viewer.MissionData.TutorialState. is_skip=1 is handled server-side
by honoring whatever tutorial_step value the client sends (client
already sends 100 when skipping). Adds TutorialUpdateRequest DTO,
TutorialUpdateResponse DTO, injects SVSimDbContext into
TutorialController, and adds GetViewerTutorialStateAsync helper to
SVSimTestFactory.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Returns an empty data object (result_code=1 from middleware envelope).
Client uses SkipAllNetworkChecks so the response body is never read.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds the portal pair (shadowverse-portal.com deck-builder endpoints) as
anonymous routes on the app server. The translation middleware learns a new
[NoWireEncryption] attribute that skips both AES calls but keeps the rest of
the msgpack + base64 + envelope pipeline intact, matching prod's portal wire
profile observed in data_dumps/traffic_prod_deckcode.ndjson.
Storage is a 3-minute IMemoryCache — codes are anonymous-global, 4-char
lowercase alphanumeric (matches the shortest prod sample). Foil bit is
stripped on mint to match prod's normalize-on-encode behaviour.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add two spec-prescribed tests that the implementation plan missed:
- Create_proceeds_when_client_possession_snapshot_disagrees_with_server
- Protect_then_load_index_emits_is_protected_one
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Five wire-level integration tests covering: flag set, round-trip unset,
401 without auth, 400 unknown_card, and empty-object response shape.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
POST /tool/signup upserts a Viewer keyed on the resolved request UDID
(via the existing SID->UDID dict). Stashes the viewer on HttpContext so
the translation middleware emits viewer_id/short_udid/udid in
data_headers. Empty data payload -- all signup outputs flow in
data_headers per spec. Idempotent: repeat signups for the same UDID
return the existing viewer.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three endpoints + 9 integration tests. Captured-data-is-catalog: viewer's
achievement Level starts at MIN(Level) per type from the catalog (not 1),
so the assembler always has a row to render against.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds BattlePassSalesPeriodInfoDto, BattlePassProductDto, BattlePassItemListResponse DTOs,
GetItemListAsync on BattlePassService (one product if not premium + CanPurchase, empty if
already premium or off-season), and the /battle_pass/item_list controller action.
2 new integration tests; all 408 pass.
Also fixes BattlePassRepository.GetActiveSeasonAsync to use client-side
DateTimeOffset filtering (SQLite provider cannot translate DateTimeOffset
comparisons in LINQ WHERE/ORDER BY clauses).
Wire IBattlePassService.GetLevelCurveAsync into LoadController so /load/index
emits the 100-entry battle_pass_level_info dict when levels are seeded.
Also adds BattlePassRepository.ResetLevelCurveCache() to bust the process-level
static cache in tests that seed levels after earlier HTTP calls have primed it
with an empty list, and updates SVSimTestFactory.SeedGlobalsAsync + the stale
Index_surfaces_seeded_globals_after_bootstrap assertion accordingly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>