Groups cdr_feed legs by Correlation ID for call-level summaries, fixes report-column field parsing and Docker proxy routing, and adds /calltest post-call Twilio and CDR enrichment. Co-authored-by: Cursor <cursoragent@cursor.com>
183 lines
5 KiB
JavaScript
183 lines
5 KiB
JavaScript
// tests/callTest.service.test.js
|
|
|
|
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
|
|
import {
|
|
handleStatusCallback,
|
|
handleVoiceWebhook,
|
|
tryFinalizeFromVoiceDoneForTests,
|
|
_clearPendingFinalizeTimersForTests,
|
|
} from '../services/callTest/callTestService.js';
|
|
import {
|
|
createSession,
|
|
_clearAllSessionsForTests,
|
|
getSession,
|
|
} from '../services/callTest/sessionStore.js';
|
|
import { renderCallTestResultMarkdown } from '../services/renderers/callTestRenderer.js';
|
|
import { _clearTwilioEnrichmentTimersForTests } from '../services/callTest/twilioEnrichment.js';
|
|
import { _clearCdrTimersForTests } from '../services/callTest/cdrMatcher.js';
|
|
|
|
const TEST_ID = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee';
|
|
|
|
function clearCallTestTimers() {
|
|
_clearPendingFinalizeTimersForTests();
|
|
_clearTwilioEnrichmentTimersForTests();
|
|
_clearCdrTimersForTests();
|
|
}
|
|
|
|
test('handleVoiceWebhook: store path marks core on intro', () => {
|
|
_clearAllSessionsForTests();
|
|
clearCallTestTimers();
|
|
createSession(TEST_ID, {
|
|
mode: 'store',
|
|
dialNumber: '+12125550100',
|
|
config: { listenSec: 60, introTts: 'x', outroTts: 'y', dtmf: '1', greetingPauseSec: 5, answerWaitSec: 45 },
|
|
});
|
|
|
|
handleVoiceWebhook(TEST_ID, 'store', 'wait');
|
|
let s = getSession(TEST_ID);
|
|
assert.equal(s.lastStep, 'wait');
|
|
assert.equal(s.reachedCore, false);
|
|
|
|
handleVoiceWebhook(TEST_ID, 'store', 'intro');
|
|
s = getSession(TEST_ID);
|
|
assert.equal(s.lastStep, 'intro');
|
|
assert.equal(s.reachedCore, true);
|
|
assert.ok(s.events.coreStartedAt);
|
|
});
|
|
|
|
test('handleVoiceWebhook done step finalizes without status callback', async () => {
|
|
_clearAllSessionsForTests();
|
|
clearCallTestTimers();
|
|
createSession(TEST_ID, {
|
|
mode: 'dial',
|
|
dialNumber: '+12125550100',
|
|
roomId: 'room-1',
|
|
reachedCore: true,
|
|
config: { twilioEnrich: false, cdrEnrich: false },
|
|
});
|
|
|
|
handleVoiceWebhook(TEST_ID, 'dial', 'outro');
|
|
handleVoiceWebhook(TEST_ID, 'dial', 'done');
|
|
|
|
const pending = getSession(TEST_ID);
|
|
assert.equal(pending.lastStep, 'done');
|
|
|
|
const finalized = await tryFinalizeFromVoiceDoneForTests(TEST_ID);
|
|
assert.equal(finalized.result, 'pass');
|
|
assert.equal(finalized.status, 'completed');
|
|
clearCallTestTimers();
|
|
});
|
|
|
|
test('finalize leaves enrichment pending on session', async () => {
|
|
_clearAllSessionsForTests();
|
|
clearCallTestTimers();
|
|
createSession(TEST_ID, {
|
|
mode: 'dial',
|
|
dialNumber: '+17247795574',
|
|
roomId: 'room-1',
|
|
callSid: 'CAENRICH',
|
|
reachedCore: true,
|
|
lastStep: 'done',
|
|
config: { twilioEnrich: false, cdrEnrich: false },
|
|
});
|
|
|
|
handleVoiceWebhook(TEST_ID, 'dial', 'done');
|
|
await tryFinalizeFromVoiceDoneForTests(TEST_ID);
|
|
|
|
const s = getSession(TEST_ID);
|
|
assert.equal(s.status, 'completed');
|
|
assert.deepEqual(s.enrichment, { twilio: {}, cdr: {} });
|
|
clearCallTestTimers();
|
|
});
|
|
|
|
test('handleStatusCallback: in-progress maps to answered and passes on completed', async () => {
|
|
_clearAllSessionsForTests();
|
|
clearCallTestTimers();
|
|
createSession(TEST_ID, {
|
|
mode: 'dial',
|
|
dialNumber: '+12125550100',
|
|
reachedCore: true,
|
|
lastStep: 'done',
|
|
config: { twilioEnrich: false, cdrEnrich: false },
|
|
});
|
|
|
|
await handleStatusCallback(TEST_ID, {
|
|
CallStatus: 'in-progress',
|
|
CallSid: 'CA123',
|
|
});
|
|
await handleStatusCallback(TEST_ID, {
|
|
CallStatus: 'completed',
|
|
CallDuration: '75',
|
|
CallSid: 'CA123',
|
|
});
|
|
|
|
const s = getSession(TEST_ID);
|
|
assert.ok(s.events.answered);
|
|
assert.equal(s.result, 'pass');
|
|
clearCallTestTimers();
|
|
});
|
|
|
|
test('handleStatusCallback: completed with core yields pass in renderer', async () => {
|
|
_clearAllSessionsForTests();
|
|
clearCallTestTimers();
|
|
createSession(TEST_ID, {
|
|
mode: 'dial',
|
|
dialNumber: '+12125550100',
|
|
reachedCore: true,
|
|
lastStep: 'done',
|
|
config: { twilioEnrich: false, cdrEnrich: false },
|
|
});
|
|
|
|
await handleStatusCallback(TEST_ID, {
|
|
CallStatus: 'answered',
|
|
CallSid: 'CA123',
|
|
});
|
|
await handleStatusCallback(TEST_ID, {
|
|
CallStatus: 'completed',
|
|
CallDuration: '75',
|
|
CallSid: 'CA123',
|
|
});
|
|
|
|
const s = getSession(TEST_ID);
|
|
assert.equal(s.result, 'pass');
|
|
assert.equal(s.durationSec, 75);
|
|
|
|
const md = renderCallTestResultMarkdown(s);
|
|
assert.match(md, /PASSED/);
|
|
assert.match(md, /CA123/);
|
|
clearCallTestTimers();
|
|
});
|
|
|
|
test('handleStatusCallback: no-answer fails', async () => {
|
|
_clearAllSessionsForTests();
|
|
clearCallTestTimers();
|
|
createSession(TEST_ID, {
|
|
mode: 'dial',
|
|
dialNumber: '+12125550100',
|
|
config: { twilioEnrich: false, cdrEnrich: false },
|
|
});
|
|
|
|
await handleStatusCallback(TEST_ID, {
|
|
CallStatus: 'no-answer',
|
|
CallSid: 'CA999',
|
|
});
|
|
|
|
const s = getSession(TEST_ID);
|
|
assert.equal(s.result, 'fail');
|
|
const md = renderCallTestResultMarkdown(s);
|
|
assert.match(md, /FAILED/);
|
|
clearCallTestTimers();
|
|
});
|
|
|
|
test('renderCallTestResultMarkdown shows in progress before final result', () => {
|
|
const md = renderCallTestResultMarkdown({
|
|
mode: 'dial',
|
|
status: 'in_progress',
|
|
dialNumber: '+12125550100',
|
|
testId: TEST_ID,
|
|
events: { coreStartedAt: new Date().toISOString() },
|
|
});
|
|
assert.match(md, /IN PROGRESS/);
|
|
});
|