--- phase: 16-multi-user-data-model plan: 04 type: execute wave: 3 depends_on: ["16-01", "16-02"] files_modified: - tests/services/item.service.test.ts - tests/services/category.service.test.ts - tests/services/thread.service.test.ts - tests/services/setup.service.test.ts - tests/services/totals.test.ts - tests/services/csv.service.test.ts - tests/services/auth.service.test.ts - tests/services/oauth.service.test.ts - tests/routes/items.test.ts - tests/routes/categories.test.ts - tests/routes/threads.test.ts - tests/routes/setups.test.ts - tests/routes/auth.test.ts - tests/routes/images.test.ts - tests/routes/oauth.test.ts - tests/routes/params.test.ts - tests/mcp/tools.test.ts autonomous: true requirements: - MULTI-02 - MULTI-04 - MULTI-05 must_haves: truths: - "All existing tests pass after updating to use { db, userId } from createTestDb" - "Service tests pass userId to every service function call" - "Route tests set userId on the test app context" - "MCP tests pass userId to MCP server creation" - "At least one cross-user isolation test exists proving User A cannot see User B's data" artifacts: - path: "tests/services/item.service.test.ts" provides: "User-scoped item service tests" contains: "userId" - path: "tests/mcp/tools.test.ts" provides: "User-scoped MCP tool tests" contains: "userId" key_links: - from: "tests/helpers/db.ts" to: "tests/services/*.test.ts" via: "createTestDb returns { db, userId }" pattern: "const \\{ db, userId \\}" - from: "tests/services/item.service.test.ts" to: "src/server/services/item.service.ts" via: "passes userId to all service calls" pattern: "getAllItems.*db.*userId" --- Update all test files to work with the new multi-user data model: destructure { db, userId } from createTestDb(), pass userId to all service calls, set userId in route test contexts, and add cross-user isolation tests. Purpose: Tests validate that multi-user isolation works correctly. Without updated tests, we cannot verify that MULTI-02 (cross-user isolation) is enforced. The test suite must pass green before the phase is complete. Output: All 17 test files updated, cross-user isolation tests added, full test suite passes. @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/16-multi-user-data-model/16-CONTEXT.md @.planning/phases/16-multi-user-data-model/16-RESEARCH.md @.planning/phases/16-multi-user-data-model/16-01-SUMMARY.md @.planning/phases/16-multi-user-data-model/16-02-SUMMARY.md @tests/helpers/db.ts @tests/services/item.service.test.ts @tests/services/category.service.test.ts @tests/routes/items.test.ts @tests/mcp/tools.test.ts Test update pattern for service tests: ```typescript // Before: let db: any; beforeEach(async () => { db = await createTestDb(); }); // ... const items = await getAllItems(db); // After: let db: any; let userId: number; beforeEach(async () => { ({ db, userId } = await createTestDb()); }); // ... const items = await getAllItems(db, userId); ``` Test update pattern for route tests: ```typescript // Before: const testApp = new Hono(); testApp.use("*", async (c, next) => { c.set("db", db); await next(); }); // After: const testApp = new Hono(); testApp.use("*", async (c, next) => { c.set("db", db); c.set("userId", userId); await next(); }); ``` Cross-user isolation test pattern: ```typescript import { createSecondTestUser } from "../helpers/db"; test("user cannot see other user's items", async () => { const userId2 = await createSecondTestUser(db); await createItem(db, userId, { name: "User 1 Item", ... }); await createItem(db, userId2, { name: "User 2 Item", ... }); const user1Items = await getAllItems(db, userId); const user2Items = await getAllItems(db, userId2); expect(user1Items).toHaveLength(1); expect(user1Items[0].name).toBe("User 1 Item"); expect(user2Items).toHaveLength(1); expect(user2Items[0].name).toBe("User 2 Item"); }); ``` Task 1: Update all service test files to pass userId tests/services/item.service.test.ts, tests/services/category.service.test.ts, tests/services/thread.service.test.ts, tests/services/setup.service.test.ts, tests/services/totals.test.ts, tests/services/csv.service.test.ts, tests/services/auth.service.test.ts, tests/services/oauth.service.test.ts tests/services/item.service.test.ts, tests/services/category.service.test.ts, tests/services/thread.service.test.ts, tests/services/setup.service.test.ts, tests/services/totals.test.ts, tests/services/csv.service.test.ts, tests/services/auth.service.test.ts, tests/services/oauth.service.test.ts, tests/helpers/db.ts For EACH of the 8 service test files, apply this systematic transformation: 1. **Change createTestDb destructuring** from `db = await createTestDb()` to `({ db, userId } = await createTestDb())`. Add `userId` variable declaration alongside `db`. 2. **Add userId to every service function call** as the second argument after `db`. Go through every call to any service function in each test and add `userId`. 3. **Fix category references**: Tests that reference "Uncategorized" by hardcoded ID 1 must instead look up the category by name+userId or use the ID returned from the seeded data. The test helper already seeds Uncategorized with the test user's ID. 4. **Specific file notes:** **item.service.test.ts** (~160 lines): - Destructure `{ db, userId }` from createTestDb - Every `getAllItems(db)` becomes `getAllItems(db, userId)` - Every `getItemById(db, id)` becomes `getItemById(db, userId, id)` - Every `createItem(db, data)` becomes `createItem(db, userId, data)` - Every `updateItem(db, id, data)` becomes `updateItem(db, userId, id, data)` - Every `deleteItem(db, id)` becomes `deleteItem(db, userId, id)` - ADD a cross-user isolation test: create a second user with `createSecondTestUser(db)`, create items for each user, verify each user only sees their own items, and verify getItemById returns null for another user's item ID **category.service.test.ts** (~97 lines): - Same destructuring pattern - Add userId to all category service calls - ADD a test for composite unique constraint: two users can have categories with the same name - Fix any hardcoded "Uncategorized" ID references **thread.service.test.ts** (~523 lines): - Same destructuring pattern - Add userId to ALL thread, candidate, and resolve calls - Candidate operations need userId (for parent thread verification) - Thread resolution test: verify the created item has the correct userId - ADD a cross-user isolation test for threads **setup.service.test.ts** (~293 lines): - Same destructuring pattern - Add userId to all setup service calls - syncSetupItems calls need userId - ADD a test that verifies a user cannot add another user's items to their setup **totals.test.ts** (~79 lines): - Same destructuring pattern - Add userId to getTotals calls **csv.service.test.ts** (~196 lines): - Same destructuring pattern - Add userId to import/export calls - CSV import must create categories with userId **auth.service.test.ts** (~68 lines): - Same destructuring pattern - Add userId to createApiKey, listApiKeys, deleteApiKey calls - Update verifyApiKey assertions to check for `{ userId }` return instead of boolean `true` **oauth.service.test.ts** (~290 lines): - Same destructuring pattern - Add userId where needed (createTokens, verifyAccessToken) - Update verifyAccessToken assertions to check for `{ userId }` return instead of boolean 5. **Import `createSecondTestUser`** from helpers/db.ts in files that add isolation tests (at minimum: item, category, thread, setup test files). After all files are updated, run `bun test` to verify the full suite passes. bun test tests/services/ 2>&1 | tail -5 - All 8 service test files use `{ db, userId } = await createTestDb()` - Every service function call in tests includes userId argument - `item.service.test.ts` has a cross-user isolation test using `createSecondTestUser` - `category.service.test.ts` has a composite unique constraint test (same name, different users) - `auth.service.test.ts` checks `verifyApiKey` returns `{ userId }` not boolean - `oauth.service.test.ts` checks `verifyAccessToken` returns `{ userId }` not boolean - `bun test tests/services/` passes all tests All 8 service test files updated with userId, cross-user isolation tests added, all service tests pass Task 2: Update route tests, MCP tests, and run full suite tests/routes/items.test.ts, tests/routes/categories.test.ts, tests/routes/threads.test.ts, tests/routes/setups.test.ts, tests/routes/auth.test.ts, tests/routes/images.test.ts, tests/routes/oauth.test.ts, tests/routes/params.test.ts, tests/mcp/tools.test.ts tests/routes/items.test.ts, tests/routes/categories.test.ts, tests/routes/threads.test.ts, tests/routes/setups.test.ts, tests/routes/auth.test.ts, tests/routes/images.test.ts, tests/routes/oauth.test.ts, tests/routes/params.test.ts, tests/mcp/tools.test.ts **Route tests** (7 files): Route tests create a test Hono app with middleware that sets `db` on context. They need TWO changes: 1. **Destructure `{ db, userId }`** from createTestDb 2. **Set userId on the test app context** in the middleware setup: ```typescript testApp.use("*", async (c, next) => { c.set("db", db); c.set("userId", userId); await next(); }); ``` For each route test file, read the file first to understand its middleware setup pattern, then add `c.set("userId", userId)` to the middleware. The route handlers will then pick it up via `c.get("userId")`. **Specific notes per file:** **items.test.ts** (~207 lines): Set userId in context middleware. Tests should work as-is since routes now pass userId to services. **categories.test.ts** (~91 lines): Set userId in context middleware. **threads.test.ts** (~413 lines): Set userId in context middleware. Thread tests may reference hardcoded Uncategorized category ID -- fix these. **setups.test.ts** (~305 lines): Set userId in context middleware. **auth.test.ts** (~139 lines): Set userId in context middleware for API key management routes. Auth-specific routes (login, setup) may not need userId. **images.test.ts** (~26 lines): Set userId in context middleware if needed. **oauth.test.ts** (~443 lines): May not need userId for OAuth flow tests, but set it for consistency. OAuth token creation may need userId. **params.test.ts** (~81 lines): Set userId in context middleware. **MCP tests** (1 file): **tools.test.ts** (~253 lines): - Destructure `{ db, userId }` from createTestDb - Update `createMcpServer(db)` calls to `createMcpServer(db, userId)` - MCP tool calls should then work since tools now receive userId from the server - ADD an isolation test: create data as user 1, verify MCP tools as user 2 don't return user 1's data After ALL test files are updated, run the FULL test suite: ```bash bun test ``` Fix any remaining failures. Common issues to watch for: - Missing userId argument (TypeScript will flag this) - Hardcoded category ID 1 (use the seeded category from createTestDb) - Boolean vs object return type from verifyApiKey/verifyAccessToken - Settings tests using single key lookup instead of composite - Route tests not setting userId in context Also run lint to ensure no formatting issues: ```bash bun run lint ``` bun test 2>&1 | tail -10 - All route test files set `c.set("userId", userId)` in test middleware - All route test files use `{ db, userId } = await createTestDb()` - MCP tools.test.ts passes userId to createMcpServer - MCP tools.test.ts has a cross-user isolation test - `bun test` passes ALL tests (0 failures) - `bun run lint` passes All 17 test files updated, full test suite passes green, lint passes, cross-user isolation verified by tests After all tasks complete: 1. `bun test` exits with 0 failures 2. `bun run lint` passes 3. `grep -r "createSecondTestUser" tests/` shows isolation tests exist 4. `grep -r 'c.set("userId"' tests/routes/` shows userId set in all route test middleware 5. `grep "createMcpServer(db, userId)" tests/mcp/tools.test.ts` confirms MCP test update - Full test suite passes (`bun test` returns 0 failures) - All service tests pass userId to every service call - All route tests set userId in test app context - MCP tests pass userId to createMcpServer - Cross-user isolation tests exist for items, categories, threads, setups - Composite unique constraint test exists for categories - Lint passes After completion, create `.planning/phases/16-multi-user-data-model/16-04-SUMMARY.md`