import type { ObjectLiteral } from 'typeorm'; import type { ConfigService } from '@nestjs/config'; import type { Mock, Mocked } from 'vitest'; /** * Mock DataSource for services that use @InjectDataSource() with raw SQL. * Follows ISP: separate from repository mocks. */ export interface MockDataSource { query: Mock; getRepository: Mock; createQueryRunner: Mock; } /** * Creates a mock DataSource for services using raw SQL queries. * The query mock resolves to empty arrays by default. * Override per-test with mockDataSource.query.mockResolvedValueOnce(...) */ export function createMockDataSource(): MockDataSource { return { query: vi.fn().mockResolvedValue([]), getRepository: vi.fn(), createQueryRunner: vi.fn(), }; } /** * Mock repository type for services using @InjectRepository(). */ export interface MockRepository<_T extends ObjectLiteral = ObjectLiteral> { query: Mock; find: Mock; findOne: Mock; findOneBy: Mock; save: Mock; create: Mock; update: Mock; delete: Mock; remove: Mock; count: Mock; createQueryBuilder: Mock; queryBuilder: MockQueryBuilder; } /** * Creates a mock TypeORM repository with chainable query builder. */ export function createMockRepository(): MockRepository { const queryBuilder = createMockQueryBuilder(); return { query: vi.fn().mockResolvedValue([]), find: vi.fn(), findOne: vi.fn(), findOneBy: vi.fn(), save: vi.fn(), create: vi.fn(), update: vi.fn(), delete: vi.fn(), remove: vi.fn(), count: vi.fn(), createQueryBuilder: vi.fn().mockReturnValue(queryBuilder), queryBuilder, }; } export interface MockQueryBuilder { select: Mock; addSelect: Mock; where: Mock; andWhere: Mock; orWhere: Mock; groupBy: Mock; addGroupBy: Mock; orderBy: Mock; limit: Mock; offset: Mock; getRawOne: Mock; getRawMany: Mock; getOne: Mock; getMany: Mock; getCount: Mock; leftJoin: Mock; innerJoin: Mock; } /** * Creates a mock SelectQueryBuilder with chainable methods. */ export function createMockQueryBuilder(): MockQueryBuilder { const builder: MockQueryBuilder = { select: vi.fn(), addSelect: vi.fn(), where: vi.fn(), andWhere: vi.fn(), orWhere: vi.fn(), groupBy: vi.fn(), addGroupBy: vi.fn(), orderBy: vi.fn(), limit: vi.fn(), offset: vi.fn(), getRawOne: vi.fn().mockResolvedValue(null), getRawMany: vi.fn().mockResolvedValue([]), getOne: vi.fn().mockResolvedValue(null), getMany: vi.fn().mockResolvedValue([]), getCount: vi.fn().mockResolvedValue(0), leftJoin: vi.fn(), innerJoin: vi.fn(), }; // Make chainable methods return builder const chainable = [ 'select', 'addSelect', 'where', 'andWhere', 'orWhere', 'groupBy', 'addGroupBy', 'orderBy', 'limit', 'offset', 'leftJoin', 'innerJoin', ] as const; for (const method of chainable) { builder[method].mockReturnValue(builder); } return builder; } /** * Creates a mock ConfigService with configurable return values. */ export function createMockConfigService( config: Record = {}, ): Mocked { return { get: vi.fn((key: string) => config[key]), getOrThrow: vi.fn((key: string) => { if (config[key] === undefined) { throw new Error(`Config key ${key} not found`); } return config[key]; }), } as unknown as Mocked; }