diff --git a/orchestrator/src/basic.test.ts b/orchestrator/src/basic.test.ts deleted file mode 100644 index 98c9f24..0000000 --- a/orchestrator/src/basic.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { describe, it, expect } from 'vitest'; - -describe('Basic Test', () => { - it('should pass', () => { - expect(1 + 1).toBe(2); - }); -}); diff --git a/orchestrator/src/server/services/modelSelection.test.ts b/orchestrator/src/server/services/modelSelection.test.ts new file mode 100644 index 0000000..39e9af5 --- /dev/null +++ b/orchestrator/src/server/services/modelSelection.test.ts @@ -0,0 +1,146 @@ +// src/server/services/modelSelection.test.ts +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { scoreJobSuitability } from './scorer.js'; +import { generateTailoring } from './summary.js'; +import { pickProjectIdsForJob } from './projectSelection.js'; +import * as settingsRepo from '../repositories/settings.js'; + +// Mock the settings repository +vi.mock('../repositories/settings.js', () => ({ + getSetting: vi.fn(), +})); + +describe('Model Selection Logic', () => { + const originalEnv = process.env; + + beforeEach(() => { + vi.resetAllMocks(); + // Set environment variables to ensure we don't hit early exits + process.env = { ...originalEnv, OPENROUTER_API_KEY: 'test-key', MODEL: 'env-model' }; + + // Mock global fetch to capture the request and return a dummy success response + global.fetch = vi.fn().mockResolvedValue({ + ok: true, + json: async () => ({ + choices: [{ message: { content: JSON.stringify({ + score: 50, explanation: 'ok', + summary: 'sum', headline: 'head', skills: [], + selectedProjectIds: ['1'] + }) } }], + }), + }); + }); + + afterEach(() => { + process.env = originalEnv; + vi.restoreAllMocks(); + }); + + describe('Scoring Service', () => { + it('should use scoring specific model when set', async () => { + vi.mocked(settingsRepo.getSetting).mockImplementation(async (key) => { + if (key === 'modelScorer') return 'specific-scorer-model'; + if (key === 'model') return 'global-model'; + return null; + }); + + await scoreJobSuitability({ title: 'Test Job', jobDescription: 'desc' } as any, {}); + + const fetchCall = vi.mocked(fetch).mock.calls[0]; + const body = JSON.parse(fetchCall[1]?.body as string); + expect(body.model).toBe('specific-scorer-model'); + }); + + it('should fall back to global model for scoring when specific not set', async () => { + vi.mocked(settingsRepo.getSetting).mockImplementation(async (key) => { + if (key === 'modelScorer') return null; + if (key === 'model') return 'global-model'; + return null; + }); + + await scoreJobSuitability({ title: 'Test Job' } as any, {}); + + const fetchCall = vi.mocked(fetch).mock.calls[0]; + const body = JSON.parse(fetchCall[1]?.body as string); + expect(body.model).toBe('global-model'); + }); + + it('should fall back to env model for scoring when no settings set', async () => { + vi.mocked(settingsRepo.getSetting).mockResolvedValue(null); + + await scoreJobSuitability({ title: 'Test Job' } as any, {}); + + const fetchCall = vi.mocked(fetch).mock.calls[0]; + const body = JSON.parse(fetchCall[1]?.body as string); + expect(body.model).toBe('env-model'); + }); + }); + + describe('Tailoring Service', () => { + it('should use tailoring specific model when set', async () => { + vi.mocked(settingsRepo.getSetting).mockImplementation(async (key) => { + if (key === 'modelTailoring') return 'specific-tailoring-model'; + if (key === 'model') return 'global-model'; + return null; + }); + + await generateTailoring('job desc', {}); + + const fetchCall = vi.mocked(fetch).mock.calls[0]; + const body = JSON.parse(fetchCall[1]?.body as string); + expect(body.model).toBe('specific-tailoring-model'); + }); + + it('should fall back to global model when specific not set', async () => { + vi.mocked(settingsRepo.getSetting).mockImplementation(async (key) => { + if (key === 'modelTailoring') return null; + if (key === 'model') return 'global-model'; + return null; + }); + + await generateTailoring('job desc', {}); + + const fetchCall = vi.mocked(fetch).mock.calls[0]; + const body = JSON.parse(fetchCall[1]?.body as string); + expect(body.model).toBe('global-model'); + }); + }); + + describe('Project Selection Service', () => { + it('should use project selection specific model when set', async () => { + vi.mocked(settingsRepo.getSetting).mockImplementation(async (key) => { + if (key === 'modelProjectSelection') return 'specific-project-model'; + if (key === 'model') return 'global-model'; + return null; + }); + + await pickProjectIdsForJob({ + jobDescription: 'desc', + eligibleProjects: [{ id: '1', name: 'p1', description: 'd1', summaryText: 'summary' } as any], + desiredCount: 1 + }); + + const fetchCall = vi.mocked(fetch).mock.calls[0]; + const body = JSON.parse(fetchCall[1]?.body as string); + expect(body.model).toBe('specific-project-model'); + }); + + it('should fall back to global model when specific not set', async () => { + vi.mocked(settingsRepo.getSetting).mockImplementation(async (key) => { + if (key === 'modelProjectSelection') return null; + if (key === 'model') return 'global-model'; + return null; + }); + + await pickProjectIdsForJob({ + jobDescription: 'desc', + eligibleProjects: [{ id: '1', name: 'p1', description: 'd1', summaryText: 'summary' } as any], + desiredCount: 1 + }); + + const fetchCall = vi.mocked(fetch).mock.calls[0]; + const body = JSON.parse(fetchCall[1]?.body as string); + expect(body.model).toBe('global-model'); + }); + }); +});