SuperAPI AI Directory

glubean

Testing & quality

View repository

Updated 10-15 21:53

About this skill

Glubean

Use this skill as a Glubean best-practice guide. Default to the real project workflow. Scratch mode is an extension quick demo path, not the normal agent path.

Modes

  1. Docs: product questions, concepts, comparison, migration, editor support, or cloud features.
  2. Onboarding: no Glubean project yet; the user needs extension, MCP, cookbook, or project init guidance.
  3. Project: the user is already in a Glubean project, or clearly wants to do real project test work, including migration from existing API assets.

Route first

Before choosing a mode:

  • Check for package.json, config/, schemas/, contracts/, tests/, explore/, .env, .env.secrets, and GLUBEAN.md.
  • Check whether @glubean/sdk is already present in dependencies or devDependencies.
  • Check whether MCP tools are available.
  • If GLUBEAN.md exists, read it first as the project's context file.
  • Users should keep key context there, including pointers to relevant code, docs, specs, and sibling workspaces.
  • If the user explicitly provides additional context locations, read those too before guessing from the API alone.

Then route by intent plus environment:

Intent examples

  • Docs
    • "What is Glubean?"
    • "How does Glubean compare to Postman?"
    • "How do I run tests in CI?"
    • "How do I migrate from Postman?" (answer from patterns/migration.md)
  • Onboarding
    • "Set up Glubean for my project"
    • "I want to try Glubean"
    • "Configure MCP for Cursor"
  • Migration (cross-cutting — execution requests, ask before routing)
    • "Migrate our Postman collection"
    • "Convert our Apifox or OpenAPI export"
    • "Port these old API tests into Glubean"
    • → Ask: "Set up a new Glubean project for this, or add to the current one?"
    • → New project → Onboarding → init → migration pattern
    • → Current project → Project → test-after → migration pattern
  • Project — simple mode (default, test-after)
    • "Write smoke tests for /users"
    • "Improve my test coverage"
    • "Fix this failing test"
    • "Add auth boundary tests"
    • → For existing behavior, read implementation-derived-verification.md; use test() in explore/ or tests/ for the resulting imperative evidence
  • Cloud diagnosis
  • Project — implementation-derived verification
    • "Write tests for this existing endpoint"
    • "Generate contracts from this API implementation"
    • "Improve coverage without making me list every case"
    • → Inspect route/controller, validation, auth, domain/service branches, persistence constraints, error mapping, and existing tests
    • → Derive a behavior/case matrix; do not default to a single happy-path or status-only case unless the user explicitly asks for smoke
    • → Express stable promises as contracts, lifecycle promises as workflow(), and imperative evidence as test()
  • Project — contract-first mode (advanced, new structured spec)
    • "I need a users API with CRUD"
    • "Design the billing endpoint before I implement it"
    • "Write contracts for an API we have not implemented"
    • "What's my contract coverage?"
    • "Generate a projection report"
    • → Treat this as design-first only when defining new behavior; read contract-first.md before simple-mode defaults
    • → Use contract.http.with() for endpoint promises and workflow() for lifecycle promises
    • → Only if user explicitly asks for contracts, or project already has contracts/
  • Project — load testing (opt-in; performance under concurrency)
    • "Load test /checkout at 100 concurrent users"
    • "What's the p95 / throughput under load?"
    • "Add a performance gate to CI"
    • → Read references/load-testing.md; author a *.load.ts with loadScenario() + loadRunner() + thresholds, run with glubean load <file|dir|glob> and add --upload / --upload-target only when the user intentionally wants Cloud upload
    • → Opt-in only — it generates real concurrent traffic; never auto-write load tests during routine functional coverage
  • Project — browser and Chrome extension testing
    • "Test my unpacked extension's options page, content script, or Side Panel"
    • "Prove the toolbar action opens and closes the native Side Panel"
    • → Read references/patterns/browser.md; use createExtensionTest() for extension-backed pages and keep direct document rendering proof separate from the native toolbar-action lifecycle

Global rules

