add analysis recommendation
This commit is contained in:
133
app/home/(user)/_lib/server/load-recommendations.test.ts
Normal file
133
app/home/(user)/_lib/server/load-recommendations.test.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
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']);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user