262 lines
10 KiB
Markdown
262 lines
10 KiB
Markdown
---
|
|
phase: 14-postgresql-migration
|
|
plan: 06
|
|
type: execute
|
|
wave: 3
|
|
depends_on: [14-01, 14-03, 14-04]
|
|
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/auth.service.test.ts
|
|
- tests/services/oauth.service.test.ts
|
|
- tests/services/csv.service.test.ts
|
|
- tests/services/image.service.test.ts
|
|
- tests/services/totals.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/oauth.test.ts
|
|
- tests/routes/images.test.ts
|
|
- tests/routes/params.test.ts
|
|
- tests/mcp/tools.test.ts
|
|
autonomous: true
|
|
requirements: [DB-02, DB-03]
|
|
must_haves:
|
|
truths:
|
|
- "All 18 test files use async createTestDb() in beforeEach"
|
|
- "All test assertions await async service/route calls"
|
|
- "bun test tests/ passes with zero failures"
|
|
- "No test file imports from bun:sqlite or drizzle-orm/bun-sqlite"
|
|
artifacts:
|
|
- path: "tests/services/item.service.test.ts"
|
|
provides: "Async item service tests"
|
|
contains: "await createTestDb"
|
|
- path: "tests/routes/items.test.ts"
|
|
provides: "Async item route tests"
|
|
contains: "await createTestDb"
|
|
- path: "tests/mcp/tools.test.ts"
|
|
provides: "Async MCP tools tests"
|
|
contains: "await createTestDb"
|
|
key_links:
|
|
- from: "tests/**/*.test.ts"
|
|
to: "tests/helpers/db.ts"
|
|
via: "import { createTestDb }"
|
|
pattern: "createTestDb"
|
|
- from: "tests/services/*.test.ts"
|
|
to: "src/server/services/*.ts"
|
|
via: "import service functions"
|
|
pattern: "from.*services/"
|
|
---
|
|
|
|
<objective>
|
|
Convert all 18 test files to async: await createTestDb(), await all service/route calls, await all assertions involving DB operations. Run the full test suite to confirm everything passes on PGlite.
|
|
|
|
Purpose: This is the final verification that the entire stack works on PostgreSQL. Tests must pass on PGlite (DB-03) and confirm async operations work correctly (DB-02).
|
|
Output: All tests green. Full `bun test tests/` passes.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
|
@$HOME/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/ROADMAP.md
|
|
@.planning/phases/14-postgresql-migration/14-CONTEXT.md
|
|
@.planning/phases/14-postgresql-migration/14-RESEARCH.md
|
|
@.planning/phases/14-postgresql-migration/14-01-SUMMARY.md
|
|
@.planning/phases/14-postgresql-migration/14-03-SUMMARY.md
|
|
|
|
@tests/helpers/db.ts
|
|
</context>
|
|
|
|
<interfaces>
|
|
<!-- Test helper (from Plan 01): -->
|
|
<!-- export async function createTestDb() { ... } -->
|
|
<!-- Returns: PGlite-backed Drizzle instance (same query API, but async) -->
|
|
|
|
<!-- Db type issue (Pitfall 8 from research): -->
|
|
<!-- Production uses PostgresJsDatabase<typeof schema> from drizzle-orm/postgres-js -->
|
|
<!-- Tests use PgliteDatabase<typeof schema> from drizzle-orm/pglite -->
|
|
<!-- These types may not be directly compatible for the `Db` type parameter in services -->
|
|
<!-- Solution: Use `any` cast when passing test db to service functions, OR define a shared type -->
|
|
<!-- Simplest: `const db = await createTestDb() as any` if type errors occur -->
|
|
|
|
Conversion rules for ALL test files:
|
|
1. `beforeEach(() => { db = createTestDb(); })` -> `beforeEach(async () => { db = await createTestDb(); })`
|
|
2. Every service call in tests: add `await` (they are now async)
|
|
3. Every direct DB call in tests (inserts for setup, selects for assertions): add `await`, remove `.all()/.get()/.run()`
|
|
4. Route tests: if using `app.request()`, those are already async. But ensure the test app factory is also async.
|
|
5. If `type Db = typeof prodDb` causes type mismatch with PGlite db, use `as any` cast
|
|
</interfaces>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Convert all 9 service test files to async</name>
|
|
<files>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/auth.service.test.ts, tests/services/oauth.service.test.ts, tests/services/csv.service.test.ts, tests/services/image.service.test.ts, tests/services/totals.test.ts</files>
|
|
<read_first>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/auth.service.test.ts, tests/services/oauth.service.test.ts, tests/services/csv.service.test.ts, tests/services/image.service.test.ts, tests/services/totals.test.ts</read_first>
|
|
<action>
|
|
For EACH of the 9 service test files, apply these changes:
|
|
|
|
**1. Make beforeEach async:**
|
|
```typescript
|
|
// BEFORE:
|
|
let db: any;
|
|
beforeEach(() => {
|
|
db = createTestDb();
|
|
});
|
|
|
|
// AFTER:
|
|
let db: any;
|
|
beforeEach(async () => {
|
|
db = await createTestDb();
|
|
});
|
|
```
|
|
|
|
**2. Add `await` to every service function call in test bodies:**
|
|
```typescript
|
|
// BEFORE:
|
|
const items = getAllItems(db);
|
|
const item = createItem(db, { name: "Test", categoryId: 1 });
|
|
|
|
// AFTER:
|
|
const items = await getAllItems(db);
|
|
const item = await createItem(db, { name: "Test", categoryId: 1 });
|
|
```
|
|
|
|
**3. Add `await` to direct DB calls used for test setup/assertions:**
|
|
```typescript
|
|
// BEFORE:
|
|
db.insert(schema.items).values({ ... }).run();
|
|
const [cat] = db.select().from(schema.categories).all();
|
|
|
|
// AFTER:
|
|
await db.insert(schema.items).values({ ... });
|
|
const [cat] = await db.select().from(schema.categories);
|
|
```
|
|
|
|
**4. Make test callbacks async if not already:**
|
|
```typescript
|
|
// BEFORE:
|
|
it("should return all items", () => {
|
|
|
|
// AFTER:
|
|
it("should return all items", async () => {
|
|
```
|
|
|
|
**5. Handle Db type compatibility:**
|
|
If TypeScript complains about passing PGlite db to service functions that expect `PostgresJsDatabase`, use `as any` on the db variable:
|
|
```typescript
|
|
let db: any; // Use any to accommodate PGlite/postgres-js type difference
|
|
```
|
|
|
|
**6. OAuth tests -- boolean conversion:**
|
|
If any OAuth test checks `used === 0` or `used === 1`, change to `used === false` or `used === true`.
|
|
|
|
After converting each file, run it individually:
|
|
```bash
|
|
bun test tests/services/item.service.test.ts
|
|
```
|
|
Fix any issues before moving to the next file.
|
|
</action>
|
|
<verify>
|
|
<automated>bun test tests/services/ 2>&1; [ $? -eq 0 ] && echo "PASS" || echo "FAIL"</automated>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- Every service test file has `beforeEach(async () => { db = await createTestDb(); })`
|
|
- Every test callback (`it(...)`) that calls service functions or DB is `async`
|
|
- No test file contains `.all()`, `.get()`, or `.run()` on db objects
|
|
- No test file imports from `bun:sqlite` or `drizzle-orm/bun-sqlite`
|
|
- `bun test tests/services/` exits 0 with all tests passing
|
|
</acceptance_criteria>
|
|
<done>All 9 service test files converted to async and passing on PGlite.</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Convert all route tests + MCP test to async, run full suite</name>
|
|
<files>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/oauth.test.ts, tests/routes/images.test.ts, tests/routes/params.test.ts, tests/mcp/tools.test.ts</files>
|
|
<read_first>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/oauth.test.ts, tests/routes/images.test.ts, tests/routes/params.test.ts, tests/mcp/tools.test.ts</read_first>
|
|
<action>
|
|
Route tests typically create a test app with a test database injected. The pattern is usually:
|
|
|
|
```typescript
|
|
// Common route test pattern:
|
|
function createTestApp() {
|
|
const db = createTestDb();
|
|
// ... create Hono app with db injected
|
|
return { app, db };
|
|
}
|
|
```
|
|
|
|
This must become:
|
|
```typescript
|
|
async function createTestApp() {
|
|
const db = await createTestDb();
|
|
// ... create Hono app with db injected
|
|
return { app, db };
|
|
}
|
|
```
|
|
|
|
**For each of the 8 route test files + 1 MCP test file:**
|
|
|
|
1. Make the test app factory `async` and `await createTestDb()`
|
|
2. Make `beforeEach` async if it calls the factory
|
|
3. Route tests use `app.request()` which returns a Promise -- these should already be awaited. Verify each test awaits the response.
|
|
4. If any test does direct DB calls for setup/assertions, apply same async conversion as service tests
|
|
5. Make all test callbacks async
|
|
|
|
**MCP test (tests/mcp/tools.test.ts):**
|
|
- Same pattern: async createTestDb, await all MCP tool calls
|
|
- MCP tools internally call services which are now async
|
|
|
|
**After all files converted, run the FULL test suite:**
|
|
```bash
|
|
bun test tests/
|
|
```
|
|
|
|
This is the gate check. ALL tests must pass. If any test fails:
|
|
1. Read the error message carefully
|
|
2. Common issues: missing `await`, `.get()` not removed, type mismatch
|
|
3. Fix and re-run
|
|
|
|
**Also verify no SQLite references remain anywhere in test files:**
|
|
```bash
|
|
grep -rn "bun:sqlite\|drizzle-orm/bun-sqlite\|\.all()\|\.get()\|\.run()" tests/
|
|
```
|
|
Should return NO matches (except possibly string literals in test descriptions).
|
|
</action>
|
|
<verify>
|
|
<automated>bun test tests/ 2>&1; [ $? -eq 0 ] && echo "PASS" || echo "FAIL"</automated>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- Every route test file has async `createTestApp` or async `beforeEach` with `await createTestDb()`
|
|
- Every test callback is `async`
|
|
- tests/mcp/tools.test.ts uses `await createTestDb()`
|
|
- `grep -rn "bun:sqlite\|drizzle-orm/bun-sqlite" tests/` returns NO matches
|
|
- `bun test tests/` exits 0 with ALL tests passing (zero failures)
|
|
</acceptance_criteria>
|
|
<done>All 18 test files pass on PGlite. Full test suite green. No SQLite test infrastructure remains.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
- `bun test tests/` -- ALL tests pass (exit code 0)
|
|
- `grep -rn "bun:sqlite\|drizzle-orm/bun-sqlite" tests/` -- NO matches
|
|
- `grep -rn "\.all()\b" tests/ | grep -v "describe\|it(" ` -- NO matches on DB calls (may appear in test descriptions)
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
All 18 test files converted to async PGlite. Full test suite (`bun test tests/`) passes with zero failures. No SQLite test infrastructure remains anywhere in the tests/ directory.
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/14-postgresql-migration/14-06-SUMMARY.md`
|
|
</output>
|