mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-20 14:06:27 +00:00
724c88c201
A mechanical prettier --write over server/src, server/tests, server/scripts and shared/src with the workspaces' .prettierrc; nothing but layout and import order changes, and every file keeps the same set of imports. shared is formatted throughout and its format baseline is empty. The server baseline keeps the 35 files that Prettier would grow past their lint:size limit (they need a split first, which is not a formatting change), and the generated entity files stay as their generator writes them.
189 lines
7.0 KiB
TypeScript
189 lines
7.0 KiB
TypeScript
import { Addons } from '../../../../src/db/entities/Addons.entity';
|
|
import type { AddonsRepository } from '../../../../src/db/repositories/Addons.repository';
|
|
import { createSnapshotTestDb } from '../../../helpers/db-mock';
|
|
import { deleteRows, insertRow, updateRows } from '../../../helpers/factories/rows';
|
|
import { resetTestDb } from '../../../helpers/test-db';
|
|
import { createTestOrm, type TestOrm } from '../../../helpers/test-orm';
|
|
|
|
import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
|
|
|
const testDb = createSnapshotTestDb();
|
|
let t: TestOrm;
|
|
let addons: AddonsRepository;
|
|
|
|
beforeAll(async () => {
|
|
t = await createTestOrm(testDb);
|
|
addons = t.repo(Addons);
|
|
});
|
|
beforeEach(async () => {
|
|
resetTestDb(testDb);
|
|
// resetTestDb deliberately keeps the seeded addons catalogue (test-db.ts's
|
|
// KEEP_TABLES) — these tests want a known, empty table so every case
|
|
// controls its own rows.
|
|
await deleteRows(t, Addons);
|
|
t.clear();
|
|
});
|
|
afterAll(async () => {
|
|
await t.close();
|
|
testDb.close();
|
|
});
|
|
|
|
function rawRow(id: string): unknown {
|
|
// test-sql-allow: the stored integer is under test, which the entity reads back as a boolean.
|
|
return testDb.prepare('SELECT * FROM addons WHERE id = ?').get(id);
|
|
}
|
|
|
|
async function insertAddon(row: {
|
|
id: string;
|
|
name: string;
|
|
description?: string | null;
|
|
type?: string;
|
|
icon?: string | null;
|
|
enabled: 0 | 1;
|
|
sort_order?: number;
|
|
}): Promise<void> {
|
|
await insertRow(t, Addons, {
|
|
id: row.id,
|
|
name: row.name,
|
|
description: row.description ?? null,
|
|
type: row.type ?? 'global',
|
|
icon: row.icon ?? null,
|
|
enabled: row.enabled === 1,
|
|
sort_order: row.sort_order ?? 0,
|
|
});
|
|
}
|
|
|
|
describe('AddonsRepository', () => {
|
|
describe('isEnabled', () => {
|
|
it('ADDONSREPO-001: reads a stored 1 as true', async () => {
|
|
await insertAddon({ id: 'budget', name: 'Budget', enabled: 1 });
|
|
expect(await addons.isEnabled('budget')).toBe(true);
|
|
});
|
|
|
|
it('ADDONSREPO-002: reads a stored 0 as false', async () => {
|
|
await insertAddon({ id: 'budget', name: 'Budget', enabled: 0 });
|
|
expect(await addons.isEnabled('budget')).toBe(false);
|
|
});
|
|
|
|
it('ADDONSREPO-003: a missing row reads false', async () => {
|
|
expect(await addons.isEnabled('does-not-exist')).toBe(false);
|
|
});
|
|
|
|
// I1 (Task 0 review, carried forward): a primary-key findOne answers a
|
|
// repeat call from the identity map unless refresh: true is set —
|
|
// invisible to a write on the same id in the same request.
|
|
describe('sees a write on the same id in the same request (I1, identity-map regression)', () => {
|
|
it('ADDONSREPO-005: an UPDATE from another context on the same id then isEnabled reads the new value', async () => {
|
|
await insertAddon({ id: 'budget', name: 'Budget', enabled: 0 });
|
|
expect(await addons.isEnabled('budget')).toBe(false); // populate the identity map
|
|
await updateRows(t, Addons, { id: 'budget' }, { enabled: true });
|
|
expect(await addons.isEnabled('budget')).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('listEnabled', () => {
|
|
it('ADDONSREPO-006: returns only enabled addons, ordered by sort_order', async () => {
|
|
await insertAddon({ id: 'atlas', name: 'Atlas', type: 'page', icon: 'globe', enabled: 1, sort_order: 2 });
|
|
await insertAddon({ id: 'budget', name: 'Costs', type: 'trip', icon: 'wallet', enabled: 1, sort_order: 1 });
|
|
await insertAddon({ id: 'vacay', name: 'Vacay', type: 'page', icon: 'sun', enabled: 0, sort_order: 0 });
|
|
|
|
const rows = await addons.listEnabled();
|
|
expect(rows.map((r) => r.id)).toEqual(['budget', 'atlas']);
|
|
});
|
|
|
|
it('ADDONSREPO-007: the enabled column comes back as a JS boolean, not the stored int', async () => {
|
|
await insertAddon({ id: 'atlas', name: 'Atlas', enabled: 1 });
|
|
const [row] = await addons.listEnabled();
|
|
expect(row.enabled).toBe(true);
|
|
expect(rawRow('atlas')).toMatchObject({ enabled: 1 });
|
|
});
|
|
|
|
it('ADDONSREPO-008: an empty table returns an empty array', async () => {
|
|
expect(await addons.listEnabled()).toEqual([]);
|
|
});
|
|
|
|
it('ADDONSREPO-009: carries the full row shape (id, name, description, type, icon, enabled, config, sort_order)', async () => {
|
|
await insertAddon({
|
|
id: 'atlas',
|
|
name: 'Atlas',
|
|
description: 'Visited countries map',
|
|
type: 'page',
|
|
icon: 'globe',
|
|
enabled: 1,
|
|
sort_order: 3,
|
|
});
|
|
const [row] = await addons.listEnabled();
|
|
expect(row).toEqual({
|
|
id: 'atlas',
|
|
name: 'Atlas',
|
|
description: 'Visited countries map',
|
|
type: 'page',
|
|
icon: 'globe',
|
|
enabled: true,
|
|
config: {},
|
|
sort_order: 3,
|
|
});
|
|
});
|
|
});
|
|
|
|
// Plan 3i Task 4 fix wave (should-land 5): AD26 (listAllOrdered) and
|
|
// AD30/AD39 (findById) shipped in Task 1 with no repository-level test —
|
|
// full-key parity against the exact legacy SQL each method's own docstring
|
|
// names.
|
|
describe('listAllOrdered (AD26)', () => {
|
|
it('ADDONSREPO-010: matches SELECT * FROM addons ORDER BY sort_order, id — unfiltered, including a disabled addon', async () => {
|
|
await insertAddon({ id: 'vacay', name: 'Vacay', type: 'page', icon: 'sun', enabled: 0, sort_order: 1 });
|
|
await insertAddon({ id: 'budget', name: 'Costs', type: 'trip', icon: 'wallet', enabled: 1, sort_order: 0 });
|
|
// test-sql-allow: the legacy statement is the oracle the repository read is held to.
|
|
const legacy = testDb.prepare('SELECT * FROM addons ORDER BY sort_order, id').all();
|
|
const rows = await addons.listAllOrdered();
|
|
expect(rows.map((r) => r.id)).toEqual(['budget', 'vacay']); // includes the disabled one, unlike listEnabled
|
|
expect(rows).toEqual(
|
|
(legacy as Array<{ enabled: number; config: string | null }>).map((r) => ({
|
|
...r,
|
|
enabled: !!r.enabled,
|
|
config: JSON.parse(r.config ?? '{}'),
|
|
})),
|
|
);
|
|
});
|
|
|
|
it('ADDONSREPO-011: an empty table returns an empty array', async () => {
|
|
expect(await addons.listAllOrdered()).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('findById (AD30/AD39)', () => {
|
|
it('ADDONSREPO-012: matches SELECT * FROM addons WHERE id = ?, on both a pre-write read and a post-write re-select (byte-identical text at both call sites)', async () => {
|
|
await insertAddon({
|
|
id: 'budget',
|
|
name: 'Costs',
|
|
description: 'Track spend',
|
|
type: 'trip',
|
|
icon: 'wallet',
|
|
enabled: 0,
|
|
sort_order: 4,
|
|
});
|
|
const preWrite = await addons.findById('budget');
|
|
expect(preWrite).toEqual({
|
|
id: 'budget',
|
|
name: 'Costs',
|
|
description: 'Track spend',
|
|
type: 'trip',
|
|
icon: 'wallet',
|
|
enabled: false,
|
|
config: {},
|
|
sort_order: 4,
|
|
});
|
|
|
|
await updateRows(t, Addons, { id: 'budget' }, { enabled: true });
|
|
const postWrite = await addons.findById('budget');
|
|
expect(postWrite?.enabled).toBe(true);
|
|
});
|
|
|
|
it('ADDONSREPO-013: a missing id returns null', async () => {
|
|
expect(await addons.findById('does-not-exist')).toBeNull();
|
|
});
|
|
});
|
|
});
|