2026-03-10 00:00:00 -07:00
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog ](https://keepachangelog.com/en/1.0.0/ ),
and this project adheres to [Semantic Versioning ](https://semver.org/spec/v2.0.0.html ).
2026-05-21 20:39:36 -07:00
---
## [APOPHIS 2.7.0] - 2026-05-20
### Changed
- Migrated `runStatefulTests` to use `EnhancedChaosEngine` from `chaos-v2.ts` (was using deprecated `ChaosEngine` from `chaos.ts` ). Stateful and contract runners now share a single chaos stack.
- Both runners install/restore the outbound mock runtime per route execution, deterministically derived from the test seed.
2026-03-10 00:00:00 -07:00
### Added
2026-05-21 20:39:36 -07:00
- CLI route discovery for apps without pre-registered APOPHIS: routes can be detected via `hasRoute` introspection, but inline `x-ensures` /`x-requires` contract annotations on route schemas are only discoverable when the APOPHIS plugin is registered before routes (via the `onRoute` hook). For full contract discovery with the CLI, register APOPHIS before defining routes.
- Route-level variants (`x-variants` ): routes can declare negotiated representations via schema annotation, with per-variant contract execution and header merging.
- Protocol pack presets: reusable OAuth 2.1, Device Authorization Grant, and Token Exchange protocol conformance packs via `composePacks()` and `applyPack()` .
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
### Fixed
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- Config validation errors now return exit code 2 (usage error) instead of 3 (internal error).
- Replay correctly handles apps without pre-registered APOPHIS plugin.
- Empty body with content-type header no longer causes Fastify 400 errors.
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
## [APOPHIS 2.6.0] - 2026-04-29
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
### Changed
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
#### Justin Support Removed
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- **Removed**: Justin (subscript) expression evaluator. APOSTL is now the exclusive contract expression language.
- **Removed**: `src/formula/justin.ts` , `src/formula/context-builder.ts` .
- **Removed**: `subscript` dependency from package.json.
- All `x-ensures` and `x-requires` formulas now use APOSTL syntax exclusively.
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
#### WATCHDOG Branding Removed
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- All internal references to WATCHDOG renamed to APOPHIS.
- Package name finalized as `@apophis/fastify` .
- Binary renamed from `watchdog` to `apophis` .
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
### Migration
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
All formulas must use APOSTL syntax:
``` javascript
// APOSTL (required)
'x-ensures' : [ 'status:201' , 'response_body(this).id != null' ]
// Justin (removed in v2.6.0)
'x-ensures' : [ 'statusCode == 201' , 'response.body.id != null' ]
```
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
See [Getting Started Guide ](docs/getting-started.md ) for full APOSTL reference.
## [APOPHIS 2.5.0] - 2026-02-22 — APOSTL Discovery
### Project Renamed
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
The project has been renamed from **WATCHDOG ** to **APOPHIS ** following the discovery of the APOSTL expression language. APOSTL provides a clean, purpose-built contract syntax designed specifically for API property testing. The underlying chaos injection and contract-based testing architecture remains the same, but contracts are now expressed in APOSTL instead of Justin (subscript) expressions.
2026-03-10 00:00:00 -07:00
### Added
2026-05-21 20:39:36 -07:00
#### APOSTL Expression Language
- **New**: APOSTL parser, tokenizer, evaluator, and substitutor (`src/formula/` ).
- **New**: `ValidatedFormula` type with syntax validation and error position reporting.
- **New**: Extension predicates registered as APOSTL context variables.
- **New**: Async APOSTL evaluation via `evaluateAsync()` .
2026-03-10 00:00:00 -07:00
#### Contract-Driven Outbound Mocking
2026-05-21 20:39:36 -07:00
Routes can now declare the contracts and expectations of their outbound dependencies. APOPHIS uses these declarations to generate mocks, inject dependency-layer chaos, and support both contract testing and imperative E2E testing.
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- **New**: `ApophisOptions.outboundContracts` — register shared dependency contracts once.
- **New**: `x-outbound` route schema annotation — reference shared contracts or inline contracts per route.
- **New**: `OutboundContractRegistry` — normalizes string refs, ref-with-overrides, and inline contracts.
- **New**: `OutboundMockRuntime` — patches `globalThis.fetch` during route execution.
- **New**: `TestConfig.outboundMocks` — control mode, overrides, and unmatched behavior.
- **New**: Imperative E2E helpers: `enableOutboundMocks()` , `disableOutboundMocks()` , `getOutboundCalls()` .
- **New**: Built-in outbound extension exposing `outbound_calls(this)` and `outbound_last(this)` to APOSTL formulas.
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
``` javascript
2026-03-10 00:00:00 -07:00
await fastify . register ( apophis , {
outboundContracts : {
'stripe.paymentIntents.create' : {
target : 'https://api.stripe.com/v1/payment_intents' ,
method : 'POST' ,
response : {
200 : { type : 'object' , properties : { id : { type : 'string' } } } ,
402 : { type : 'object' , properties : { error : { type : 'object' } } }
}
}
}
} )
const schema = {
'x-outbound' : [ 'stripe.paymentIntents.create' ] ,
'x-ensures' : [
'if response_code == 200 then outbound_last(this).stripe.paymentIntents.create.response.statusCode == 200 else true'
]
}
```
2026-05-21 20:39:36 -07:00
#### Mutation Testing
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- **New**: `src/quality/mutation.ts` — synthetic bug injection to measure contract strength.
- **New**: `runMutationTesting()` — generates mutations and verifies tests catch them.
- **New**: Mutation score reporting (0-100%) with weak contract identification.
2026-03-10 00:00:00 -07:00
### Changed
2026-05-21 20:39:36 -07:00
- Package name: `@watchdog/fastify` → `@apophis/fastify` .
- Binary: `watchdog` → `apophis` .
- Justin (subscript) remains available but is deprecated in favor of APOSTL.
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
---
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
## [WATCHDOG 2.4.0] - 2025-08-14
2026-03-10 00:00:00 -07:00
### Added
2026-05-21 20:39:36 -07:00
#### Dependency-Aware Chaos Testing
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- **New**: `ChaosConfig.outbound` — intercept outbound HTTP requests to dependencies.
- **New**: Chaos event reporting in test diagnostics.
- **New**: Configurable dropout status codes (default 504 Gateway Timeout).
- **New**: `ChaosConfig.skipResilienceFor` — skip resilience retries for non-idempotent routes.
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
``` javascript
await fastify . watchdog . contract ( {
2026-03-10 00:00:00 -07:00
depth : 'quick' ,
chaos : {
probability : 0.1 ,
outbound : [
{
target : 'api.stripe.com' ,
error : {
probability : 0.05 ,
responses : [
{ statusCode : 429 , headers : { 'retry-after' : '60' } } ,
{ statusCode : 503 , body : { error : 'stripe_unavailable' } }
]
}
}
] ,
skipResilienceFor : [ 'constructor' , 'mutator' ]
}
} )
```
2026-05-21 20:39:36 -07:00
#### Route Targeting for Chaos
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- **New**: `TestConfig.routes` — test only specific routes.
- **New**: `ChaosConfig.include` / `ChaosConfig.exclude` — include/exclude routes from chaos with wildcards.
- **New**: `ChaosConfig.routes` — per-route chaos overrides.
- **New**: `ChaosConfig.resilience` — verify system recovery after chaos injection.
- **New**: `ChaosConfig.maxInjectionsPerSuite` — circuit breaker for total injections.
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
#### Performance
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- Full SHA-256 hashes for determinism (64 chars) instead of truncated 16-char hashes.
- Configurable parse cache with `setParseCacheLimit()` , `clearParseCache()` .
- Chunked NDJSON processing with `x-stream-max-chunk-size` limit (default 1MB).
- Lazy topological sorting for extension registry.
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
### Fixed
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- Chaos events now visible in test diagnostics with type and status code.
- ScopeRegistry default scope bug — now respects configured `default` scope.
- Plugin contract builder — `routes` option now propagated to test runner.
- Dropout returns 504 Gateway Timeout instead of status code 0.
- Resilience verification skips non-idempotent routes by default.
- Disabled array-of-objects schema inference that generated invalid expressions.
- Schema inference no longer crashes on collection schemas.
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
---
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
## [WATCHDOG 2.3.0] - 2025-07-22
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
### Changed
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
#### Chaos System Final Cutover
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- **Unified**: Single `ChaosConfig` type — deleted `EnhancedChaosConfig` , `DependencyChaosConfig` , and duplicate type files.
- **Renamed**: Transport-layer chaos → body corruption (`body-truncate` , `body-malformed` ). Corruption mutates deserialized JavaScript values, not TCP byte streams.
- **Removed**: `services` field (documented but unimplemented).
- **Removed**: `corruption.strategies` array (documented 3 ways, used 0 ways).
- **Removed**: `reportInDiagnostics` flag (dead config).
- **Removed**: `makeInvalidJson` strategy (dead code).
- **Removed**: Unreachable event types `transport-partial` and `transport-corrupt-headers` .
- **Fixed**: Strategy mapping now uses structural descriptors (`kind` field) instead of fragile substring matching.
- **Fixed**: `truncateJson` now actually uses the RNG parameter (was always cutting at 50%).
- **Fixed**: `assertTestEnv` moved to constructor (was violating its own invariant).
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
#### Outbound Chaos Now Usable
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- **New**: `wrapFetch()` helper — wraps any `fetch` implementation to route outbound requests through the interceptor.
- **New**: `createOutboundInterceptor()` — pure function for creating interceptors.
- **Wired**: Per-route outbound config resolution now works.
- **Wired**: Outbound interceptor accessible from test runner via `result.interceptor` .
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
#### Safety & Reproducibility
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- **New**: `maxInjectionsPerSuite` — circuit breaker to prevent `probability: 1` from masking all assertions.
- **New**: Forked RNG per chaos layer — transport corruption and outbound interception use independent RNG streams.
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
---
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
## [WATCHDOG 2.2.0] - 2025-06-10
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
### Added
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
#### Scenario Execution Engine
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- **New**: `runScenario()` — execute multi-step request sequences with capture/rebind, cookie jars, form encoding, and stop-on-failure.
- **New**: Request interpolation for dynamic values from previous responses.
- **New**: Step-level header overrides and Content-Type injection.
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
#### Stateful Testing Engine
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- **New**: `runStatefulTests()` — constructor/mutator/observer/destructor sequence generation from schema annotations.
- **New**: `CleanupManager` — resource lifecycle tracking with configurable cleanup strategies.
- **New**: Invariant checking across stateful sequences.
- **New**: Outbound mock runtime integration for stateful tests.
2026-03-10 00:00:00 -07:00
---
2026-05-21 20:39:36 -07:00
## [WATCHDOG 2.1.0] - 2025-05-03
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
### Added
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
#### CLI Commands
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- **New**: `watchdog` binary with seven commands: verify, qualify, observe, doctor, replay, migrate, init.
- **New**: Route discovery from Fastify's `hasRoute` introspection.
- **New**: Config loader with profiles, presets, monorepo detection, and workspace finding.
- **New**: Human and machine output renderers (text, JSON, NDJSON).
- **New**: Artifact-based replay with seed determinism.
- **New**: Environment safety checks via `doctor` command.
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
#### Config System
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- **New**: Presets (`safe-ci` , `staging` , `dev` , `full` , `nightly` ) with pre-configured safety policies.
- **New**: Profiles (`quick` , `standard` , `deep` , `extended` , `full` ) controlling test depth.
- **New**: Generation profiles for property-based test sampling.
- **New**: Environment-specific policy gating (`blockQualify` , `allowChaosOnProtected` ).
2026-03-10 00:00:00 -07:00
---
2026-05-21 20:39:36 -07:00
## [WATCHDOG 2.0.0] - 2025-04-14
2026-03-10 00:00:00 -07:00
### Added
2026-05-21 20:39:36 -07:00
#### Justin Expression Language
- **New**: Justin (subscript) expression evaluator — ~3KB sandboxed JavaScript evaluator for `x-ensures` and `x-requires` formulas.
- **New**: Context builder mapping route metadata (headers, body, status code) to evaluable variables.
- Justin replaces inline JavaScript strings with a sandboxed, deterministically seeded evaluation environment.
2026-03-10 00:00:00 -07:00
#### Chaos Mode
2026-05-21 20:39:36 -07:00
- Config-driven failure injection: delay, error, dropout, corruption.
- Content-type aware corruption: JSON, NDJSON, SSE, multipart, text.
- Extension-provided corruption strategies with wildcard matching.
- Seeded RNG for reproducible pseudo-random choices.
- Environment guard: `NODE_ENV=test` only.
- `ChaosEngine` class with event recording and diagnostics.
2026-03-10 00:00:00 -07:00
#### Auth Extension Factory
2026-05-21 20:39:36 -07:00
- `createAuthExtension({ getToken, headerName, prefix, matcher })` for JWT, API key, session auth.
- Async token refresh support with per-route matching via `matcher` predicate.
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
#### Schema-to-Contract Inference
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- Automatically derive Justin expressions from JSON Schema response definitions.
- Infers `!= null` for `required` fields, `>=` /`<=` for `minimum` /`maximum` bounds.
- Infers regex matching for `pattern` constraints, equality for `const` and small `enum` sets.
- Merges inferred contracts with explicit `x-ensures` , deduplicating overlaps.
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
#### Extension System
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- Plugin system for custom Justin predicates, headers, and lifecycle hooks.
- Extension state isolation (frozen copies per extension).
- Hook timeout and severity configuration.
- Dependency ordering via `dependsOn` with topological sort.
- Async boot: `onSuiteStart` hooks run in dependency order.
- Health checks: extensions validate before running hooks.
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
#### Extensions
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- **SSE** (`src/extensions/sse/` ): Parse `text/event-stream` responses into structured events.
- **Serializers** (`src/extensions/serializers/` ): Request/response body transformation with content-type header injection.
- **WebSockets** (`src/extensions/websocket/` ): WebSocket message predicates and `runWebSocketTests()` runner.
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
### Changed
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- `WatchdogExtension` interface includes `headers` , `dependsOn` , `healthCheck` fields.
- `parse()` accepts optional `extensionHeaders` parameter.
- `ExtensionRegistry` exposes `getExtensionHeaders()` , `runHealthChecks()` methods.
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
### Fixed
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- Justin expression parsing handles nested accessors and undefined guards.
- Extension predicate return type narrowing.
- Multipart files type safety in request builder.
2026-03-10 00:00:00 -07:00
---
2026-05-21 20:39:36 -07:00
## [WATCHDOG 1.2.0] - 2025-03-01
2026-03-10 00:00:00 -07:00
### Added
#### Multipart Uploads
2026-05-21 20:39:36 -07:00
- `multipart/form-data` request generation from JSON Schema annotations.
- Fake file generation with size, MIME type, and count constraints.
- Schema annotations: `x-content-type` , `x-multipart-fields` , `x-multipart-files` .
2026-03-10 00:00:00 -07:00
#### Streaming / NDJSON
2026-05-21 20:39:36 -07:00
- Response chunk collection for streaming routes.
- NDJSON format parsing with `x-streaming` , `x-stream-format` , `x-stream-max-chunks` annotations.
- Integration tests with Fastify NDJSON routes.
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
#### Core Improvements
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- `evaluateAsync()` for async predicate resolvers.
- `validateFormula()` with error position and suggestions.
- `ContractViolation` includes full request/response context.
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
### Fixed
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- TypeScript strict mode: ~50 errors fixed across 15+ files.
- Evaluator exports restored.
- Status node handling in both sync and async evaluators.
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
---
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
## [WATCHDOG 1.1.0] - 2025-02-10
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
### Added
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
#### Contract-Driven Testing
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- Property-based testing with fast-check: generated requests against `x-ensures` and `x-requires` contracts.
- Timeout enforcement and redirect capture.
- Seeded RNG for reproducible concurrent tests.
#### Documentation
2026-03-10 00:00:00 -07:00
2026-05-21 20:39:36 -07:00
- Fastify App Structure Guide (`docs/fastify-structure.md` ).
- Protocol Extensions Specification (`docs/protocol-extensions-spec.md` ).
2026-03-10 00:00:00 -07:00
### Fixed
2026-05-21 20:39:36 -07:00
- Contract formulas support optional `else` clauses.
- Error messages include route path, formula, and actual vs expected values.
---
## [WATCHDOG 1.0.0] - 2025-01-06
### Added
- Contract-driven API testing plugin for Fastify.
- `x-ensures` and `x-requires` schema annotations for property contracts.
- JSON Schema validation integrated into the test lifecycle.
- 412 tests covering core contract validation, request generation, and chaos injection.
2026-03-10 00:00:00 -07:00
---
2026-05-21 20:39:36 -07:00
## [WATCHDOG 0.1.0] - 2024-09-18
2026-03-10 00:00:00 -07:00
### Added
2026-05-21 20:39:36 -07:00
- Initial chaos injection engine for Fastify response interception.
- Configurable failure modes: delay, error, dropout, and body corruption.
- Content-type aware response body mutation.
- Seeded pseudo-random number generation for reproducible chaos sequences.
- Environment guard preventing chaos injection outside `NODE_ENV=test` .
- 85 tests covering all four chaos strategies and content-type handling.
2026-03-10 00:00:00 -07:00
## License
2026-05-21 20:39:36 -07:00
MIT