feat: wrap multi-step DB writes in transactions (#118)

- saveSourceConfig: upsert + credential update run atomically
- updateSourceConfig: SELECT FOR UPDATE prevents lost updates
- Widen Database type to accept transaction handles

Co-authored-by: Ona <no-reply@ona.com>
This commit is contained in:
2026-04-12 15:46:30 +01:00
committed by GitHub
parent e54c5d5462
commit 62c8dfe0b1
5 changed files with 78 additions and 33 deletions

View File

@@ -81,6 +81,24 @@ mock.module("../sources/user-sources.ts", () => ({
updatedAt: now,
}
},
async findForUpdate(sourceId: string) {
// Delegates to find — row locking is a no-op in tests.
if (mockFindResult !== undefined) return mockFindResult
const now = new Date()
return {
id: crypto.randomUUID(),
userId,
sourceId,
enabled: true,
config: {},
credentials: null,
createdAt: now,
updatedAt: now,
}
},
async updateConfig(_sourceId: string, _update: { enabled?: boolean; config?: unknown }) {
// no-op for tests
},
async upsertConfig(_sourceId: string, _data: { enabled: boolean; config: unknown }) {
// no-op for tests
},
@@ -93,7 +111,9 @@ mock.module("../sources/user-sources.ts", () => ({
}),
}))
const fakeDb = {} as Database
const fakeDb = {
transaction: <T>(fn: (tx: unknown) => Promise<T>) => fn(fakeDb),
} as unknown as Database
function createStubSource(id: string, items: FeedItem[] = []): FeedSource {
return {