// tools/challenge-spec/resilience-challenge-scenarios-a.ts — SPEC-034
// Signal detectors + validation boundary + cascade failure scenarios
// Keywords indicating external service calls or shared state
export const EXTERNAL_CALL_KEYWORDS = [
    'api',
    'http',
    'fetch',
    'request',
    'endpoint',
    'service',
    'downstream',
    'external',
    'grpc',
    'webhook',
    'integration',
    'client',
    'sdk',
];
export const VALIDATION_KEYWORDS = [
    'input',
    'form',
    'body',
    'payload',
    'request',
    'submit',
    'create',
    'update',
    'post',
    'put',
    'patch',
    'validate',
    'schema',
    'endpoint',
];
export const RATE_LIMIT_KEYWORDS = [
    'login',
    'auth',
    'register',
    'public',
    'api',
    'bulk',
    'export',
    'import',
];
export const CONCURRENCY_KEYWORDS = [
    'counter',
    'cache',
    'session',
    'pool',
    'shared',
    'concurrent',
    'parallel',
    'queue',
    'lock',
    'mutex',
    'atomic',
    'thread',
];
export const SAGA_KEYWORDS = [
    'distributed',
    'saga',
    'multi-step',
    'compensat',
    'rollback',
    'orchestrat',
    'choreograph',
    'transaction',
];
/**
 * Returns true when the spec describes an external service call.
 */
export function hasExternalCallSignal(spec, specContent) {
    const lower = `${spec.title} ${spec.tags.join(' ')} ${specContent}`.toLowerCase();
    return EXTERNAL_CALL_KEYWORDS.some((kw) => lower.includes(kw));
}
/**
 * Returns true when the spec describes input validation.
 */
export function hasValidationSignal(spec, specContent) {
    const lower = `${spec.title} ${spec.tags.join(' ')} ${specContent}`.toLowerCase();
    return VALIDATION_KEYWORDS.some((kw) => lower.includes(kw));
}
/**
 * Returns true when the spec is a rate-limit-sensitive endpoint.
 */
export function hasRateLimitSignal(spec, specContent) {
    const lower = `${spec.title} ${spec.tags.join(' ')} ${specContent}`.toLowerCase();
    return RATE_LIMIT_KEYWORDS.some((kw) => lower.includes(kw));
}
/**
 * Returns true when the spec involves shared state.
 */
export function hasConcurrencySignal(spec, specContent) {
    const lower = `${spec.title} ${spec.tags.join(' ')} ${specContent}`.toLowerCase();
    return CONCURRENCY_KEYWORDS.some((kw) => lower.includes(kw));
}
/**
 * Returns true when the spec describes a distributed saga.
 */
export function hasSagaSignal(spec, specContent) {
    const lower = `${spec.title} ${spec.tags.join(' ')} ${specContent}`.toLowerCase();
    return SAGA_KEYWORDS.some((kw) => lower.includes(kw));
}
/**
 * Generate validation boundary challenge scenarios.
 */
