feat(04-02): implement listEmitter + listAccess; all 9 tests GREEN

listEmitter.ts:
- Module-level EventEmitter singleton; setMaxListeners(200) (T-04-04)
- publishListEvent(listId, event): emits on list:${listId} channel
- subscribeListEvents(listId, handler): registers listener, returns unsub closure
- ListEvent type union: item:added/updated/deleted, list:updated/deleted
- D-04 isolation guaranteed by per-list channel keying

listAccess.ts:
- getAccessibleListIds(userId): two SELECT queries (owned + shared), Set dedupe
- Satisfies T-04-02/T-04-03: over-returning proven impossible by Test 7

listAccess.test.ts fix:
- Use randomUUID() suffix in seedUser to avoid oidc_sub unique-key collisions
  across test re-runs (users table not truncated by global afterEach)

vitest.config.ts:
- pool: 'forks' + singleFork: true to prevent FK violations from concurrent
  DB workers racing against the shared-state global afterEach cleanup
- sequence.concurrent: false as belt-and-suspenders

ioredis NOT introduced (D-18 abstraction boundary satisfied)
This commit is contained in:
Lucas Berger
2026-06-09 12:20:38 -04:00
parent 2d250afce2
commit 792efeb3df
4 changed files with 116 additions and 4 deletions
+7 -4
View File
@@ -11,17 +11,20 @@
* Run: pnpm --filter @familysync/api exec vitest run tests/lib/listAccess.test.ts
*/
import { describe, it, expect, beforeEach } from 'vitest'
import { describe, it, expect } from 'vitest'
import { randomUUID } from 'node:crypto'
import { db } from '../../src/db/client.js'
import { users, lists, listShares } from '../../src/db/schema.js'
import { getAccessibleListIds } from '../../src/lib/listAccess.js'
// Seed helpers — insert minimal rows and return their IDs.
async function seedUser(suffix: string): Promise<number> {
// oidc_sub uses a UUID suffix so rows never collide across test runs
// even when the users table is not truncated between runs.
async function seedUser(label: string): Promise<number> {
const [result] = await db.insert(users).values({
oidcIss: 'https://auth.test',
oidcSub: `sub-${suffix}`,
displayName: `User ${suffix}`,
oidcSub: `sub-${label}-${randomUUID()}`,
displayName: `User ${label}`,
color: '#000000',
}).$returningId()
return result.id