A suite is a versioned YAML or JSON document. Version 1 requires ruleset and at least one case.
Unknown fields are errors. Each input and expected-output fixture must contain exactly one YAML or JSON document.
The example uses defined, which accepts an empty string. If your policy requires a nonempty ID,
add that check and include an empty-string fixture.
The example suite:
version: 1
ruleset: ./rules.yaml
cases:
- id: valid-operation
input: ./valid.yaml
rules: [operation-id-required]
expect:
diagnostics: []
execution:
operation-id-required:
status: completed
matches: {min: 1, max: 1}
invocations: {min: 1, max: 1}
- id: missing-operation-id
input: ./missing-id.yaml
rules: [operation-id-required]
expect:
diagnostics:
- rule: operation-id-required
severity: error
path: $.paths['/pets'].get
execution:
operation-id-required:
status: completed
invocations: {min: 1}
- id: verify-repair
input: ./missing-id.yaml
after: ./valid.yaml
rules: [operation-id-required]
expect:
diagnostics:
- rule: operation-id-required
afterExpect:
diagnostics: []
execution:
operation-id-required:
status: completed
invocations: {min: 1}
change:
noNew: true
removed:
- rule: operation-id-required
Suite settings
ruleset is a local ruleset file. Use the normal extends syntax to select built-in or shared rules.
Each case can select rule IDs with rules; omit it to execute the loaded ruleset. Unknown or disabled IDs
are errors. Rules need at least one nonempty selector and one action.
Document and reference diagnostics produced by the engine remain part of the results.
functions points to a directory of existing vacuum custom functions. ignoreFile points to the usual
ignore-file format. Both paths resolve relative to the suite.
Write functions with the JavaScript function guide
or Go function guide.
For JavaScript, getSchema().name is the name used by the rule’s then.function; runRule(input) returns
an array of findings. Set functions: ./functions in the suite to load the directory.
options:
timeoutMs: 5000
lookupTimeoutMs: 500
maxRuleConcurrency: 32
runTimeoutMs: 30000
remote: false
resolveAllRefs: false
nestedRefsDocContext: false
Zero or omitted per-rule, lookup, and concurrency limits use the defaults shown above. Omitted
runTimeoutMs means no overall motor deadline. The deadline applies to each engine invocation;
before/after and autofix cases perform more than one invocation. Preparation checks cancellation between
phases; it is not a hard interruption of an active parser or arbitrary Go callback.
JavaScript script loading and getSchema() evaluation each use timeoutMs. A timeout, thrown error,
or invalid schema fails the load, including when other functions in the directory loaded successfully.
remote controls document-reference lookup. Existing ruleset extensions use the ruleset loader;
remote extensions are recorded as a reproducibility limitation. JavaScript fetch retains vacuum’s existing
security defaults. The harness does not enable HTTP or private-network access.
Use format: auto (the default) for OpenAPI, AsyncAPI, and Arazzo. Use json-schema for JSON Schema,
or fragment to bypass the document-type check for a fragment.
Exact diagnostics
diagnostics: [] requires no findings. Otherwise every expected entry consumes exactly one diagnostic.
Extra findings fail the test. Ordering does not matter, duplicate counts do.
expect:
diagnostics:
- rule: operation-id-required
severity: error
path: $.paths['/pets'].get
source: missing-id.yaml
rule is required. Optional fields are severity, path, paths, source, line, column, and message.
Source coordinates are one-based. Sources are suite-relative. path checks the primary path; paths
checks the complete alternate-path set, without depending on order. An incomplete path set cannot satisfy
an exact paths assertion. paths: [] requires an empty alternate-path set; omit paths to skip that check.
Omit message if wording changes should not fail the test.
Omit diagnostics when a case only asserts execution evidence. Every expectation must contain at least
one diagnostic or execution assertion. An omitted diagnostic assertion does not mean “expect no findings”.
Prove that the rule ran
expect:
diagnostics: []
execution:
operation-id-required:
status: completed
matches: {min: 1}
invocations: {min: 1}
ignored: {max: 0}
Counts accept inclusive min and max; set both for an exact count. status accepts completed or
excluded. You cannot assert a timeout as a successful test. See execution evidence.
Verify a repair
Supply after and either afterExpect, change, or both. Both documents use the same rules and options.
change.added and change.removed are exact diagnostic lists. noNew: true rejects new findings.
unchanged: true requires no added or removed findings. Comparison accounts for source locations, reference
aliases and duplicate counts. Message or severity changes count as a removed finding and an added finding.
Verify an autofix
fix:
output: ./expected-fixed.yaml
expect:
diagnostics: []
The harness applies registered fixes in memory, compares the output semantically with the expected document,
lints it again, and checks that another fix makes no further semantic change. Input files are never overwritten.
fix.expect.diagnostics checks the separate lint run after the repair. fix.expect.execution checks the
repair pass, including its fixed, matches and invocations counts.
A case cannot combine fix and after.
Go callers register AutoFixFunctions in RunOptions. Go plugins can register repairs through
Manager.RegisterAutoFixFunction in their existing Boot function. Those repairs also work with
vacuum lint --fix --functions ./plugins. An unregistered repair is an execution error.
A repair callback error or panic fails the run, even if its unchanged output matches the expected fixture.
The harness does not infer repairs from a JavaScript lint function.
The suite JSON Schema describes the fields. Semantic checks also reject contradictory count bounds, duplicate case IDs, and incomplete before/after declarations. Case IDs start with a letter or digit and contain only letters, digits, underscores, dots, and hyphens.
