fix(api): detect live 'healthy' stacks + reject 200 ok:false promo codes#197
Merged
mastermanas805 merged 1 commit intoJun 6, 2026
Merged
Conversation
Two UI↔API contract-mismatch bugs in src/api/index.ts, both surfaced by the
2026-06-06 bug-hunt pass (docs/ci/BUGHUNT-BACKLOG-2026-06-06.md P1-2, P1-3).
Bug 1 (P1-2) — StackCreatePage never detected a live stack.
The api emits status="healthy" for a successfully-deployed stack
(stack.go:382), but the dashboard's StackStatus union omits 'healthy' and the
create-page poll only treats === 'running' as live. A successful dashboard
stack deploy therefore spun in the 'building' stage until the ~5-min poll
timeout instead of flipping to 'live'. Add normaliseStackStatus() mirroring
normaliseDeploymentStatus ('healthy'/'deploying' → 'running', 'deleting' →
'stopped') and apply it in fetchStackStatus(), listStacks(), and createStack()
so the type and runtime value agree in one place; the page's existing
=== 'running' checks now trip for a live stack.
Bug 2 (P1-3) — invalid promo codes validated as "ok".
POST /api/v1/billing/promotion/validate is LIVE and reports validation
FAILURES as 200 ok:false (not 4xx). call() only throws on non-2xx, so
validatePromotion built a phantom "valid" promo (empty discount) from an
ok:false body; the 404→seeds fallback was dead code (handler never 404s).
Now inspect `ok`: a 200 ok:false (or ok:true without a discount) throws an
APIError carrying the api's error/message so the UI renders the real
rejection. Drop the dead seed table + fix the stale "mocked until api ships"
header comment (P3-2). Also correct the misleading "LIVE" comment on
cancelSubscription (P3-1 — the /billing/cancel route was removed by policy).
Tests (src/api/index.test.ts): proven fail-before/pass-after by stashing the
src fix — the 'healthy'/'deploying' stack cases and the 200 ok:false promo
cases fail against old code. 100% patch coverage on both changed regions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
mastermanas805
enabled auto-merge (squash)
June 6, 2026 06:00
size-limit report 📦
|
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.
Fixes two UI↔API contract-mismatch bugs in
src/api/index.tsfound by the 2026-06-06 bug-hunt pass (docs/ci/BUGHUNT-BACKLOG-2026-06-06.mdP1-2, P1-3). Both touch the same file, so they share one PR.Bug 1 (P1-2) — StackCreatePage never detected a live stack
The api sets a successfully-deployed stack's status to
"healthy"(api/internal/handlers/stack.go:382), but the dashboard'sStackStatusunion omits'healthy'and the create-page poll (StackCreatePage.tsx:210/:169) +fetchStackStatus/listStacksonly treat=== 'running'as live. A successful dashboard stack deploy therefore spun in thebuildingstage until the ~5-min poll timeout instead of flipping tolive.Fix: add
normaliseStackStatus()mirroring the existingnormaliseDeploymentStatus('healthy'/'deploying'→'running','deleting'→'stopped', unknown →'building'), applied infetchStackStatus(),listStacks(), andcreateStack(). The type and the runtime value now agree in one place; the page's existing=== 'running'live-checks trip for a live stack with no page change.Bug 2 (P1-3) — invalid promo codes validated as "ok"
POST /api/v1/billing/promotion/validateis LIVE and reports validation FAILURES as 200ok:false(not a 4xx — confirmed inbilling_promotion.go:216and the OpenAPI snapshot).call()only throws on non-2xx, sovalidatePromotionbuilt a phantom "valid" promo (empty/undefined discount) from anok:falsebody; the404→seedsfallback was dead code (the handler never 404s).Fix: inspect
ok— a 200ok:false(orok:truewithout adiscount) throws anAPIErrorcarrying the api'serror/messageso the UI renders the real rejection. Removed the dead seed table; corrected the stale// mocked until api shipsheader comment (P3-2) and the misleading// LIVEcomment oncancelSubscription(P3-1 —/billing/cancelwas removed by policy). Money bug once Razorpay offers wire up.Tests — fail-before / pass-after (proven)
src/api/index.test.ts. Stashing the src fix and re-running confirmed:healthy→running,deploying→running,deleting→stopped, unknown→buildingcases fail against old code (infetchStackStatus,listStacks,createStack).ok:falserejection cases + the malformedok:true-no-discount case fail against old code.100% patch coverage on both changed regions (verified via v8 coverage JSON — zero uncovered statements/branches in
normaliseStackStatusandvalidatePromotion). Fullnpm run gateGREEN (tsc + build + prerender + 1141 vitest).🤖 Generated with Claude Code