joplock/tests/historyService.test.js

130 lines
4.5 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const { createHistoryService, hashBody } = require('../app/historyService');
const makeDb = (rows = [], queries = []) => ({
query: async (sql, params) => {
queries.push({ sql, params });
if (/SELECT.*joplock_history.*LIMIT 1/.test(sql)) return { rows };
if (/SELECT.*joplock_history/.test(sql) && sql.includes('ORDER BY saved_time')) return { rows };
return { rows: [] };
},
});
test('historyService: hashBody produces consistent hash', () => {
assert.equal(hashBody('hello'), hashBody('hello'));
assert.notEqual(hashBody('hello'), hashBody('world'));
});
test('historyService: saveSnapshot skips when table unavailable', async () => {
const service = createHistoryService({
query: async () => { throw new Error('permission denied'); },
});
// should not throw
await service.saveSnapshot('u1', 'n1', 'Title', 'Body');
});
test('historyService: saveSnapshot inserts first snapshot', async () => {
const queries = [];
const service = createHistoryService({
query: async (sql, params) => {
queries.push({ sql, params });
// Most-recent check returns no previous snapshot
if (sql.includes('SELECT') && sql.includes('LIMIT 1')) return { rows: [] };
return { rows: [] };
},
});
await service.saveSnapshot('u1', 'n1', 'Title', 'Body text');
const insert = queries.find(q => q.sql.includes('INSERT INTO joplock_history'));
assert.ok(insert, 'should insert');
assert.equal(insert.params[0], 'n1');
assert.equal(insert.params[1], 'u1');
assert.equal(insert.params[2], 'Title');
assert.equal(insert.params[3], 'Body text');
});
test('historyService: saveSnapshot skips identical hash', async () => {
const queries = [];
const hash = hashBody('Same body');
const service = createHistoryService({
query: async (sql, params) => {
queries.push({ sql, params });
if (sql.includes('SELECT') && sql.includes('LIMIT 1')) {
return { rows: [{ body_hash: hash, saved_time: Date.now() - 60000 }] };
}
return { rows: [] };
},
});
await service.saveSnapshot('u1', 'n1', 'Title', 'Same body');
const insert = queries.find(q => q.sql.includes('INSERT INTO joplock_history'));
assert.ok(!insert, 'should skip insert when hash identical');
});
test('historyService: saveSnapshot skips when too recent', async () => {
const queries = [];
const service = createHistoryService({
query: async (sql) => {
queries.push({ sql });
if (sql.includes('SELECT') && sql.includes('LIMIT 1')) {
return { rows: [{ body_hash: hashBody('Old body'), saved_time: Date.now() - 5000 }] };
}
return { rows: [] };
},
});
await service.saveSnapshot('u1', 'n1', 'Title', 'New body');
const insert = queries.find(q => q.sql.includes('INSERT INTO joplock_history'));
assert.ok(!insert, 'should skip when last snapshot was < 30s ago');
});
test('historyService: listSnapshots returns empty array when table unavailable', async () => {
const service = createHistoryService({
query: async () => { throw new Error('nope'); },
});
const list = await service.listSnapshots('n1');
assert.deepEqual(list, []);
});
test('historyService: listSnapshots maps rows correctly', async () => {
const rows = [
{ id: '5', title: 'My note', saved_time: '1700000000000' },
{ id: '3', title: 'Earlier', saved_time: '1699000000000' },
];
const service = createHistoryService({
query: async (sql) => {
if (sql.includes('CREATE')) return { rows: [] };
if (sql.includes('CREATE INDEX')) return { rows: [] };
return { rows };
},
});
const list = await service.listSnapshots('n1');
assert.equal(list.length, 2);
assert.equal(list[0].id, '5');
assert.equal(list[0].title, 'My note');
assert.equal(list[0].savedTime, 1700000000000);
});
test('historyService: getSnapshot returns null when not found', async () => {
const service = createHistoryService({
query: async (sql) => {
if (sql.includes('CREATE')) return { rows: [] };
return { rows: [] };
},
});
const snap = await service.getSnapshot('999');
assert.equal(snap, null);
});
test('historyService: getSnapshot returns snapshot with body', async () => {
const service = createHistoryService({
query: async (sql) => {
if (sql.includes('CREATE')) return { rows: [] };
return { rows: [{ id: '7', note_id: 'note-abc', title: 'Hi', body: 'Hello world', saved_time: '1700000000000' }] };
},
});
const snap = await service.getSnapshot('7');
assert.ok(snap);
assert.equal(snap.id, '7');
assert.equal(snap.noteId, 'note-abc');
assert.equal(snap.body, 'Hello world');
assert.equal(snap.savedTime, 1700000000000);
});