collabSupport/tests/callReport.join.test.js
jmcqueen a25fc08fe2 Rename /voicereport to /callreport with scoped user and phone targets.
Add store/email/phone filtering, richer call-line formatting, CDR feed pagination and queueing, and split Jira poller enrichment into testable modules.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 10:10:03 -04:00

184 lines
5.1 KiB
JavaScript

// tests/callReport.join.test.js
import test from 'node:test';
import assert from 'node:assert/strict';
import { buildScopedCallFilter } from '../services/callReport/filterCallsByTarget.js';
import { joinCallQuality, worstPrismaBucketForCall } from '../services/callReport/joinCallQuality.js';
import { CALL_BUCKETS } from '../services/callReport/groupCdrCalls.js';
const window = {
startTime: '2026-07-23T13:00:00.000Z',
endTime: '2026-07-24T01:00:00.000Z',
};
const INBOUND_PHONE_LEGS = [
{
'Start time': '2026-07-23T13:00:00.000Z',
'Release time': '2026-07-23T13:05:00.000Z',
'Call type': 'SIP_INBOUND',
'Correlation ID': 'corr-1',
'Report ID': 'r1',
'Calling number': '+15551234567',
'Called number': '+12122194600',
'Site main number': '+12122194600',
'Call outcome': 'Success',
'Call outcome reason': 'Normal',
'Answer indicator': 'Yes',
Answered: 'true',
Duration: 300,
},
{
'Start time': '2026-07-23T13:00:30.000Z',
'Call type': 'SIP_ENTERPRISE',
'Correlation ID': 'corr-1',
'Report ID': 'r2',
'User type': 'User',
User: 'Store 02477',
Model: 'DBS-210-3PC',
'Called number': '52477',
'Calling number': '+15551234567',
'Call outcome': 'Success',
'Call outcome reason': 'Normal',
'Answer indicator': 'Yes',
Answered: 'true',
Duration: 270,
},
];
test('joinCallQuality groups legs into correlated calls', () => {
const joined = joinCallQuality({
cdrItems: [
...INBOUND_PHONE_LEGS,
{
Direction: 'Outbound',
'Start time': '2026-07-23T15:00:00.000Z',
Duration: 10,
'Answer indicator': 'No',
'Report ID': 'r3',
'Call type': 'SIP_OUTBOUND',
'Call outcome': 'Failed',
'Call outcome reason': 'Busy',
},
],
appAudio: null,
window,
});
assert.equal(joined.summary.total, 2);
assert.equal(joined.summary.inbound, 1);
assert.equal(joined.summary.outbound, 1);
assert.equal(joined.summary.abnormal, 1);
assert.equal(joined.abnormalCalls.length, 1);
assert.equal(joined.buckets[CALL_BUCKETS.inboundReachedPhone].length, 1);
});
test('joinCallQuality attaches WAN only for answered inbound', () => {
const appAudio = {
mos: { values: [4.4, 4.4, 3.1, 4.4, 4.4] },
loss: { values: [0, 0, 2, 0, 0] },
jitter: { values: [5, 5, 5, 5, 5] },
};
const joined = joinCallQuality({
cdrItems: INBOUND_PHONE_LEGS,
appAudio,
window,
});
assert.ok(joined.calls[0].wan);
assert.equal(joined.calls[0].wan.mos, 4.4);
});
test('joinCallQuality skips WAN for unanswered outbound', () => {
const appAudio = {
mos: { values: [4.0, 4.0, 4.0] },
loss: { values: [0, 0, 0] },
jitter: { values: [5, 5, 5] },
};
const joined = joinCallQuality({
cdrItems: [{
'Start time': '2026-07-23T15:00:00.000Z',
'Report ID': 'r4',
'Call type': 'SIP_OUTBOUND',
'Call outcome': 'Failed',
'Call outcome reason': 'Busy',
'Answer indicator': 'No',
}],
appAudio,
window,
});
assert.equal(joined.calls[0].wan, null);
});
test('worstPrismaBucketForCall picks low MOS bucket', () => {
const call = {
start: '2026-07-23T13:05:00.000Z',
duration: 600,
};
const appAudio = {
mos: { values: [4.4, 4.4, 3.1, 4.4, 4.4] },
loss: { values: [0, 0, 2, 0, 0] },
jitter: { values: [5, 5, 5, 5, 5] },
};
const w = worstPrismaBucketForCall(call, appAudio, window);
assert.ok(w);
assert.equal(w.mos, 3.1);
});
test('worstPrismaBucketForCall matches Prisma datapoint timestamps', () => {
const call = {
start: '2026-07-23T15:18:00.000Z',
duration: 455,
};
const appAudio = {
mos: {
interval: '5min',
values: [4.4, 4.3, 3.1],
timedPoints: [
{ time: '2026-07-23T13:00:00.000Z', value: 4.4 },
{ time: '2026-07-23T13:05:00.000Z', value: null },
{ time: '2026-07-23T15:15:00.000Z', value: 4.3 },
{ time: '2026-07-23T15:20:00.000Z', value: 3.1 },
],
},
loss: {
timedPoints: [
{ time: '2026-07-23T15:15:00.000Z', value: 0 },
{ time: '2026-07-23T15:20:00.000Z', value: 2 },
],
},
jitter: {
timedPoints: [
{ time: '2026-07-23T15:15:00.000Z', value: 1.2 },
{ time: '2026-07-23T15:20:00.000Z', value: 3.4 },
],
},
};
const w = worstPrismaBucketForCall(call, appAudio, window);
assert.ok(w);
assert.equal(w.mos, 3.1);
assert.equal(w.loss, 2);
assert.equal(w.jitter, 3.4);
});
test('joinCallQuality filters scoped calls when filter provided', () => {
const joined = joinCallQuality({
cdrItems: [
...INBOUND_PHONE_LEGS,
{
Direction: 'Outbound',
'Start time': '2026-07-23T15:00:00.000Z',
Duration: 10,
'Answer indicator': 'No',
'Report ID': 'r3',
'Call type': 'SIP_OUTBOUND',
'Call outcome': 'Failed',
'Call outcome reason': 'Busy',
},
],
appAudio: null,
window,
filter: buildScopedCallFilter({ kind: 'number', extension: '52477' }),
});
assert.equal(joined.summary.total, 1);
assert.equal(joined.summary.inbound, 1);
assert.equal(joined.summary.outbound, 0);
});