SuperAPI AI Directory

mockstar-mock

Testing & quality

View repository

Updated 10-15 21:09

About this skill

mockstar-mock

The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY in this skill are to be interpreted as described in BCP 14 (RFC 2119, RFC 8174) when, and only when, they appear in all capitals.

Converts any combination of API specs and docs into a runnable mockstar mock project. It accepts OpenAPI 3.x (JSON/YAML), Postman collections, HAR captures, curl command files, GraphQL SDL/introspection, and prose documentation (Markdown, PDF, DOCX, or a URL) — one or several at once — and produces a fully configured mockstar project with realistic scenarios, dynamic handlers, and webhooks, verified by booting the server and smoke-testing every route.

The defining rule: no fabricated endpoints. Every mock traces back to a fetched or provided source. Behavior that is reasoned from context rather than explicitly documented is tagged confidence: "inferred" in the Endpoint Inventory and flagged in the coverage report. Speculation is visible and reviewable; it is never silently promoted to ground truth.

When to use

Use when you need to stand up a mock backend quickly from existing specs or documentation: mocking a third-party API for local development, building a test double from an OpenAPI spec, scaffolding fixtures from a HAR capture of real traffic, or generating stubs from API prose docs. Works on any mix of input types; the skill normalizes them all into one Endpoint Inventory before generating.

When not to use

  • mockstar HTTPS proxy mode — this skill generates config-driven mocks; it does not configure the mockstar proxy for intercepting live traffic.
  • Native GraphQL server semantics — GraphQL is modeled as a single POST /graphql endpoint with operation routing via scenarios[].when.body. If you need a proper GraphQL resolver server (subscriptions, schema stitching, persisted queries), use a dedicated GraphQL toolchain.
  • Non-mockstar mock servers — MSW, Prism, WireMock, json-server. This skill targets only the mockstar CLI.

Prerequisites

  • bunx @dhaneshpurohit/mockstar available (install: bun add -g @dhaneshpurohit/mockstar or use bunx directly with Bun). Package name matters: mockstar is published on npm as the scoped package @dhaneshpurohit/mockstar (latest ≥ 0.2.2). You MUST NOT invoke bare bunx mockstar, because the unscoped mockstar on npm is an unrelated project. Equivalent Docker image: ghcr.io/dhanesh/mockstar:latest (--runtime docker). The importer's schema-derived bodies and mixed-segment path-param handling require mockstar ≥ 0.2.2; configurable webhook signing schemes require ≥ 0.3.0 (below that, emit only the pre-0.3 signing fields — see Stage 0 step 2). The coverage report records the resolved runtime and version.
  • uv available — used to run assets/extract_text.py for binary input conversion.
  • curl available — used by assets/smoke.sh for smoke testing routes.
  • For --runtime docker: a reachable Docker daemon and the ghcr.io/dhanesh/mockstar image.

Invariants (do not violate)

  1. No fabricated endpoints. Every endpoint in the output MUST trace to a fetched or provided source. If an endpoint is absent from every input, you MUST NOT emit it, because a consumer would build against a route no source defines.
  2. Prefer native tooling. You SHOULD use bunx @dhaneshpurohit/mockstar import for OpenAPI (and losslessly-liftable Postman/HAR) rather than hand-authoring what the importer can produce. You SHOULD use bunx @dhaneshpurohit/mockstar enhance for Tier 2 placeholder rewriting rather than hand-tokenizing bodies.
  3. Schema-valid output. Verification (Stage 5) boots the server. You MUST NOT skip the boot with --no-verify except in CI pre-check mode, where boot is deferred, because a mock project that fails to boot is not a valid deliverable.
  4. No silent truncation. When --max-endpoints caps the inventory, every dropped endpoint MUST be logged to MOCKSTAR-COVERAGE.md under the "Dropped" section with its source.
  5. Read-only inputs. You MUST NOT modify source spec files, HAR archives, or documentation, because they are the sources every endpoint traces back to. The only writes are to the --into output directory and to MOCKSTAR-COVERAGE.md.

