| 7 | import { createD1ExecutorDb } from "./d1"; |
| 8 | |
| 9 | const makeRecordingD1 = ( |
| 10 | client: SqliteDataMigrationClient, |
| 11 | ): { |
| 12 | readonly db: D1Database; |
| 13 | readonly statements: string[]; |
| 14 | readonly failWhen: (predicate: ((sql: string) => boolean) | null) => void; |
| 15 | } => { |
| 16 | const statements: string[] = []; |
| 17 | let failurePredicate: ((sql: string) => boolean) | null = null; |
| 18 | const record = (sql: string): void => { |
| 19 | statements.push(sql); |
| 20 | if (failurePredicate?.(sql)) { |
| 21 | // oxlint-disable-next-line executor/no-try-catch-or-throw, executor/no-error-constructor -- test boundary: the fake D1 adapter must reject exactly where the real D1 query would reject |
| 22 | throw new Error("forced D1 failure"); |
| 23 | } |
| 24 | }; |
| 25 | const prepare = (sql: string) => { |
| 26 | const statement = (args: readonly unknown[]): Record<string, unknown> => ({ |
| 27 | bind: (...values: readonly unknown[]) => statement([...args, ...values]), |
| 28 | all: async () => { |
| 29 | record(sql); |
| 30 | const result = await client.execute({ sql, args }); |
| 31 | return { success: true, meta: {}, results: result.rows }; |
| 32 | }, |
| 33 | run: async () => { |
| 34 | record(sql); |
| 35 | await client.execute({ sql, args }); |
| 36 | return { success: true, meta: {}, results: [] }; |
| 37 | }, |
| 38 | }); |
| 39 | return statement([]); |
| 40 | }; |
| 41 | |
| 42 | // oxlint-disable-next-line executor/no-double-cast -- test double: only the D1 methods used by schema preparation and migrations are implemented |
| 43 | const db = { |
| 44 | prepare, |
| 45 | withSession: () => ({ prepare }), |
| 46 | } as unknown as D1Database; |
| 47 | return { |
| 48 | db, |
| 49 | statements, |
| 50 | failWhen: (predicate) => { |
| 51 | failurePredicate = predicate; |
| 52 | }, |
| 53 | }; |
| 54 | }; |
| 55 | |
| 56 | const isRuntimeSchemaStatement = (sql: string): boolean => { |
| 57 | const normalized = sql.trim().toUpperCase(); |