1import { describe, expect, it } from "vite-plus/test";
2import { ProviderDriverKind, type ModelCapabilities } from "@t3tools/contracts";
3
4import {
5 DESCRIPTOR_PRESETS_BY_KIND,
6 descriptorFromPreset,
7 definitionFromDraft,
8 descriptorsFromCapabilities,
9 draftFromDefinition,
10 validateDraft,
11 type CustomModelDraft,
12} from "./customModelEditor.logic";
13
14const draft = (overrides: Partial<CustomModelDraft>): CustomModelDraft => ({
15 slug: "my-model",
16 name: "",
17 descriptors: [],
18 ...overrides,
19});
20
21describe("customModelEditor.logic", () => {
22 it("round-trips a definition through the draft, marking the current value as default", () => {
23 const definition = definitionFromDraft(
24 draft({
25 name: " My Model ",
26 descriptors: [
27 {
28 key: "a",
29 type: "select",
30 id: "reasoningEffort",
31 label: "Reasoning",
32 choices: [
33 { key: "a1", id: "low", label: "Low", isDefault: false },
34 { key: "a2", id: "high", label: "", isDefault: true },
35 ],
36 },
37 { key: "b", type: "boolean", id: "fastMode", label: "Fast Mode", choices: [] },
38 ],
39 }),
40 );
41
42 expect(definition).toEqual({
43 slug: "my-model",
44 name: "My Model",
45 capabilities: {
46 optionDescriptors: [
47 {
48 id: "reasoningEffort",
49 label: "Reasoning",
50 type: "select",
51 options: [
52 { id: "low", label: "Low" },
53 { id: "high", label: "high", isDefault: true },
54 ],
55 currentValue: "high",
56 },
57 { id: "fastMode", label: "Fast Mode", type: "boolean" },
58 ],
59 },
60 });
61
62 const reopened = draftFromDefinition(definition);
63 expect(reopened.name).toBe("My Model");
64 expect(reopened.descriptors.map((descriptor) => descriptor.id)).toEqual([
65 "reasoningEffort",
66 "fastMode",
67 ]);
68 expect(reopened.descriptors[0]!.choices.map((choice) => choice.isDefault)).toEqual([
69 false,
70 true,
71 ]);
72 });
73
74 it("preserves the current choice when it differs from the built-in default", () => {
75 const descriptors = descriptorsFromCapabilities(
76 {
77 optionDescriptors: [
78 {
79 id: "effort",
80 label: "Reasoning",
81 type: "select",
82 currentValue: "high",
83 options: [
84 { id: "low", label: "Low", isDefault: true },
85 { id: "high", label: "High" },
86 ],
87 },
88 ],
89 },
90 ProviderDriverKind.make("claudeAgent"),
91 );
92 expect(descriptors[0]!.choices.map((choice) => choice.isDefault)).toEqual([false, true]);
93 expect(
94 definitionFromDraft(draft({ descriptors })).capabilities?.optionDescriptors?.[0],
95 ).toMatchObject({ currentValue: "high" });
96 });
97
98 it("drops prompt-injected choices when copying a built-in's descriptors", () => {
99 const [copied] = descriptorsFromCapabilities(
100 {
101 optionDescriptors: [
102 {
103 id: "effort",
104 label: "Reasoning",
105 type: "select",
106 options: [
107 { id: "high", label: "High", isDefault: true },
108 { id: "ultrathink", label: "Ultrathink" },
109 ],
110 promptInjectedValues: ["ultrathink"],
111 },
112 ],
113 },
114 ProviderDriverKind.make("claudeAgent"),
115 );
116 expect(copied!.choices.map((choice) => choice.id)).toEqual(["high"]);
117 });
118
119 it.each([true, false, undefined])(
120 "preserves boolean values through copy and edit: %s",
121 (currentValue) => {
122 const capabilities: ModelCapabilities = {
123 optionDescriptors: [
124 {
125 id: "thinking",
126 label: "Thinking",
127 type: "boolean",
128 ...(currentValue !== undefined ? { currentValue } : {}),
129 },
130 ],
131 };
132 const copied = definitionFromDraft(
133 draft({
134 descriptors: descriptorsFromCapabilities(capabilities, ProviderDriverKind.make("cursor")),
135 }),
136 );
137 expect(copied.capabilities).toEqual(capabilities);
138 const reopened = draftFromDefinition(copied);
139 expect(definitionFromDraft({ ...reopened, name: "Renamed" }).capabilities).toEqual(
140 capabilities,
141 );
142 },
143 );
144
145 it("excludes Claude context choices from presets and copies without changing other providers or authored entries", () => {
146 const capabilities: ModelCapabilities = {
147 optionDescriptors: [
148 {
149 id: "contextWindow",
150 label: "Context",
151 type: "select",
152 options: [{ id: "1m", label: "1M", isDefault: true }],
153 },
154 { id: "thinking", label: "Thinking", type: "boolean", currentValue: true },
155 ],
156 };
157 const claude = ProviderDriverKind.make("claudeAgent");
158 const copied = definitionFromDraft(
159 draft({ descriptors: descriptorsFromCapabilities(capabilities, claude) }),
160 );
161 expect(copied.capabilities?.optionDescriptors).toEqual([capabilities.optionDescriptors![1]]);
162 const presets = definitionFromDraft(
163 draft({
164 descriptors: (DESCRIPTOR_PRESETS_BY_KIND[claude] ?? []).map(descriptorFromPreset),
165 }),
166 );
167 expect(
168 presets.capabilities?.optionDescriptors?.some((option) => option.id === "contextWindow"),
169 ).toBe(false);
170 const cursorCopy = descriptorsFromCapabilities(capabilities, ProviderDriverKind.make("cursor"));
171 expect(cursorCopy.map((option) => option.id)).toEqual(["contextWindow", "thinking"]);
172 const authored = { slug: "custom", name: "Custom", capabilities };
173 expect(
174 definitionFromDraft(draftFromDefinition(authored)).capabilities?.optionDescriptors?.[0],
175 ).toMatchObject(capabilities.optionDescriptors![0]!);
176 });
177
178 it("preserves choice descriptions when copying, renaming, and saving", () => {
179 const capabilities: ModelCapabilities = {
180 optionDescriptors: [
181 {
182 id: "effort",
183 label: "Reasoning",
184 description: "Choose a reasoning level.",
185 type: "select",
186 options: [
187 { id: "high", label: "High", isDefault: true },
188 { id: "ultracode", label: "Ultracode", description: "Uses additional reasoning." },
189 ],
190 },
191 ],
192 };
193 const copied = definitionFromDraft(
194 draft({
195 descriptors: descriptorsFromCapabilities(
196 capabilities,
197 ProviderDriverKind.make("claudeAgent"),
198 ),
199 }),
200 );
201 const reopened = draftFromDefinition(copied);
202 const saved = definitionFromDraft({ ...reopened, name: "Renamed" });
203 expect(saved.capabilities?.optionDescriptors?.[0]).toMatchObject(
204 capabilities.optionDescriptors![0]!,
205 );
206 expect(copied.capabilities?.optionDescriptors?.[0]).toMatchObject(
207 capabilities.optionDescriptors![0]!,
208 );
209 });
210
211 it.each(Object.entries(DESCRIPTOR_PRESETS_BY_KIND))(
212 "offers saveable presets for %s",
213 (_driver, presets) => {
214 expect(
215 validateDraft(draft({ descriptors: (presets ?? []).map(descriptorFromPreset) })),
216 ).toBeNull();
217 },
218 );
219
220 it("collapses a blank name and no options back to a bare definition", () => {
221 expect(definitionFromDraft(draft({ name: " " }))).toEqual({
222 slug: "my-model",
223 name: "my-model",
224 capabilities: null,
225 });
226 expect(draftFromDefinition({ slug: "x", name: "x", capabilities: null }).name).toBe("");
227 });
228
229 it("rejects duplicate ids, blank ids, and selects without choices", () => {
230 const select = (id: string, choices: Array<{ id: string }>) => ({
231 key: id,
232 type: "select" as const,
233 id,
234 label: "Label",
235 choices: choices.map((choice) => ({
236 key: choice.id,
237 label: "",
238 isDefault: false,
239 ...choice,
240 })),
241 });
242
243 expect(validateDraft(draft({ descriptors: [select("", [{ id: "a" }])] }))).toBe(
244 "Option 1 needs an id.",
245 );
246 expect(
247 validateDraft(
248 draft({ descriptors: [select("effort", [{ id: "a" }]), select("effort", [{ id: "b" }])] }),
249 ),
250 ).toBe('Option 2: id "effort" is used twice.');
251 expect(validateDraft(draft({ descriptors: [select("effort", [])] }))).toBe(
252 "Option 1 needs at least one choice.",
253 );
254 expect(
255 validateDraft(draft({ descriptors: [select("effort", [{ id: "a" }, { id: "a" }])] })),
256 ).toBe('Option 1: choice "a" is used twice.');
257 expect(validateDraft(draft({ descriptors: [select("effort", [{ id: "a" }])] }))).toBeNull();
258 });
259});