test: add refractor integration test plan and preflight script
All checks were successful
Ruff Lint / lint (pull_request) Successful in 21s
All checks were successful
Ruff Lint / lint (pull_request) Successful in 21s
82-case in-app test plan for Playwright automation covering /refractor status, tier badges, post-game hooks, tier-up notifications, and edge cases. Preflight script for quick deploy verification via curl. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
24a59e9670
commit
190aa88d43
659
tests/refractor-integration-test-plan.md
Normal file
659
tests/refractor-integration-test-plan.md
Normal file
@ -0,0 +1,659 @@
|
||||
# Refractor System -- In-App Integration Test Plan
|
||||
|
||||
**Target environment**: Dev Discord server (Guild ID: `613880856032968834`)
|
||||
**Dev API**: `pddev.manticorum.com`
|
||||
**Bot container**: `paper-dynasty_discord-app_1` on `sba-bots`
|
||||
**Date created**: 2026-03-25
|
||||
|
||||
This test plan is designed for browser automation (Playwright against the Discord
|
||||
web client) or manual execution. Each test case specifies an exact slash command,
|
||||
the expected bot response, and pass/fail criteria.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Prerequisites](#prerequisites)
|
||||
2. [API Health Checks](#1-api-health-checks)
|
||||
3. [/refractor status -- Basic Functionality](#2-refractor-status----basic-functionality)
|
||||
4. [/refractor status -- Filters](#3-refractor-status----filters)
|
||||
5. [/refractor status -- Pagination](#4-refractor-status----pagination)
|
||||
6. [/refractor status -- Edge Cases and Errors](#5-refractor-status----edge-cases-and-errors)
|
||||
7. [Tier Badges on Card Embeds](#6-tier-badges-on-card-embeds)
|
||||
8. [Post-Game Hook -- Stat Accumulation and Evaluation](#7-post-game-hook----stat-accumulation-and-evaluation)
|
||||
9. [Tier-Up Notifications](#8-tier-up-notifications)
|
||||
10. [Cross-Command Badge Propagation](#9-cross-command-badge-propagation)
|
||||
11. [Known Gaps and Risks](#known-gaps-and-risks)
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before running these tests, ensure the following state exists:
|
||||
|
||||
### Bot State
|
||||
- [ ] Bot is online and healthy: `GET http://sba-bots:8080/health` returns 200
|
||||
- [ ] Refractor cog is loaded: check bot logs for `Loaded extension 'cogs.refractor'`
|
||||
- [ ] Test user has the `PD Players` role on the dev server
|
||||
|
||||
### Team and Card State
|
||||
- [ ] Test user owns a team (verify with `/team` or `/myteam`)
|
||||
- [ ] Team has at least 15 cards on its roster (needed for pagination tests)
|
||||
- [ ] At least one batter card, one SP card, and one RP card exist on the roster
|
||||
- [ ] At least one card has refractor state initialized in the database (the API must have a `RefractorCardState` row for this player+team pair)
|
||||
- [ ] Record the team ID, and at least 3 card IDs for use in tests:
|
||||
- `CARD_BATTER` -- a batter card ID with refractor state
|
||||
- `CARD_SP` -- a starting pitcher card ID with refractor state
|
||||
- `CARD_RP` -- a relief pitcher card ID with refractor state
|
||||
- `CARD_NO_STATE` -- a card ID that exists but has no RefractorCardState row
|
||||
- `CARD_INVALID` -- a card ID that does not exist (e.g. 999999)
|
||||
|
||||
### API State
|
||||
- [ ] Refractor tracks are seeded: `GET /api/v2/refractor/tracks` returns at least 3 tracks (batter, sp, rp)
|
||||
- [ ] At least one RefractorCardState row exists for a card on the test team
|
||||
- [ ] Verify manually: `GET /api/v2/refractor/cards/{CARD_BATTER}` returns a valid response
|
||||
|
||||
### Data Setup Script (run against dev API)
|
||||
If refractor state does not yet exist for test cards, trigger initialization:
|
||||
```bash
|
||||
# Force-evaluate a specific card to create its RefractorCardState
|
||||
curl -X POST "https://pddev.manticorum.com/api/v2/refractor/cards/${CARD_BATTER}/evaluate" \
|
||||
-H "Authorization: Bearer ${API_TOKEN}"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. API Health Checks
|
||||
|
||||
These are pre-flight checks run before any Discord interaction. They verify the
|
||||
API layer is functional. Execute via shell or Playwright network interception.
|
||||
|
||||
### REF-API-01: Bot health endpoint
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Command** | `curl -sf http://sba-bots:8080/health` |
|
||||
| **Expected** | HTTP 200, body contains health status |
|
||||
| **Pass criteria** | Non-empty 200 response |
|
||||
|
||||
### REF-API-02: Refractor tracks endpoint responds
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Command** | `curl -s "https://pddev.manticorum.com/api/v2/refractor/tracks" -H "Authorization: Bearer $TOKEN"` |
|
||||
| **Expected** | JSON with `count >= 3` and `items` array containing batter, sp, rp tracks |
|
||||
| **Pass criteria** | `count` field >= 3; each item has `card_type`, `t1_threshold`, `t2_threshold`, `t3_threshold`, `t4_threshold` |
|
||||
|
||||
### REF-API-03: Single card refractor state endpoint
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Command** | `curl -s "https://pddev.manticorum.com/api/v2/refractor/cards/${CARD_BATTER}" -H "Authorization: Bearer $TOKEN"` |
|
||||
| **Expected** | JSON with `player_id`, `team_id`, `current_tier`, `current_value`, `fully_evolved`, `next_threshold`, `track` |
|
||||
| **Pass criteria** | `current_tier` is an integer 0-4; `track` object exists with threshold fields |
|
||||
|
||||
### REF-API-04: Card state 404 for nonexistent card
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Command** | `curl -s -o /dev/null -w "%{http_code}" "https://pddev.manticorum.com/api/v2/refractor/cards/999999" -H "Authorization: Bearer $TOKEN"` |
|
||||
| **Expected** | HTTP 404 |
|
||||
| **Pass criteria** | Status code is exactly 404 |
|
||||
|
||||
### REF-API-05: Old evolution endpoint removed
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Command** | `curl -s -o /dev/null -w "%{http_code}" "https://pddev.manticorum.com/api/v2/evolution/cards/1" -H "Authorization: Bearer $TOKEN"` |
|
||||
| **Expected** | HTTP 404 |
|
||||
| **Pass criteria** | Status code is 404 (confirms evolution->refractor rename is complete) |
|
||||
|
||||
---
|
||||
|
||||
## 2. /refractor status -- Basic Functionality
|
||||
|
||||
### REF-01: Basic status command (no filters)
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | Invoke /refractor status with no arguments; verify the embed appears |
|
||||
| **Discord command** | `/refractor status` |
|
||||
| **Expected result** | An ephemeral embed with: |
|
||||
| | - Title: `{team short name} Refractor Status` |
|
||||
| | - Purple embed color (hex `0x6F42C1` = RGB 111, 66, 193) |
|
||||
| | - Description containing card entries (player names, progress bars, tier labels) |
|
||||
| | - Footer: `Page 1/N . M card(s) total` |
|
||||
| **Pass criteria** | 1. Embed title contains team short name and "Refractor Status" |
|
||||
| | 2. Embed color is purple (`#6F42C1`) |
|
||||
| | 3. At least one card entry is visible in the description |
|
||||
| | 4. Footer contains page number and total card count |
|
||||
| | 5. Response is ephemeral (only visible to the invoking user) |
|
||||
|
||||
### REF-02: Card entry format -- batter
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | Verify a batter card entry has correct format in the status embed |
|
||||
| **Discord command** | `/refractor status card_type:batter` |
|
||||
| **Expected result** | Each entry in the embed follows this pattern: |
|
||||
| | Line 1: `**{badge} Player Name** (Tier Label)` |
|
||||
| | Line 2: `[====------] value/threshold (PA+TB x 2) -- T{n} -> T{n+1}` |
|
||||
| **Pass criteria** | 1. Player name appears in bold (`**...**`) |
|
||||
| | 2. Tier label is one of: Base Card, Base Chrome, Refractor, Gold Refractor, Superfractor |
|
||||
| | 3. Progress bar has format `[====------]` (10 chars of `=` and `-` between brackets) |
|
||||
| | 4. Formula label shows `PA+TB x 2` for batters |
|
||||
| | 5. Tier progression arrow shows `T{current} -> T{next}` |
|
||||
|
||||
### REF-03: Card entry format -- starting pitcher
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | Verify SP cards show the correct formula label |
|
||||
| **Discord command** | `/refractor status card_type:sp` |
|
||||
| **Expected result** | SP card entries show `IP+K` as the formula label |
|
||||
| **Pass criteria** | Formula label in progress line is `IP+K` (not `PA+TB x 2`) |
|
||||
|
||||
### REF-04: Card entry format -- relief pitcher
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | Verify RP cards show the correct formula label |
|
||||
| **Discord command** | `/refractor status card_type:rp` |
|
||||
| **Expected result** | RP card entries show `IP+K` as the formula label |
|
||||
| **Pass criteria** | Formula label in progress line is `IP+K` |
|
||||
|
||||
### REF-05: Tier badge display per tier
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | Verify correct tier badges appear for each tier level |
|
||||
| **Discord command** | `/refractor status` (examine entries across tiers) |
|
||||
| **Expected result** | Badge mapping: |
|
||||
| | T0 (Base Card): no badge prefix |
|
||||
| | T1 (Base Chrome): `[BC]` prefix |
|
||||
| | T2 (Refractor): `[R]` prefix |
|
||||
| | T3 (Gold Refractor): `[GR]` prefix |
|
||||
| | T4 (Superfractor): `[SF]` prefix |
|
||||
| **Pass criteria** | Each card's badge matches its tier per the mapping above |
|
||||
|
||||
### REF-06: Fully evolved card display
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | Verify T4 (Superfractor) cards show the fully evolved indicator |
|
||||
| **Discord command** | `/refractor status tier:4` |
|
||||
| **Expected result** | Fully evolved cards show: |
|
||||
| | Line 1: `**[SF] Player Name** (Superfractor)` |
|
||||
| | Line 2: `[==========] FULLY EVOLVED (star)` |
|
||||
| **Pass criteria** | 1. Progress bar is completely filled (`[==========]`) |
|
||||
| | 2. Text says "FULLY EVOLVED" with a star character |
|
||||
| | 3. No tier progression arrow (no `->` text) |
|
||||
|
||||
---
|
||||
|
||||
## 3. /refractor status -- Filters
|
||||
|
||||
### REF-10: Filter by card_type=batter
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Discord command** | `/refractor status card_type:batter` |
|
||||
| **Expected result** | Only batter cards appear; formula label is `PA+TB x 2` on all entries |
|
||||
| **Pass criteria** | No entries show `IP+K` formula label |
|
||||
|
||||
### REF-11: Filter by card_type=sp
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Discord command** | `/refractor status card_type:sp` |
|
||||
| **Expected result** | Only SP cards appear; formula label is `IP+K` on all entries |
|
||||
| **Pass criteria** | No entries show `PA+TB x 2` formula label |
|
||||
|
||||
### REF-12: Filter by card_type=rp
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Discord command** | `/refractor status card_type:rp` |
|
||||
| **Expected result** | Only RP cards appear; formula label is `IP+K` on all entries |
|
||||
| **Pass criteria** | No entries show `PA+TB x 2` formula label |
|
||||
|
||||
### REF-13: Filter by tier=0
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Discord command** | `/refractor status tier:0` |
|
||||
| **Expected result** | Only T0 (Base Card) entries appear; no tier badges on any entry |
|
||||
| **Pass criteria** | No entries contain `[BC]`, `[R]`, `[GR]`, or `[SF]` badges |
|
||||
|
||||
### REF-14: Filter by tier=1
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Discord command** | `/refractor status tier:1` |
|
||||
| **Expected result** | Only T1 entries appear; all show `[BC]` badge and `(Base Chrome)` label |
|
||||
| **Pass criteria** | Every entry contains `[BC]` and `(Base Chrome)` |
|
||||
|
||||
### REF-15: Filter by tier=4
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Discord command** | `/refractor status tier:4` |
|
||||
| **Expected result** | Only T4 entries appear; all show `[SF]` badge and `FULLY EVOLVED` |
|
||||
| **Pass criteria** | Every entry contains `[SF]`, `(Superfractor)`, and `FULLY EVOLVED` |
|
||||
|
||||
### REF-16: Filter by progress=close
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Discord command** | `/refractor status progress:close` |
|
||||
| **Expected result** | Only cards within 80% of their next tier threshold appear |
|
||||
| **Pass criteria** | 1. For each entry, the formula_value/next_threshold ratio >= 0.8 |
|
||||
| | 2. No fully evolved (T4) cards appear |
|
||||
| | 3. If no cards qualify, message says "No cards are currently close to a tier advancement." |
|
||||
|
||||
### REF-17: Combined filter -- tier + card_type
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Discord command** | `/refractor status card_type:batter tier:1` |
|
||||
| **Expected result** | Only T1 batter cards appear |
|
||||
| **Pass criteria** | All entries have `[BC]` badge AND `PA+TB x 2` formula label |
|
||||
|
||||
### REF-18: Combined filter -- tier=4 + progress=close (empty result)
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Discord command** | `/refractor status tier:4 progress:close` |
|
||||
| **Expected result** | Message: "No cards are currently close to a tier advancement." |
|
||||
| **Pass criteria** | No embed appears; plain text message about no close cards |
|
||||
| **Notes** | T4 cards are fully evolved and cannot be "close" to any threshold |
|
||||
|
||||
### REF-19: Filter by season
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Discord command** | `/refractor status season:1` |
|
||||
| **Expected result** | Only cards from season 1 appear (or empty message if none exist) |
|
||||
| **Pass criteria** | Response is either a valid embed or the "no data" message |
|
||||
|
||||
---
|
||||
|
||||
## 4. /refractor status -- Pagination
|
||||
|
||||
### REF-20: Page 1 shows first 10 cards
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Discord command** | `/refractor status page:1` |
|
||||
| **Expected result** | Embed shows up to 10 card entries; footer says `Page 1/N` |
|
||||
| **Pass criteria** | 1. At most 10 card entries in the description |
|
||||
| | 2. Footer page number is `1` |
|
||||
| | 3. Total pages `N` matches `ceil(total_cards / 10)` |
|
||||
|
||||
### REF-21: Page 2 shows next batch
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Discord command** | `/refractor status page:2` |
|
||||
| **Expected result** | Embed shows cards 11-20; footer says `Page 2/N` |
|
||||
| **Pass criteria** | 1. Different cards than page 1 |
|
||||
| | 2. Footer shows `Page 2/N` |
|
||||
| **Prerequisite** | Team has > 10 cards with refractor state |
|
||||
|
||||
### REF-22: Page beyond total clamps to last page
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Discord command** | `/refractor status page:999` |
|
||||
| **Expected result** | Embed shows the last page of cards |
|
||||
| **Pass criteria** | 1. Footer shows `Page N/N` (last page) |
|
||||
| | 2. No error or empty response |
|
||||
|
||||
### REF-23: Page 0 clamps to page 1
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Discord command** | `/refractor status page:0` |
|
||||
| **Expected result** | Embed shows page 1 |
|
||||
| **Pass criteria** | Footer shows `Page 1/N` |
|
||||
|
||||
---
|
||||
|
||||
## 5. /refractor status -- Edge Cases and Errors
|
||||
|
||||
### REF-30: User with no team
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | Invoke command as a user who does not own a team |
|
||||
| **Discord command** | `/refractor status` (from a user with no team) |
|
||||
| **Expected result** | Plain text message: "You don't have a team. Sign up with /newteam first." |
|
||||
| **Pass criteria** | 1. No embed appears |
|
||||
| | 2. Message mentions `/newteam` |
|
||||
| | 3. Response is ephemeral |
|
||||
|
||||
### REF-31: Team with no refractor data
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | Invoke command for a team that has cards but no RefractorCardState rows |
|
||||
| **Discord command** | `/refractor status` (from a team with no refractor initialization) |
|
||||
| **Expected result** | Plain text message: "No refractor data found for your team." |
|
||||
| **Pass criteria** | 1. No embed appears |
|
||||
| | 2. Message mentions "no refractor data" |
|
||||
|
||||
### REF-32: Invalid card_type filter
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Discord command** | `/refractor status card_type:xyz` |
|
||||
| **Expected result** | Empty result -- "No refractor data found for your team." |
|
||||
| **Pass criteria** | No crash; clean empty-state message |
|
||||
|
||||
### REF-33: Negative tier filter
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Discord command** | `/refractor status tier:-1` |
|
||||
| **Expected result** | Empty result or Discord input validation rejection |
|
||||
| **Pass criteria** | No crash; either a clean message or Discord prevents submission |
|
||||
|
||||
### REF-34: Negative page number
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Discord command** | `/refractor status page:-5` |
|
||||
| **Expected result** | Clamps to page 1 |
|
||||
| **Pass criteria** | Footer shows `Page 1/N`; no crash |
|
||||
|
||||
---
|
||||
|
||||
## 6. Tier Badges on Card Embeds
|
||||
|
||||
These tests verify that tier badges appear in card embed titles across all
|
||||
commands that display card embeds via `get_card_embeds()`.
|
||||
|
||||
### REF-40: Tier badge on /card command (player lookup)
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | Look up a card that has a refractor tier > 0 |
|
||||
| **Discord command** | `/card {player_name}` (use a player known to have refractor state) |
|
||||
| **Expected result** | Embed title is `[BC] Player Name` (or appropriate badge for their tier) |
|
||||
| **Pass criteria** | 1. Embed title starts with the correct tier badge in brackets |
|
||||
| | 2. Player name follows the badge |
|
||||
| | 3. Embed color is still from the card's rarity (not refractor-related) |
|
||||
|
||||
### REF-41: No badge for T0 card
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | Look up a card with current_tier=0 |
|
||||
| **Discord command** | `/card {player_name}` (use a player at T0) |
|
||||
| **Expected result** | Embed title is just `Player Name` with no bracket prefix |
|
||||
| **Pass criteria** | Title does not contain `[BC]`, `[R]`, `[GR]`, or `[SF]` |
|
||||
|
||||
### REF-42: No badge when refractor state is missing
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | Look up a card that has no RefractorCardState row |
|
||||
| **Discord command** | `/card {player_name}` (use a player with no refractor state) |
|
||||
| **Expected result** | Embed title is just `Player Name` with no bracket prefix |
|
||||
| **Pass criteria** | 1. Title has no badge prefix |
|
||||
| | 2. No error in bot logs about the refractor API call |
|
||||
| | 3. Card display is otherwise normal |
|
||||
|
||||
### REF-43: Badge on /buy confirmation embed
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | Start a card purchase for a player with refractor state |
|
||||
| **Discord command** | `/buy {player_name}` |
|
||||
| **Expected result** | The card embed shown during purchase confirmation includes the tier badge |
|
||||
| **Pass criteria** | Embed title includes tier badge if the player has refractor state |
|
||||
| **Notes** | The buy flow uses `get_card_embeds(get_blank_team_card(...))`. Since blank team cards have no team association, the refractor lookup by card_id may 404. Verify graceful fallback. |
|
||||
|
||||
### REF-44: Badge on pack opening cards
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | Open a pack and check if revealed cards show tier badges |
|
||||
| **Discord command** | `/openpack` (or equivalent pack opening command) |
|
||||
| **Expected result** | Cards displayed via `display_cards()` -> `get_card_embeds()` show tier badges if applicable |
|
||||
| **Pass criteria** | Cards with refractor state show badges; cards without state show no badge and no error |
|
||||
|
||||
### REF-45: Badge consistency between /card and /refractor status
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | Compare the badge shown for the same player in both views |
|
||||
| **Discord command** | Run both `/card {player}` and `/refractor status` for the same player |
|
||||
| **Expected result** | The badge in the `/card` embed title (`[BC]`, `[R]`, etc.) matches the tier shown in `/refractor status` |
|
||||
| **Pass criteria** | Tier badge letter matches: T1=[BC], T2=[R], T3=[GR], T4=[SF] |
|
||||
|
||||
---
|
||||
|
||||
## 7. Post-Game Hook -- Stat Accumulation and Evaluation
|
||||
|
||||
These tests verify the end-to-end flow: play a game -> stats update -> refractor
|
||||
evaluation -> optional tier-up notification.
|
||||
|
||||
### Prerequisites for Game Tests
|
||||
- Two teams exist on the dev server (the test user's team + an AI opponent)
|
||||
- The test user's team has a valid lineup and starting pitcher set
|
||||
- Record the game ID from the game channel name after starting
|
||||
|
||||
### REF-50: Start a game against AI
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | Start a new game to create a game context |
|
||||
| **Discord command** | `/new-game mlb-campaign league:Minor League away_team_abbrev:{user_team} home_team_abbrev:{ai_team}` |
|
||||
| **Expected result** | Game channel is created; game starts successfully |
|
||||
| **Pass criteria** | A new channel appears; scorebug embed is posted |
|
||||
| **Notes** | This is setup for REF-51 through REF-54 |
|
||||
|
||||
### REF-51: Complete a game (manual or auto-roll)
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | Play the game through to completion |
|
||||
| **Discord command** | Use `/log ab` repeatedly or auto-roll to finish the game |
|
||||
| **Expected result** | Game ends; final score is posted; game summary embed appears |
|
||||
| **Pass criteria** | 1. Game over message appears |
|
||||
| | 2. No errors in bot logs during the post-game hook |
|
||||
|
||||
### REF-52: Verify season stats updated post-game
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | After game completion, check that season stats were updated |
|
||||
| **Verification** | Check bot logs for successful POST to `season-stats/update-game/{game_id}` |
|
||||
| **Pass criteria** | 1. Bot logs show the season-stats POST was made |
|
||||
| | 2. No error logged for that call |
|
||||
| **API check** | `curl "https://pddev.manticorum.com/api/v2/season-stats?team_id={team_id}" -H "Authorization: Bearer $TOKEN"` returns updated stats |
|
||||
|
||||
### REF-53: Verify refractor evaluation triggered post-game
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | After game completion, check that refractor evaluation was called |
|
||||
| **Verification** | Check bot logs for successful POST to `refractor/evaluate-game/{game_id}` |
|
||||
| **Pass criteria** | 1. Bot logs show the refractor evaluate-game POST was made |
|
||||
| | 2. The call happened AFTER the season-stats call (ordering matters) |
|
||||
| | 3. Log does not show "Post-game refractor processing failed" |
|
||||
|
||||
### REF-54: Verify refractor values changed after game
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | After a completed game, check that formula values increased for participating players |
|
||||
| **Discord command** | `/refractor status` (compare before/after values for a participating player) |
|
||||
| **Expected result** | `formula_value` for batters who had PAs and pitchers who recorded outs should be higher than before the game |
|
||||
| **Pass criteria** | At least one card's formula_value has increased |
|
||||
| **API check** | `curl "https://pddev.manticorum.com/api/v2/refractor/cards/{CARD_BATTER}" -H "Authorization: Bearer $TOKEN"` -- compare `current_value` before and after |
|
||||
|
||||
### REF-55: Post-game hook is non-fatal
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | Even if the refractor API fails, the game completion should succeed |
|
||||
| **Verification** | This is tested via unit tests (test_complete_game_hook.py). For integration: verify that if the API has a momentary error, the game result is still saved and the channel reflects the final score. |
|
||||
| **Pass criteria** | Game results persist even if refractor evaluation errors appear in logs |
|
||||
|
||||
---
|
||||
|
||||
## 8. Tier-Up Notifications
|
||||
|
||||
### REF-60: Tier-up embed format (T0 -> T1)
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | When a card tiers up from T0 to T1 (Base Chrome), a notification embed is sent |
|
||||
| **Trigger** | Complete a game where a player's formula_value crosses the T1 threshold |
|
||||
| **Expected result** | An embed appears in the game channel with: |
|
||||
| | - Title: "Refractor Tier Up!" |
|
||||
| | - Description: `**{Player Name}** reached **Tier 1 (Base Chrome)** on the **{Track Name}** track` |
|
||||
| | - Color: green (`0x2ECC71`) |
|
||||
| | - Footer: "Paper Dynasty Refractor" |
|
||||
| **Pass criteria** | 1. Embed title is exactly "Refractor Tier Up!" |
|
||||
| | 2. Player name appears bold in description |
|
||||
| | 3. Tier number and name are correct |
|
||||
| | 4. Track name is one of: Batter Track, Starting Pitcher Track, Relief Pitcher Track |
|
||||
| | 5. Footer text is "Paper Dynasty Refractor" |
|
||||
|
||||
### REF-61: Tier-up embed colors per tier
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | Each tier has a distinct embed color |
|
||||
| **Expected colors** | T1: green (`0x2ECC71`), T2: gold (`0xF1C40F`), T3: purple (`0x9B59B6`), T4: teal (`0x1ABC9C`) |
|
||||
| **Pass criteria** | Embed color matches the target tier |
|
||||
| **Notes** | May require manual API manipulation to trigger specific tier transitions |
|
||||
|
||||
### REF-62: Superfractor notification (T3 -> T4)
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | The Superfractor tier-up has special formatting |
|
||||
| **Trigger** | A player crosses the T4 threshold |
|
||||
| **Expected result** | Embed with: |
|
||||
| | - Title: "SUPERFRACTOR!" (not "Refractor Tier Up!") |
|
||||
| | - Description: `**{Player Name}** has reached maximum refractor tier on the **{Track Name}** track` |
|
||||
| | - Color: teal (`0x1ABC9C`) |
|
||||
| | - Extra field: "Rating Boosts" with value "Rating boosts coming in a future update!" |
|
||||
| **Pass criteria** | 1. Title is "SUPERFRACTOR!" |
|
||||
| | 2. Description mentions "maximum refractor tier" |
|
||||
| | 3. "Rating Boosts" field is present |
|
||||
|
||||
### REF-63: Multiple tier-ups in one game
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | When multiple players tier up in the same game, each gets a separate notification |
|
||||
| **Trigger** | Complete a game where 2+ players cross thresholds |
|
||||
| **Expected result** | One embed per tier-up, posted sequentially in the game channel |
|
||||
| **Pass criteria** | Each tier-up gets its own embed; no tier-ups are lost |
|
||||
|
||||
### REF-64: No notification when no tier-ups occur
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | Most games will not produce any tier-ups; verify no spurious notifications |
|
||||
| **Trigger** | Complete a game where no thresholds are crossed |
|
||||
| **Expected result** | No tier-up embeds appear in the channel |
|
||||
| **Pass criteria** | The only game-end messages are the standard game summary and rewards |
|
||||
|
||||
---
|
||||
|
||||
## 9. Cross-Command Badge Propagation
|
||||
|
||||
These tests verify that tier badges appear (or correctly do not appear) in all
|
||||
commands that display card information.
|
||||
|
||||
### REF-70: /roster command -- cards show tier badges
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Discord command** | `/roster` or equivalent command that lists team cards |
|
||||
| **Expected result** | If roster display uses `get_card_embeds()`, cards with refractor state show tier badges |
|
||||
| **Pass criteria** | Cards at T1+ have badges; T0 cards have none |
|
||||
|
||||
### REF-71: /show-card defense (in-game) -- no badge expected
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | During an active game, the `/show-card defense` command uses `image_embed()` directly, NOT `get_card_embeds()` |
|
||||
| **Discord command** | `/show-card defense position:Catcher` (during an active game) |
|
||||
| **Expected result** | Card image is shown without a tier badge in the embed title |
|
||||
| **Pass criteria** | This is EXPECTED behavior -- in-game card display does not fetch refractor state |
|
||||
| **Notes** | This is a known limitation, not a bug. Document for future consideration. |
|
||||
|
||||
### REF-72: /scouting view -- badge on scouted cards
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Discord command** | `/scout {player_name}` (if the scouting cog uses get_card_embeds) |
|
||||
| **Expected result** | If the scouting view calls get_card_embeds, badges should appear |
|
||||
| **Pass criteria** | Verify whether scouting uses get_card_embeds or its own embed builder |
|
||||
|
||||
---
|
||||
|
||||
## 10. Force-Evaluate Endpoint (Admin/Debug)
|
||||
|
||||
### REF-80: Force evaluate a single card
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Description** | Use the API to force-recalculate a card's refractor state |
|
||||
| **Command** | `curl -X POST "https://pddev.manticorum.com/api/v2/refractor/cards/${CARD_BATTER}/evaluate" -H "Authorization: Bearer $TOKEN"` |
|
||||
| **Expected result** | JSON response with updated `current_tier`, `current_value` |
|
||||
| **Pass criteria** | Response includes tier and value fields; no 500 error |
|
||||
|
||||
### REF-81: Force evaluate a card with no stats
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Command** | `curl -X POST "https://pddev.manticorum.com/api/v2/refractor/cards/${CARD_NO_STATS}/evaluate" -H "Authorization: Bearer $TOKEN"` |
|
||||
| **Expected result** | Either 404 or a response with `current_tier: 0` and `current_value: 0` |
|
||||
| **Pass criteria** | No 500 error; graceful handling |
|
||||
|
||||
### REF-82: Force evaluate nonexistent card
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Command** | `curl -X POST "https://pddev.manticorum.com/api/v2/refractor/cards/999999/evaluate" -H "Authorization: Bearer $TOKEN"` |
|
||||
| **Expected result** | HTTP 404 with `"Card 999999 not found"` |
|
||||
| **Pass criteria** | Status 404; clear error message |
|
||||
|
||||
---
|
||||
|
||||
## Known Gaps and Risks
|
||||
|
||||
### CRITICAL: Missing team-level refractor cards list endpoint
|
||||
|
||||
The `/refractor status` slash command calls `db_get("refractor/cards", params=[("team_id", team_id)])`,
|
||||
which sends `GET /api/v2/refractor/cards?team_id=X` to the API.
|
||||
|
||||
However, the API only defines these refractor routes:
|
||||
- `GET /refractor/tracks` -- list all tracks
|
||||
- `GET /refractor/tracks/{track_id}` -- single track
|
||||
- `GET /refractor/cards/{card_id}` -- single card state by card ID
|
||||
- `POST /refractor/cards/{card_id}/evaluate` -- force evaluate one card
|
||||
- `POST /refractor/evaluate-game/{game_id}` -- evaluate all players in a game
|
||||
|
||||
There is **no** `GET /refractor/cards` (list) endpoint that accepts a `team_id`
|
||||
query parameter. This means `/refractor status` will likely receive a 404 or
|
||||
routing error from the API, causing the "No refractor data found for your team"
|
||||
fallback message for ALL users.
|
||||
|
||||
**Action required**: Either:
|
||||
1. Add a `GET /refractor/cards` list endpoint to the database API that accepts
|
||||
`team_id`, `card_type`, `season`, and `tier` query parameters, OR
|
||||
2. Restructure the bot command to fetch the team's card list first, then call
|
||||
`GET /refractor/cards/{card_id}` for each card individually (N+1 problem).
|
||||
|
||||
This gap should be verified immediately by running REF-01 against the dev server.
|
||||
If the command returns "No refractor data found for your team" even when
|
||||
refractor state exists, this is the cause.
|
||||
|
||||
### In-game card display does not show badges
|
||||
|
||||
The `/show-card defense` command in the gameplay cog uses `image_embed()` which
|
||||
renders the card image directly. It does not call `get_card_embeds()` and
|
||||
therefore does not fetch or display refractor tier badges. This is a design
|
||||
decision, not a bug, but should be documented as a known limitation.
|
||||
|
||||
### Tier badge format inconsistency (by design)
|
||||
|
||||
Two `TIER_BADGES` dicts exist:
|
||||
- `cogs/refractor.py`: `{1: "[BC]", 2: "[R]", 3: "[GR]", 4: "[SF]"}` (with brackets)
|
||||
- `helpers/main.py`: `{1: "BC", 2: "R", 3: "GR", 4: "SF"}` (without brackets)
|
||||
|
||||
This is intentional -- `helpers/main.py` wraps the value in brackets when building
|
||||
the embed title (`f"[{badge}] "`). The existing unit test
|
||||
`TestTierBadgesFormatConsistency` in `test_card_embed_refractor.py` enforces this
|
||||
contract. Both dicts must stay in sync.
|
||||
|
||||
### Notification delivery is non-fatal
|
||||
|
||||
The tier-up notification send is wrapped in `try/except`. If Discord's API has a
|
||||
momentary error, the notification is lost silently (logged but not retried). There
|
||||
is no notification queue or retry mechanism. This is acceptable for the current
|
||||
design but means tier-up notifications are best-effort.
|
||||
|
||||
---
|
||||
|
||||
## Test Execution Checklist
|
||||
|
||||
Run order for Playwright automation:
|
||||
|
||||
1. [ ] Execute REF-API-01 through REF-API-05 (API health)
|
||||
2. [ ] Execute REF-01 through REF-06 (basic /refractor status)
|
||||
3. [ ] Execute REF-10 through REF-19 (filters)
|
||||
4. [ ] Execute REF-20 through REF-23 (pagination)
|
||||
5. [ ] Execute REF-30 through REF-34 (edge cases)
|
||||
6. [ ] Execute REF-40 through REF-45 (tier badges on card embeds)
|
||||
7. [ ] Execute REF-50 through REF-55 (post-game hook -- requires live game)
|
||||
8. [ ] Execute REF-60 through REF-64 (tier-up notifications -- requires threshold crossing)
|
||||
9. [ ] Execute REF-70 through REF-72 (cross-command badge propagation)
|
||||
10. [ ] Execute REF-80 through REF-82 (force-evaluate API)
|
||||
|
||||
### Approximate Time Estimates
|
||||
- API health checks: 1-2 minutes
|
||||
- /refractor status tests (REF-01 through REF-34): 10-15 minutes
|
||||
- Tier badge tests (REF-40 through REF-45): 5-10 minutes
|
||||
- Game simulation tests (REF-50 through REF-55): 15-30 minutes (depends on game length)
|
||||
- Tier-up notification tests (REF-60 through REF-64): Requires setup; 10-20 minutes
|
||||
- Cross-command tests (REF-70 through REF-72): 5 minutes
|
||||
- Force-evaluate API tests (REF-80 through REF-82): 2-3 minutes
|
||||
|
||||
**Total estimated time**: 50-85 minutes for full suite
|
||||
22
tests/refractor-preflight.sh
Executable file
22
tests/refractor-preflight.sh
Executable file
@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
# refractor-preflight.sh — run from workstation after dev deploy
|
||||
# Verifies the Refractor system endpoints and bot health
|
||||
|
||||
echo "=== Dev API ==="
|
||||
# Refractor endpoint exists (expect 401 = auth required)
|
||||
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://pddev.manticorum.com/api/v2/refractor/cards/1")
|
||||
[ "$STATUS" = "401" ] && echo "PASS: refractor/cards responds (401)" || echo "FAIL: refractor/cards ($STATUS, expected 401)"
|
||||
|
||||
# Old evolution endpoint removed (expect 404)
|
||||
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://pddev.manticorum.com/api/v2/evolution/cards/1")
|
||||
[ "$STATUS" = "404" ] && echo "PASS: evolution/cards removed (404)" || echo "FAIL: evolution/cards ($STATUS, expected 404)"
|
||||
|
||||
echo ""
|
||||
echo "=== Discord Bot ==="
|
||||
# Health check
|
||||
curl -sf http://sba-bots:8080/health >/dev/null 2>&1 && echo "PASS: bot health OK" || echo "FAIL: bot health endpoint"
|
||||
|
||||
# Recent refractor activity in logs
|
||||
echo ""
|
||||
echo "=== Recent Bot Logs (refractor) ==="
|
||||
ssh sba-bots "docker logs --since 10m paper-dynasty_discord-app_1 2>&1 | grep -i refract" || echo "(no recent refractor activity)"
|
||||
Loading…
Reference in New Issue
Block a user