Some checks failed
CI / verify (push) Has been cancelled
Two bugs surfaced by the same failed finalize run for store 7311.
1. addPhoneNumbersToLocation was POSTing to /locations/{id}/numbers,
which Webex treats as "provision a NEW number" — that triggered
duplicate PSTN orders against numbers AE had already manually
ordered through Control Hub, and the provider rejected them with
ORDER_CREATION_FAILED. AE's workflow is manual ordering, so this
step now VERIFIES the number's current state in the org via
GET /telephony/config/numbers?phoneNumber=..., classifies the
outcome (already here / pending / wrong location / not in org),
and either succeeds silently or throws with a specific
Control-Hub fix-it instruction. The bot never triggers a PSTN
order now.
2. findDectNetworkInLocation was hitting
GET /telephony/config/locations/{id}/dectNetworks, which Webex
does not implement (returns HTTP 404 "No static resource ..."
unconditionally). That silently broke both the finalize
idempotency pre-check and the /provisionDect fallback, so
/provisionDect kept showing the "create network" card for stores
that already had one, then 409'd on the create attempt. Switched
to the real endpoint GET /telephony/config/dectNetworks with a
locationId filter. Also added translateCreateDectError to
rewrite the deeply-nested 27453 "access code in use" 409 blob
into an actionable "already exists, re-run /provisionDect"
message.
Extracted both error/state translators as pure exported functions
(classifyNumberAssignment, translateCreateDectError) with unit-test
coverage locking down the exact Webex payload shapes.
README: updated /finalizeStore to reflect the manual-order
expectation and specific fix-it instructions on mismatch.
Co-authored-by: Cursor <cursoragent@cursor.com>
83 lines
3.6 KiB
JavaScript
83 lines
3.6 KiB
JavaScript
import { describe, it } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
|
|
import {
|
|
dectNetworkName,
|
|
generateDectAccessCode,
|
|
translateCreateDectError,
|
|
} from '../src/webex/dect.js';
|
|
|
|
// AE's access code convention: 4-digit stores use their own digits; anything
|
|
// shorter prefixes '8' and pads the rest to 3. Ports collabFinder's rule
|
|
// verbatim so cross-project behaviour stays in sync.
|
|
describe('generateDectAccessCode', () => {
|
|
it('uses the store number verbatim for 4-digit stores', () => {
|
|
assert.equal(generateDectAccessCode(1234), '1234');
|
|
assert.equal(generateDectAccessCode('0499'), '0499');
|
|
});
|
|
|
|
it('truncates 5+ digit stores to the first 4', () => {
|
|
assert.equal(generateDectAccessCode(12345), '1234');
|
|
});
|
|
|
|
it("prefixes '8' and pads for shorter stores", () => {
|
|
assert.equal(generateDectAccessCode(347), '8347');
|
|
assert.equal(generateDectAccessCode(67), '8067');
|
|
assert.equal(generateDectAccessCode(1), '8001');
|
|
});
|
|
|
|
it('strips non-digit characters from the input', () => {
|
|
assert.equal(generateDectAccessCode('4-9-9'), '8499');
|
|
assert.equal(generateDectAccessCode('Store 1234'), '1234');
|
|
});
|
|
});
|
|
|
|
describe('dectNetworkName', () => {
|
|
it('produces the canonical "Store XXXX" name (4-digit padding)', () => {
|
|
assert.equal(dectNetworkName(499), 'Store 0499');
|
|
assert.equal(dectNetworkName(1), 'Store 0001');
|
|
assert.equal(dectNetworkName(1234), 'Store 1234');
|
|
assert.equal(dectNetworkName('67'), 'Store 0067');
|
|
});
|
|
});
|
|
|
|
// Regression: when /provisionDect's location lookup was broken (see
|
|
// findDectNetworkInLocation notes) the create card would appear even
|
|
// though a DECT network already existed, and the resulting POST
|
|
// 409'd with a deeply-nested Webex error blob. The translator
|
|
// surfaces the actionable "re-run /provisionDect" instruction.
|
|
describe('translateCreateDectError', () => {
|
|
function fakeErr(status, body) {
|
|
const err = new Error(`HTTP ${status} for POST /whatever\n${body}`);
|
|
err.status = status;
|
|
err.body = body;
|
|
return err;
|
|
}
|
|
|
|
const CONFLICT_BODY =
|
|
'{"error":[{"key":"409","message":[{"description":"POST failed: HTTP/1.1 409 Conflict (url = https://cpapi-a.wbx2.com/api/v1/customers/DUMMY/locations/DUMMY/dects, request/response TrackingId = ROUTERGW_xxx, errorCode = \'27453\', error = \'[Error 27453] Default access code is in use by another DECT network: 7311\')","code":"27453"}]}],"trackingId":"ROUTERGW_xxx"}';
|
|
|
|
it('rewrites Webex error 27453 into an actionable message', () => {
|
|
const raw = fakeErr(409, CONFLICT_BODY);
|
|
const translated = translateCreateDectError(raw, 7311);
|
|
assert.notEqual(translated, raw, 'should be a new Error instance');
|
|
assert.match(translated.message, /already exists/i);
|
|
assert.match(translated.message, /\/provisionDect 7311/);
|
|
assert.match(translated.message, /Store 7311/);
|
|
assert.equal(translated.status, 409);
|
|
assert.equal(translated.cause, raw);
|
|
});
|
|
|
|
it('pads short store numbers in the error text (matches dectNetworkName convention)', () => {
|
|
const raw = fakeErr(409, CONFLICT_BODY);
|
|
const translated = translateCreateDectError(raw, 499);
|
|
assert.match(translated.message, /Store 0499/);
|
|
});
|
|
|
|
it('returns unrelated errors unchanged', () => {
|
|
const raw = fakeErr(400, '{"message":"something else","errorCode":9999}');
|
|
const result = translateCreateDectError(raw, 7311);
|
|
assert.equal(result, raw);
|
|
assert.doesNotMatch(result.message, /already exists/i);
|
|
});
|
|
});
|