export function generateValidationBoundaryScenarios(spec) {
    return [
        {
            scenario: `[${spec.id}] Validation Boundary — String Fields: ` +
                'What happens when a required string field receives: (a) empty string "", (b) null, (c) field absent from body entirely, ' +
                '(d) string with only whitespace "   ", (e) Unicode multibyte string (emoji) that exceeds maxLength if the library counts bytes instead of codepoints? ' +
                'Are all three cases (absent vs null vs empty) handled with distinct error codes?',
            probability: 'high',
            impact: 'high',
            currentHandling: 'Validation may treat empty and null as equivalent, or silently pass absent fields as undefined without failing required checks.',
            requiredHandling: 'Define explicit behavior for each case in the validation schema. ' +
                'Empty string "" → 400 if field is required. ' +
                'null → 400 if field is non-nullable. ' +
                'Absent field → 400 if field is required. ' +
                'Whitespace-only → trim and validate length after trimming. ' +
                'Unicode: use character/codepoint counting, not byte length.',
            dataConsistency: 'Accepting empty strings as valid inputs leads to records with meaningless values polluting the database.',
            userExperience: 'Clear per-field error messages with the specific constraint violated improve form UX significantly.',
        },
        {
            scenario: `[${spec.id}] Validation Boundary — Numeric Fields: ` +
                'What happens when a numeric field receives: (a) value exactly at min boundary (e.g., min=0 → test with -1, 0, 1), ' +
                '(b) value exactly at max boundary, (c) NaN, (d) Infinity or -Infinity, ' +
                '(e) number sent as string "42" — does the library coerce or reject?',
            probability: 'medium',
            impact: 'high',
            currentHandling: 'Boundary conditions (exactly at min/max) are often omitted from tests, leading to off-by-one bugs in production.',
            requiredHandling: 'Test all boundary values explicitly: min-1 (fail), min (pass), max (pass), max+1 (fail). ' +
                'NaN and Infinity must be rejected with 400, not passed through. ' +
                'Coercion policy must be explicit: either always reject type mismatches or always coerce with documented behavior.',
            dataConsistency: 'NaN or Infinity stored in numeric columns causes silent corruption or runtime exceptions.',
            userExperience: 'Off-by-one errors in validation create confusing UX where users cannot submit valid data.',
        },
        {
            scenario: `[${spec.id}] Validation Security — Injection Payloads: ` +
                'Does the validation layer handle security-relevant strings without crashing or leaking internals? ' +
                "Test with: SQL injection ('; DROP TABLE users; --), XSS (<script>alert(1)</script>), " +
                'path traversal (../../etc/passwd), null bytes (\\0), template injection ({{7*7}}). ' +
                'Validation is not the primary defense (that is parameterized queries / output encoding), ' +
                'but it must not crash and must not expose stack traces.',
            probability: 'medium',
            impact: 'critical',
            currentHandling: 'Injection payloads may cause unhandled exceptions in validation middleware, exposing internal error details.',
            requiredHandling: 'All string inputs must be treated as untrusted. ' +
                'Validation rejects malformed types; sanitization and parameterized queries handle injection prevention downstream. ' +
                'Error responses must never include the rejected payload or stack trace — return sanitized field name + constraint only.',
            dataConsistency: 'If injection payloads reach the database layer (even as errors), they can leak schema information via error messages.',
            userExperience: 'Stack traces exposed in 400 responses are a security vulnerability — use opaque error IDs instead.',
        },
    ];
}
/**
 * Generate cascade failure challenge scenarios.
 */
export function generateCascadeFailureScenarios(spec) {
    return [
        {
            scenario: `[${spec.id}] Cascade Failure — Connection Pool Exhaustion: ` +
                'If the external service starts responding in 30 seconds (OS timeout) instead of the expected 200ms, ' +
                'how many simultaneous requests accumulate in the connection pool before the circuit breaker activates? ' +
                'If the pool has size N and each request holds a connection for T seconds, the pool fills in N * T seconds. ' +
                'Calculate this with the actual pool size and timeout values. ' +
                'Does the circuit breaker open before the pool is exhausted?',
            probability: 'medium',
            impact: 'critical',
            currentHandling: 'Without a circuit breaker, slow external services cause connection pool exhaustion, ' +
                'blocking all requests to this service (not just those calling the external service).',
            requiredHandling: 'Set connect_timeout (2s) and read_timeout (10s) — never rely on OS timeout (30s+). ' +
                'Circuit breaker must open before pool exhaustion: threshold 50% failures in 10 calls, open duration 60s. ' +
                'When pool is at 80% capacity, emit a warning metric. ' +
                'When circuit is open, return 503 immediately without touching the pool.',
            dataConsistency: 'Pool exhaustion can cascade: if this service pool is exhausted, callers of this service also accumulate requests.',
            userExperience: 'Without fast-fail (circuit open), users wait for OS timeout (30s) instead of receiving a 503 in under 1s.',
        },
        {
            scenario: `[${spec.id}] Resilience — Retry Thundering Herd: ` +
                'If the external service recovers after a 60-second outage and the circuit breaker transitions to HALF-OPEN, ' +
                'all queued retries fire simultaneously. ' +
                'Does the retry strategy use jitter (random delay offset) to spread the load, ' +
                'or do all retries hit the recovering service at the same time (thundering herd)?',
            probability: 'medium',
            impact: 'high',
            currentHandling: 'Retry without jitter causes thundering herd: all retries fire simultaneously after backoff, ' +
                'potentially re-overloading the service as it recovers.',
            requiredHandling: 'Exponential backoff with jitter: delay = random(0, base * 2^attempt). ' +
                'In HALF-OPEN state: limit to 1-3 probe requests, not all queued requests simultaneously. ' +
                'If probes succeed, circuit closes gradually.',
            dataConsistency: 'Thundering herd can cause the recovering service to fail again immediately after recovery.',
            userExperience: 'Jitter is invisible to the user but prevents the oscillation pattern where the service flaps between healthy and degraded.',
        },
    ];
}
//# sourceMappingURL=resilience-challenge-scenarios-a.js.map