dánwòdánwò

Examples

Go subscriptions

Quillwork, a team plan sold through Stripe Checkout: a Vue 3 single-page app on a Go JSON API and Postgres, whose Stripe webhook starts the plan and sends a welcome email through Resend.

Quillwork, a team plan sold through Stripe Checkout: a Vue 3 single-page app on a Go JSON API and Postgres, whose Stripe webhook starts the plan and sends a welcome email through Resend. See it on GitHub.

What it shows

  • A Go app behind danwo's proxy with no Node preload: Go's own HTTPS_PROXY support, and danwo's CA trusted through the SSL_CERT_FILE danwo sets, on Linux and macOS
  • A Vue single-page app whose calls to the Go API go through danwo's proxy, so the flight record shows them and the matrix fails them: 500s, dropped connections, lost answers and double submits
  • The official Stripe and Resend Go SDKs against the emulators, a webhook verified with the Stripe SDK and deduped in the activation's transaction
  • Invariants that read both sides: one plan per Stripe subscription with Stripe's status, one Checkout Session and one welcome email per team
  • danwo matrix faulting the page's API calls, Stripe, Resend, the webhooks and Postgres (44 trials), and QUILLWORK_BUG switches the matrix catches

Stack

Gonet/httppgxPostgresVueVite

Emulates: Stripe, Resend. Runs on: A Go server and its Vue app in Chromium, with Postgres in Docker.

Run it

Copy examples/with-go anywhere, then from its folder:

npm install
npm test

Its scripts:

npm run build          # vite build web && go -C api build -o ../dist/quillwork .
npm run typecheck      # vue-tsc --noEmit -p web && tsc --noEmit
npm test               # danwo test
npm run test:agentic   # danwo test tests/agentic.danwo.ts
npm run matrix         # danwo matrix tests/deterministic.danwo.ts --grep "upgrades to the Team plan"
npm run matrix:plan    # danwo matrix tests/deterministic.danwo.ts --grep "upgrades to the Team plan" --plan
npm run serve          # danwo serve

Its README says what else it needs and what the last full matrix found.

Until the danwo version it pins is on npm, install it from a local build instead: from the repository's root, bun run release <version> --pack, then node examples/link.ts with-go, which installs a copy and prints where it is.

Apps in other languages explains what this example leans on.

The test

tests/deterministic.danwo.ts
import { resendInbox, test as base } from "@danwo/sdk"

import {
  DECLINED_CARD,
  expectPlansMatchStripe,
  pay,
  type QuillworkFixtures,
  signUp,
} from "./quillwork.ts"

const test = base.extend<QuillworkFixtures>()

test("asks for a valid email and a team name", async ({ app, screen, expect }) => {
  await app.open("/")
  await expect(
    screen.getByRole("heading", { name: "Notes your whole team can find" }),
  ).toBeVisible()
  await expect(screen.getByRole("region", { name: "Team", exact: true })).toContainText("$24")

  await screen.getByLabel("Work email").fill("not-an-email")
  await screen.getByRole("button", { name: "Create account" }).click()

  await expect(screen.getByText("Enter a valid email address.")).toBeVisible()
  await expect(screen.getByText("Give your team a name.")).toBeVisible()
  await expect(screen.getByLabel("Work email")).toHaveAttribute("aria-invalid", "true")
}).invariant(async ({ sql, expect }) => {
  expect(await sql("select id from accounts")).toHaveLength(0)
})

test("signs up on the Free plan", async ({ app, screen, expect }) => {
  await signUp({ app, screen, expect }, { email: "[email protected]", team: "Hopper" })

  await expect(screen.getByText("Signed in as [email protected]")).toBeVisible()
  await expect(screen.getByTestId("plan-name")).toHaveText("Free")
  await expect(screen.getByRole("button", { name: "Upgrade to Team" })).toBeVisible()
}).invariant(async (fixtures) => {
  // A free team owes Stripe nothing and gets no welcome email.
  await expectPlansMatchStripe(fixtures)
  fixtures.expect(await fixtures.sql("select id from subscriptions")).toHaveLength(0)
})

test("upgrades to the Team plan through Stripe Checkout", async ({ app, screen, expect }) => {
  await pay({ app, screen, expect }, { email: "[email protected]", team: "Northwind" })

  await expect(screen.getByTestId("plan-name")).toHaveText("Team")
  await expect(screen.getByTestId("plan-status")).toHaveText("Active")
  await expect(screen.getByRole("button", { name: "Upgrade to Team" })).toBeHidden()
}).invariant(async (fixtures) => {
  await expectPlansMatchStripe(fixtures)
})

test("a declined card shows Stripe's error and starts no plan", async ({ app, screen, expect }) => {
  await pay(
    { app, screen, expect },
    { email: "[email protected]", team: "Kestrel", card: DECLINED_CARD },
  )

  await expect(screen.getByRole("alert")).toHaveText("Your card was declined.")

  await app.open("/account")
  await expect(screen.getByTestId("plan-name")).toHaveText("Free")
}).invariant(async (fixtures) => {
  await expectPlansMatchStripe(fixtures)
  fixtures.expect(await fixtures.sql("select id from subscriptions")).toHaveLength(0)
  fixtures.expect(resendInbox(fixtures.resend).list("[email protected]")).toHaveLength(0)
})

On this page

Run it on every pull request

A GitHub check and one comment per pull request, with the flight records attached.

Set up CI