feat: add /dev refractor-test integration test command #161
No reviewers
Labels
No Label
ai-changes-requested
ai-failed
ai-pr-opened
ai-reviewed
ai-reviewing
ai-working
autonomous
bug
enhancement
feature
in-queue
performance
security
size:M
size:S
tech-debt
todo
type:feature
type:stability
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: cal/paper-dynasty-discord#161
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "feat/refractor-integration-test"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
/dev refractor-test card_id:<int>slash command that validates the full refractor chain end-to-end using synthetic game data (create game → insert plays → update season stats → evaluate refractor → render card)helpers/refractor_test_data.pyfor stat calculations and synthetic data building (HR plays for batters, K plays for pitchers)CleanupViewwith buttons to clean up or keep test data after each runDevToolsCogconditionally loaded only whenDATABASE != prodTest plan
/dev refractor-teston a T0 batter card🤖 Generated with Claude Code
AI Code Review
Files Reviewed
cogs/dev_tools.py(added)helpers/refractor_test_data.py(added)tests/test_dev_tools.py(added)tests/test_refractor_test_data.py(added)paperdynasty.py(modified).gitignore(modified)Findings
Correctness
No issues found.
db_deletesignature matches all three cleanup calls (endpoint: str, object_id: int). Deletion order (decisions → plays → games) is correct for FK constraints.calculate_plays_needed(gap=0):max(1, ceil(0/9)) = 1— correctly generates one play to push past an at-threshold card.tier_upsvariable is always defined when Step 6 is reached (early returns on exception prevent scope gaps).update_embedfield-manipulation logic is correct: keeps field[0] (Setup), replaces everything else with Results. Theembed.set_image()call in Step 6 is not cleared byupdate_embed(images are stored separately from fields), so the card image survives to the final Discord message."prod" not in os.getenv("DATABASE", "dev").lower()correctly gates dev-only loading; productionDATABASE=Prodfails the check.PA + TB*2 = 1 + 4*2 = 9✓. Pitcher value:IP + K = 1/3 + 1 = 4/3✓.side_effectcounts match actualdb_getcall counts in bothtest_batting_card_lookup(3 calls) andtest_pitching_card_fallback(4 calls).Security
No issues found.
card_idis typed asintby the slash command framework — no injection risk. No secrets or credentials introduced.Style & Conventions
final: bool = Falseinupdate_embed: The parameter is accepted in the closure signature but never read inside the function body. The meaningful distinction is alreadyview is not None. Removefinalto avoid confusion about what it controls.player_namein_execute_refractor_test: Received but never referenced inside the method. It's used in the callingrefractor_testmethod to build the Setup embed, but passing it to_execute_refractor_testserves no purpose.Suggestions
params=[("team_id", team_id), ("limit", 100)]— if a team has >100 refractor entries and the target player falls beyond position 100,card_statewill beNoneand the code assumes T0/0 progress. This could cause the tool to generate far more plays than necessary for a high-progress card. Not a blocker for a dev tool, but worth a comment noting the limitation.test_cleanup_calls_delete_endpoints:assert "decisions/game" in str(calls[0])is fragile (matches substrings in call repr). Considercalls[0].args[0] == "decisions/game"for precision. Consistent with prior PR patterns in this repo, so not a blocker.Verdict: COMMENT
Functionally correct. The test coverage is thorough (25 tests across pure helpers and cog behavior), the prod-guard pattern is clean, and the step-by-step reporting with per-step error stops is solid. Only minor dead code in two parameter lists and an optional note on the 100-item lookup limit.
Automated review by Claude PR Reviewer
AI Code Review
Files Reviewed
cogs/dev_tools.py(added)helpers/refractor_test_data.py(added)tests/test_dev_tools.py(added)tests/test_refractor_test_data.py(added)paperdynasty.py(modified).gitignore(modified)Findings
Correctness
No issues found.
Key checks passed:
"prod" not in os.getenv("DATABASE", "dev").lower()guard correctly keeps the cog off in production for anyDATABASEvalue containing "prod" (case-insensitive). The env var docs in CLAUDE.md confirm it is set toDevorProd.tier_upsvariable scope is safe: Step 5'sexceptbranch alwaysreturns immediately, sotier_upsis never referenced before assignment.calculate_plays_needed(gap=0)returnsmax(1, ceil(0 / value))=max(1, 0)= 1, which correctly handles a card already sitting exactly at its threshold.card_type_keycorrectly before both calls, so the fallback path carries the right type through.db_deletetakes(endpoint: str, object_id: int)— the three cleanup calls match this signature exactly.db_getdefaultsnone_okay=True, so the card render call with explicitnone_okay=Trueis redundant but harmless.get_team_by_owneracceptsintand the code passesinteraction.user.id(a Discord snowflake int) — correct.Security
No issues found. This cog is intentionally dev-only and is never loaded when
DATABASEcontains "prod". No user-supplied input is interpolated into any API path —card_idis typedintby Discord's slash command framework, and all constructed endpoint paths use internal IDs fetched from the API, not raw user strings. The button ownership check (interaction.user.id != self.owner_id) correctly gates cleanup to the command invoker.Style & Conventions
No issues found. Code follows the project's cog patterns: top-level imports only, no lazy imports, docstrings on all public functions and classes, consistent use of
db_get/db_post/db_deletehelpers.Minor observation (not a blocker): the
CURRENT_SEASON = PD_SEASONmodule-level alias indev_tools.pyadds an indirection layer with no benefit —PD_SEASONcould be used directly. Purely stylistic.Suggestions
on_timeouthandler clears items locally but cannot update the Discord message without a stored message reference — this is already documented in the code comment. For a dev tool the current behavior (buttons become unresponsive after 5 min) is acceptable as-is.refractor/cardsis fetched withlimit=100. If a team has more than 100 refractor-tracked cards the lookup could miss the target player. Unlikely for a dev tool, butlimit=200or a player-ID filter would be more robust.Verdict: APPROVED
Clean, well-structured implementation. The helper module is pure and fully unit tested (15 tests covering all branches including the zero-gap edge case). The cog is correctly gated to non-prod environments. The step-by-step failure/stop chain is sound. Button auth check is correct. The stale
player_name/finalparameters flagged in the previous review have been removed. Ready to merge.Automated review by Claude PR Reviewer
Approved via pd-pr