api-test
Overview
api-test is the suite orchestrator for the api-testing stack. It loads a v1
suite manifest, dispatches each case to the matching backend runner
(REST / GraphQL / gRPC / WebSocket), captures results JSON to stdout, and
optionally writes a JUnit XML file plus a Markdown summary.
The dispatch + run loop is implemented by api_testing_core::suite::runner;
JUnit rendering by api_testing_core::suite::junit; results by
api_testing_core::suite::results::{SuiteRunResults, SuiteCaseResult}. See
crates/api-testing-core/README.md for the
shared library surface.
Usage
Usage: api-test <command> [args]
Commands:
run Run a suite (default)
summary Render a Markdown summary from results JSON
completion Print shell completion script
Common options (run; see `api-test run --help` for full details):
--suite <name> Resolve suite under tests/api/suites/<name>.suite.json
--suite-file <path> Explicit suite file path
--tag <tag> Filter cases by tag (repeatable; AND semantics)
--only <csv> Run only listed case IDs (comma-separated)
--skip <csv> Skip listed case IDs (comma-separated)
--fail-fast Stop after first failure
--out <path> Write results JSON to a file (stdout still emits JSON)
--junit <path> Write optional JUnit XML to a file
-h, --help Print help
Examples:
api-test --help
api-test --suite smoke --help
api-test run --suite smoke --out results.json
api-test completion zsh
The first positional run is implicit: bare flag invocations
(e.g. api-test --suite smoke) are rewritten to
api-test run --suite smoke before parsing. Explicit run / summary /
completion words are honored as-is.
Commands
run(default): execute a suite and write results JSON to stdout. Required: exactly one of--suite <name>or--suite-file <path>. Options:--out <path>,--junit <path>,--allow-writes,--tag <tag>(repeatable; AND semantics),--only <csv>,--skip <csv>,--fail-fast/--continue(mutually exclusive;--continueis the default).summary: render a Markdown summary from a results JSON document (stdin by default, or--in <path>). Options:--out <path>,--slow <n>(default5),--hide-skipped,--max-failed <n>(default50),--max-skipped <n>(default50),--no-github-summary(skip writing to$GITHUB_STEP_SUMMARY).completion: print a shell completion script. Argument:<SHELL>(bashorzsh).
run exit codes (driven by SuiteRunResults::exit_code):
0— every casepassedorskipped.2— at least one casefailed.1— orchestrator-level error (suite not found, schema invalid, results serialization failure, JUnit write failure).
Suite manifest (v1)
Top-level fields (camelCase):
version(required, must be1).name(optional; falls back to the suite filename).defaults(optional; per-protocol overrides applied to each case).auth(optional; shared login flow that produces a bearer token reused by REST/GraphQL cases — seeapi-testing-coredocs for details).cases(required, ordered list of case objects).
defaults accepts one block per backend (every block is independently
optional):
| Block | Fields |
|---|---|
defaults.rest |
configDir (default setup/rest), url, token |
defaults.graphql |
configDir (default setup/graphql), url, jwt |
defaults.grpc |
configDir (default setup/grpc), url, token |
defaults.websocket |
configDir (default setup/websocket), url, token |
url is either an endpoint preset name resolved against the backend's
endpoints.env or a literal URL/target; token / jwt is a profile name
resolved against the backend's token store. Suite-level defaults.env
and defaults.noHistory apply to every case unless overridden inline.
Case dispatch
Each case object selects a backend via the type field. The runner
normalizes ws to websocket before dispatch (see
api_testing_core::suite::runner::context::case_type_normalized), so both
spellings are accepted.
type value |
Backend runner | Required case field | Notes |
|---|---|---|---|
rest |
api-rest (in-process via api_testing_core::rest) |
request (path to *.request.json) |
Optional: env, url, token, configDir, noHistory, allowWrite, cleanup. |
rest-flow (alias rest_flow) |
api-rest login + main request |
loginRequest and request |
Optional: tokenJq (jq filter that extracts the bearer token from the login response; defaults to a permissive accessToken/access_token/token selector). |
graphql |
api-gql (in-process via api_testing_core::graphql) |
op (path to *.graphql) |
Optional: vars (path to JSON variables), jwt, allowErrors, expect.jq (required when allowErrors=true). |
grpc |
api-grpc (in-process via api_testing_core::grpc) |
request (path to *.grpc.json) |
Optional: env, url, token, configDir, noHistory, plus gRPC-only fields handled by the request file (grpcProto, grpcImportPaths, grpcPlaintext). |
websocket (alias ws) |
api-websocket (in-process via api_testing_core::websocket) |
request (path to *.ws.json or *.websocket.json) |
Optional: env, url, token, configDir, noHistory. |
Cases are filtered before dispatch using --tag (AND semantics across
repeats), --only, --skip, and the allowWrite safety gate. A case
that opts into writes (allowWrite: true) only runs when --allow-writes
is set, or when the suite-level
API_TEST_ALLOW_WRITES_ENABLED env var is truthy.
Mixed protocol suite example
Results JSON envelope
Stdout is exactly one JSON object per run invocation, also written to
--out <path> when supplied:
Per-case fields (camelCase, optional fields are omitted when empty):
statusis one ofpassed,failed,skipped.typeis the normalized dispatch value (wscollapses towebsocket).commandcarries the rendered backend command snippet (REST/GraphQL emit the equivalentapi-rest call/api-gql callinvocation; gRPC and WebSocket emit the in-process equivalent).messageis set onfailed/skippedcases (e.g.cleanup_failed,rest_flow_login_failed, the selection-skip reason, or the runner's own diagnostic).assertionsmirrors per-runner expect output (currently surfaced by GraphQL expect/allow-errors flow and gRPC/WebSocket assertions).stdoutFile/stderrFileare repo-relative paths underoutputDir.
The output directory defaults to <repo>/out/api-test-runner/<runId>/
and can be overridden with the API_TEST_OUTPUT_DIR env var (relative
paths resolve against the repo root).
JUnit XML output
--junit <path> writes a single <testsuite> element per run via
api_testing_core::suite::junit::render_junit_xml. The shape is:
command: api-gql call ...
stdoutFile: out/.../gql-health.response.json
stderrFile: out/.../gql-health.stderr.log
Notes:
<testsuite>attributes:name(suite name or filename),tests,failures,skipped(taken fromsummary).<testcase>attributes:name= caseid,classname= normalized casetype,time=durationMs / 1000formatted to 3 decimals.<failure>carries the runnermessageand a body listingcommand,stdoutFile,stderrFilewhen present (XML-escaped).<skipped>carries the skip reason inmessage.- Passed cases emit
<testcase ... />with no child element.
Environment variables
| Variable | Effect |
|---|---|
API_TEST_OUTPUT_DIR |
Override the per-run output directory base (default <repo>/out/api-test-runner). Relative values resolve against the repo root. |
API_TEST_PROGRESS |
auto (default), on/1/true/yes, or off/0/false/no. Controls the stderr progress bar. |
API_TEST_ALLOW_WRITES_ENABLED |
Truthy value forces --allow-writes on for every case (still gated by per-case allowWrite: true). |
API_TEST_REST_URL |
Overrides REST endpoint resolution for suite runs. |
API_TEST_GQL_URL |
Overrides GraphQL endpoint resolution for suite runs. |
API_TEST_GRPC_URL |
Overrides gRPC target resolution for suite runs. |
API_TEST_WS_URL |
Overrides WebSocket target resolution for suite runs. |
API_TEST_AUTH_JSON |
Default secret-env name for auth.secretEnv (overridable in the manifest). |
GITHUB_STEP_SUMMARY |
When set and summary --no-github-summary is not passed, the rendered Markdown summary is appended to this file. |
Reuse matrix (unchanged vs additive protocol paths)
| Capability | Status | Evidence |
|---|---|---|
| suite selection/filtering | unchanged | cargo test -p nils-api-testing-core --test suite_rest_graphql_matrix |
| run/result envelope | unchanged | cargo test -p nils-api-testing-core --test suite_runner_loopback |
| summary/JUnit generation | unchanged | cargo test -p nils-api-testing-core suite::summary suite::junit |
| grpc protocol dispatch | additive grpc | cargo test -p nils-api-testing-core --test suite_runner_grpc_matrix |
| websocket protocol dispatch | additive websocket | cargo test -p nils-api-testing-core --test suite_runner_websocket_matrix |
| suite schema validation | additive | cargo test -p nils-api-test suite_schema |
| env override wiring | additive | cargo test -p nils-api-testing-core suite::runtime_tests |
Docs
- Docs index
- Sibling backend crates:
api-rest,api-gql,api-grpc,api-websocket. - Shared library:
api-testing-core.