- P0: CLI verify now honors test budget with seeded multi-sample - P0: Observe sampling enforced via Math.random() gate in hook-validator - P1: Remove misleading undici-mock-agent isolation option - P1: Qualify reuses shared discoverRouteDetails() with warnings - P1: Chaos/scenario config exposed via preset schema - P1: README/docs limitations updated to current state - P2: Nested response annotations prefer 2xx deterministically - P2: --changed documented as heuristic in verify.md - Add observe sink tests (sampling 0/1, sink failure non-interference) - Add verify runs regression tests (scale, determinism, variants) - Add configured-scenario qualify test (independent of OAuth fixture) - Add coverageBreakdown to qualify artifacts (per-gate route coverage) - Add production-style observe example with real sink in docs/observe.md - Add nightly/staging vs PR gating guidance to docs/qualify.md - Enrich VerifyFailure with formula-aware diagnostics: status:201 => 'HTTP 200', body field checks => actual values - Remove stale observe CLI activation message - Document outbound mocks as process-global in getting-started.md - Refresh APOPHIS_ADOPTION_AUDIT.md with current state 903 tests pass, build clean, typecheck clean.
15 KiB
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[APOPHIS 2.7.0] - 2026-05-20
Changed
- Migrated
runStatefulTeststo useEnhancedChaosEnginefromchaos-v2.ts(was using deprecatedChaosEnginefromchaos.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.
Added
- CLI route discovery for apps without pre-registered APOPHIS: routes can be detected via
hasRouteintrospection, but inlinex-ensures/x-requirescontract annotations on route schemas are only discoverable when the APOPHIS plugin is registered before routes (via theonRoutehook). 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()andapplyPack().
Fixed
- 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.
[APOPHIS 2.6.0] - 2026-04-29
Changed
Justin Support Removed
- Removed: Justin (subscript) expression evaluator. APOSTL is now the exclusive contract expression language.
- Removed:
src/formula/justin.ts,src/formula/context-builder.ts. - Removed:
subscriptdependency from package.json. - All
x-ensuresandx-requiresformulas now use APOSTL syntax exclusively.
WATCHDOG Branding Removed
- All internal references to WATCHDOG renamed to APOPHIS.
- Package name finalized as
@apophis/fastify. - Binary renamed from
watchdogtoapophis.
Migration
All formulas must use APOSTL syntax:
// 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']
See Getting Started Guide for full APOSTL reference.
[APOPHIS 2.5.0] - 2026-02-22 — APOSTL Discovery
Project Renamed
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.
Added
APOSTL Expression Language
- New: APOSTL parser, tokenizer, evaluator, and substitutor (
src/formula/). - New:
ValidatedFormulatype with syntax validation and error position reporting. - New: Extension predicates registered as APOSTL context variables.
- New: Async APOSTL evaluation via
evaluateAsync().
Contract-Driven Outbound Mocking
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.
- New:
ApophisOptions.outboundContracts— register shared dependency contracts once. - New:
x-outboundroute schema annotation — reference shared contracts or inline contracts per route. - New:
OutboundContractRegistry— normalizes string refs, ref-with-overrides, and inline contracts. - New:
OutboundMockRuntime— patchesglobalThis.fetchduring 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)andoutbound_last(this)to APOSTL formulas.
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'
]
}
Mutation Testing
- 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.
Changed
- Package name:
@watchdog/fastify→@apophis/fastify. - Binary:
watchdog→apophis. - Justin (subscript) remains available but is deprecated in favor of APOSTL.
[WATCHDOG 2.4.0] - 2025-08-14
Added
Dependency-Aware Chaos Testing
- 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.
await fastify.watchdog.contract({
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']
}
})
Route Targeting for Chaos
- 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.
Performance
- 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-sizelimit (default 1MB). - Lazy topological sorting for extension registry.
Fixed
- Chaos events now visible in test diagnostics with type and status code.
- ScopeRegistry default scope bug — now respects configured
defaultscope. - Plugin contract builder —
routesoption 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.
[WATCHDOG 2.3.0] - 2025-07-22
Changed
Chaos System Final Cutover
- Unified: Single
ChaosConfigtype — deletedEnhancedChaosConfig,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:
servicesfield (documented but unimplemented). - Removed:
corruption.strategiesarray (documented 3 ways, used 0 ways). - Removed:
reportInDiagnosticsflag (dead config). - Removed:
makeInvalidJsonstrategy (dead code). - Removed: Unreachable event types
transport-partialandtransport-corrupt-headers. - Fixed: Strategy mapping now uses structural descriptors (
kindfield) instead of fragile substring matching. - Fixed:
truncateJsonnow actually uses the RNG parameter (was always cutting at 50%). - Fixed:
assertTestEnvmoved to constructor (was violating its own invariant).
Outbound Chaos Now Usable
- New:
wrapFetch()helper — wraps anyfetchimplementation 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.
Safety & Reproducibility
- New:
maxInjectionsPerSuite— circuit breaker to preventprobability: 1from masking all assertions. - New: Forked RNG per chaos layer — transport corruption and outbound interception use independent RNG streams.
[WATCHDOG 2.2.0] - 2025-06-10
Added
Scenario Execution Engine
- 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.
Stateful Testing Engine
- 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.
[WATCHDOG 2.1.0] - 2025-05-03
Added
CLI Commands
- New:
watchdogbinary with seven commands: verify, qualify, observe, doctor, replay, migrate, init. - New: Route discovery from Fastify's
hasRouteintrospection. - 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
doctorcommand.
Config System
- 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).
[WATCHDOG 2.0.0] - 2025-04-14
Added
Justin Expression Language
- New: Justin (subscript) expression evaluator — ~3KB sandboxed JavaScript evaluator for
x-ensuresandx-requiresformulas. - 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.
Chaos Mode
- 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=testonly. ChaosEngineclass with event recording and diagnostics.
Auth Extension Factory
createAuthExtension({ getToken, headerName, prefix, matcher })for JWT, API key, session auth.- Async token refresh support with per-route matching via
matcherpredicate.
Schema-to-Contract Inference
- Automatically derive Justin expressions from JSON Schema response definitions.
- Infers
!= nullforrequiredfields,>=/<=forminimum/maximumbounds. - Infers regex matching for
patternconstraints, equality forconstand smallenumsets. - Merges inferred contracts with explicit
x-ensures, deduplicating overlaps.
Extension System
- 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
dependsOnwith topological sort. - Async boot:
onSuiteStarthooks run in dependency order. - Health checks: extensions validate before running hooks.
Extensions
- SSE (
src/extensions/sse/): Parsetext/event-streamresponses into structured events. - Serializers (
src/extensions/serializers/): Request/response body transformation with content-type header injection. - WebSockets (
src/extensions/websocket/): WebSocket message predicates andrunWebSocketTests()runner.
Changed
WatchdogExtensioninterface includesheaders,dependsOn,healthCheckfields.parse()accepts optionalextensionHeadersparameter.ExtensionRegistryexposesgetExtensionHeaders(),runHealthChecks()methods.
Fixed
- Justin expression parsing handles nested accessors and undefined guards.
- Extension predicate return type narrowing.
- Multipart files type safety in request builder.
[WATCHDOG 1.2.0] - 2025-03-01
Added
Multipart Uploads
multipart/form-datarequest 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.
Streaming / NDJSON
- Response chunk collection for streaming routes.
- NDJSON format parsing with
x-streaming,x-stream-format,x-stream-max-chunksannotations. - Integration tests with Fastify NDJSON routes.
Core Improvements
evaluateAsync()for async predicate resolvers.validateFormula()with error position and suggestions.ContractViolationincludes full request/response context.
Fixed
- TypeScript strict mode: ~50 errors fixed across 15+ files.
- Evaluator exports restored.
- Status node handling in both sync and async evaluators.
[WATCHDOG 1.1.0] - 2025-02-10
Added
Contract-Driven Testing
- Property-based testing with fast-check: generated requests against
x-ensuresandx-requirescontracts. - Timeout enforcement and redirect capture.
- Seeded RNG for reproducible concurrent tests.
Documentation
- Fastify App Structure Guide (
docs/fastify-structure.md). - Protocol Extensions Specification (
docs/protocol-extensions-spec.md).
Fixed
- Contract formulas support optional
elseclauses. - 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-ensuresandx-requiresschema annotations for property contracts.- JSON Schema validation integrated into the test lifecycle.
- 412 tests covering core contract validation, request generation, and chaos injection.
[WATCHDOG 0.1.0] - 2024-09-18
Added
- 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.
License
MIT