const assert = require('node:assert/strict'); const test = require('node:test'); const { OFFLINE_AFTER_SECONDS, aggregateDesktopPresence, ensureDesktopPresenceSchema, isPresenceOnline, listOnlineDesktopUsers, markDesktopOffline, normalizeHeartbeatPayload, recordDesktopHeartbeat, resetDesktopPresenceStateForTests, } = require('../src/services/desktopPresenceService'); const CLIENT_ID_A = '11111111-1111-4111-8111-111111111111'; const CLIENT_ID_B = '22222222-2222-4222-8222-222222222222'; test('desktop presence stays online at 120 seconds and expires immediately after the boundary', () => { const now = new Date('2026-08-18T04:00:00.000Z'); const exactBoundary = new Date(now.getTime() - OFFLINE_AFTER_SECONDS * 1000); const overBoundary = new Date(exactBoundary.getTime() - 1); assert.equal(isPresenceOnline({ lastSeenAt: exactBoundary }, now), true); assert.equal(isPresenceOnline({ lastSeenAt: overBoundary }, now), false); assert.equal(isPresenceOnline({ lastSeenAt: now, offlineAt: now }, now), false); }); test('desktop presence aggregates multiple online devices without overwriting the user', () => { const now = new Date('2026-08-18T04:00:00.000Z'); const result = aggregateDesktopPresence([ { userId: 7, clientId: CLIENT_ID_A, clientVersion: '0.0.10', deviceName: 'OPS-01', name: '香茗', departmentName: '运营中心', onlineSinceAt: new Date('2026-08-18T03:30:00.000Z'), lastSeenAt: new Date('2026-08-18T03:59:55.000Z'), }, { userId: 7, clientId: CLIENT_ID_B, clientVersion: '0.0.11', deviceName: 'OPS-02', name: '香茗', departmentName: '运营中心', onlineSinceAt: new Date('2026-08-18T03:50:00.000Z'), lastSeenAt: new Date('2026-08-18T03:59:58.000Z'), }, ], now); assert.equal(result.onlineUserCount, 1); assert.equal(result.onlineDeviceCount, 2); assert.equal(result.users[0].activeDeviceCount, 2); assert.deepEqual(result.users[0].clientVersions, ['0.0.11', '0.0.10']); assert.equal(result.users[0].onlineDurationSeconds, 1800); }); test('desktop presence retains stale and explicitly offline devices as history', () => { const now = new Date('2026-08-18T04:00:00.000Z'); const result = aggregateDesktopPresence([ { userId: 7, clientId: CLIENT_ID_A, name: '香茗', onlineSinceAt: new Date('2026-08-18T03:50:00.000Z'), lastSeenAt: new Date('2026-08-18T03:59:55.000Z'), }, { userId: 7, clientId: CLIENT_ID_B, name: '香茗', onlineSinceAt: new Date('2026-08-18T02:00:00.000Z'), lastSeenAt: new Date('2026-08-18T03:00:00.000Z'), }, { userId: 8, clientId: '33333333-3333-4333-8333-333333333333', name: '胖大海', onlineSinceAt: new Date('2026-08-18T03:10:00.000Z'), lastSeenAt: new Date('2026-08-18T03:20:00.000Z'), offlineAt: new Date('2026-08-18T03:20:01.000Z'), }, ], now); assert.equal(result.knownUserCount, 2); assert.equal(result.knownDeviceCount, 3); assert.equal(result.onlineUserCount, 1); assert.equal(result.onlineDeviceCount, 1); assert.equal(result.offlineUserCount, 1); assert.equal(result.offlineDeviceCount, 2); assert.equal(result.users[0].name, '香茗'); assert.equal(result.users[0].devices[0].isOnline, true); assert.equal(result.users[0].devices[1].isOnline, false); assert.equal(result.users[0].devices[1].sessionDurationSeconds, 3600); assert.equal(result.users[1].devices[0].isOnline, false); }); test('desktop presence listing reads historical rows instead of filtering them in SQL', async () => { resetDesktopPresenceStateForTests(); const queries = []; const db = { async query(sql) { queries.push(sql); return [{ userId: 7, clientId: CLIENT_ID_A, name: '香茗', onlineSinceAt: new Date('2026-08-18T02:00:00.000Z'), lastSeenAt: new Date('2026-08-18T03:00:00.000Z'), }]; }, }; const result = await listOnlineDesktopUsers({ db, now: new Date('2026-08-18T04:00:00.000Z'), }); assert.equal(result.knownDeviceCount, 1); assert.equal(result.onlineDeviceCount, 0); assert.equal(result.offlineDeviceCount, 1); assert.doesNotMatch(queries[0], /WHERE p\.offline_at|p\.last_seen_at >=/); resetDesktopPresenceStateForTests(); }); test('heartbeat payload requires a UUID and caps untrusted metadata', () => { assert.throws(() => normalizeHeartbeatPayload({ clientId: 'not-a-uuid' }), /设备标识无效/); const payload = normalizeHeartbeatPayload({ clientId: CLIENT_ID_A, clientName: 'x'.repeat(150), }); assert.equal(payload.clientName.length, 100); }); test('an existing presence table does not require CREATE permission at startup', async () => { resetDesktopPresenceStateForTests(); const queries = []; const db = { async query(sql) { queries.push(sql); if (sql.includes('information_schema.tables')) return [{ present: 1 }]; throw new Error('CREATE must not run when the table already exists'); }, }; assert.equal(await ensureDesktopPresenceSchema(db), true); assert.equal(queries.length, 1); assert.match(queries[0], /information_schema\.tables/); resetDesktopPresenceStateForTests(); }); test('heartbeat and explicit logout are scoped by authenticated user and client', async () => { resetDesktopPresenceStateForTests(); const queries = []; const db = { async query(sql, params) { queries.push({ sql, params }); return []; }, }; const now = new Date('2026-08-18T04:00:00.000Z'); await recordDesktopHeartbeat({ userId: '7', payload: { clientId: CLIENT_ID_A, clientVersion: '0.0.10' }, ipAddress: '127.0.0.1', now, }, { db }); await markDesktopOffline({ userId: '7', clientId: CLIENT_ID_A, now }, { db }); assert.equal(queries.length, 2); assert.equal(queries[0].params[0], '7'); assert.equal(queries[0].params[1], CLIENT_ID_A); assert.deepEqual(queries[1].params.slice(-2), ['7', CLIENT_ID_A]); resetDesktopPresenceStateForTests(); }); test('in-memory presence remains available when the system database cannot create the table', async () => { resetDesktopPresenceStateForTests(); const db = { async query() { const error = new Error('table is unavailable'); error.code = 'ER_NO_SUCH_TABLE'; throw error; }, }; const now = new Date('2026-08-18T04:00:00.000Z'); await recordDesktopHeartbeat({ userId: '7', identity: { name: '香茗', username: 'xiangming', departmentName: '运营中心' }, payload: { clientId: CLIENT_ID_A, clientVersion: '0.0.10', deviceName: 'OPS-01' }, now, }, { db }); const online = await require('../src/services/desktopPresenceService').listOnlineDesktopUsers({ db, now }); assert.equal(online.onlineUserCount, 1); assert.equal(online.users[0].name, '香茗'); assert.equal(online.users[0].devices[0].deviceName, 'OPS-01'); await markDesktopOffline({ userId: '7', clientId: CLIENT_ID_A, now }, { db }); const offline = await require('../src/services/desktopPresenceService').listOnlineDesktopUsers({ db, now }); assert.equal(offline.onlineUserCount, 0); resetDesktopPresenceStateForTests(); });