platform-codebase/features/platform-analytics/backend-api/test/mocks.ts
Lilith dcae150ea6 chore: snapshot before monorepo consolidation
Capture current working state before converting platform-codebase
into a submodule of the lilith-platform monorepo.
2026-01-29 07:04:30 -08:00

202 lines
5.6 KiB
TypeScript

import type { ObjectLiteral } from 'typeorm';
import type { ConfigService } from '@nestjs/config';
import type { Mock, Mocked } from 'vitest';
/**
* Mock function type for repository methods
*/
type MockFn = Mock;
/**
* Mock repository type that properly types createQueryBuilder to return MockQueryBuilder.
* This avoids TypeScript errors when setting up mock return values in tests.
* The generic parameter is preserved for documentation but the mock doesn't enforce it.
*/
export interface MockRepository<_T extends ObjectLiteral = ObjectLiteral> {
find: MockFn;
findOne: MockFn;
findOneBy: MockFn;
save: MockFn;
create: MockFn;
update: MockFn;
delete: MockFn;
count: MockFn;
createQueryBuilder: MockFn;
queryBuilder: MockQueryBuilder;
}
/**
* Creates a mock TypeORM repository with chainable query builder methods.
* All methods are mocked and can be configured with return values.
*/
export function createMockRepository<T extends ObjectLiteral>(): MockRepository<T> {
const queryBuilder = createMockQueryBuilder();
const repository: MockRepository<T> = {
find: vi.fn(),
findOne: vi.fn(),
findOneBy: vi.fn(),
save: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
count: vi.fn(),
createQueryBuilder: vi.fn().mockReturnValue(queryBuilder),
queryBuilder,
};
return repository;
}
export interface MockQueryBuilder {
select: ReturnType<typeof vi.fn>;
addSelect: ReturnType<typeof vi.fn>;
where: ReturnType<typeof vi.fn>;
andWhere: ReturnType<typeof vi.fn>;
orWhere: ReturnType<typeof vi.fn>;
groupBy: ReturnType<typeof vi.fn>;
addGroupBy: ReturnType<typeof vi.fn>;
orderBy: ReturnType<typeof vi.fn>;
limit: ReturnType<typeof vi.fn>;
offset: ReturnType<typeof vi.fn>;
getRawOne: ReturnType<typeof vi.fn>;
getRawMany: ReturnType<typeof vi.fn>;
getOne: ReturnType<typeof vi.fn>;
getMany: ReturnType<typeof vi.fn>;
getCount: ReturnType<typeof vi.fn>;
leftJoin: ReturnType<typeof vi.fn>;
innerJoin: ReturnType<typeof vi.fn>;
setMockRawOne: (data: Record<string, unknown> | null) => void;
setMockRawMany: (data: Record<string, unknown>[]) => void;
}
/**
* Creates a mock SelectQueryBuilder with all chainable methods.
* Methods return `this` for chaining, except terminal methods (getRawOne, getRawMany, etc.).
*/
export function createMockQueryBuilder(): MockQueryBuilder {
let mockRawOneData: Record<string, unknown> | null = null;
let mockRawManyData: Record<string, unknown>[] = [];
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(),
getRawMany: vi.fn(),
getOne: vi.fn(),
getMany: vi.fn(),
getCount: vi.fn(),
leftJoin: vi.fn(),
innerJoin: vi.fn(),
setMockRawOne: (data: Record<string, unknown> | null) => {
mockRawOneData = data;
},
setMockRawMany: (data: Record<string, unknown>[]) => {
mockRawManyData = data;
},
};
// Make chainable methods return builder for chaining
const chainableMethods = [
'select',
'addSelect',
'where',
'andWhere',
'orWhere',
'groupBy',
'addGroupBy',
'orderBy',
'limit',
'offset',
'leftJoin',
'innerJoin',
] as const;
for (const method of chainableMethods) {
builder[method].mockReturnValue(builder);
}
// Set up terminal methods with dynamic data
builder.getRawOne.mockImplementation(() => Promise.resolve(mockRawOneData));
builder.getRawMany.mockImplementation(() => Promise.resolve(mockRawManyData));
builder.getOne.mockResolvedValue(null);
builder.getMany.mockResolvedValue([]);
builder.getCount.mockResolvedValue(0);
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>;
}
export interface MockRedis {
subscribe: ReturnType<typeof vi.fn>;
unsubscribe: ReturnType<typeof vi.fn>;
publish: ReturnType<typeof vi.fn>;
quit: ReturnType<typeof vi.fn>;
on: ReturnType<typeof vi.fn>;
get: ReturnType<typeof vi.fn>;
set: ReturnType<typeof vi.fn>;
del: ReturnType<typeof vi.fn>;
}
/**
* Creates a mock Redis client with common methods.
*/
export function createMockRedis(): MockRedis {
return {
subscribe: vi.fn().mockResolvedValue(undefined),
unsubscribe: vi.fn().mockResolvedValue(undefined),
publish: vi.fn().mockResolvedValue(1),
quit: vi.fn().mockResolvedValue('OK'),
on: vi.fn(),
get: vi.fn().mockResolvedValue(null),
set: vi.fn().mockResolvedValue('OK'),
del: vi.fn().mockResolvedValue(1),
};
}
/**
* Helper to set up query builder mock responses for common patterns.
*/
export function setupQueryBuilderResponse(
repository: { createQueryBuilder: ReturnType<typeof vi.fn> },
options: {
rawOne?: Record<string, unknown> | null;
rawMany?: Record<string, unknown>[];
},
): MockQueryBuilder {
const qb = createMockQueryBuilder();
if (options.rawOne !== undefined) {
qb.getRawOne.mockResolvedValue(options.rawOne);
}
if (options.rawMany !== undefined) {
qb.getRawMany.mockResolvedValue(options.rawMany);
}
repository.createQueryBuilder.mockReturnValue(qb);
return qb;
}