Files
medreport_mrb2b/app/home/(user)/_lib/server/load-recommendations.test.ts
2025-09-17 17:35:04 +03:00

134 lines
4.0 KiB
TypeScript

import type { Mock } from 'jest-mock';
// ---- Mocks you can tweak per test ----
const createResponseMock = jest.fn();
const getLatestResponseTimeMock = jest.fn();
const getLatestUniqueAnalysResponsesMock = jest.fn();
const parsePersonalCodeMock = jest.fn(() => ({ gender: { value: 'male' } }));
// Mock OpenAI SDK
jest.mock('openai', () => {
return {
__esModule: true,
default: class OpenAI {
responses = { create: createResponseMock };
},
};
});
// Mock next/cache (global cache map so it persists between calls)
const globalCache = new Map<string, unknown>();
jest.mock('next/cache', () => {
return {
__esModule: true,
unstable_cache:
(fn: (...args: any[]) => Promise<any>, keyParts: any[], _opts?: any) =>
async (...args: any[]) => {
const key = JSON.stringify(keyParts);
if (globalCache.has(key)) return globalCache.get(key);
const val = await fn(...args);
globalCache.set(key, val);
return val;
},
};
});
// Mock your analysis helpers + personal code parser
jest.mock('../src/analysis-utils', () => ({
__esModule: true,
getLatestUniqueAnalysResponses: getLatestUniqueAnalysResponsesMock,
getLatestResponseTime: getLatestResponseTimeMock,
}));
jest.mock('../src/personal-code', () => ({
__esModule: true,
PersonalCode: { parsePersonalCode: parsePersonalCodeMock },
}));
describe('loadRecommendations', () => {
beforeEach(() => {
createResponseMock.mockReset();
getLatestResponseTimeMock.mockReset();
getLatestUniqueAnalysResponsesMock.mockReset();
globalCache.clear();
});
it('should call OpenAI once when latest date stays the same (cache hit on 2nd call)', async () => {
const date1 = new Date('2025-09-16T12:00:00Z');
getLatestResponseTimeMock.mockReturnValue(date1);
getLatestUniqueAnalysResponsesMock.mockImplementation((arr: any[]) => arr);
createResponseMock.mockResolvedValue({
output_text: JSON.stringify({ recommended: ['A', 'B'] }),
});
const { loadRecommendations } = await import('./load-recommendations');
const analysisResponses = [
{ name: 'x', value: 1, responseTime: date1 },
] as any[];
const analyses = [{ title: 't', description: 'd' }] as any[];
const account = { id: 'u1', personal_code: '12345678901' } as any;
// Act: 1st call (MISS)
const out1 = await loadRecommendations(
analysisResponses,
analyses,
account,
);
// Act: 2nd call with same date (HIT)
const out2 = await loadRecommendations(
analysisResponses,
analyses,
account,
);
// Assert: only one API call, result reused
expect(createResponseMock).toHaveBeenCalledTimes(1);
expect(out1).toEqual(['A', 'B']);
expect(out2).toEqual(['A', 'B']);
});
it('should call OpenAI again when latest date changes (new cache key)', async () => {
const date1 = new Date('2025-09-16T12:00:00Z');
const date2 = new Date('2025-09-17T00:00:00Z');
getLatestResponseTimeMock.mockReturnValueOnce(date1);
getLatestResponseTimeMock.mockReturnValueOnce(date2);
getLatestUniqueAnalysResponsesMock.mockImplementation((arr: any[]) => arr);
createResponseMock
.mockResolvedValueOnce({
output_text: JSON.stringify({ recommended: ['A', 'B'] }),
})
.mockResolvedValueOnce({
output_text: JSON.stringify({ recommended: ['C'] }),
});
const { loadRecommendations } = await import('./load-recommendations');
const analysisResponses = [
{ name: 'x', value: 1, responseTime: date1 },
] as any[];
const analyses = [{ title: 't', description: 'd' }] as any[];
const account = { id: 'u1', personal_code: '12345678901' } as any;
const out1 = await loadRecommendations(
analysisResponses,
analyses,
account,
);
const out2 = await loadRecommendations(
analysisResponses,
analyses,
account,
);
expect(createResponseMock).toHaveBeenCalledTimes(2);
expect(out1).toEqual(['A', 'B']);
expect(out2).toEqual(['C']);
});
});