chore: crush git history - reborn from consolidation on 2026-03-10

This commit is contained in:
John Dvorak
2026-03-10 00:00:00 -07:00
commit d278c4b105
313 changed files with 87549 additions and 0 deletions
+755
View File
@@ -0,0 +1,755 @@
/**
* Integration Tests - Complete end-to-end testing of APOPHIS functionality.
* Tests plugin registration, scope discovery, route contracts, hooks, test execution, and cleanup.
*/
import { test } from 'node:test'
import assert from 'node:assert'
import Fastify from 'fastify'
import type { FastifyInstance } from 'fastify'
import apophisPlugin from '../index.js'
import { runPetitTests } from '../test/petit-runner.js'
import { CleanupManager } from '../infrastructure/cleanup-manager.js'
import { ScopeRegistry } from '../infrastructure/scope-registry.js'
import { discoverRoutes } from '../domain/discovery.js'
import { registerValidationHooks } from '../infrastructure/hook-validator.js'
import swagger from '@fastify/swagger'
import type { ApophisDecorations, RouteContract } from '../types.js'
// Extend FastifyInstance type for tests
type TestFastifyInstance = FastifyInstance & {
apophis: ApophisDecorations
}
// ---------------------------------------------------------------------------
// Test Helpers
// ---------------------------------------------------------------------------
const createTestApi = () => {
const fastify = Fastify()
fastify.get('/health', {
schema: {
response: {
200: {
type: 'object',
properties: { status: { type: 'string' } }
}
}
}
}, async () => ({ status: 'ok' }))
fastify.post('/items', {
schema: {
body: {
type: 'object',
properties: { name: { type: 'string' } },
required: ['name']
},
response: {
201: {
type: 'object',
properties: { id: { type: 'string' }, name: { type: 'string' } }
}
}
}
}, async (req) => {
return { id: '123', name: (req.body as any).name }
})
fastify.get('/items/:id', {
schema: {
params: {
type: 'object',
properties: { id: { type: 'string' } },
required: ['id']
},
response: {
200: {
type: 'object',
properties: { id: { type: 'string' }, name: { type: 'string' } }
}
}
}
}, async (req) => {
return { id: (req.params as any).id, name: 'test-item' }
})
fastify.delete('/items/:id', {
schema: {
params: {
type: 'object',
properties: { id: { type: 'string' } },
required: ['id']
}
}
}, async () => {
return { deleted: true }
})
return fastify
}
const createContractApi = () => {
const fastify = Fastify()
fastify.post('/resources', {
schema: {
'x-category': 'constructor',
'x-requires': [],
'x-ensures': ['status:201'],
body: {
type: 'object',
properties: { name: { type: 'string' } },
required: ['name']
}
}
}, async (req, reply) => {
reply.status(201)
return { id: 'res-123', name: (req.body as any).name }
})
fastify.get('/resources/:id', {
schema: {
'x-category': 'observer',
'x-requires': ['resources:res-123'],
params: {
type: 'object',
properties: { id: { type: 'string' } },
required: ['id']
}
}
}, async (req) => {
return { id: (req.params as any).id, name: 'test-resource' }
})
return fastify
}
// ---------------------------------------------------------------------------
// Integration Tests
// ---------------------------------------------------------------------------
test('plugin registers apophis decorations on fastify', async () => {
const fastify = Fastify()
let decorations: ApophisDecorations | undefined
try {
// Register swagger first as it's a dependency, then apophis
await fastify.register(swagger, {})
await fastify.register(apophisPlugin, { runtime: 'error' })
// Register a test plugin to capture the decorations from within the same scope
await fastify.register(async (instance) => {
decorations = (instance as unknown as TestFastifyInstance).apophis
})
// Ready must be called after all plugins are registered
await fastify.ready()
assert.ok(decorations, 'apophis decoration should exist')
assert.ok(typeof decorations?.contract === 'function', 'contract should be a function')
assert.ok(typeof decorations?.stateful === 'function', 'stateful should be a function')
assert.ok(typeof decorations?.check === 'function', 'check should be a function')
assert.ok(typeof decorations?.cleanup === 'function', 'cleanup should be a function')
assert.ok(typeof decorations?.spec === 'function', 'spec should be a function')
assert.ok(decorations?.scope, 'scope should exist')
} finally {
await fastify.close()
}
})
test('scope auto-discovery loads scopes from environment variables', async () => {
const originalEnv = process.env
process.env = {
...originalEnv,
APOPHIS_SCOPE_TEST: JSON.stringify({
tenantId: 'test-tenant',
applicationId: 'test-app',
headers: { 'x-api-key': 'secret123' }
}),
APOPHIS_SCOPE_PROD: JSON.stringify({
tenantId: 'prod-tenant',
applicationId: 'prod-app',
headers: { 'x-api-key': 'prod-secret' }
})
}
try {
const registry = new ScopeRegistry()
assert.ok(registry.scopes.has('test'), 'test scope should be discovered')
assert.ok(registry.scopes.has('prod'), 'prod scope should be discovered')
const testScope = registry.scopes.get('test')
assert.strictEqual(testScope?.metadata?.tenantId, 'test-tenant')
assert.strictEqual(testScope?.metadata?.applicationId, 'test-app')
assert.strictEqual(testScope?.headers['x-api-key'], 'secret123')
const prodScope = registry.scopes.get('prod')
assert.strictEqual(prodScope?.metadata?.tenantId, 'prod-tenant')
} finally {
process.env = originalEnv
}
})
test('route discovery extracts contracts from registered routes', async () => {
const fastify = createTestApi()
try {
await fastify.ready()
// Fastify v5 doesn't expose routes directly, so we construct the expected route array
const mockRoutes = [
{ method: 'GET', url: '/health', schema: { response: { 200: { type: 'object', properties: { status: { type: 'string' } } } } } },
{ method: 'POST', url: '/items', schema: { body: { type: 'object', properties: { name: { type: 'string' } }, required: ['name'] } } },
{ method: 'GET', url: '/items/:id', schema: { params: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] } } },
{ method: 'DELETE', url: '/items/:id', schema: { params: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] } } }
]
const routes = discoverRoutes({ routes: mockRoutes })
assert.strictEqual(routes.length, 4, 'should discover 4 routes')
const healthRoute = routes.find(r => r.path === '/health' && r.method === 'GET')
assert.ok(healthRoute, 'health route should be discovered')
assert.strictEqual(healthRoute?.category, 'utility')
const createRoute = routes.find(r => r.path === '/items' && r.method === 'POST')
assert.ok(createRoute, 'create items route should be discovered')
assert.strictEqual(createRoute?.category, 'constructor')
const getRoute = routes.find(r => r.path === '/items/:id' && r.method === 'GET')
assert.ok(getRoute, 'get item route should be discovered')
const deleteRoute = routes.find(r => r.path === '/items/:id' && r.method === 'DELETE')
assert.ok(deleteRoute, 'delete item route should be discovered')
} finally {
await fastify.close()
}
})
test('spec generation returns OpenAPI spec with x-apophis-contracts', async () => {
const fastify = Fastify() as unknown as TestFastifyInstance
try {
// Register swagger first as it's a dependency, then apophis
await fastify.register(swagger, {})
await fastify.register(apophisPlugin, {})
// Mock routes array for discovery (Fastify v5 doesn't expose routes directly)
const mockRoutes = [
{
method: 'GET',
url: '/test',
schema: {
'x-category': 'observer',
'x-requires': ['auth'],
response: { 200: { type: 'object' } }
}
}
]
Object.assign(fastify, { routes: mockRoutes })
// Ready must be called after all plugins and routes are registered
await fastify.ready()
const spec = fastify.apophis.spec()
assert.ok(spec, 'spec should be generated')
assert.ok(Array.isArray(spec['x-apophis-contracts']), 'should have x-apophis-contracts array')
const contracts = spec['x-apophis-contracts'] as any[]
const testContract = contracts.find(c => c.path === '/test')
assert.ok(testContract, 'test route contract should exist')
assert.strictEqual(testContract.method, 'GET')
assert.strictEqual(testContract.category, 'observer')
assert.deepStrictEqual(testContract.requires, ['auth'])
} finally {
await fastify.close()
}
})
test('petit-runner executes tests against real API', async () => {
const fastify = createTestApi()
try {
await fastify.ready()
// Mock routes for petit-runner since Fastify v5 doesn't expose them directly
const mockRoutes = [
{ method: 'GET', url: '/health', schema: { response: { 200: { type: 'object', properties: { status: { type: 'string' } } } } } },
{ method: 'POST', url: '/items', schema: { body: { type: 'object', properties: { name: { type: 'string' } }, required: ['name'] }, response: { 201: { type: 'object', properties: { id: { type: 'string' }, name: { type: 'string' } } } } } },
{ method: 'GET', url: '/items/:id', schema: { params: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] } } },
{ method: 'DELETE', url: '/items/:id', schema: { params: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] } } }
]
const fastifyWithRoutes = Object.assign(fastify, { routes: mockRoutes })
const result = await runPetitTests(fastifyWithRoutes as any, {
depth: 'quick',
scope: undefined,
seed: undefined
})
assert.ok(result.tests.length > 0, 'should have test results')
assert.ok(result.summary.timeMs >= 0, 'should have timing')
const passed = result.tests.filter(t => t.ok && !t.directive).length
const failed = result.tests.filter(t => !t.ok).length
assert.strictEqual(result.summary.passed, passed, 'passed count should match')
assert.strictEqual(result.summary.failed, failed, 'failed count should match')
} finally {
await fastify.close()
}
})
test('cleanup manager tracks and deletes resources', async () => {
const fastify = createTestApi()
try {
const scope = new ScopeRegistry()
const cleanup = new CleanupManager(fastify, scope)
cleanup.track({
type: 'items',
id: '123',
url: '/items/123',
scope: null,
timestamp: Date.now()
})
cleanup.track({
type: 'items',
id: '456',
url: '/items/456',
scope: null,
timestamp: Date.now()
})
assert.strictEqual(cleanup.resources.length, 2, 'should track 2 resources')
await fastify.ready()
const results = await cleanup.cleanup()
assert.strictEqual(results.length, 2, 'should cleanup 2 resources')
assert.strictEqual(cleanup.resources.length, 0, 'resources should be cleared after cleanup')
const firstResult = results[0]
assert.ok(firstResult?.resource, 'should have resource info')
} finally {
await fastify.close()
}
})
test('hook validator fires on routes with x-requires', async () => {
const fastify = createContractApi()
try {
registerValidationHooks(fastify, { validateRuntime: true, runtimeLevel: 'error' })
let preHandlerCalled = false
let onResponseCalled = false
fastify.addHook('preHandler', async () => {
preHandlerCalled = true
})
fastify.addHook('onResponse', async () => {
onResponseCalled = true
})
await fastify.ready()
const response = await fastify.inject({
method: 'POST',
url: '/resources',
payload: { name: 'test' }
})
assert.strictEqual(response.statusCode, 201, 'should return 201')
} finally {
await fastify.close()
}
})
test('full integration: plugin + routes + test execution', async () => {
const fastify = Fastify() as unknown as TestFastifyInstance
try {
// Register swagger first as it's a dependency, then apophis
await fastify.register(swagger, {})
await fastify.register(apophisPlugin, { runtime: 'error' })
// Mock routes array for discovery (Fastify v5 doesn't expose routes directly)
const mockRoutes = [
{
method: 'POST',
url: '/users',
schema: {
'x-category': 'constructor',
'x-ensures': ['status:201'],
body: {
type: 'object',
properties: {
email: { type: 'string', format: 'email' },
name: { type: 'string', minLength: 1 }
},
required: ['email', 'name']
},
response: {
201: {
type: 'object',
properties: {
id: { type: 'string' },
email: { type: 'string' },
name: { type: 'string' }
}
}
}
}
},
{
method: 'GET',
url: '/users/:id',
schema: {
'x-category': 'observer',
params: {
type: 'object',
properties: { id: { type: 'string' } },
required: ['id']
}
}
}
]
Object.assign(fastify, { routes: mockRoutes })
await fastify.ready()
assert.ok(fastify.apophis, 'plugin should be registered')
const spec = fastify.apophis.spec()
assert.ok(spec['x-apophis-contracts'], 'spec should have contracts')
const contracts = spec['x-apophis-contracts'] as any[]
assert.strictEqual(contracts.length, 2, 'should have 2 route contracts')
const createUserContract = contracts.find(c => c.path === '/users' && c.method === 'POST')
assert.ok(createUserContract, 'create user contract should exist')
assert.strictEqual(createUserContract.category, 'constructor')
const testResult = await fastify.apophis.contract({ depth: 'quick' })
assert.ok(Array.isArray(testResult.tests), 'tests should be an array')
assert.ok(testResult.tests.length > 0, 'tests should not be empty')
await fastify.apophis.cleanup()
} finally {
await fastify.close()
}
})
test('mode filtering: stateful mode only runs constructor/mutator routes', async () => {
const fastify = Fastify() as unknown as TestFastifyInstance
try {
await fastify.register(swagger, {})
await fastify.register(apophisPlugin, { runtime: 'error' })
// Register real routes with different categories
fastify.post('/items', {
schema: {
'x-category': 'constructor',
'x-ensures': ['status:201'],
body: {
type: 'object',
properties: { name: { type: 'string' } },
required: ['name']
},
response: {
201: {
type: 'object',
properties: { id: { type: 'string' }, name: { type: 'string' } }
}
}
}
}, async (req, reply) => {
reply.status(201)
return { id: '123', name: (req.body as any).name }
})
fastify.put('/items/:id', {
schema: {
'x-category': 'mutator',
'x-ensures': ['status:200'],
params: {
type: 'object',
properties: { id: { type: 'string' } },
required: ['id']
}
}
}, async (req) => {
return { id: (req.params as any).id, updated: true }
})
fastify.get('/items/:id', {
schema: {
'x-category': 'observer',
params: {
type: 'object',
properties: { id: { type: 'string' } },
required: ['id']
}
}
}, async (req) => {
return { id: (req.params as any).id, name: 'test' }
})
fastify.get('/health', {
schema: {
'x-category': 'utility',
response: {
200: {
type: 'object',
properties: { status: { type: 'string' } }
}
}
}
}, async () => ({ status: 'ok' }))
await fastify.ready()
// Run in stateful mode
const result = await fastify.apophis.contract({ depth: 'quick' })
// In stateful mode, utility routes should be excluded
// The test should only run constructor and mutator routes
assert.ok(Array.isArray(result.tests), 'tests should be an array')
// Verify no utility routes were executed
const utilityTests = result.tests.filter(t => t.name.includes('/health'))
assert.strictEqual(utilityTests.length, 0, 'utility routes should not run in stateful mode')
// In stateful mode, observer routes may still be present (they're not utility)
// The key assertion is that utility routes are excluded
const constructorTests = result.tests.filter(t => t.name.includes('POST /items'))
assert.ok(constructorTests.length > 0, 'constructor routes should run in stateful mode')
} finally {
await fastify.close()
}
})
test('failing contract produces ContractViolation with suggestion', async () => {
const fastify = Fastify() as unknown as TestFastifyInstance
try {
await fastify.register(swagger, {})
await fastify.register(apophisPlugin, {})
// Register a real route that returns 200 but contract expects 201
fastify.post('/broken', {
schema: {
'x-category': 'constructor',
'x-ensures': ['status:201'],
body: {
type: 'object',
properties: { name: { type: 'string' } },
required: ['name']
}
}
}, async () => {
return { status: 'created' } // Returns 200, not 201
})
await fastify.ready()
const result = await fastify.apophis.contract({ depth: 'quick' })
// Find the failing test
const failingTests = result.tests.filter(t => !t.ok)
assert.ok(failingTests.length > 0, 'should have at least one failing test')
const failure = failingTests[0]
assert.ok(failure!.diagnostics, 'failure should have diagnostics')
const violation = failure!.diagnostics!.violation as { formula: string; suggestion: string } | undefined
assert.ok(violation, 'failure should have a ContractViolation')
assert.strictEqual(violation!.formula, 'status:201', 'violation should be for status:201')
assert.ok(violation!.suggestion, 'violation should have a suggestion')
assert.ok(violation!.suggestion.includes('201'), 'suggestion should mention expected status')
assert.ok((violation as any).request, 'violation should include request context')
assert.ok((violation as any).response, 'violation should include response context')
} finally {
await fastify.close()
}
})
test('contracts extracted from routes with annotations', async () => {
const fastify = Fastify() as unknown as TestFastifyInstance
try {
await fastify.register(swagger, {})
await fastify.register(apophisPlugin, { runtime: 'error' })
// Register routes with full contract annotations
fastify.post('/orders', {
schema: {
'x-category': 'constructor',
'x-requires': ['auth'],
'x-ensures': ['status:201', 'response_body(this).id != null'],
body: {
type: 'object',
properties: { product: { type: 'string' }, quantity: { type: 'number' } },
required: ['product', 'quantity']
},
response: {
201: {
type: 'object',
properties: {
id: { type: 'string' },
product: { type: 'string' },
quantity: { type: 'number' },
total: { type: 'number' }
}
}
}
}
}, async (req) => {
return {
id: 'order-123',
product: (req.body as any).product,
quantity: (req.body as any).quantity,
total: (req.body as any).quantity * 10
}
})
fastify.get('/orders/:id', {
schema: {
'x-category': 'observer',
'x-requires': ['request_params(this).id != null'],
params: {
type: 'object',
properties: { id: { type: 'string' } },
required: ['id']
}
}
}, async (req) => {
return {
id: (req.params as any).id,
product: 'widget',
quantity: 2,
total: 20
}
})
await fastify.ready()
const spec = fastify.apophis.spec()
const contracts = spec['x-apophis-contracts'] as any[]
// Verify POST /orders contract
const orderContract = contracts.find(c => c.path === '/orders' && c.method === 'POST')
assert.ok(orderContract, 'order contract should exist')
assert.strictEqual(orderContract.category, 'constructor')
assert.deepStrictEqual(orderContract.requires, ['auth'])
assert.ok(orderContract.ensures.includes('status:201'))
assert.ok(orderContract.ensures.includes('response_body(this).id != null'))
assert.ok(Array.isArray(orderContract.invariants), 'invariants should be represented as an array')
// Verify GET /orders/:id contract
const getOrderContract = contracts.find(c => c.path === '/orders/:id' && c.method === 'GET')
assert.ok(getOrderContract, 'get order contract should exist')
assert.strictEqual(getOrderContract.category, 'observer')
assert.deepStrictEqual(getOrderContract.requires, ['request_params(this).id != null'])
} finally {
await fastify.close()
}
})
test('integration: prefix option is captured in route discovery', async () => {
const fastify = Fastify() as unknown as TestFastifyInstance
try {
await fastify.register(swagger, {})
await fastify.register(apophisPlugin, { runtime: 'error' })
// Register a nested plugin with a prefix
await fastify.register(async (instance) => {
instance.get('/users', {
schema: {
response: {
200: {
type: 'object',
properties: { id: { type: 'string' } }
}
}
}
}, async () => ({ id: 'user-1' }))
}, { prefix: '/api/v1' })
await fastify.ready()
const spec = fastify.apophis.spec()
const contracts = spec['x-apophis-contracts'] as any[]
// Should discover the route with the prefix included
const userContract = contracts.find(c => c.path === '/api/v1/users')
assert.ok(userContract, 'route with prefix should be discovered as /api/v1/users')
assert.strictEqual(userContract.method, 'GET')
} finally {
await fastify.close()
}
})
test('integration: cache enabled by default, disabled via APOPHIS_DISABLE_CACHE', async () => {
const originalEnv = process.env.NODE_ENV
const originalDisable = process.env.APOPHIS_DISABLE_CACHE
try {
// Cache is enabled by default in all environments
delete process.env.NODE_ENV
delete process.env.APOPHIS_DISABLE_CACHE
const cacheModule = await import('../incremental/cache.js')
cacheModule.invalidateCache()
const route: RouteContract = {
path: '/test',
method: 'GET',
category: 'observer',
requires: [],
ensures: [],
invariants: [],
regexPatterns: {},
validateRuntime: true,
}
cacheModule.storeCache(route, [{ params: {}, headers: {} }])
const entry = cacheModule.lookupCache(route)
assert.ok(entry, 'cache should be enabled by default')
// Disable cache via env var
process.env.APOPHIS_DISABLE_CACHE = '1'
const cacheModule2 = await import('../incremental/cache.js')
cacheModule2.invalidateCache()
cacheModule2.storeCache(route, [{ params: {}, headers: {} }])
const entry2 = cacheModule2.lookupCache(route)
assert.strictEqual(entry2, undefined, 'cache should be disabled when APOPHIS_DISABLE_CACHE=1')
} finally {
process.env.NODE_ENV = originalEnv
process.env.APOPHIS_DISABLE_CACHE = originalDisable
}
})
test('integration: contract routes option limits tested routes', async () => {
const fastify = Fastify() as unknown as TestFastifyInstance
try {
await fastify.register(swagger, {})
await fastify.register(apophisPlugin, {})
fastify.get('/included', {
schema: {
'x-category': 'observer',
'x-ensures': ['status:200'],
} as Record<string, unknown>
}, async () => ({ ok: true }))
fastify.get('/excluded', {
schema: {
'x-category': 'observer',
'x-ensures': ['status:201'],
} as Record<string, unknown>
}, async () => ({ ok: true }))
await fastify.ready()
const result = await fastify.apophis.contract({
depth: 'quick',
routes: ['GET /included'],
})
const includedTests = result.tests.filter(t => t.name.includes('GET /included'))
const excludedTests = result.tests.filter(t => t.name.includes('GET /excluded'))
assert.ok(includedTests.length > 0, 'included route should be tested')
assert.strictEqual(excludedTests.length, 0, 'excluded route should not be tested')
assert.strictEqual(result.summary.failed, 0, 'excluded failing route should not affect results')
} finally {
await fastify.close()
}
})
test('integration: contract variants are tagged and run in declared order', async () => {
const fastify = Fastify() as unknown as TestFastifyInstance
try {
await fastify.register(swagger, {})
await fastify.register(apophisPlugin, {})
fastify.get('/variant-order', {
schema: {
'x-category': 'observer',
'x-ensures': ['status:200'],
} as Record<string, unknown>
}, async () => ({ ok: true }))
await fastify.ready()
const result = await fastify.apophis.contract({
depth: 'quick',
variants: [
{ name: 'json', headers: { accept: 'application/json' } },
{ name: 'xml', headers: { accept: 'application/xml' } },
],
})
const jsonTests = result.tests.filter((t) => t.name.startsWith('[variant:json]'))
const xmlTests = result.tests.filter((t) => t.name.startsWith('[variant:xml]'))
assert.ok(jsonTests.length > 0, 'json variant should produce tests')
assert.ok(xmlTests.length > 0, 'xml variant should produce tests')
const firstXmlIndex = result.tests.findIndex((t) => t.name.startsWith('[variant:xml]'))
const firstJsonIndex = result.tests.findIndex((t) => t.name.startsWith('[variant:json]'))
assert.ok(firstJsonIndex >= 0 && firstXmlIndex >= 0 && firstJsonIndex < firstXmlIndex, 'variant order should follow declaration order')
} finally {
await fastify.close()
}
})
test('integration: variant headers override scope headers', async () => {
const fastify = Fastify() as unknown as TestFastifyInstance
try {
await fastify.register(swagger, {})
await fastify.register(apophisPlugin, {
scopes: {
default: {
headers: {
accept: 'application/json',
},
},
},
})
fastify.get('/variant-header', {
schema: {
'x-category': 'observer',
'x-ensures': [
'request_headers(this).accept == "application/xml"',
'status:200',
],
} as Record<string, unknown>
}, async () => ({ ok: true }))
await fastify.ready()
const result = await fastify.apophis.contract({
depth: 'quick',
variants: [
{ name: 'xml', headers: { accept: 'application/xml' } },
],
})
assert.strictEqual(result.summary.failed, 0)
assert.ok(result.tests.some((t) => t.name.startsWith('[variant:xml]')))
} finally {
await fastify.close()
}
})
test('integration: route-level x-variants are extracted and executed', async () => {
const fastify = Fastify() as unknown as TestFastifyInstance
try {
await fastify.register(swagger, {})
await fastify.register(apophisPlugin, {})
fastify.get('/route-variant', {
schema: {
'x-category': 'observer',
'x-variants': [
{ name: 'json', headers: { accept: 'application/json' } },
{ name: 'xml', headers: { accept: 'application/xml' } },
],
'x-ensures': ['status:200'],
} as Record<string, unknown>
}, async () => ({ ok: true }))
await fastify.ready()
// No call-site variants; route-level variants should drive execution
const result = await fastify.apophis.contract({ depth: 'quick' })
const jsonTests = result.tests.filter((t) => t.name.includes('[variant:json]'))
const xmlTests = result.tests.filter((t) => t.name.includes('[variant:xml]'))
assert.ok(jsonTests.length > 0, 'route json variant should produce tests')
assert.ok(xmlTests.length > 0, 'route xml variant should produce tests')
} finally {
await fastify.close()
}
})