Spaces:
Paused
Paused
File size: 4,855 Bytes
9c1c54e 1e54caa 9c1c54e 1e54caa 9c1c54e 1e54caa 9c1c54e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | /**
* Tests for plan-based model routing.
*
* Verifies that:
* 1. applyBackendModelsForPlan correctly builds planModelMap
* 2. getModelPlanTypes returns correct plan associations
* 3. When both free and team plans include a model, both are returned
* 4. Account pool respects plan routing when acquiring accounts
*/
import { describe, it, expect, beforeEach, vi } from "vitest";
vi.mock("../../config.js", () => ({
getConfig: vi.fn(() => ({
server: {},
model: {},
api: { base_url: "https://chatgpt.com/backend-api" },
client: { app_version: "1.0.0" },
})),
}));
vi.mock("fs", async (importOriginal) => {
const actual = await importOriginal<typeof import("fs")>();
return {
...actual,
readFileSync: vi.fn(() => "models: []"),
writeFileSync: vi.fn(),
writeFile: vi.fn((_p: string, _d: string, _e: string, cb: (err: Error | null) => void) => cb(null)),
existsSync: vi.fn(() => false),
mkdirSync: vi.fn(),
};
});
vi.mock("js-yaml", () => ({
default: {
load: vi.fn(() => ({ models: [], aliases: {} })),
dump: vi.fn(() => ""),
},
}));
import {
loadStaticModels,
applyBackendModelsForPlan,
getModelPlanTypes,
getModelStoreDebug,
} from "../model-store.js";
// Minimal backend model entry matching what Codex API returns
function makeModel(slug: string) {
return { slug, id: slug, name: slug };
}
describe("plan-based model routing", () => {
beforeEach(() => {
// Reset model store state by reloading empty static catalog
loadStaticModels();
});
it("applyBackendModelsForPlan registers models for a plan", () => {
applyBackendModelsForPlan("free", [
makeModel("gpt-5.2-codex"),
makeModel("gpt-5.4"),
]);
expect(getModelPlanTypes("gpt-5.2-codex")).toContain("free");
expect(getModelPlanTypes("gpt-5.4")).toContain("free");
});
it("models available in both plans return both plan types", () => {
applyBackendModelsForPlan("free", [
makeModel("gpt-5.2-codex"),
makeModel("gpt-5.4"),
]);
applyBackendModelsForPlan("team", [
makeModel("gpt-5.2-codex"),
makeModel("gpt-5.4"),
makeModel("gpt-5.4-mini"),
]);
const plans54 = getModelPlanTypes("gpt-5.4");
expect(plans54).toContain("free");
expect(plans54).toContain("team");
const plansCodex = getModelPlanTypes("gpt-5.2-codex");
expect(plansCodex).toContain("free");
expect(plansCodex).toContain("team");
});
it("model only in team plan does not include free", () => {
applyBackendModelsForPlan("free", [
makeModel("gpt-5.2-codex"),
]);
applyBackendModelsForPlan("team", [
makeModel("gpt-5.2-codex"),
makeModel("gpt-5.4"),
]);
const plans54 = getModelPlanTypes("gpt-5.4");
expect(plans54).toContain("team");
expect(plans54).not.toContain("free");
});
it("replacing a plan's models updates the index", () => {
// Initially free doesn't have gpt-5.4
applyBackendModelsForPlan("free", [makeModel("gpt-5.2-codex")]);
expect(getModelPlanTypes("gpt-5.4")).not.toContain("free");
// Backend now returns gpt-5.4 for free → re-fetch
applyBackendModelsForPlan("free", [
makeModel("gpt-5.2-codex"),
makeModel("gpt-5.4"),
]);
expect(getModelPlanTypes("gpt-5.4")).toContain("free");
});
it("unknown model returns empty plan list", () => {
applyBackendModelsForPlan("free", [makeModel("gpt-5.2-codex")]);
expect(getModelPlanTypes("nonexistent-model")).toEqual([]);
});
it("all backend model slugs are admitted (no client-side filtering)", () => {
applyBackendModelsForPlan("free", [
makeModel("gpt-5.2-codex"),
makeModel("research"),
makeModel("gpt-5-2"),
makeModel("some-internal-slug"),
]);
expect(getModelPlanTypes("gpt-5.2-codex")).toContain("free");
expect(getModelPlanTypes("research")).toContain("free");
expect(getModelPlanTypes("gpt-5-2")).toContain("free");
expect(getModelPlanTypes("some-internal-slug")).toContain("free");
});
it("gpt-oss-* models are admitted", () => {
applyBackendModelsForPlan("free", [
makeModel("gpt-oss-120b"),
makeModel("gpt-oss-20b"),
]);
expect(getModelPlanTypes("gpt-oss-120b")).toContain("free");
expect(getModelPlanTypes("gpt-oss-20b")).toContain("free");
});
it("planMap in store info reflects current state", () => {
applyBackendModelsForPlan("free", [
makeModel("gpt-5.2-codex"),
makeModel("gpt-5.4"),
]);
applyBackendModelsForPlan("team", [
makeModel("gpt-5.4"),
]);
const info = getModelStoreDebug();
expect(info.planMap.free).toContain("gpt-5.2-codex");
expect(info.planMap.free).toContain("gpt-5.4");
expect(info.planMap.team).toContain("gpt-5.4");
expect(info.planMap.team).not.toContain("gpt-5.2-codex");
});
});
|