Jobber/orchestrator/src/server/services/modelSelection.test.ts

181 lines
5.7 KiB
TypeScript

// src/server/services/modelSelection.test.ts
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import * as settingsRepo from "../repositories/settings";
import { pickProjectIdsForJob } from "./projectSelection";
import { scoreJobSuitability } from "./scorer";
import { generateTailoring } from "./summary";
// Mock the settings repository
vi.mock("../repositories/settings", () => ({
getSetting: vi.fn(),
getAllSettings: 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 getAllSettings to return empty settings (no overrides)
vi.mocked(settingsRepo.getAllSettings).mockResolvedValue({});
// 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");
});
});
});