dc7a4205ec
- Fix verify.md --changed exit code (0 → 2) - Add 'deep' as alias for 'thorough' in generation profile resolution - Fix PLUGIN_CONTRACTS_SPEC status: Partially implemented (registry done, runner pending) - Fix OUTBOUND_CONTRACT_MOCKING_SPEC status: Implemented (Phase 1) - Fix cli.md environment matrix to match actual code granularity - Fix chaos.md: document delay handler is currently a no-op - Fix getting-started.md warning: note APOPHIS does not proactively detect nondeterminism - Add variants section to getting-started.md - Build: clean | Tests: 849 pass, 0 fail
146 lines
3.8 KiB
Markdown
146 lines
3.8 KiB
Markdown
# Getting Started with APOPHIS
|
|
|
|
Get from install to your first behavioral bug in 10 minutes.
|
|
|
|
APOPHIS is inspired by [Invariant-Driven Automated Testing](https://arxiv.org/abs/2602.23922) (Malhado Ribeiro, 2021): instead of only validating request and response shape, encode intended behavior as executable contracts and let the tool find violations automatically.
|
|
|
|
## Prerequisites
|
|
|
|
- Node.js 20.x or 22.x
|
|
- A Fastify app with `@fastify/swagger` registered
|
|
|
|
## Step 1: Install
|
|
|
|
```bash
|
|
npm install apophis-fastify fastify @fastify/swagger
|
|
```
|
|
|
|
## Step 2: Scaffold
|
|
|
|
```bash
|
|
apophis init --preset safe-ci
|
|
```
|
|
|
|
This creates:
|
|
|
|
- `apophis.config.js` — config with a `quick` profile
|
|
- `APOPHIS.md` — preset-specific guidance
|
|
- Package script: `npm run apophis:verify`
|
|
|
|
## Step 3: Add One Behavioral Contract
|
|
|
|
Pick one important route. Add an `x-ensures` clause that checks behavior across operations:
|
|
|
|
```javascript
|
|
import crypto from 'crypto';
|
|
|
|
app.post('/users', {
|
|
schema: {
|
|
'x-category': 'constructor',
|
|
'x-ensures': [
|
|
// BEHAVIORAL: Creating a user must make it retrievable
|
|
'response_code(GET /users/{response_body(this).id}) == 200'
|
|
]
|
|
}
|
|
}, async (request, reply) => {
|
|
const { name } = request.body;
|
|
const id = `usr-${crypto.createHash('sha256').update(name).digest('hex').slice(0, 8)}`;
|
|
reply.status(201);
|
|
return { id, name };
|
|
});
|
|
```
|
|
|
|
> **Warning:** Using `Date.now()` or `Math.random()` in handlers breaks determinism and replay. Use a stable function of the input instead. APOPHIS does not proactively detect nondeterministic handlers; it warns only when a replay diverges from the original run.
|
|
|
|
## Step 4: Run Verify
|
|
|
|
```bash
|
|
apophis verify --profile quick --routes "POST /users"
|
|
```
|
|
|
|
## Example Failure
|
|
|
|
If your `GET /users/:id` handler has a bug (always returns 404), APOPHIS catches it:
|
|
|
|
```text
|
|
Contract violation
|
|
POST /users
|
|
Profile: quick
|
|
Seed: 42
|
|
|
|
Expected
|
|
response_code(GET /users/{response_body(this).id}) == 200
|
|
|
|
Observed
|
|
GET /users/usr-7d865e returned 404
|
|
|
|
Why this matters
|
|
The resource created by POST /users is not retrievable.
|
|
|
|
Replay
|
|
apophis replay --artifact reports/apophis/failure-2026-04-28T12-30-22Z.json
|
|
|
|
Next
|
|
Check the create/read consistency for POST /users and GET /users/{id}.
|
|
```
|
|
|
|
## Step 5: Replay and Fix
|
|
|
|
Copy the replay command and run it:
|
|
|
|
```bash
|
|
apophis replay --artifact reports/apophis/failure-2026-04-28T12-30-22Z.json
|
|
```
|
|
|
|
Fix the bug in your handler. Re-run verify. The failure should now pass.
|
|
|
|
## Next Steps
|
|
|
|
- Add more routes to your profile: `apophis verify --profile quick --routes "POST /users,PUT /users/:id"`
|
|
- Use wildcards to match route patterns: `apophis verify --routes 'POST /api/*'`
|
|
- Run all routes: `apophis verify --profile quick`
|
|
- Run only changed routes in CI: `apophis verify --profile ci --changed`
|
|
- Requires a git repository.
|
|
- Use machine-readable output in CI: `apophis verify --profile ci --format json-summary`
|
|
- Add observe mode for runtime drift detection: see [observe.md](observe.md)
|
|
- Add qualify mode for scenario, stateful, and chaos checks: see [qualify.md](qualify.md)
|
|
|
|
## Variants
|
|
|
|
Test the same route with different headers or content types:
|
|
|
|
```javascript
|
|
await fastify.apophis.contract({
|
|
variants: [
|
|
{ name: 'json', headers: { accept: 'application/json' } },
|
|
{ name: 'xml', headers: { accept: 'application/xml' } }
|
|
]
|
|
})
|
|
```
|
|
|
|
Or declare variants in the route schema:
|
|
|
|
```javascript
|
|
app.get('/users', {
|
|
schema: {
|
|
'x-variants': [
|
|
{ name: 'json', headers: { accept: 'application/json' } }
|
|
]
|
|
}
|
|
})
|
|
```
|
|
|
|
## Config Reference
|
|
|
|
For the full configuration reference, see [CLI Reference](cli.md).
|
|
|
|
## Monorepo Workspaces
|
|
|
|
Use `--workspace` to run verify or doctor across all packages:
|
|
|
|
```bash
|
|
apophis verify --workspace --profile quick --format json
|
|
```
|
|
|
|
See [CLI Reference](cli.md) for workspace output format and exit codes.
|