Files
pulse-signage/test/client-name-check.test.js
lzstealth 3f4f57020a
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m13s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 31s
Release v2.10.3
2026-08-29 01:56:45 +01:00

108 lines
3.6 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const crypto = require('node:crypto');
const {
collectLiveConnections,
findAvailableClientName,
isClientNameAvailable,
normalizeClientName,
normalizeDeviceId,
withClientNameReservation
} = require('../src/data/client-name-check');
test('normalize helpers trim names and sanitize device ids', () => {
assert.equal(normalizeClientName(' Screen A '), 'Screen A');
assert.equal(normalizeClientName(''), '');
assert.equal(normalizeDeviceId(' device-01 /abc!? '), 'device-01abc');
assert.equal(normalizeDeviceId('x'.repeat(200)).length, 128);
});
test('collectLiveConnections returns an array safely', () => {
assert.deepEqual(collectLiveConnections(null), []);
assert.deepEqual(collectLiveConnections([{ clientName: 'A' }]), [{ clientName: 'A' }]);
});
test('isClientNameAvailable ignores stored names for offline devices', async () => {
const pool = {
async query(sql, params) {
assert.match(sql, /FROM d_onboarding_devices/);
assert.deepEqual(params, ['Screen A', 'device-01']);
return [[{ device_id: 'device-99' }]];
}
};
assert.equal(await isClientNameAvailable(pool, ' Screen A ', 'device-01', []), true);
assert.equal(await isClientNameAvailable(pool, 'Screen A', 'device-01', [{ clientName: 'screen a', deviceId: 'device-99' }]), false);
assert.equal(await isClientNameAvailable(null, 'Screen A', 'device-01', [{ clientName: 'screen a', clientId: 'device-99' }]), false);
assert.equal(await isClientNameAvailable(null, 'Screen A', 'device-01', [{ clientName: 'screen a', clientId: 'device-01' }]), true);
assert.equal(await isClientNameAvailable(null, ' ', 'device-01', []), false);
});
test('findAvailableClientName adds the first free numeric suffix', async () => {
const occupiedNames = new Set(['Lobby', 'Lobby (1)']);
const pool = {
async query(_sql, params) {
const name = params[0];
return [[occupiedNames.has(name) ? { device_id: 'active-device' } : undefined].filter(Boolean)];
}
};
assert.equal(
await findAvailableClientName(pool, 'Lobby', 'new-device', [{ deviceId: 'active-device' }]),
'Lobby (2)'
);
});
test('withClientNameReservation acquires and releases locks around the handler', async () => {
const calls = [];
const lockName = `ps_client_name_${crypto.createHash('sha1').update('screen a').digest('hex')}`;
const connection = {
async query(sql, params) {
calls.push({ sql, params });
if (sql.includes('GET_LOCK')) {
return [[{ lock_result: 1 }]];
}
return [[{ released: 1 }]];
},
release() {
calls.push({ sql: 'RELEASE_CONNECTION' });
}
};
const pool = {
async getConnection() {
return connection;
}
};
const result = await withClientNameReservation(pool, ' Screen A ', async () => 'ok');
assert.equal(result, 'ok');
assert.deepEqual(calls, [
{ sql: 'SELECT GET_LOCK(?, 5) AS lock_result', params: [lockName] },
{ sql: 'SELECT RELEASE_LOCK(?)', params: [lockName] },
{ sql: 'RELEASE_CONNECTION' }
]);
});
test('withClientNameReservation rejects busy names', async () => {
const connection = {
async query(sql) {
if (sql.includes('GET_LOCK')) {
return [[{ lock_result: 0 }]];
}
throw new Error('unexpected query');
},
release() {}
};
const pool = {
async getConnection() {
return connection;
}
};
await assert.rejects(
() => withClientNameReservation(pool, 'Screen A', async () => 'ok'),
(error) => error && error.statusCode === 409 && error.message === 'Client name is busy. Please try again.'
);
});