/** * A3 regression: Transaction boundary — repos use (tx?.db ?? this.db) * Bug: EventStore passed _tx to repos, but repos ignored it (always used this.db). * Fix: all repos use (tx?.db ?? this.db).prepare(...) and TransactionHandle has db field. */ import { describe, it, expect } from 'bun:test' import { readFileSync, readdirSync } from 'fs' import { join } from 'path' describe('A3: Transaction boundary fix', () => { const repos_dir = join(import.meta.dir, '..', '..', 'src', 'storage', 'repositories') it('TransactionHandle interface includes db field', () => { const contracts_src = readFileSync( join(import.meta.dir, '..', '..', '..', 'contracts', 'src', 'task.ts'), 'utf-8' ) expect(contracts_src).toMatch(/db\s*\?\s*:/) }) it('core CRUD methods in repositories use (tx?.db ?? this.db) pattern', () => { const repo_files = readdirSync(repos_dir).filter(f => f.endsWith('.ts') && !f.endsWith('.d.ts')) for (const file of repo_files) { const src = readFileSync(join(repos_dir, file), 'utf-8') const crud_methods = ['async get(', 'async insert(', 'async update('] for (const method_sig of crud_methods) { const idx = src.indexOf(method_sig) if (idx === -1) continue const method_body = src.slice(idx, src.indexOf('\n }', idx) + 4) if (method_body.includes('this.db.prepare')) { expect(method_body).toContain('tx?.db') } } } }) it('EventStore passes _tx to repository calls in project()', () => { const event_store_src = readFileSync( join(import.meta.dir, '..', '..', 'src', 'events', 'EventStore.ts'), 'utf-8' ) const project_method = event_store_src.match(/project\s*\([^)]*\)[^{]*\{/s) expect(project_method).not.toBeNull() const repo_calls = event_store_src.match(/\?\.(?:insert|update|get)\([^)]*,\s*_tx\s*\)/g) expect(repo_calls).not.toBeNull() expect(repo_calls!.length).toBeGreaterThan(0) }) })