E2E Tests Real Stack Validation
Files:
client_backend/src/tests/e2e-call-test.tsSingle test callclient_backend/src/tests/e2e-campaign-test.tsFull campaign orchestration
Why
The integration tests and load tests simulate webhooks by writing directly to MongoDB. They prove the backend logic is correct but don't touch the real infrastructure. These E2E tests trigger REAL calls through the production stack:
API → Redis/BullMQ → LiveKit room → Agent joins → SIP dispatch →
VoiceLink → PSTN → real phone rings → recording → S3 → transcription → analysisIf any component is down, misconfigured, or broken, these tests catch it.
Prerequisites
All of these must be running:
- Backend server (local or remote)
- Redis
- LiveKit server
- Agent worker(s) (Python)
- Analysis service (FastAPI)
- A real phone number that someone will answer
Setup
Add these to your .env file (or export as env vars):
E2E_BASE_URL=http://localhost:3000
E2E_EMAIL[email protected]
E2E_PASSWORD=yourpassword
E2E_AGENT_ID=your-agent-id
E2E_PHONE=+919876543210Test 1: Single Call (e2e-call-test.ts)
Triggers one test call via POST /test-agent and validates the full Contact document.
cd client_backend
# Basic run
npx tsx src/tests/e2e-call-test.ts
# Override phone number
npx tsx src/tests/e2e-call-test.ts --phone +919876543210
# Skip S3 check (if no AWS creds locally)
npx tsx src/tests/e2e-call-test.ts --skip-s3
# Longer timeout (default 120s)
npx tsx src/tests/e2e-call-test.ts --timeout 180What it validates:
- Call reaches
donestatus - All timestamps set (participantJoinedAt, participantLeftAt, callAnsweredAt)
- SIP data stored (sipCallId, livekitData)
- billingDuration computed correctly
- Recording started (egressId, s3RecordingKey)
- S3 file exists (actual HeadObject check)
- recordingUrl generated (presigned URL)
- Transcription stored with agent + user turns
- Analysis stored with sentiment
- isTestCall flag set
- No credit deduction (test calls are free)
How it works
- Logs in via
POST /auth/loginto get a JWT token - Triggers
POST /test-agentwith phone number and agent ID - Polls the Contact document every 3s until terminal state
- Waits 10s for async data (egress_ended, transcription, analysis)
- Validates every field and prints a pass/fail report
Test 2: Campaign Orchestration (e2e-campaign-test.ts)
Starts a REAL campaign through the API and monitors it to completion. This is the only test that exercises BullMQ orchestration, Redis state management, and concurrent call dispatch.
cd client_backend
# Start an existing campaign
npx tsx src/tests/e2e-campaign-test.ts --campaign <campaignUID> --agent <agentId>
# Longer timeout for large campaigns (default 300s)
npx tsx src/tests/e2e-campaign-test.ts --campaign <uid> --agent <id> --timeout 600What it validates:
- Campaign stops running after all contacts processed
- No contacts stuck in
in_progress - All done contacts have: timestamps, SIP data, duration, recording, transcription, analysis, credit transactions
- All failed contacts have: disconnect reason, no billing duration
- Total done + failed + skipped = total contacts
How it works
- Logs in and verifies the campaign exists
- Calls
POST /start-campaign/:uid(handles restart confirmation if needed) - Polls campaign status + contact counts every 5s
- Waits 15s after completion for async data
- Validates all contacts and prints a report
Recommended Testing Flow
1. Run integration tests (no infra needed, fast)
npx vitest --run src/tests/webhook-pipeline.test.ts
2. Run E2E single call (one person answers phone)
npx tsx src/tests/e2e-call-test.ts
3. Run E2E campaign (small batch, 3-5 contacts)
npx tsx src/tests/e2e-campaign-test.ts --campaign <uid> --agent <id>
4. Run load test (synthetic, no real calls)
npx tsx src/tests/load-test-pipeline.ts --contacts 500 --concurrency 50
5. Validate any campaign post-hoc
npx tsx src/scripts/validate-campaign.ts <campaignUID>Daily Health Check
Run the single call test as a daily smoke test. If it passes, your entire stack is working:
npx tsx src/tests/e2e-call-test.ts --phone +91XXXXXXXXXXOne team member's phone as the dedicated test number. Takes ~30 seconds.