Build and Push Docker Image / build-and-push (push) Successful in 10m22s
- Reader: chapter cross-reference map (M) from IQ Bible's reference graph - Admin: Health tab with disk use, snapshots (back up, download, restore), failure logs - Passkey sign-in (WebAuthn), signed-in device list, sign out others, optional sign-in alerts - Settings split into Account, Preferences, Notifications, Security, Your data tabs - ntfy server field: catch a topic typed into the server box Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
51 lines
2.3 KiB
JavaScript
51 lines
2.3 KiB
JavaScript
// @vitest-environment node
|
|
import { afterEach, beforeEach, expect, test, vi } from 'vitest';
|
|
import { mkdtempSync, rmSync } from 'fs';
|
|
import { tmpdir } from 'os';
|
|
import { join } from 'path';
|
|
|
|
let dir;
|
|
beforeEach(() => { dir = mkdtempSync(join(tmpdir(), 'health-')); process.env.DATA_DIR = dir; vi.resetModules(); });
|
|
afterEach(() => { rmSync(dir, { recursive: true, force: true }); delete process.env.DATA_DIR; });
|
|
|
|
const load = async () => {
|
|
const db = await import('./db.js'); db.initDb();
|
|
return { db, backups: await import('./backups.js'), health: await import('./health.js') };
|
|
};
|
|
|
|
test('the event log keeps the newest first, filters by kind and is capped', async () => {
|
|
const { health } = await load();
|
|
health.logEvent('signin', 'a'); health.logEvent('notify', 'b'); health.logEvent('signin', 'c');
|
|
expect(health.recentEvents('signin').map((e) => e.detail)).toEqual(['c', 'a']);
|
|
for (let i = 0; i < 300; i += 1) health.logEvent('signin', String(i));
|
|
expect(health.recentEvents(null, 1000)).toHaveLength(200);
|
|
});
|
|
|
|
test('a snapshot can be restored, and what was there is kept as a safety copy', async () => {
|
|
const { db, backups } = await load();
|
|
db.createUser({ id: 'u1', email: 'a@x.test', passwordHash: 'h' });
|
|
await backups.runBackup(new Date('2026-10-01T10:00:00Z'));
|
|
db.createUser({ id: 'u2', email: 'b@x.test', passwordHash: 'h' });
|
|
expect(db.countUsers()).toBe(2);
|
|
const [snap] = backups.listSnapshots();
|
|
const result = await backups.restoreSnapshot(snap.name, new Date('2026-10-02T10:00:00Z'));
|
|
expect(db.countUsers()).toBe(1); // back to when the snapshot was taken
|
|
expect(backups.listSnapshots().map((s) => s.name)).toContain(result.safetyCopy.split('/').pop());
|
|
});
|
|
|
|
test('only real snapshot names resolve', async () => {
|
|
const { backups } = await load();
|
|
expect(backups.snapshotPath('../projects.db')).toBeNull();
|
|
expect(backups.snapshotPath('projects-2026-01-01-00-00.db')).toBeNull(); // valid name, but no such file
|
|
await expect(backups.restoreSnapshot('nope')).rejects.toThrow('No such snapshot');
|
|
});
|
|
|
|
test('disk use reports the database and snapshot sizes', async () => {
|
|
const { backups, health } = await load();
|
|
await backups.runBackup();
|
|
const use = health.diskUse();
|
|
expect(use.db).toBeGreaterThan(0);
|
|
expect(use.backups).toBeGreaterThan(0);
|
|
expect(health.backupStatus().latest).not.toBeNull();
|
|
});
|