wbxcallprov/test/storePhoneExport.test.js
jmcqueen 63f35027f6
Some checks failed
CI / verify (push) Has been cancelled
Add daily store phone export to Webex space
Weekday 10am Eastern cron exports all Webex Calling locations whose
name starts with "Store" to a legacy-format CSV (+E164,Store XXXX),
diffs against the previous baseline, and posts the file plus a change
summary to a configured Webex space.

- src/webex/storePhones.js: paginated GET /telephony/config/locations
- src/services/storePhoneExport.js: CSV format, filename, diff, message
- src/webex/messages.js: bot-token multipart POST /messages with file
- src/jobs/storePhoneExport.js + cron in src/index.js
- scripts/exportStorePhones.js for manual runs (npm run export-store-phones)
- Persist baseline + archives under data/store-phone-export/ (gitignored)
- Dockerfile: create /app/data for volume mount in prod
- CI: align node-version with Dockerfile (20)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 14:57:45 -04:00

135 lines
4.6 KiB
JavaScript

import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import {
diffStorePhones,
formatExportFilename,
formatExportMessage,
parseStorePhoneCsv,
rowsToCsv,
} from '../src/services/storePhoneExport.js';
describe('rowsToCsv / parseStorePhoneCsv', () => {
it('round-trips rows with no header', () => {
const rows = [
{ phoneNumber: '+14044708802', name: 'Store 7311' },
{ phoneNumber: '+17245537090', name: 'Store 0006 - TestLab' },
];
const csv = rowsToCsv(rows);
assert.equal(csv, '+14044708802,Store 7311\n+17245537090,Store 0006 - TestLab\n');
const map = parseStorePhoneCsv(csv);
assert.equal(map.size, 2);
assert.equal(map.get('Store 7311'), '+14044708802');
assert.equal(map.get('Store 0006 - TestLab'), '+17245537090');
});
it('returns empty string for no rows', () => {
assert.equal(rowsToCsv([]), '');
assert.equal(parseStorePhoneCsv('').size, 0);
});
it('ignores blank and malformed lines when parsing', () => {
const text = '+15551234567,Store 0499\n\nbadline\n,+1\n';
const map = parseStorePhoneCsv(text);
assert.equal(map.size, 1);
assert.equal(map.get('Store 0499'), '+15551234567');
});
});
describe('formatExportFilename', () => {
it('matches StorePhone-YYYY-MM-DDT…csv with underscores in time and offset', () => {
// Jul 15 2026 09:10:03.455 EDT = UTC-4
const date = new Date('2026-07-15T13:10:03.455Z');
const name = formatExportFilename(date, 'America/New_York');
assert.match(
name,
/^StorePhone-\d{4}-\d{2}-\d{2}T\d{2}_\d{2}_\d{2}\.\d{3}[+-]\d{2}_\d{2}\.csv$/,
);
assert.ok(name.includes('2026-07-15'), name);
assert.ok(name.includes('09_10_03'), name);
});
});
describe('diffStorePhones', () => {
const prev = new Map([
['Store 0499', '+15551111111'],
['Store 0500', '+15552222222'],
['Store 0999', '+15559999999'],
]);
it('detects added, removed, and changed rows', () => {
const curr = new Map([
['Store 0499', '+15559876543'],
['Store 0500', '+15552222222'],
['Store 7311', '+14044708802'],
]);
const diff = diffStorePhones(prev, curr);
assert.equal(diff.added.length, 1);
assert.equal(diff.added[0].name, 'Store 7311');
assert.equal(diff.removed.length, 1);
assert.equal(diff.removed[0].name, 'Store 0999');
assert.equal(diff.changed.length, 1);
assert.equal(diff.changed[0].name, 'Store 0499');
assert.equal(diff.unchangedCount, 1);
});
it('reports no changes when maps are identical', () => {
const diff = diffStorePhones(prev, new Map(prev));
assert.equal(diff.added.length, 0);
assert.equal(diff.removed.length, 0);
assert.equal(diff.changed.length, 0);
assert.equal(diff.unchangedCount, 3);
});
});
describe('formatExportMessage', () => {
const date = new Date('2026-07-15T14:00:00.000Z');
it('notes first run when there is no baseline', () => {
const msg = formatExportMessage({
date,
timeZone: 'America/New_York',
rowCount: 1081,
diff: { added: [], removed: [], changed: [], unchangedCount: 0 },
isFirstRun: true,
});
assert.match(msg, /First export/);
assert.match(msg, /1,081/);
});
it('lists changes when present', () => {
const msg = formatExportMessage({
date,
timeZone: 'America/New_York',
rowCount: 3,
isFirstRun: false,
diff: {
added: [{ name: 'Store 7311', phoneNumber: '+14044708802' }],
removed: [],
changed: [
{
name: 'Store 0499',
oldPhone: '+15551111111',
newPhone: '+15559876543',
},
],
unchangedCount: 1,
},
});
assert.match(msg, /Added \(1\)/);
assert.match(msg, /Store 7311/);
assert.match(msg, /Changed \(1\)/);
assert.match(msg, /0499/);
});
it('says no changes when diff is empty', () => {
const msg = formatExportMessage({
date,
timeZone: 'America/New_York',
rowCount: 10,
isFirstRun: false,
diff: { added: [], removed: [], changed: [], unchangedCount: 10 },
});
assert.match(msg, /No changes since last run/);
});
});