analytics/services/api/test/mocks.ts
2026-01-29 08:20:57 -08:00

139 lines
3.3 KiB
TypeScript

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> {
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<T extends ObjectLiteral>(): MockRepository<T> {
const queryBuilder = createMockQueryBuilder();
return {
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<string, string | undefined> = {},
): Mocked<ConfigService> {
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<ConfigService>;
}