numbers: any free internal number of 1 to 6 digits; ranges only suggest
Owner wanted the first user on 1. Numbering ranges now choose the Next free suggestion (Settings → Numbering), the setup wizard takes a number per person, and a one-digit number can't also be a menu key of a digital receptionist with direct dialing. Dialplan patterns that take an internal number match one digit (_X!); golden case short-numbers proves it in Asterisk.
This commit is contained in:
1 parent
4143f6d064
commit
aa6681da03
42 files changed
+932
-137
No files matched your search
@@ -141,6 +141,14 @@ async function check(name) {
|
||||
if (!shownQ.startsWith(`${q} has`) || listed !== members)
|
||||
problems.push(`queue ${q}: expected ${members} members, got: ${shownQ.trim()}`);
|
||||
}
|
||||
// A case can list [number, context] pairs Asterisk must match (short-numbers: one-digit
|
||||
// numbers, which a pattern like _X. needs two digits for).
|
||||
const snap = JSON.parse(readFileSync(join(GOLDEN, name, 'snapshot.json'), 'utf8'));
|
||||
for (const [exten, ctx] of snap.asteriskMatches ?? []) {
|
||||
const d = cli(`dialplan show ${exten}@${ctx}`).out;
|
||||
if (!/-= \d+ extensions? \(/.test(d) || /There is no existence/.test(d))
|
||||
problems.push(`${exten} doesn't match in ${ctx}`);
|
||||
}
|
||||
// Asterisk colors its log: strip the escape codes before looking for ERROR[.
|
||||
// eslint-disable-next-line no-control-regex -- matching ANSI escapes is the point
|
||||
const log = docker('logs', NAME).out.replace(/\x1b\[[0-9;]*m/g, '');
|
||||
|
||||
@@ -62,7 +62,7 @@ describe.skipIf(!hasTestDb)('/api/conferences', () => {
|
||||
it('checks the number and the PINs', async () => {
|
||||
const bad = (body: object) =>
|
||||
req('POST', '/api/conferences', { number: '801', name: 'X', ...body });
|
||||
expect((await bad({ number: '700' })).statusCode).toBe(400); // parking, not 800–849
|
||||
expect((await bad({ number: '700' })).statusCode).toBe(409); // the parking lot
|
||||
expect((await bad({ number: '800' })).statusCode).toBe(409);
|
||||
expect((await bad({ pin: '12' })).statusCode).toBe(400);
|
||||
expect((await bad({ pin: '12a4' })).statusCode).toBe(400);
|
||||
|
||||
@@ -72,9 +72,12 @@ describe.skipIf(!hasTestDb)('extensions API', () => {
|
||||
const make = (number: string, extra = {}) =>
|
||||
req('POST', '/api/extensions', { number, name: 'X', ...extra });
|
||||
expect((await make('211')).json().error.message).toMatch(/reserved/);
|
||||
expect((await make('600')).json().error.message).toMatch(/from 100 to 499/);
|
||||
expect((await make('12')).statusCode).toBe(400);
|
||||
expect((await make('710')).statusCode).toBe(400); // outside the users range anyway
|
||||
expect((await make('1234567')).statusCode).toBe(400); // 1 to 6 digits
|
||||
expect((await make('710')).json().error.message).toMatch(/parking/);
|
||||
// Any free number, in or out of the users range (owner, 2026-10-21): "1" for the first user.
|
||||
const one = await make('1');
|
||||
expect(one.statusCode, one.body).toBe(201);
|
||||
expect((await req('DELETE', `/api/extensions/${one.json().id}`)).statusCode).toBe(200);
|
||||
const dup = await make('101');
|
||||
expect(dup.statusCode).toBe(409);
|
||||
expect(dup.json().error.message).toMatch(/extension "Dan Doe"/);
|
||||
|
||||
@@ -81,7 +81,24 @@ describe.skipIf(!hasTestDb)('/api/ivrs', () => {
|
||||
const post = (body: object) => req('POST', '/api/ivrs', { ...base(), number: '601', ...body });
|
||||
const missing = '00000000-0000-4000-8000-000000000999';
|
||||
expect((await post({ number: '600' })).statusCode).toBe(409);
|
||||
expect((await post({ number: '500' })).statusCode).toBe(400); // ring-group range
|
||||
expect((await post({ number: '1234567' })).statusCode).toBe(400);
|
||||
// A one-digit number and a menu key (direct dialing on) can't be the same, either way round.
|
||||
const one = await req('POST', '/api/extensions', { number: '1', name: 'X' });
|
||||
expect(one.statusCode).toBe(409);
|
||||
expect(one.json().error.message).toMatch(/menu key of the digital receptionist "Main menu"/);
|
||||
const four = await req('POST', '/api/extensions', { number: '4', name: 'Four' });
|
||||
expect(four.statusCode, four.body).toBe(201);
|
||||
const clash = await post({ options: [{ digit: '4', destination: { type: 'hangup' } }] });
|
||||
expect(clash.statusCode).toBe(409);
|
||||
expect(clash.json().error.message).toMatch(/Key 4 is also the number of extension "Four"/);
|
||||
const noDial = await post({
|
||||
allowDirectDial: false,
|
||||
number: '602',
|
||||
options: [{ digit: '4', destination: { type: 'hangup' } }],
|
||||
});
|
||||
expect(noDial.statusCode).toBe(201);
|
||||
await req('DELETE', `/api/ivrs/${noDial.json().id}`);
|
||||
await req('DELETE', `/api/extensions/${four.json().id}`);
|
||||
expect((await post({ greetingSoundId: missing })).statusCode).toBe(400);
|
||||
const twice = await post({
|
||||
options: [
|
||||
|
||||
@@ -53,7 +53,7 @@ describe.skipIf(!hasTestDb)('/api/paging-groups', () => {
|
||||
it('checks the number and the members', async () => {
|
||||
const bad = (body: object) =>
|
||||
req('POST', '/api/paging-groups', { number: '851', name: 'X', ...body });
|
||||
expect((await bad({ number: '800' })).statusCode).toBe(400); // conference range
|
||||
expect((await bad({ number: '1234567' })).statusCode).toBe(400); // 1 to 6 digits
|
||||
expect((await bad({ number: '850' })).statusCode).toBe(409);
|
||||
expect((await bad({ memberIds: [ext['101'], ext['101']] })).statusCode).toBe(400);
|
||||
expect((await bad({ memberIds: ['00000000-0000-4000-8000-000000000000'] })).statusCode).toBe(
|
||||
|
||||
@@ -99,7 +99,7 @@ describe.skipIf(!hasTestDb)('/api/queues', () => {
|
||||
timeoutDestination: { type: 'hangup' },
|
||||
...body,
|
||||
});
|
||||
expect((await bad({ number: '500' })).statusCode).toBe(400); // outside 550–599
|
||||
expect((await bad({ number: '1234567' })).statusCode).toBe(400); // 1 to 6 digits
|
||||
expect((await bad({ number: '550' })).statusCode).toBe(409);
|
||||
const twice = { extensionId: ext['101'], penalty: 0 };
|
||||
expect((await bad({ members: [twice, twice] })).statusCode).toBe(400);
|
||||
|
||||
@@ -64,7 +64,7 @@ describe.skipIf(!hasTestDb)('/api/ring-groups', () => {
|
||||
noAnswerDestination: { type: 'hangup' },
|
||||
...body,
|
||||
});
|
||||
expect((await bad({ number: '101' })).statusCode).toBe(400); // outside 500–549
|
||||
expect((await bad({ number: '101' })).statusCode).toBe(409); // extension 101 has it
|
||||
expect((await bad({ number: '500' })).statusCode).toBe(409);
|
||||
expect((await bad({ memberIds: [ext['101'], ext['101']] })).statusCode).toBe(400);
|
||||
expect((await bad({ memberIds: ['00000000-0000-4000-8000-000000000999'] })).statusCode).toBe(
|
||||
|
||||
Reference in new issue
Block a user