codeburn/tests/sync-infra-e2e.test.ts
Andrew Lee 163842edac test(sync): unit, mock-IdP e2e, and developer-only integration tests
Offline, CI-safe (70 tests):
- sync.test.ts (26): discovery parsing, PKCE, auth URL, scope
  negotiation, callback server (state mismatch, IdP error, timeout,
  port fallback), config round-trip
- sync-ledger-otlp.test.ts (23): deterministic ID derivation, OTLP
  structure/attributes, batching, ledger append/dedup/prune/clear
- sync-push.test.ts (15): pipeline against a scriptable mock OTLP
  server — success+ledger+cost, partial-success batch NOT ledgered,
  401 stop, 429 wait-and-retry / persistent-429 give-up / Retry-After
  parsing (delta-seconds, HTTP-date, garbage), 5xx deferral,
  idempotent failure recovery
- sync-e2e.test.ts (6) + fixtures/mock-idp.ts: full setup→refresh→
  rotate→logout round-trip against an in-process mock IdP

Developer-only (skip unless env vars set, never CI):
- sync-headless-e2e.test.ts (1): real browser PKCE flow via Playwright
- sync-infra-e2e.test.ts (4): push/auth-reject/batch/idempotent
  re-push against a deployed backend

AI-Origin: human
2026-07-12 16:05:34 +00:00

232 lines
7.1 KiB
TypeScript

