Files
apophis-fastify/CHANGELOG.md
T
John Dvorak d0523fcc2d fix: harden engine, enrich failure diagnostics, close adoption gaps
- 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.
2026-05-21 20:39:36 -07:00

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 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.

Added

  • 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().

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: subscript dependency from package.json.
  • All x-ensures and x-requires formulas 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 watchdog to apophis.

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: ValidatedFormula type 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-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.
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: watchdogapophis.
  • 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-size limit (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 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.

[WATCHDOG 2.3.0] - 2025-07-22

Changed

Chaos System Final Cutover

  • 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).

Outbound Chaos Now Usable

  • 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.

Safety & Reproducibility

  • 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.

[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: 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.

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-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.

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=test only.
  • ChaosEngine class 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 matcher predicate.

Schema-to-Contract Inference

  • 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.

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 dependsOn with topological sort.
  • Async boot: onSuiteStart hooks run in dependency order.
  • Health checks: extensions validate before running hooks.

Extensions

  • 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.

Changed

  • WatchdogExtension interface includes headers, dependsOn, healthCheck fields.
  • parse() accepts optional extensionHeaders parameter.
  • ExtensionRegistry exposes getExtensionHeaders(), 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-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.

Streaming / NDJSON

  • 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.

Core Improvements

  • evaluateAsync() for async predicate resolvers.
  • validateFormula() with error position and suggestions.
  • ContractViolation includes 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-ensures and x-requires contracts.
  • 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 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.

[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