refactor: modularize application domains

This commit is contained in:
kkfluous
2026-08-13 12:03:34 +08:00
parent d610e4b841
commit 06fe75b4c7
142 changed files with 14645 additions and 8885 deletions
@@ -0,0 +1,116 @@
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import test from 'node:test';
import type { Vehicle, VehicleRow } from '../../types.js';
import {
FLOW_STATS_SQL,
MAIN_SQL,
VehicleRepository,
WEEKLY_DETAIL_SQL,
WEEKLY_TRUCK_ID_SQLS,
} from './repository.js';
class FakeDatabase {
readonly calls: Array<{ sql: string; values?: any[] }> = [];
constructor(private readonly responses: any[][]) {}
async query<T = any[]>(sql: string, values?: any[]): Promise<[T, ...any[]]> {
this.calls.push({ sql, values });
return [this.responses.shift() as T];
}
}
function vehicle(id: string): Vehicle {
return { id } as Vehicle;
}
test('keeps all vehicle GET endpoints in their established order', () => {
const source = readFileSync(new URL('../vehicles.ts', import.meta.url), 'utf8');
const paths = Array.from(source.matchAll(/app\.get\('([^']+)'/g), (match) => match[1]);
assert.deepEqual(paths, [
'/summary',
'/by-type',
'/by-batch',
'/inventory-analysis',
'/dept-stats',
'/region-stats',
'/customer-stats',
'/list',
'/inventory-stats',
'/weekly-detail',
'/flow-stats',
'/subjects',
'/refresh',
'/debug',
'/region-chart',
]);
assert.doesNotMatch(source, /\bSELECT\b|pool\.query|MAIN_SQL/);
});
test('keeps the non-empty vehicle cache for less than 60 seconds', async () => {
let now = 100_000;
const db = new FakeDatabase([[{ id: 'first' }], [{ id: 'second' }]]);
const repository = new VehicleRepository(
db,
() => now,
(row: VehicleRow) => vehicle(row.id),
);
assert.deepEqual(await repository.getVehicles(), [vehicle('first')]);
now += 59_999;
assert.deepEqual(await repository.getVehicles(), [vehicle('first')]);
now += 1;
assert.deepEqual(await repository.getVehicles(), [vehicle('second')]);
assert.deepEqual(db.calls.map((call) => call.sql), [MAIN_SQL, MAIN_SQL]);
});
test('keeps empty vehicle results uncached', async () => {
const db = new FakeDatabase([[], []]);
const repository = new VehicleRepository(db, () => 100_000);
await repository.getVehicles();
await repository.getVehicles();
assert.equal(db.calls.length, 2);
});
test('refresh invalidates vehicles but retains the weekly truck-id cache', async () => {
const db = new FakeDatabase([
[{ id: 'first' }],
[{ truck_id: 'pending' }],
[{ truck_id: 'delivered' }],
[{ truck_id: 'returned' }],
[{ truck_id: 'replaced' }, { truck_id: null }],
[{ id: 'second' }],
]);
const repository = new VehicleRepository(
db,
() => 100_000,
(row: VehicleRow) => vehicle(row.id),
);
await repository.getVehicles();
const weeklyBefore = await repository.getWeeklyTruckIds();
repository.invalidateRefreshCaches();
assert.deepEqual(await repository.getVehicles(), [vehicle('second')]);
assert.equal(await repository.getWeeklyTruckIds(), weeklyBefore);
assert.deepEqual(db.calls.map((call) => call.sql), [MAIN_SQL, ...WEEKLY_TRUCK_ID_SQLS, MAIN_SQL]);
assert.deepEqual([...weeklyBefore.replaced], ['replaced']);
});
test('keeps weekly detail SQL selection and flow date parameters unchanged', async () => {
const db = new FakeDatabase([[{ truck_id: '1' }], [{ id: 'delivered-1' }]]);
const repository = new VehicleRepository(db);
assert.deepEqual(await repository.getWeeklyDetailRows('new'), [{ truck_id: '1' }]);
assert.deepEqual(await repository.getFlowDetailRows('2026-08-01', '2026-08-13'), [{ id: 'delivered-1' }]);
assert.deepEqual(db.calls, [
{ sql: WEEKLY_DETAIL_SQL.new, values: undefined },
{
sql: FLOW_STATS_SQL,
values: ['2026-08-01', '2026-08-13', '2026-08-01', '2026-08-13', '2026-08-01', '2026-08-13'],
},
]);
});