const test = require('node:test'); const assert = require('node:assert/strict'); const crypto = require('node:crypto'); const { collectLiveConnections, 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 rejects matching db rows and live connections', 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', []), 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('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.' ); });