chore: crush git history - reborn from consolidation on 2026-03-10
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
import { test } from 'node:test'
|
||||
import assert from 'node:assert'
|
||||
import {
|
||||
applyChaosToExecution,
|
||||
applyChaosToDependencyResponse,
|
||||
applyChaosToAllResponses,
|
||||
createChaosEventArbitrary,
|
||||
extractDelays,
|
||||
sleep,
|
||||
hasAppliedChaos,
|
||||
formatChaosEvents,
|
||||
} from '../quality/chaos-v3.js'
|
||||
import * as fc from 'fast-check'
|
||||
|
||||
test('applyChaosToExecution: no chaos when events are empty', () => {
|
||||
const ctx = {
|
||||
request: { body: null, headers: {}, query: {}, params: {} },
|
||||
response: { body: 'ok', headers: {}, statusCode: 200, responseTime: 0 },
|
||||
timedOut: false,
|
||||
redirects: [],
|
||||
}
|
||||
|
||||
const result = applyChaosToExecution(ctx, [])
|
||||
assert.strictEqual(result.applied, false)
|
||||
assert.strictEqual(result.ctx.response.statusCode, 200)
|
||||
})
|
||||
|
||||
test('applyChaosToExecution: inbound error changes status code', () => {
|
||||
const ctx = {
|
||||
request: { body: null, headers: {}, query: {}, params: {} },
|
||||
response: { body: 'ok', headers: {}, statusCode: 200, responseTime: 0 },
|
||||
timedOut: false,
|
||||
redirects: [],
|
||||
}
|
||||
|
||||
const result = applyChaosToExecution(ctx, [
|
||||
{ type: 'inbound-error', target: 'inbound', statusCode: 500, body: { error: 'fail' } },
|
||||
])
|
||||
assert.strictEqual(result.applied, true)
|
||||
assert.strictEqual(result.ctx.response.statusCode, 500)
|
||||
assert.deepStrictEqual(result.ctx.response.body, { error: 'fail' })
|
||||
})
|
||||
|
||||
test('applyChaosToExecution: inbound dropout simulates gateway timeout', () => {
|
||||
const ctx = {
|
||||
request: { body: null, headers: {}, query: {}, params: {} },
|
||||
response: { body: 'ok', headers: {}, statusCode: 200, responseTime: 0 },
|
||||
timedOut: false,
|
||||
redirects: [],
|
||||
}
|
||||
|
||||
const result = applyChaosToExecution(ctx, [
|
||||
{ type: 'inbound-dropout', target: 'inbound', statusCode: 504 },
|
||||
])
|
||||
assert.strictEqual(result.applied, true)
|
||||
assert.strictEqual(result.ctx.response.statusCode, 504)
|
||||
})
|
||||
|
||||
test('applyChaosToExecution: inbound corruption truncates response body', () => {
|
||||
const ctx = {
|
||||
request: { body: null, headers: {}, query: {}, params: {} },
|
||||
response: { body: { a: 1, b: 2, c: 3 }, headers: {}, statusCode: 200, responseTime: 0 },
|
||||
timedOut: false,
|
||||
redirects: [],
|
||||
}
|
||||
|
||||
const result = applyChaosToExecution(ctx, [
|
||||
{ type: 'inbound-corruption', target: 'inbound', corruptionStrategy: 'truncate' },
|
||||
])
|
||||
assert.strictEqual(result.applied, true)
|
||||
assert.ok(Object.keys(result.ctx.response.body as object).length < 3)
|
||||
})
|
||||
|
||||
test('applyChaosToExecution: inbound corruption with field-corrupt', () => {
|
||||
const ctx = {
|
||||
request: { body: null, headers: {}, query: {}, params: {} },
|
||||
response: { body: { name: 'test', value: 42 }, headers: {}, statusCode: 200, responseTime: 0 },
|
||||
timedOut: false,
|
||||
redirects: [],
|
||||
}
|
||||
|
||||
const result = applyChaosToExecution(ctx, [
|
||||
{ type: 'inbound-corruption', target: 'inbound', corruptionStrategy: 'field-corrupt', corruptionField: 'value' },
|
||||
])
|
||||
assert.strictEqual(result.applied, true)
|
||||
assert.strictEqual((result.ctx.response.body as Record<string, unknown>).value, null)
|
||||
assert.strictEqual((result.ctx.response.body as Record<string, unknown>).name, 'test')
|
||||
})
|
||||
|
||||
test('applyChaosToExecution: inbound corruption malformed', () => {
|
||||
const ctx = {
|
||||
request: { body: null, headers: {}, query: {}, params: {} },
|
||||
response: { body: { ok: true }, headers: {}, statusCode: 200, responseTime: 0 },
|
||||
timedOut: false,
|
||||
redirects: [],
|
||||
}
|
||||
|
||||
const result = applyChaosToExecution(ctx, [
|
||||
{ type: 'inbound-corruption', target: 'inbound', corruptionStrategy: 'malformed' },
|
||||
])
|
||||
assert.strictEqual(result.applied, true)
|
||||
assert.strictEqual(result.ctx.response.body, '{"broken":')
|
||||
})
|
||||
|
||||
test('applyChaosToDependencyResponse: outbound error changes status', () => {
|
||||
const response = { contractName: 'stripe', statusCode: 200, body: { ok: true } }
|
||||
|
||||
const result = applyChaosToDependencyResponse(response, [
|
||||
{ type: 'outbound-error', target: 'outbound', contractName: 'stripe', statusCode: 429, body: { error: 'rate_limited' } },
|
||||
])
|
||||
assert.strictEqual(result.statusCode, 429)
|
||||
assert.deepStrictEqual(result.body, { error: 'rate_limited' })
|
||||
})
|
||||
|
||||
test('applyChaosToDependencyResponse: ignores events for other contracts', () => {
|
||||
const response = { contractName: 'stripe', statusCode: 200, body: { ok: true } }
|
||||
|
||||
const result = applyChaosToDependencyResponse(response, [
|
||||
{ type: 'outbound-error', target: 'outbound', contractName: 'other', statusCode: 500 },
|
||||
])
|
||||
assert.strictEqual(result.statusCode, 200)
|
||||
assert.deepStrictEqual(result.body, { ok: true })
|
||||
})
|
||||
|
||||
test('applyChaosToAllResponses: applies chaos to multiple responses', () => {
|
||||
const responses = [
|
||||
{ contractName: 'stripe', statusCode: 200, body: { id: 'pi_123' } },
|
||||
{ contractName: 'paypal', statusCode: 200, body: { id: 'pp_456' } },
|
||||
]
|
||||
|
||||
const result = applyChaosToAllResponses(responses, [
|
||||
{ type: 'outbound-error', target: 'outbound', contractName: 'stripe', statusCode: 429 },
|
||||
])
|
||||
|
||||
assert.strictEqual(result[0]!.statusCode, 429)
|
||||
assert.strictEqual(result[1]!.statusCode, 200)
|
||||
})
|
||||
|
||||
test('extractDelays: computes total delay', () => {
|
||||
const delays = extractDelays([
|
||||
{ type: 'inbound-delay', target: 'inbound', delayMs: 100 },
|
||||
{ type: 'outbound-delay', target: 'outbound', contractName: 'stripe', delayMs: 50 },
|
||||
{ type: 'inbound-error', target: 'inbound', statusCode: 500 },
|
||||
])
|
||||
assert.strictEqual(delays.totalMs, 150)
|
||||
assert.strictEqual(delays.events.length, 2)
|
||||
})
|
||||
|
||||
test('sleep: resolves after specified ms', async () => {
|
||||
const start = Date.now()
|
||||
await sleep(10)
|
||||
const elapsed = Date.now() - start
|
||||
assert.ok(elapsed >= 9) // Allow small timing variance
|
||||
})
|
||||
|
||||
test('hasAppliedChaos: detects applied chaos', () => {
|
||||
assert.strictEqual(hasAppliedChaos([{ type: 'none', target: 'inbound' }]), false)
|
||||
assert.strictEqual(hasAppliedChaos([{ type: 'inbound-error', target: 'inbound', statusCode: 500 }]), true)
|
||||
})
|
||||
|
||||
test('formatChaosEvents: formats events for diagnostics', () => {
|
||||
const formatted = formatChaosEvents([
|
||||
{ type: 'inbound-error', target: 'inbound', statusCode: 500 },
|
||||
{ type: 'outbound-delay', target: 'outbound', contractName: 'stripe', delayMs: 100 },
|
||||
])
|
||||
assert.ok(formatted.includes('inbound-error'))
|
||||
assert.ok(formatted.includes('outbound-delay'))
|
||||
assert.ok(formatted.includes('stripe'))
|
||||
assert.ok(formatted.includes('100ms'))
|
||||
})
|
||||
|
||||
test('createChaosEventArbitrary: generates deterministic events with seed', () => {
|
||||
const arb = createChaosEventArbitrary(
|
||||
{
|
||||
probability: 1,
|
||||
delay: { probability: 0.5, minMs: 10, maxMs: 100 },
|
||||
error: { probability: 0.5, statusCode: 500 },
|
||||
},
|
||||
['stripe']
|
||||
)
|
||||
|
||||
const samples1 = fc.sample(arb, { numRuns: 5, seed: 42 })
|
||||
const samples2 = fc.sample(arb, { numRuns: 5, seed: 42 })
|
||||
|
||||
assert.deepStrictEqual(samples1, samples2)
|
||||
})
|
||||
|
||||
test('createChaosEventArbitrary: returns empty array when no config', () => {
|
||||
const arb = createChaosEventArbitrary(undefined, ['stripe'])
|
||||
const samples = fc.sample(arb, { numRuns: 5, seed: 42 })
|
||||
assert.ok(samples.every((events) => events.length === 0))
|
||||
})
|
||||
|
||||
test('createChaosEventArbitrary: generates outbound events for contracts', () => {
|
||||
const arb = createChaosEventArbitrary(
|
||||
{
|
||||
probability: 1,
|
||||
outbound: [
|
||||
{
|
||||
target: 'stripe',
|
||||
error: { probability: 1, responses: [{ statusCode: 429 }] },
|
||||
},
|
||||
],
|
||||
},
|
||||
['stripe']
|
||||
)
|
||||
|
||||
const samples = fc.sample(arb, { numRuns: 20, seed: 42 })
|
||||
// Should generate some outbound-error events
|
||||
const hasOutboundError = samples.some((events) =>
|
||||
events.some((e) => e.type === 'outbound-error' && e.contractName === 'stripe')
|
||||
)
|
||||
assert.ok(hasOutboundError, 'Should generate outbound-error events for stripe')
|
||||
})
|
||||
Reference in New Issue
Block a user