/**
* Integration test: codeburn sync push against real AWS infrastructure.
*
* Requires:
* - Deployed CodeburnSyncBackend stack
* - Environment variables: CODEBURN_SYNC_URL, CODEBURN_SYNC_EMAIL, CODEBURN_SYNC_PASSWORD
* - AWS credentials (for verifying CloudWatch logs)
*
* Run:
* CODEBURN_SYNC_URL=https://xxx.execute-api.us-west-2.amazonaws.com \
* CODEBURN_SYNC_EMAIL=andklee@amazon.com \
* CODEBURN_SYNC_PASSWORD='password' \
* AWS_PROFILE=andklee-dev \
* npx vitest run tests/sync-infra-e2e.test.ts
*/
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import { randomBytes, createHash } from 'crypto'
import { fetchDiscoveryDoc } from '../src/sync/discovery.js'
import { fetchOidcConfig, refreshToken } from '../src/sync/auth.js'
import { buildOtlpPayload, deriveSpanId, type CallWithSession } from '../src/sync/otlp.js'
import type { ParsedApiCall, TokenUsage } from '../src/types.js'
const BASE_URL = process.env.CODEBURN_SYNC_URL
const TEST_EMAIL = process.env.CODEBURN_SYNC_EMAIL
const TEST_PASSWORD = process.env.CODEBURN_SYNC_PASSWORD
const AWS_REGION = process.env.AWS_REGION || 'us-west-2'
const SKIP = !BASE_URL || !TEST_EMAIL || !TEST_PASSWORD
describe.skipIf(SKIP)('sync infra e2e — push + verify', () => {
let accessToken: string
let clientId: string
let tracesEndpoint: string
const testRunId = randomBytes(4).toString('hex')
beforeAll(async () => {
// Get tokens via admin API (Cognito USER_PASSWORD_AUTH)
const discovery = await fetchDiscoveryDoc(BASE_URL!)
clientId = discovery.client_id
tracesEndpoint = `${BASE_URL}${discovery.traces_path}`
// Direct auth (USER_PASSWORD_AUTH) for testing
const tokenEndpoint = `https://codeburn-sync.auth.${AWS_REGION}.amazoncognito.com/oauth2/token`
// Use Cognito initiateAuth via fetch to the Cognito service
const cognitoUrl = `https://cognito-idp.${AWS_REGION}.amazonaws.com/`
const userPoolId = discovery.issuer.split('/').pop()!
const authResp = await fetch(cognitoUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-amz-json-1.1',
'X-Amz-Target': 'AWSCognitoIdentityProviderService.InitiateAuth',
},
body: JSON.stringify({
AuthFlow: 'USER_PASSWORD_AUTH',
ClientId: clientId,
AuthParameters: {
USERNAME: TEST_EMAIL,
PASSWORD: TEST_PASSWORD,
},
}),
})
const authResult = await authResp.json() as { AuthenticationResult?: { AccessToken?: string } }
accessToken = authResult.AuthenticationResult?.AccessToken ?? ''
expect(accessToken).toBeTruthy()
})
it('pushes a test span and receives 200', async () => {
const testDedup = `test:infra-e2e:${testRunId}:span1`
const usage: TokenUsage = {
inputTokens: 999,
outputTokens: 111,
cacheCreationInputTokens: 0,
cacheReadInputTokens: 0,
cachedInputTokens: 0,
reasoningTokens: 0,
webSearchRequests: 0,
}
const call: ParsedApiCall = {
provider: 'test',
model: 'test-model',
usage,
costUSD: 0.001,
tools: ['TestTool'],
mcpTools: [],
skills: [],
subagentTypes: [],
hasAgentSpawn: false,
hasPlanMode: false,
speed: 'standard',
timestamp: new Date().toISOString(),
bashCommands: [],
deduplicationKey: testDedup,
}
const callWithSession: CallWithSession = {
call,
sessionId: `test-session-${testRunId}`,
project: 'infra-e2e-test',
}
const payload = buildOtlpPayload([callWithSession])
const response = await fetch(tracesEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`,
},
body: JSON.stringify(payload),
})
expect(response.status).toBe(200)
const body = await response.json() as Record<string, unknown>
// No partial_success means full success
expect(body.partialSuccess?.rejectedSpans).toBeUndefined()
})
it('rejects unauthenticated requests', async () => {
const payload = buildOtlpPayload([])
const response = await fetch(tracesEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer invalid-token',
},
body: JSON.stringify(payload),
})
expect(response.status).toBe(401)
})
it('pushes multiple spans in one batch', async () => {
const calls: CallWithSession[] = Array.from({ length: 5 }, (_, i) => {
const usage: TokenUsage = {
inputTokens: 100 * (i + 1),
outputTokens: 50 * (i + 1),
cacheCreationInputTokens: 0,
cacheReadInputTokens: 0,
cachedInputTokens: 0,
reasoningTokens: 0,
webSearchRequests: 0,
}
return {
call: {
provider: 'test',
model: 'test-batch-model',
usage,
costUSD: 0.01 * (i + 1),
tools: [],
mcpTools: [],
skills: [],
subagentTypes: [],
hasAgentSpawn: false,
hasPlanMode: false,
speed: 'standard' as const,
timestamp: new Date().toISOString(),
bashCommands: [],
deduplicationKey: `test:infra-e2e:${testRunId}:batch:${i}`,
},
sessionId: `test-batch-session-${testRunId}`,
project: 'infra-e2e-batch',
}
})
const payload = buildOtlpPayload(calls)
const response = await fetch(tracesEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`,
},
body: JSON.stringify(payload),
})
expect(response.status).toBe(200)
})
it('deterministic span IDs allow safe re-push', async () => {
const dedup = `test:infra-e2e:${testRunId}:idempotent`
const spanId = deriveSpanId(dedup)
// Push same data twice
const call: CallWithSession = {
call: {
provider: 'test',
model: 'test-idempotent',
usage: { inputTokens: 1, outputTokens: 1, cacheCreationInputTokens: 0, cacheReadInputTokens: 0, cachedInputTokens: 0, reasoningTokens: 0, webSearchRequests: 0 },
costUSD: 0,
tools: [],
mcpTools: [],
skills: [],
subagentTypes: [],
hasAgentSpawn: false,
hasPlanMode: false,
speed: 'standard',
timestamp: new Date().toISOString(),
bashCommands: [],
deduplicationKey: dedup,
},
sessionId: 'idem-session',
project: 'idem-project',
}
const payload = buildOtlpPayload([call])
// First push
const r1 = await fetch(tracesEndpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${accessToken}` },
body: JSON.stringify(payload),
})
expect(r1.status).toBe(200)
// Second push (identical payload)
const r2 = await fetch(tracesEndpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${accessToken}` },
body: JSON.stringify(payload),
})
expect(r2.status).toBe(200)
// Both succeed — server tolerates duplicates per OTLP spec
})
})