Flags

  • --into <dir> — output directory for the generated mockstar project (default: mock-<service-name> in cwd).
  • --tenant <name> — mockstar tenant name used as the mocks subdirectory (default: default). Important: when passed to mockstar import, the equals form --tenant=<name> is required — the space form is silently ignored by the importer.
  • --fidelity full|static — full generates scenarios, dynamic handlers, and webhooks from IR hints; static emits one default response per endpoint only (default: full).
  • --no-verify — skip Stage 5 boot-and-smoke verification (the generated project is not started).
  • --deterministic — passed through to bunx @dhaneshpurohit/mockstar during smoke testing; disables random Faker values for reproducible responses.
  • --max-endpoints N — cap the merged Endpoint Inventory at N records; excess are dropped in reverse-priority order and reported.
  • --runtime auto|local|docker — selects how mockstar is invoked (default: auto). auto prefers Docker (the ghcr.io/dhanesh/mockstar image) when the Docker daemon is reachable and the image is available, else falls back to local bunx @dhaneshpurohit/mockstar. local always uses bunx @dhaneshpurohit/mockstar. docker always uses Docker and fails fast if the daemon is down.
  • --image <ref> — the Docker image reference used when --runtime is docker or auto resolves to Docker (default: ghcr.io/dhanesh/mockstar:latest). For reproducibility, pin by digest: ghcr.io/dhanesh/mockstar@sha256:<digest>.

Procedure

Python interpreter. Most commands in this skill and its references are written with python3. Find the interpreter once, before the first of them. If $SKILL_PYTHON is set, use it. Otherwise, on macOS, if command -v python3 prints /usr/bin/python3 and xcode-select -p fails, that python3 is the install stub: skip it without running it. Use the first of python3, python and py -3 that is not skipped and for which <interpreter> -c 'import sys; sys.exit(sys.version_info < (3, 10))' exits 0. Write the interpreter you found in place of python3 in every command, and in any Bash(...) permission rule you suggest. Commands that start with uv run or {python} keep their own form. In a session resumed under a Bash(...) allowlist, skip this lookup and use the interpreter that the allowlist names. If none passes, follow this skill's own fallback for running without Python where it gives one; otherwise stop and tell the user that this skill needs Python 3.10 or newer.

Run these stages in order. Fan out subagents where noted.

Locating asset helpers (do this first). The bundled helpers assets/extract_text.py and assets/smoke.sh are referenced by paths relative to this skill's directory, but subagents run from the target repository — a bare assets/ path will not resolve for them. Before dispatching any subagent, resolve this skill's absolute base directory once and pass the absolute paths into every subagent prompt:

  • Obtain the skill's base directory from the harness (it is provided when the skill loads).
  • Set EXTRACT="$SKILL_DIR/assets/extract_text.py" and SMOKE="$SKILL_DIR/assets/smoke.sh".
  • If the harness does not expose the base directory, discover it: find ~/.claude ~/.config ~/.agents -path '*mockstar-mock*/assets/extract_text.py' 2>/dev/null | head -1
  • Verify: python3 "$EXTRACT" --help should print usage and exit 0.
  • You MUST hand subagents the literal absolute $EXTRACT and $SMOKE values, because a relative assets/-prefixed form will not resolve from the target repo.

Stage 0 — Compatibility preflight

Before any other work, resolve the runtime environment and validate the live CLI surface.

1. Resolve runtime.

Evaluate --runtime:

  • local — use bunx @dhaneshpurohit/mockstar directly.
  • docker — use the Docker image (--image value). Fail fast if docker version (server) is unreachable.
  • auto (default) — prefer Docker: run docker version and confirm the server is reachable, then confirm the image is reachable via docker manifest inspect <image>. If either check fails, fall back to local and note the fallback in the coverage report.

Record the resolved runtime (local or docker) and the image ref when docker is chosen.

2. Detect mockstar version.

  • Local: bunx @dhaneshpurohit/mockstar version — capture the printed version string.
  • Docker: docker run --rm <image> version — capture the printed version string.

Record the version. Note the known caveat: the CLI's printed version may lag the package version (treat it as advisory, not definitive).

