Repository navigation
test(e2e): codify end-to-end customer flow into a regression test - #20
mastermanas805 wants to merge 1 commit into
Conversation
Captures the full anonymous → claim → upgrade → cancel funnel in a single
runnable test. Codifies the contract for /claim's session_token, /whoami's
tier+email enrichment, /api/v1/billing's trial/active/cancelled status,
and ElevateResourceTiersByTeam's promote-on-upgrade behaviour.
Adds three tests:
- TestE2E_FullCustomerFlow_AnonymousToProToCancelled — 11-step happy
path. Provisions /db/new anonymously, claims with email, uses the
returned session_token to hit /whoami, /billing, /resources, fires
a subscription.charged webhook, asserts tier flips to pro on all
surfaces and existing resources are elevated, fires
subscription.cancelled, asserts downgrade to hobby and that
existing resources keep tier=pro (documented snapshot behaviour).
- TestE2E_FullCustomerFlow_WhoamiBeforeClaim — regression guard that
the anonymous upgrade_jwt cannot auth against /api/v1/whoami; must
return 401.
- TestE2E_FullCustomerFlow_StoragePathReturnsSpacesCreds — post-Spaces
switch sanity check that /storage/new returns S3-compatible creds
and the public endpoint does not leak the in-cluster MinIO hostname.
All three skip cleanly when the required secrets are absent
(E2E_JWT_SECRET, E2E_RAZORPAY_WEBHOOK_SECRET) and pass against the live
local k8s cluster (verified: 26s wall-clock, 3/3 PASS).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Rebase attempt 2026-05-20 hit conflicts vs current master ( Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com |
|
Closing as superseded — PR #20's content was forward-merged via #21 ( Scenario coverage citationsAll three tests are present on master at
Forward-port detail (the +8-line drift)The branch's step 4 asserts status, _ := billing["subscription_status"].(string)
if status == "trial" {
t.Errorf("step 4: /billing subscription_status must never be 'trial' — no trial period exists on the platform")
}
if status != "none" {
t.Errorf("step 4: /billing subscription_status: want none, got %q", status)
}Re-opening this branch would regress the trial-policy guard. Closing. Adjacent integration tests (Track 5 from INTEGRATION-TESTS-2026-05-20.md)PR #20's scope (customer flow happy-path + 2 regression guards) is orthogonal to Track 5's reliability integration suite — backup/restore, brevo webhook, readyz, reliability contract, propagation chaos all cover different surfaces. No cross-coverage gaps exist that PR #20 would fill. Coverage block: |
… 404 leak, Team dedicated (#218) * fix(api): bug-bash batch — free TTL, brevo non-clobber, dedup expiry, 404 leak, Team dedicated Five confirmed bugs from the 2026-06-02 platform bug bash: - #4 (P1) free-tier resources never expired: authenticated provisions hardcoded ExpiresAt=nil even for free/anonymous tiers. Add resourceExpiryForTier (24h for ephemeral tiers, nil for paid) and apply it at all 10 authenticated CreateResource sites. Per product decision: enforce plans.yaml's documented 24h TTL for claimed-unpaid resources. - #6 (P1) Brevo 'delivered' webhook clobbered a terminal bounce/complaint on out-of-order delivery, corrupting the email truth surface (rule 12). Guard the UPDATE against terminal classes; distinguish terminal-kept from unknown. - #17/#20 (P2) fingerprint dedup-return handed back credentials for active-but-expired anonymous resources: add the expires_at filter to both GetActiveResourceByFingerprint[Type], matching GetAllActiveResourcesByFingerprint. - #22 (P3) deploy CancelDelete returned 403 cross-tenant (leaking existence); now 404 like the other deploy endpoints. - #12 (P2) Team tier gets dedicated infra: add dedicated:true to team + team_yearly in plans.yaml (pairs with common defaultYAML). Per product decision 2026-06-02. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(brevo): cover #6 terminal-kept non-clobber path + fix delivered mocks The delivered UPDATE now carries the terminal-class guard (8 args) and a 0-row result triggers an existence probe. Update expectDeliveredUpdate to the new arg list, add the SELECT mock to the unknown-message test, and add a terminal-kept regression (delivered-after-bounce → matched:true, class preserved). Closes the batch-1 patch-coverage gap on brevo_webhook.go. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(api): update existing tests for batch-1 behavior changes CI (full real-DB suite) caught three existing tests that encoded the pre-fix behavior batch-1 deliberately changed: - TestPlansRegistry_IsDedicatedTier: team is now dedicated (#12). - TestDeployCancelDelete_CrossTeam: cross-tenant now 404 not 403 (#22). - TestBrevo_Receive_UnknownMessageID (billing_coverage): delivered UPDATE now carries the terminal-class guard (8 args) + an existence-probe SELECT (#6). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(brevo): cover delivered-handler SELECT-probe error branch (#218 coverage) diff-cover flagged brevo_webhook.go:530-531 — the `if qErr != nil` arm of the delivered handler's existence probe (bug bash #6). When the terminal-class- guarded UPDATE affects 0 rows, a follow-up SELECT distinguishes terminal-kept from genuinely-unknown; a non-ErrNoRows fault on that probe must surface as an error (→ 500) so Brevo retries rather than the message being mislabeled. Adds TestBrevo_Receive_Delivered_ProbeError (sqlmock, hermetic): UPDATE → 0 rows, SELECT probe → generic error, asserts 500. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Codifies the manual end-to-end customer flow that was hand-driven to verify the instanode.dev funnel works. Three new tests in
e2e/customer_flow_e2e_test.go(gated by//go:build e2e):TestE2E_FullCustomerFlow_AnonymousToProToCancelled— 11-step happy path:POST /db/new→ capturesupgrade_jwt+tokenPOST /claimwith randomized email → assertsok:true,session_token,team_idGET /api/v1/whoamiwith session token → assertstier=hobby,plan_tier=hobby,email,team_idGET /api/v1/billing→ assertstier=hobby,subscription_status=trialGET /api/v1/resources→ asserts claimed postgres present attier=hobbysubscription.chargedto/razorpay/webhook→ asserts200 ok:true/api/v1/billing→ assertstier=pro,subscription_status=active/api/v1/resources→ asserts all active items elevated totier=pro(provesElevateResourceTiersByTeam)subscription.cancelled→ asserts200/api/v1/billing→ assertstier=hobby(downgrade)/api/v1/resources→ asserts existing resources KEEPtier=pro(documented snapshot behaviour perCLAUDE.md)TestE2E_FullCustomerFlow_WhoamiBeforeClaim— regression guard: anonymousupgrade_jwtagainst/whoamimust401(proves anonymous tokens can't auth against the dashboard surface).TestE2E_FullCustomerFlow_StoragePathReturnsSpacesCreds—/storage/newreturns S3-compatibleendpoint+access_key_id+secret_access_key+prefix, and the public endpoint does NOT contain"minio"(post-Spaces switch sanity check).All three
t.Skip()cleanly when required env is absent. Uses existing helpers (uniqueIP,post,get,decodeJSON,subscriptionChargedPayload,subscriptionCancelledPayload,signRazorpayPayload).Required env to actually run
E2E_BASE_URLhttp://localhost:32108)E2E_JWT_SECRET/whoamiand management APIE2E_RAZORPAY_WEBHOOK_SECRETE2E_RAZORPAY_PLAN_ID_PROnotes; handler defaults to"pro"when emptyTest plan
go test -tags e2e -c ./e2e -o /dev/null— compiles3/3 PASSin 26s wall-clock with the secrets pulled frominstant-secretsrazorpayWebhookSecret(t)+ explicitE2E_JWT_SECRETcheck)🤖 Generated with Claude Code