joplock/tests/sessionService.test.js
igor b735224377
Some checks are pending
Build and push Joplock image / build-and-push (push) Waiting to run
Refactor route rendering and harden editor session handling
2026-04-27 00:24:23 +12:00

154 lines
5.5 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const { createSessionService, isSessionExpired, defaultSessionTtlMs } = require('../app/auth/sessionService');
// --- isSessionExpired ---
test('isSessionExpired returns true for zero/null created time', () => {
assert.ok(isSessionExpired(0));
assert.ok(isSessionExpired(null));
assert.ok(isSessionExpired(undefined));
});
test('isSessionExpired returns false for recent session', () => {
assert.ok(!isSessionExpired(Date.now() - 1000));
});
test('isSessionExpired returns true for old session', () => {
assert.ok(isSessionExpired(Date.now() - defaultSessionTtlMs - 1));
});
test('isSessionExpired returns false at exact boundary', () => {
const now = Date.now();
assert.ok(!isSessionExpired(now - defaultSessionTtlMs + 1, now));
});
test('defaultSessionTtlMs is 12 hours', () => {
assert.equal(defaultSessionTtlMs, 12 * 60 * 60 * 1000);
});
// --- createSessionService ---
test('userBySessionId returns null for empty sessionId', async () => {
const service = createSessionService({ query: async () => { throw new Error('should not query'); } });
assert.equal(await service.userBySessionId(''), null);
assert.equal(await service.userBySessionId(null), null);
});
test('userBySessionId returns null when no row found', async () => {
const db = { query: async () => ({ rows: [] }) };
const service = createSessionService(db);
assert.equal(await service.userBySessionId('nonexistent'), null);
});
test('userBySessionId returns null for expired session', async () => {
const db = {
query: async () => ({
rows: [{
session_id: 's1', id: 'u1', email: 'a@b.com', full_name: 'A',
is_admin: 0, can_upload: 1, email_confirmed: 1, account_type: 0,
created_time: Date.now(), updated_time: Date.now(), enabled: 1,
session_created_time: Date.now() - defaultSessionTtlMs - 1000,
}],
}),
};
const service = createSessionService(db);
assert.equal(await service.userBySessionId('s1'), null);
});
test('userBySessionId returns null for disabled user', async () => {
const db = {
query: async () => ({
rows: [{
session_id: 's1', id: 'u1', email: 'a@b.com', full_name: 'A',
is_admin: 0, can_upload: 1, email_confirmed: 1, account_type: 0,
created_time: Date.now(), updated_time: Date.now(), enabled: 0,
session_created_time: Date.now(),
}],
}),
};
const service = createSessionService(db);
assert.equal(await service.userBySessionId('s1'), null);
});
test('userBySessionId returns mapped user for valid session', async () => {
const now = Date.now();
const db = {
query: async () => ({
rows: [{
session_id: 's1', id: 'u1', email: 'a@b.com', full_name: 'A B',
is_admin: 1, can_upload: 1, email_confirmed: 1, account_type: 2,
created_time: now - 5000, updated_time: now - 1000, enabled: 1,
session_created_time: now - 100,
}],
}),
};
const service = createSessionService(db);
const user = await service.userBySessionId('s1');
assert.equal(user.id, 'u1');
assert.equal(user.email, 'a@b.com');
assert.equal(user.fullName, 'A B');
assert.equal(user.isAdmin, true);
assert.equal(user.sessionId, 's1');
assert.equal(user.accountType, 2);
});
test('userBySessionId passes sessionId to query', async () => {
let captured = null;
const db = { query: async (_sql, params) => { captured = params; return { rows: [] }; } };
const service = createSessionService(db);
await service.userBySessionId('my-session-id');
assert.deepEqual(captured, ['my-session-id']);
});
// --- touchSession / getLastSeen / deleteSession ---
test('touchSession upserts last_seen into joplock_sessions', async () => {
const queries = [];
const db = { query: async (sql, params) => { queries.push({ sql, params }); return { rows: [] }; } };
const service = createSessionService(db);
const now = Date.now();
await service.touchSession('sess-abc', now);
const upsert = queries.find(q => q.sql.includes('joplock_sessions') && q.sql.includes('INSERT'));
assert.ok(upsert, 'should have upserted into joplock_sessions');
assert.equal(upsert.params[0], 'sess-abc');
assert.equal(upsert.params[1], now);
});
test('getLastSeen returns null when no row exists', async () => {
const db = { query: async () => ({ rows: [] }) };
const service = createSessionService(db);
// ensureTable creates the table first (two queries), then select
const result = await service.getLastSeen('sess-xyz');
assert.equal(result, null);
});
test('getLastSeen returns numeric last_seen when row exists', async () => {
const lastSeen = Date.now() - 30000;
let callCount = 0;
const db = {
query: async (sql) => {
callCount++;
if (sql.includes('SELECT last_seen')) return { rows: [{ last_seen: lastSeen }] };
return { rows: [] };
},
};
const service = createSessionService(db);
const result = await service.getLastSeen('sess-xyz');
assert.equal(result, lastSeen);
});
test('deleteSession removes row from joplock_sessions', async () => {
const queries = [];
const db = { query: async (sql, params) => { queries.push({ sql, params }); return { rows: [] }; } };
const service = createSessionService(db);
await service.deleteSession('sess-del');
const del = queries.find(q => q.sql.includes('DELETE') && q.params && q.params[0] === 'sess-del');
assert.ok(del, 'should have deleted from joplock_sessions');
});
test('touchSession is non-fatal on db error', async () => {
const db = { query: async () => { throw new Error('db down'); } };
const service = createSessionService(db);
await assert.doesNotReject(() => service.touchSession('s1'));
});