Feature gate. Webhook signing wire-format fields (signedPayload, signatureTemplate, digestEncoding, mode) exist only on mockstar ≥ 0.3.0. If the detected version is below that — or the version could not be determined — do not emit them; fall back to the pre-0.3 signing fields and record the downgrade in the coverage report's "Runtime & compatibility" section. See references/mockstar-mapping.md § Signing.

3. Validate the live CLI surface.

Run the help command for the chosen runtime:

  • Local: bunx @dhaneshpurohit/mockstar help
  • Docker: docker run --rm <image> help

Confirm that import, enhance, and serve-as-default are all present in the output. For mockstar import, discover the real --tenant flag form from the live help output rather than assuming. The skill's default assumption is the equals form --tenant=<name> (verified for current mockstar), but if the live surface shows a different form, prefer the live surface and note the drift in the coverage report's "Runtime & compatibility" section.

Principle: when the live CLI surface contradicts this skill's baked-in assumptions, prefer the live truth and flag the drift in the coverage report.

4. Record findings.

Record the detected runtime, mockstar version, and image ref/digest in the coverage report's "Runtime & compatibility" section (see references/coverage-report.md).


Stage 1 — Intake & classify

Examine every provided input and classify each by type using these detection signals:

Signal Type
Top-level openapi: key starting with 3. OpenAPI 3.x
Top-level openapi: key starting with 2. OpenAPI 2.x (Swagger) — upgrade to 3.x first
{"info":{"schema":"https://schema.getpostman.com/...collection/v2.1..."} Postman v2.1
{"log":{"version":"1.2",...}} HAR 1.2
Lines starting with curl curl command file
.graphql / .gql extension, or {"data":{"__schema":...}} GraphQL SDL / introspection
.md, .pdf, .docx, plain text, or http(s):// URL Prose documentation

For binary inputs (PDF, DOCX) and local text files, convert to plain text before proceeding:

uv run "$EXTRACT" <local-file>

For documentation URLs, fetch the content first with curl -L (or WebFetch), save it to a temp file, then pass the temp file to $EXTRACT if it is binary, or use it directly if it is already plain text or Markdown. You MUST NOT pass a URL directly to $EXTRACT — the helper handles only local .pdf, .docx, .txt, .md, and .markdown files; a URL causes exit 3 ("unsupported extension").

# URL: fetch first, then optionally extract
curl -L -o /tmp/apidoc.md "https://example.com/api-docs"
# /tmp/apidoc.md is already text — use directly as prose source

# Binary (PDF, DOCX): use $EXTRACT
uv run "$EXTRACT" /path/to/spec.pdf

assets/extract_text.py emits the extracted text to stdout; capture it to a temp file and use that as the prose source for Stage 2. This is the only stage where the absolute $EXTRACT path is needed for subagents.


Stage 2 — Extract → Endpoint Inventory

Extract each input with the adapter rules in references/input-adapters.md, emitting records that are valid against references/inventory.schema.json. With two or more input types, fan out one subagent per input type by default, so the types extract in parallel. With a single input type, extract it yourself: a subagent re-reads its source and reports back, which costs more than it saves there. Key rules per adapter:

  • OpenAPI — preferred path: if the sole input is a raw OpenAPI 3.x file and no extra examples need merging, skip manual extraction and hand the file directly to bunx @dhaneshpurohit/mockstar import in Stage 3. Manual extraction is needed only when merging additional examples or overriding generated config.
  • Postman — walk item[] recursively; rewrite {{var}} placeholders to Hono :param. Lift to OpenAPI with postman-to-openapi first when saved responses are well-structured.
  • HAR — group entries by (method, normalised-path); rewrite numeric/UUID path segments to :id / :uuid. Discard entries outside the target origin.
  • curl — one record per curl invocation; all records are confidence: "inferred" because curl carries no response information.
  • GraphQL — model the entire API as a single POST /graphql record; each named operation becomes a responses[] entry with a when.body predicate matching on operationName.
  • Prose — extract from code fences, Markdown tables, and inline backtick references in that order; you MUST NOT invent endpoints that are absent from the text; otherwise the mock serves a route no source defines.

Once every input is extracted, merge the records into a single Endpoint Inventory array. Deduplication key: (method, path) (case-insensitive method, exact Hono-style path). Resolve conflicts in priority order: OpenAPI > Postman/HAR > curl > prose. Union responses[] by status code. Enforce --max-endpoints by dropping records in reverse-priority order and logging each dropped (method, path) to the coverage report with reason "dropped: max-endpoints".


Stage 3 — Generate config (hybrid)

For each endpoint in the merged Endpoint Inventory, generate a mockstar JSON mock entry following references/mockstar-mapping.md. Use the hybrid strategy:

Native path (preferred for OpenAPI and losslessly-liftable inputs):

bunx @dhaneshpurohit/mockstar import <spec-file> <out>/mocks --tenant=<tenant>

The importer writes mock JSON files to <out>/mocks/<tenant>/. Use this path for:

  • Raw OpenAPI 3.x inputs (most complete).
  • Postman or HAR inputs that have been converted to OpenAPI 3.x without data loss.

Hand-authored path (for everything else):

For curl, GraphQL, prose, and any Postman/HAR that could not be losslessly lifted, generate mock JSON entries by hand per references/mockstar-mapping.md and write them to <out>/mocks/<tenant>/:

  • Map method + path → match.method + match.path.
  • Map auth → match.headers predicate.
  • Map responses[0] (no when) → response with kind: "static".
  • Map responses[] entries with when → scenarios[] entries.
  • When --fidelity full (default):
    • statefulHints → response.kind: "dynamic" + a minimal TypeScript handler in handlers/.
    • webhookHints[] → webhooks[] on the triggering entry. When a hint carries signing, emit webhooks[].signing — expand a named provider via the cookbook in references/mockstar-mapping.md rather than hand-rolling the wire format, and you MUST NOT emit an inline secret (secretRef MUST be {{ env.NAME }} or file:/path), because it would be written into the generated project's files, and mockstar rejects it at config load.
    • For GraphQL: route operations via scenarios[].when.body matching on operationName (NOT via a match.body.jsonpath router on the parent entry).

Prefer literal example values in the initial output; Tier 2 token rewriting is deferred to Stage 4.


Stage 4 — Enhance

Run mockstar's enhance pass over the generated mocks directory:

bunx @dhaneshpurohit/mockstar enhance <out>/mocks/<tenant>

When an OpenAPI input exists, add --spec <openapi-file> so the enhancer can cross-reference the schema for more accurate placeholder selection.

The enhancer rewrites hardcoded IDs and timestamps to Tier 2 tokens ({{faker.uuid}}, {{now.iso}}, {{id("prefix_", 14)}}, etc.) and writes a _mockstarGenerated manifest (idempotent — safe to re-run). It does not validate schema conformance; that is proven by the Stage-5 boot, where mockstar validates every config file with Zod and fails fast on any invalid entry. Review the enhancer's diff — the coverage report flags any Tier 2 rewriting that changes confidence from "grounded" to "inferred".


Stage 5 — Verify

Unless --no-verify is set:

  1. Write a routes TSV file at <out>/mocks/routes.tsv with one line per endpoint:

    METHOD<TAB>/path<TAB>EXPECTED_STATUS
    

    Use the primary (no-when) status code from the Endpoint Inventory. For endpoints without a grounded status, use 200.

  2. Run the smoke suite using the absolute $SMOKE path resolved under "Locating asset helpers" (before Stage 0), passing the resolved runtime environment and the tenant via MOCKSTAR_SMOKE_TENANT:

    # local runtime
    MOCKSTAR_SMOKE_RUNTIME=local MOCKSTAR_SMOKE_TENANT=<tenant> sh "$SMOKE" <out>/mocks <out>/mocks/routes.tsv
    
    # docker runtime
    MOCKSTAR_SMOKE_RUNTIME=docker MOCKSTAR_SMOKE_IMAGE=<image> MOCKSTAR_SMOKE_TENANT=<tenant> \
      sh "$SMOKE" <out>/mocks <out>/mocks/routes.tsv
    

    Tenant selection is mandatory for any non-default tenant. mockstar resolves a tenant before routing (default modes: path + header). Only the default tenant is served at the bare path; a named tenant's routes all 404 at the bare path even though the mocks are valid. smoke.sh sends x-mockstar-tenant: <tenant> on every probe (header mode) so named tenants resolve — do not skip MOCKSTAR_SMOKE_TENANT, or a correct project will look broken and trigger a false fix-loop. See "Tenant selection" under Output layout.

    assets/smoke.sh boots mockstar via the chosen runtime. Both paths poll GET /health for liveness before testing routes (/health is tenant-agnostic; /ready reports drain state). Set MOCKSTAR_SMOKE_PORT for a non-default smoke port. The server boots via the default command (bunx @dhaneshpurohit/mockstar <config-root>); the explicit serve subcommand (bunx @dhaneshpurohit/mockstar serve <config-root>) is equivalent — both are valid.

  3. For any FAIL lines from the smoke run, inspect the generated config, fix the entry, and re-run until all routes pass. Do not ship a project with smoke failures. Before editing a mock in response to a 404, confirm the probe carried the tenant selector — a bare-path 404 on a named tenant is a selector problem, not a mock problem.


Stage 6 — Report

Write MOCKSTAR-COVERAGE.md to the output directory root, following the template in references/coverage-report.md, with these sections:

  • Summary — inputs list, tenant name, endpoint count (grounded vs. inferred), boot verdict.
  • Endpoints table — one row per mock: method, path, mock file, source, locator, confidence.
  • Grounded vs. inferred — counts and explanation.
  • Review me (speculative) — every inferred scenario, dynamic handler, webhook, and webhook signing scheme with the reason it was inferred and its source.
  • Gaps — endpoints or behaviors present in the source docs but not mocked (e.g. prose that names an endpoint without a path, or a schema type with no example).
  • Dropped — endpoints cut by --max-endpoints, with source.
  • Conflicts — endpoints described differently across inputs and how conflicts were resolved.

Output layout

The config-root is a mocks/ directory containing one <tenant>/ subdirectory per tenant, with a sibling handlers/ directory for dynamic handlers. Default layout (when --into is not set, <service> is derived from the primary spec's title or the target URL hostname):

mock-<service>/
  mocks/
    <tenant>/         # mockstar mock JSON files (e.g. users.json, orders.json)
  handlers/           # TypeScript dynamic handler files (--fidelity full only)
  routes.tsv          # routes used for smoke testing
  MOCKSTAR-COVERAGE.md

With --into <dir>:

<dir>/
  mocks/
    <tenant>/
  handlers/
  routes.tsv
  MOCKSTAR-COVERAGE.md

Tenant selection (how a consumer reaches the mocks)

mockstar runs tenant resolution as the first routing step, with default modes path and header. A request that carries no selector falls back to the default tenant. So:

  • The default tenant is served at the bare path — GET /pet/findByStatus.
  • Any named tenant (anything other than default) is reachable only via a selector:
    • Path mode: prefix the path with /t/<tenant> — GET /t/petstore/pet/findByStatus (the /t/<tenant> prefix is stripped before route matching).
    • Header mode: send x-mockstar-tenant: <tenant> — GET /pet/findByStatus with the header.
    • (Subdomain mode <tenant>.host exists but is off by default.)

Consequence for this skill: if --tenant is not default, every consumer — the smoke test, the coverage-report examples, and whoever calls the mock — MUST use a selector. The coverage report's Summary states the tenant and the exact selector to use. When in doubt, use default as the tenant so bare-path access works with zero configuration. No mockstar.config.json is required for tenant routing: the CLI serve path always enables path + header modes.

Ways to run mockstar

The generated mocks/ config-root is portable across all of mockstar's distribution channels. Pick per persona; each honours the same tenant selectors above.

  • Developer — bunx (hot reload): bunx @dhaneshpurohit/mockstar mocks/ --handlers handlers/. Editing a mocks/<tenant>/*.json file hot-reloads only that tenant.
  • Docker (shared/staging/CI): mount mocks/ → /config/mocks and handlers/ → /config/handlers (see "Docker delivery" below). The container binds 0.0.0.0:3000 and is crash-only — the orchestrator MUST set a restart policy (--restart=always, K8s restartPolicy: Always, or systemd Restart=on-failure). Use /health for liveness and /ready for load-balancer drain.
  • SDET — library embed (in-process, no daemon): import mockstar into a Jest/Vitest/bun test suite and point it at the same config-root:
    import { launch } from '@dhaneshpurohit/mockstar';
    const instance = await launch({
      configRoot: './mocks', handlersDir: './handlers',
      deterministic: true, watch: false, installCrashHandlers: false,
    });
    const res = await instance.server.hono.request('http://localhost/pet/findByStatus', {
      headers: { 'x-mockstar-tenant': 'petstore' },   // selector, as above
    });
    await instance.stop();
    
  • Offline CI / Windows — compiled binary: bun build src/cli.ts --compile --outfile mockstar then ./mockstar mocks/ (no Bun install needed at run time).

Boot the server locally from the output root (default command; serve is an equivalent alias):

bunx @dhaneshpurohit/mockstar mocks/
bunx @dhaneshpurohit/mockstar serve mocks/ --deterministic --no-watch --port 3000
# named tenant, path mode:   curl http://127.0.0.1:3000/t/<tenant>/<path>
# named tenant, header mode: curl -H 'x-mockstar-tenant: <tenant>' http://127.0.0.1:3000/<path>

Docker delivery

When --runtime docker or auto resolves to Docker, the skill emits two delivery artifacts:

(a) Mount command (local dev — no build step):

docker run --rm -p 3000:3000 \
  -v "<abs>/mocks:/config/mocks:ro" \
  -v "<abs>/handlers:/config/handlers:ro" \
  <image>

Drop the -v handlers bind-mount when no dynamic handlers exist. <abs> is the absolute path to the output directory; <image> is the resolved ref from Stage 0 (tag or digest).

(b) Baked image (share / CI):

The skill renders assets/Dockerfile.template — filling <TAG> with the detected image ref/digest from Stage 0 — and writes it as Dockerfile in the output directory root. Build and run with:

docker build -t <name> . && docker run --rm -p 3000:3000 <name>

The template (assets/Dockerfile.template) copies mocks/ into /config/mocks (and optionally handlers/ into /config/handlers) and inherits the base image's ENTRYPOINT/CMD, which serves /config/mocks on port 3000.

Air-gap fallback (Stage 0 runtime resolution): if docker manifest inspect <image> fails due to no network but the image is already present locally, docker image inspect <image> is sufficient to treat docker as available. The coverage report notes whether the image ref was verified remotely or resolved from the local cache.

Output style. Write reports and explanations for the user in about 80% ASD-STE100 Simplified Technical English. Use short sentences, common words, active voice and one action per step. This style is for prose to the user, not for the code, prompts or files that this skill makes.

References

  • references/input-adapters.md — per-format extraction rules and merge/dedupe logic.
  • references/inventory.schema.json — JSON Schema for Endpoint Inventory records.
  • references/mockstar-mapping.md — IR → mockstar config field mapping, scenarios, handlers, webhooks, Tier 2 tokens.
  • references/coverage-report.md — the MOCKSTAR-COVERAGE.md template.

Assets

  • assets/extract_text.py — converts local PDF/DOCX/Markdown/text inputs to plain text (a documentation URL is fetched separately, per stage 1).
  • assets/smoke.sh — boots mockstar and smoke-tests every route in a TSV file.
  • assets/fixtures/ — sample inputs used by the test suite (petstore-mini.yaml, routes.tsv).

Subagent dispatch note

The skill's asset helpers (assets/extract_text.py, assets/smoke.sh) live in this skill's directory, not in the target repository. Subagents launched in Stage 2 and Stage 5 run from the target repo's working directory, so a relative assets/ path will not resolve for them. The rule for handing them the absolute paths is in "Locating asset helpers" above. This mirrors the pattern used by the base-in-reality skill for its fetch_sources.py helper.