test(billing): W3 billing-block integration suite — purchasable-set, no self-serve cancel/downgrade, webhook tier transitions#247
Merged
mastermanas805 merged 2 commits intoJun 4, 2026
Conversation
…no self-serve cancel/downgrade, webhook tier transitions Closes the matrix's biggest gap (USER-FLOW-INVENTORY-AND-TEST-MATRIX.md §E): the revenue-critical billing block had near-zero real-backend integration coverage. New DB-backed tests (handlers package, TEST_DATABASE_URL), all NEW files — no edits to billing.go or existing billing_*_test.go (avoids #246 W0). - Registry-iterating purchasable-set assertion (§E3): drives EVERY plans.Registry tier through the real CreateCheckoutAPI handler; asserts the set that reaches the Razorpay CreateSubscription seam is EXACTLY {hobby, hobby_plus, pro}. Reds if Team is re-enabled or a new tier silently becomes chargeable (rule 18). Team → 400 tier_not_yet_available; growth → invalid_plan; both checkout + change-plan surfaces gated. - No self-serve cancel/downgrade (§E10): router.go source-scan negative assertion (no non-admin cancel/downgrade route) + ChangePlanAPI rejects every lower/equal-tier target with downgrade_not_self_serve + support agent_action, asserting the team tier is left UNCHANGED. same_plan edge covered. - Webhook tier transitions (§E4/E5/E6/E7): subscription.charged upgrade elevates plan_tier AND promotes all active resources (rule 5); subscription.cancelled downgrade drops plan_tier to the courtesy floor but LEAVES resource tiers (user-benefit asymmetry); bad signature → 400 invalid_signature, tier unchanged; unknown team → 404 team_not_found (rows-affected-0 / ErrTeamNotFound). - Checkout graceful failure: unconfigured plan_id → 503 billing_not_configured; live-key-in-nonprod → 503 billing_misconfigured (CreateSubscription never called). Reuses existing helpers (seedVerifiedTeamUser, cov2CheckoutApp, changePlanAppReal/Req, postCheckoutReq, cov2WebhookAppReal, signRazorpayPayload, makeSubscriptionChargedPayloadWithPlan, makeSubscriptionCancelledPayload) — none redefined. Handlers package green; the 20 unrelated handlers/models failures in full ./... are pre-existing local-env flakes (NATS/customer-DB/GitHub creds), verified to reproduce identically on clean origin/master — CI is authoritative. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
What
Closes the biggest gap in
docs/sessions/2026-06-04/USER-FLOW-INVENTORY-AND-TEST-MATRIX.md§E: the revenue-critical billing block had near-zero real-backend integration coverage. Adds a DB-backed (TEST_DATABASE_URL) integration suite in thehandlerspackage.All NEW files — no edits to
billing.goor any existingbilling_*_test.go, so no conflict with the in-flight #246 (W0). Reuses existing helpers (seedVerifiedTeamUser,cov2CheckoutApp,changePlanAppReal/changePlanReq,postCheckoutReq,cov2WebhookAppReal,signRazorpayPayload,makeSubscriptionChargedPayloadWithPlan,makeSubscriptionCancelledPayload); none redefined.Coverage
TestBillingBlock_SelfServePurchasableSet_IsExactlyHobbyHobbyPlusProdrives everyplans.Registrytier through the realCreateCheckoutAPIand asserts the set reaching the RazorpayCreateSubscriptionseam is exactly{hobby, hobby_plus, pro}. Reds if Team is re-enabled or a new tier silently becomes chargeable (rule 18). Team → 400tier_not_yet_available; growth →invalid_plan. Mirrored for the change-plan surface.ChangePlanAPIrejects every lower/equal target withdowngrade_not_self_serve+ a supportagent_action, asserting team tier is left unchanged.same_planedge covered.subscription.chargedupgrade elevatesplan_tierAND promotes all active resources (rule 5);subscription.cancelleddowngrade dropsplan_tierto the courtesy floor but leaves resource tiers (user-benefit asymmetry); bad signature → 400invalid_signature(tier unchanged); unknown team → 404team_not_found(rows-affected-0 /ErrTeamNotFound).billing_not_configured; live-key-in-nonprod → 503billing_misconfigured(CreateSubscriptionnever called).Gate
internal/handlersgreen for all new tests. The registry-iterating assertion was verified failing-then-passing (reds whenteamis added to the expected purchasable set). The 20 unrelatedhandlers/modelsfailures in full./...are pre-existing local-env flakes (NATS/customer-DB/GitHub creds unreachable on a bare laptop) — verified to reproduce identically on cleanorigin/masterwith these files stashed (diff of introduced failures is empty). CI is authoritative.🤖 Generated with Claude Code