import { vi } from 'vitest'; import type { FastifyInstance } from 'fastify'; import type { NodePgDatabase } from 'drizzle-orm/node-postgres'; import type * as schema from '../src/db/schema/index.js'; export type MockDb = { select: ReturnType['select']>; insert: ReturnType['insert']>; update: ReturnType['update']>; delete: ReturnType['delete']>; }; /** Build a select chain that resolves to the given rows at .limit(n) */ export function selectChain(resolveAtLimit: unknown[] = []) { const limitFn = vi.fn().mockResolvedValue(resolveAtLimit); return { from: vi.fn().mockReturnValue({ where: vi.fn().mockReturnValue({ limit: limitFn, orderBy: vi.fn().mockReturnValue({ limit: limitFn }), }), limit: limitFn, }), }; } /** Build a select chain for .from().where().orderBy() - orderBy is terminal */ export function selectChainOrdered(resolveAtOrderBy: unknown[] = []) { const orderByThenable = { then: (resolve: (v: unknown) => void) => resolve(resolveAtOrderBy), }; return { from: vi.fn().mockReturnValue({ where: vi.fn().mockReturnValue({ orderBy: vi.fn().mockReturnValue(orderByThenable), }), }), }; } /** Build a select chain for .from().where() with no orderBy - used by select({...}).from() */ export function selectChainSimple(resolveRows: unknown[] = []) { const thenable = { then: (resolve: (v: unknown) => void) => resolve(resolveRows) }; return { from: vi.fn().mockReturnValue({ where: vi.fn().mockReturnValue(thenable), }), }; } /** Build a select chain for .from().where() - where is terminal (no orderBy/limit) */ export function selectChainWhere(resolveAtWhere: unknown[] = []) { const thenable = { then: (resolve: (v: unknown) => void) => resolve(resolveAtWhere) }; return { from: vi.fn().mockReturnValue({ where: vi.fn().mockReturnValue(thenable), }), }; } /** Build an insert chain that resolves at .returning() or .values() */ export function insertChain(resolveAtReturning: unknown[] = []) { const returningFn = vi.fn().mockResolvedValue(resolveAtReturning); const chainFromValues = { returning: returningFn, then: (resolve: (v?: unknown) => void) => resolve(undefined), }; return { values: vi.fn().mockReturnValue(chainFromValues), returning: returningFn, }; } /** Build an update chain that resolves at .where() */ export function updateChain(resolveAtWhere: unknown[] = []) { return { set: vi.fn().mockReturnValue({ where: vi.fn().mockResolvedValue(resolveAtWhere), }), }; } /** Build an update chain with .where().returning() */ export function updateChainReturning(resolveAtReturning: unknown[] = []) { const returningFn = vi.fn().mockResolvedValue(resolveAtReturning); return { set: vi.fn().mockReturnValue({ where: vi.fn().mockReturnValue({ returning: returningFn, }), }), }; } /** Build a delete chain that resolves at .where() */ export function deleteChain() { return { where: vi.fn().mockResolvedValue(undefined), }; } /** * Create a chainable mock for Drizzle DB operations. * Use mockReturnValue with selectChain/insertChain/updateChain/deleteChain. */ export function createMockDb(): MockDb { const chain = { from: vi.fn().mockReturnThis(), where: vi.fn().mockReturnThis(), values: vi.fn().mockReturnThis(), set: vi.fn().mockReturnThis(), orderBy: vi.fn().mockReturnThis(), limit: vi.fn().mockReturnThis(), returning: vi.fn().mockReturnThis(), }; return { select: vi.fn().mockReturnValue(chain), insert: vi.fn().mockReturnValue(chain), update: vi.fn().mockReturnValue(chain), delete: vi.fn().mockReturnValue(chain), } as unknown as MockDb; } /** * Create a minimal mock Fastify app with db for route/integration tests. * Use buildApp() from app.ts for full integration tests. */ export function createMockApp(): FastifyInstance & { db: MockDb } { return { db: createMockDb(), log: { child: () => ({ debug: () => {}, info: () => {}, warn: () => {}, error: () => {}, trace: () => {}, fatal: () => {}, }), debug: () => {}, info: () => {}, warn: () => {}, error: () => {}, trace: () => {}, fatal: () => {}, }, } as FastifyInstance & { db: MockDb }; }