Per-player override supersede — Test Cases
Feature: a player:admin override replaces the admin baseline cap (ADMIN-ONLY supersede, PR #826).
player:ma / player:agent overrides do NOT supersede — they stack tightest.
Spec: plans/per-player-override-supersede.md.
Status: automated resolver coverage in backend/src/domain/exposureLimits/__tests__/resolveEffectiveLimits.test.ts; this doc adds the end-to-end matrix + dev1 manual steps.
Setup conventions
- One tree: Admin → MA → Agent → Player, currency INR.
- Limits written as
maxExposure / perBetfor one cell (e.g. Cricket, market-tier 1, tier 1). - "Default" at a level = no override row at that level (live-inherits the cascade).
- Effective = what the betslip shows and what placement enforces (both via
resolveEffectiveLimits).
Test matrix
| # | Setup | Effective (betslip + enforce) | Why |
|---|---|---|---|
| TC1 | admin→MA baseline 50/10; admin per-player override 500/100 | 500/100 | player:admin supersedes the admin baseline; override binds even though it's looser |
| TC2 | baseline 50/10 + admin per-player override row exists but has no cell for this dimension | uncapped at admin level (baseline gone) | supersede is presence-based: the override replaces the level; a silent cell = uncapped there |
| TC3 | as TC1, then switch the admin per-player override back to Default | 50/10 (baseline restored, live) | clearing deletes the override row → no supersede → cascade admin baseline binds again |
| TC3b | after TC3, admin edits the MA baseline to 80/20 | 80/20 | default player live-inherits the new baseline (no snapshot) |
| TC4 | baseline 50/10 + MA cap 500/100 + agent cap 500/100 + player:agent 300/60 | 50/10 | admin-only: player:agent does NOT supersede — it stacks; tightest of {admin 50, agent 500, player:agent 300} = 50/10 |
| TC5 | baseline 1000/200 + MA cap 500/100 + player:admin 800/150 | 500/100 | player:admin supersedes the baseline (→800/150) but the MA cap (tighter, different level) still binds |
| TC6 | the reported scenario: baseline 50/10 + MA 500/100 + agent 500/100 + player:agent 500/100 + player:admin 500/100 | 500/100 | player:admin drops the 50/10 baseline; surviving caps (MA 500, agent 500, player:agent 500) all 500/100 |
| TC6b | the admin-only regression check: baseline 50/10 + ma→agent 50/10 + agent→player 500/100 + player:admin 500/100 | 50/10 | player:admin drops the 50/10 baseline; the agent cap (carrying the ma→agent 50/10) survives and binds tightest = 50/10 (this is WHY supersede is admin-only) |
| TC7 | intermediate ancestor: Admin→MA→AgentA→AgentB→Player, AgentA cap 40/8, player:agent 500/100 | AgentA cap 40/8 still binds | player:agent does not supersede anything (admin-only); intermediate agent:<code> caps are never superseded regardless |
"View Limits" (config view) — separate, unchanged
| # | Action | Expected |
|---|---|---|
| TC8 | Open agent/admin "View Limits" for a player with a per-player override at that layer | shows the applied/configured cells (the preset/custom set at that layer), NOT the effective ceiling — by design (getPlayerConfiguredLimits) |
| TC9 | Place-time enforcement vs betslip display | identical — both run resolveEffectiveLimits (one function); a bet at the effective cap is accepted, one cent above is rejected |
Automated coverage (resolveEffectiveLimits.test.ts)
| Case | Covered by |
|---|---|
| TC1 | "player:admin override drops the admin baseline cap (override can be LOOSER and still bind)" |
| TC2 | "supersede is PRESENCE-based: baseline dropped even when the override is silent for this dimension" |
| TC3 | "Default / live-restore: with NO player:admin override the admin baseline binds again" |
| TC4 / TC6b | "admin-only: player:agent does NOT supersede — the direct agent cap STILL binds (stacks tightest)" + the supersededCascadeLabels admin-only unit tests |
Gaps left to manual / future automation: TC3b (live re-inherit after baseline edit), TC5/TC7 (multi-level + intermediate-ancestor), TC8/TC9 (config-view + place-time parity). TC5/TC7 are worth adding as resolver unit tests; TC9 is best as an order-placement integration test.
dev1 manual verification (deployed: feat/edit-preset-per-player)
- Admin → Downline → an MA's player. Note the betslip cap (baseline, e.g. 50/10).
- Admin sets a per-player admin-layer override of 500/100 on that player.
- Open the player betslip → cap shows 500/100 (TC1).
- Switch the override back to Default → betslip cap reverts to 50/10 (TC3).
- (TC5) Set the MA cap to 200/40, override to 500/100 → betslip shows 200/40 (tighter other level binds).
- (TC9) Try to place a bet whose projected win exceeds the effective cap → rejected with the binding layer; at/under → accepted.