Apply these unless project-specific instructions override them:

  1. Secrets go in .env.secrets; public config goes in .env. For multi-environment setup, read references/patterns/multi-env.md.
  2. Use configure() to create shared HTTP clients, then use the exported client (e.g. api) in tests — not ctx.http. ctx.http is only for scratch demos.
  3. Use {{KEY}} for env and secret interpolation, bare strings for literals.
  4. Put tags on every test.
  5. Use builder mode when a test() needs teardown, multi-step state passing, runtime branching, or runtime polling.
  6. Use kebab-case test IDs, unique across the project.
  7. Treat every human-readable test string as diagnostic surface area. Case titles, test names, step names, schema labels, assertion messages, warnings, and logs should explain the invariant or business rule being checked. Avoid generic labels like "works", "valid", "check response", or "should pass".
  8. When using ctx.assert, do not omit the message. Make the message specific enough to diagnose the failure, and pass { actual, expected } when the assertion compares runtime values. Prefer ctx.expect(...) matchers when they already produce structured actual/expected output.
  9. In real projects, keep reusable response types in types/. Inline them only for tiny throwaway examples the user explicitly asked for.
  10. Do not use .json<any>(). If response shape is still unknown, start with .json<unknown>(). Use Record<string, unknown> only when you already know the top-level value is an object. Then narrow to a real type or Zod as soon as fields are known.
  11. In real projects, keep reusable Zod schemas in schemas/. Inline them only for tiny throwaway examples the user explicitly asked for.
  12. Use test.each and test.pick only when every case exercises the same endpoint or the same operation pattern. If endpoints are unrelated, write separate exported tests. Test IDs must include a $field or $_pick placeholder so each case gets a unique ID at runtime (e.g. "search-$q", "user-$_pick").
  13. The first time a target needs auth and none is configured yet, STOP and get the auth details from the owner before writing the base auth config. The trigger is the target has an auth boundary and no confirmed auth config covers it yet — a 401/403, an Authorization / Cookie header, a login/token endpoint, a protected route, or a security scheme in an OpenAPI/spec all signal the boundary. Do NOT guess a scheme, and do NOT author tests that skip auth just to "get something running". Present your reading — which strategy (bearer / apiKey / login-exchange), the header and secret names, where the credential comes from, and the evidence for each — then wait for the owner to confirm, correct, or supply the missing pieces. Once an auth config exists for the target (a configure() client is already set up, the secret is in .env.secrets, or GLUBEAN.md records the auth decision), REUSE it silently — do not re-ask per contract or per test. Never silently configure auth from a guess.
  14. Do not echo secret values back to the user. In generated code and configs, keep {{KEY}} placeholders instead of resolved values; when quoting CLI output or response bodies back in chat, strip or mask Authorization headers, Cookie / Set-Cookie values, and any token-like fields (bearer tokens, API keys, session IDs). If the user shares a redacted-looking string (e.g. sk-****), do not try to un-redact it or guess the full value from context.
  15. If core project structure is missing, do not hand-create the scaffold. Recommend npx glubean@latest init --no-interactive when the target directory/base URL are known, or npx glubean@latest init when the user wants the interactive prompt.
  16. When the user confirms a project-level decision (auth strategy, context location, naming convention, business rule), suggest adding it to GLUBEAN.md so future sessions pick it up.
  17. Use GLUBEAN.md for project-level business rules, role/state semantics, and naming decisions. Do not hand-maintain endpoint matrices, status tables, request/response shapes, or case inventories there; those belong in contracts/ and should be surfaced via projection or Cloud.
  18. Author lifecycle specs with top-level workflow(). contract.http.with() defines endpoint behavior; workflow() composes existing contract cases with call() / poll() and typed state; test() remains the imperative runtime escape hatch for cases that are not worth projecting.
  19. Do not put setup / teardown inside contract cases. Contract cases are semantic promises (description, given, needs, expect, verifyRules, runnability). Runtime setup/cleanup belongs in contract.bootstrap() overlays, defineSession(), workflow().setup/teardown(), or test() builder setup/teardown depending on the problem.
  20. Keep upload and sync separate. glubean run --upload / glubean load ... --upload send target-scoped evidence. glubean sync sends project-scoped source projections for Specifications/OpenAPI/agent context and does not run tests.
  21. Current Cloud code gates glubean sync projection writes with runs:write even though the token catalog includes contracts:write for the intended contracts/sync scope. Teach the live behavior first, and call out the scope mismatch when precision matters.
  22. A contract's authored source is published verbatim for review — never inline a secret in a contract declaration. glubean ≥ 0.10.2 sync captures each contract's export const … = contract.…(…) span as-is (verify bodies included) and uploads it UNREDACTED to Cloud's Specs "Source" view — that's the point: reviewers read the real contract, so nothing inside the declaration is masked. Keep every credential OUT of the span: real secrets in .env.secrets referenced via {{KEY}} / ctx.vars.require("KEY"), and concrete non-secret values (ids, hostnames) in top-of-file consts referenced by name — both project as symbols, never values. A literal token/cookie/password written inside the contract object WILL be published.
  23. Every contract-case verify() MUST be accompanied by verifyRules — one entry per assertion, each a complete business statement of what that assertion enforces (e.g. "the returned project id matches the active dogfood project", not "id ok"). verify() is a runtime closure: projection can only record THAT it exists (hasVerify), so without verifyRules the review surface shows an opaque "custom assertions run" note and the reviewer must context-switch to the source. Cloud's Specs case card renders verifyRules as the case's Verify step, right where the reviewer reads the promise — write them as the reviewer-facing spec of the verify body, and keep them in sync when the assertions change. A verify() without verifyRules is a review-surface gap, same class as a test() without a description (rule 25). Use the object form ({ id, description, severity }) when a rule needs stable identity or its own severity; bare strings are fine otherwise.
  24. A contract id is the operation's TITLE, not a namespace. Write it as a short descriptive phrase naming what the operation does — the role an OpenAPI summary plays: list-projects, get-project, sign-in-with-email, validate-run-upload. NEVER a dot-namespaced keyword stack (platform.projects.list) — the scoped instance name, feature, tags, and endpoint already carry the hierarchy, so a dotted id just restates them and reads like machinery instead of meaning. Keep it kebab-case (rule 6) and unique across the project (when two services expose the same operation, disambiguate descriptively: platform-health-check vs dashboard-health-check). The id is the contract's IDENTITY — it projects to OpenAPI operationId, joins run evidence, and keys revision history — so renaming one later orphans its history: title it well the first time.
  25. The review surface, in priority order: the test description (#1, most important), then each assertion message (#2) — both must be complete, never shorthand. Cloud's Specs view projects a test for review WITHOUT running it: only the test description, the assertion messages, the ctx.when/switch/while branch describe labels, tags, and endpoints are shown — runtime values are NOT projected (those live in run evidence, correlated by testId). So:
    • description is the headline a reviewer reads first — give EVERY test one, a complete one-line statement of what the whole test verifies ("Authenticate; verify the session contract on success and the rejection contract on failure"), never the testId restated, never "tests login", never empty.
    • Each assertion message is the per-check statement and the ONLY thing carrying what that check verifies. Every assertion must have a message that is a full, self-contained statement of the OUTCOME — reviewable on its own, with zero abbreviation: "a valid session token is returned", not "has token"; "a missing order returns 404, not a 500", not "404 ok". A terse label, a bare restatement of code, or an empty message makes the test un-reviewable. Prefer ctx.expect(...).matcher(expected, "message") over a bare ctx.assert(cond, "message") where a matcher fits (the matcher name itself — expect.toBe, expect.toContain, expect.toHaveProperty — projects as semantic shape). Use ctx.when/switch/while (each with a describe) over bare if/for/while so EVERY outcome's intent is projected, not just the one arm execution happened to take.
  26. .track("METHOD /path") raw ctx.http calls so they review by ENDPOINT. Cloud's Specs view is organized by endpoint (the API surface): each endpoint groups its contract, the tests that hit it, and the workflows that call it — and flags coverage gaps. A test that runs a CONTRACT case is linked automatically (the case implies the endpoint). But a raw ctx.http.get(/users/${id}) projects its LITERAL url (/users/123), which fragments the coverage index away from the contract's GET /users/:id and inflates the endpoint list with one-off paths. So whenever you use raw ctx.http (the scratch/escape-hatch path — rule 2 prefers a configured client / a contract case), pin the canonical endpoint: await ctx.http.get(/users/${id}).track("GET /users/:id"). Use the SAME METHOD /path string the contract documents, so the test lands on the same endpoint entry. (If a literal URL is already canonical, e.g. GET /users, .track() is optional.)
  27. Declare a contract's FULL request + response shape, not just the status. Cloud renders each contract as an OpenAPI operation (the published api-reference view): path/query/header parameters, request body, and responses — every field with its constraint pills (format, enum, minLength/maximum, required) and a per-field description. A contract that only declares expect: { status } projects an almost-empty operation — un-reviewable. So on a contract that writes or reads a body, declare the shape with CONSTRAINTS:
    • request body + request headers at the contract level: request: { body: <SchemaLike>, headers: <SchemaLike>, contentType } (a bare request: <SchemaLike> is body shorthand). The body schema is the source of the OpenAPI requestBody.
    • query + path params per case: query: { page: { value, schema, description, required } } and params: { id: { value, schema, description } } (a ParamValue object — not a bare string — is what carries the schema + description; path params are auto-required).
    • response body + response headers per case: expect: { status, schema: <SchemaLike>, headers: <SchemaLike> }. Use a real SchemaLike ({ safeParse, toJSONSchema } returning a JSON Schema with format/enum/bounds/description, or a zod schema) — a type-only placeholder ({} as SchemaLike<T>) projects NOTHING (empty requestBody). The schema's constraints ARE the contract a reviewer reads.
  28. Every OPAQUE workflow node needs a real description. compute/action nodes project opaque (their transform/IO logic can't be statically shaped), so the node description is the ONLY thing a reviewer can read to understand the step — and one opaque node makes the whole workflow a PARTIAL projection. Never leave an opaque node as a bare two-word name: pass { id, description } instead of a string id — b.compute({ id: "mark-seed", description: "Mark the run as needing a seed user (sets state.seed=true) so a later step provisions the first account." }, (s) => ...). The description is the partial projection's way to recover the semantics the shape can't.
  29. Gate every project on typecheck + lint, not just a green run. glubean run executes via tsx — it does NOT typecheck, so a contract can run green while tsc rejects it. And tsc has NO deprecation diagnostic at all (@deprecated is only an editor strikethrough) — the only CLI/CI way to catch it is ESLint's @typescript-eslint/no-deprecated. Glubean ships Zod 4, whose deprecated surface (.passthrough(), z.string().uuid()/email()/datetime()) is exactly what Zod-3-era code uses, so projects silently rot. So: ensure the project has npm run typecheck (tsc) AND npm run lint (eslint no-deprecated), and run BOTH before "done", reporting each exit 0 alongside the run. If a project lacks them — including older glubean init output — retrofit the gates (tsconfig + eslint.config.js + scripts + devDeps). Write verify as async verify(ctx, res: T) and use Zod 4 forms (z.email()/z.uuid()/z.iso.datetime(), .loose()). Full recipe + retrofit steps: references/patterns/type-safety.md.
  30. For existing behavior, derive verification from the implementation by default. Read references/implementation-derived-verification.md before authoring tests or contracts. Inspect the accessible source and existing tests, derive meaningful success, validation, auth, state, conflict, and error cases, and map every discovered rule to executed evidence or an explicit gap. A request/response schema or OpenAPI projection documents a constraint but does not prove that the implementation enforces it. Only generate a reachability/status-only case when the user explicitly asks for smoke.
  31. Order a contract's cases by ascending response status, happy path first. Cases render in SOURCE order everywhere they are reviewed — Cloud's Specs case card, MCP x-glubean-cases, and the projection — so with 10+ cases a scattered order buries the happy path and the reviewer has to hunt for it. Order cases by ascending expect.status (2xx success first, then 4xx climbing: 400 → 401 → 403 → 404 → 409 → 422, then 5xx), and within one status, common → extreme (the everyday validation failure before the rare edge). The success case is 2xx, so this always puts it on top. Same review-surface discipline as rules 23–28: the reader should walk the operation's spine — reach it, succeed, then fail in a predictable progression — without scanning the whole block. (When cases have a lifecycle dependency — e.g. a deferred seed a later case reuses — keep the dependency readable; ordering is the default, not a reason to break a needed sequence.)
  32. Use discover for inventory and doctor before Cloud publication. Prefer the package scripts generated by glubean init: npm run discover writes the asset catalog; npm run doctor performs read-only asset plus token/project/scope/target readiness checks; npm run sync explicitly uses .env and publishes project-scoped source projections; npm run upload runs the local profile and uploads target-scoped evidence. A successful doctor check does NOT authorize sync/upload — it only proves readiness. Never collapse sync and upload into one operation or publish merely because doctor passed.

For detailed navigation, start with references/index.md.

For migration inside a real project, read references/patterns/migration.md before generating files.

If $ARGUMENTS is provided, treat it as the target endpoint, file, tag, or natural-language test request.