fix(credits): restore 402 return for insufficient_credits in credit_middleware#58
Merged
Merged
Conversation
…iddleware PR#17 (security hardening) lost the deduct_failed → 402 return block during rebase conflict resolution on the credit middleware. The deduct_failed flag was set on UPDATE ... WHERE balance >= cost returning 0 rows, but never evaluated — causing paid endpoints to return 200 instead of 402 under parallel calls (billing bypass). This commit restores the exact 7-line block that was lost, producing consistent 402 semantics across both the pre-check and the atomic deduct paths. Found by: Kersten Kroehl (kk) Original commit reference: bbe4ad7 (credit-middleware PR MoltyCel#27) Test: test_concurrent_deducts_respect_balance now passes (10 parallel calls → 3×200 + 7×402 vs previous 10×200)
Previously the fork CI only ran pytest --collect-only, never executing any tests. This meant regressions like the PR#17 credit-middleware 402-block loss were invisible to CI — the concurrency test (test_concurrent_deducts_respect_balance) would have caught it immediately, but it never ran. New job: - pytest-run: spins up a Postgres 16 service container, runs init_db.sql for the schema, executes tests/test_credit_middleware.py with proper env vars (CREDITS_ENABLED=true, MOLTSTACK_DB_PW, etc.) This is intentionally scoped to credit middleware tests only — the test_caep.py and protocol compliance tests need additional infrastructure (sandbox, admin keys) and can be added separately. Also adds httpx to the collect job's pip install (needed for import to succeed).
This was referenced May 22, 2026
Owner
|
Small post-merge process note — not a knock on the change itself, the CI job is sound: The PR description's Diff section shows only the Worth keeping as a review norm: list every changed file (or area) in the description, especially when a Two follow-ups surfaced while reviewing this PR, both now open:
Thanks for the quick 402 fix. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PR#17 (security hardening pass) lost the
deduct_failed → 402return block during rebase conflict resolution on the credit middleware.The Bug
The
deduct_failedflag was correctly set whenUPDATE ... WHERE balance >= costreturned 0 rows (insufficient balance / race condition), but it was never evaluated — the code fell through toreturn response, shipping the original 200 OK back to the caller.Consequence: Billing bypass under parallel calls — paid endpoints returned 200 instead of 402 when credits ran out mid-batch.
The Fix
Restores the exact 7-line block that was lost (originally from PR#27):
This produces consistent 402 semantics across both the pre-check (line 553) and the atomic deduct (line 582) paths.
Test
test_concurrent_deducts_respect_balancenow passes:Diff
7 lines, no new code — exact restore of the block lost from the PR#17 rebase.
Credit
Discovered and diagnosed by Kersten Kroehl (kk). Fix applied from his diagnosis on api.moltrust.ch.