mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-20 05:56:53 +00:00
refactor(server): remove DatabaseService.transaction — every transaction runs through UnitOfWork
This commit is contained in:
@@ -914,8 +914,9 @@ cycle seams are folded, nothing on the websocket path imports one, and the
|
||||
crons all inject now (`*.job.ts` providers on `scheduling/CronRegistrarService`).
|
||||
|
||||
1. **Move the SQL** into `<domain>.service.ts` as methods over an injected
|
||||
`DatabaseService` (`this.db.all<T>/get<T>/run/prepare/transaction`; strict
|
||||
constructor injection, no `@Optional()`). Preserve every quirk: falsy-coercion
|
||||
`DatabaseService` (`this.db.all<T>/get<T>/run/prepare`; multi-statement writes go
|
||||
through `await this.uow.transactional(...)` instead — see the root `CLAUDE.md`;
|
||||
strict constructor injection, no `@Optional()`). Preserve every quirk: falsy-coercion
|
||||
defaults (`x || fallback`, never `??`), post-insert/post-update re-selects (no
|
||||
RETURNING), `COALESCE` semantics. If a controller already wraps the legacy
|
||||
functions, do not change the service's method surface. The module needs no
|
||||
|
||||
@@ -38,11 +38,6 @@ export class DatabaseService {
|
||||
return this.conn.prepare(sql).run(...params);
|
||||
}
|
||||
|
||||
/** Run `fn` inside a synchronous better-sqlite3 transaction. */
|
||||
transaction<T>(fn: (conn: Database.Database) => T): T {
|
||||
return this.conn.transaction(() => fn(this.conn))();
|
||||
}
|
||||
|
||||
// Trip-access helpers delegate to the db/database exports (not this.conn):
|
||||
// tests vi.mock that module and stub the helpers themselves, so the stubs
|
||||
// must keep flowing through here.
|
||||
|
||||
@@ -19,18 +19,15 @@ describe('DatabaseService (typed query helpers)', () => {
|
||||
expect(svc.all('SELECT 3 AS three')).toEqual([{ three: 3 }]);
|
||||
});
|
||||
|
||||
it('run + transaction operate on a scratch table', () => {
|
||||
it('run operates on a scratch table', () => {
|
||||
svc.run('CREATE TEMP TABLE IF NOT EXISTS _dbsvc_test (n INTEGER)');
|
||||
svc.run('DELETE FROM _dbsvc_test');
|
||||
|
||||
const info = svc.run('INSERT INTO _dbsvc_test (n) VALUES (?)', 41);
|
||||
expect(info.changes).toBe(1);
|
||||
|
||||
const total = svc.transaction((conn) => {
|
||||
conn.prepare('INSERT INTO _dbsvc_test (n) VALUES (?)').run(1);
|
||||
return conn.prepare('SELECT SUM(n) AS s FROM _dbsvc_test').get() as { s: number };
|
||||
});
|
||||
expect(total.s).toBe(42);
|
||||
const total = svc.get('SELECT SUM(n) AS s FROM _dbsvc_test') as { s: number };
|
||||
expect(total.s).toBe(41);
|
||||
|
||||
svc.run('DROP TABLE _dbsvc_test');
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ const routeTestDb = new Database(':memory:');
|
||||
|
||||
async function setup() {
|
||||
const maps = { geocodeQuery: vi.fn(), reverseGeocode: vi.fn().mockResolvedValue({ name: null, address: null }) };
|
||||
const db = { canAccessTrip: vi.fn(() => ({ user_id: 7 })), get: () => ({ role: 'user' }), transaction: <T>(fn: () => T) => fn() };
|
||||
const db = { canAccessTrip: vi.fn(() => ({ user_id: 7 })), get: () => ({ role: 'user' }) };
|
||||
const places = { create: vi.fn((_trip: string, stop: { name: string }) => ({ id: stop.name })), broadcast: vi.fn() };
|
||||
const assignments = { dayExists: vi.fn(() => true), createAssignment: vi.fn((dayId: number, placeId: string) => ({ dayId, placeId })), broadcast: vi.fn(), reconcile: vi.fn() };
|
||||
const permissions = { checkPermission: vi.fn(() => true) };
|
||||
|
||||
@@ -40,7 +40,6 @@ function setup() {
|
||||
run: vi.fn((_sql: string, tripId: number, key: string, value: string) =>
|
||||
tripSettings.set(tripId, { ...tripSettings.get(tripId), [key]: JSON.parse(value) }),
|
||||
),
|
||||
transaction: (fn: () => unknown) => fn(),
|
||||
};
|
||||
const preferences = new RoadtripPreferencesService(preferenceDb as never, realtime as never, uowStub);
|
||||
const days = [{ id: 1, day_number: 1, title: null, date: '2026-09-11', default_transport_mode: 'driving' }];
|
||||
|
||||
@@ -42,7 +42,6 @@ async function makeService() {
|
||||
get: <T>(sql: string, ...params: unknown[]) => raw.prepare(sql).get(...params as never[]) as T | undefined,
|
||||
all: <T>(sql: string, ...params: unknown[]) => raw.prepare(sql).all(...params as never[]) as T[],
|
||||
run: (sql: string, ...params: unknown[]) => raw.prepare(sql).run(...params as never[]),
|
||||
transaction: <T>(fn: (conn: unknown) => T) => raw.transaction(() => fn(raw))(),
|
||||
};
|
||||
// The realtime side is exercised through the controller and the MCP layer; here the
|
||||
// service is under test for what it writes, so a broadcast that goes nowhere is right.
|
||||
|
||||
Reference in New Issue
Block a user