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. Internal API only; not part of the public@apophis/fastifyexport. - 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