133 lines
4.0 KiB
TypeScript
133 lines
4.0 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
chooseModel,
|
|
createRuntimeConfigPatch,
|
|
dedupeModels,
|
|
getLocalRuntimeStartReason,
|
|
pathName,
|
|
toModelFile,
|
|
type RuntimeSettingsFormState,
|
|
} from "./runtime-settings-form";
|
|
import { createDefaultRuntimeSnapshot } from "./runtime-utils";
|
|
|
|
function createFormState(overrides: Partial<RuntimeSettingsFormState> = {}): RuntimeSettingsFormState {
|
|
return {
|
|
mode: "local",
|
|
startPolicy: "manual",
|
|
serverUrl: "https://api.example.com/",
|
|
apiKey: " sk-demo ",
|
|
modelsDir: "D:/models",
|
|
selectedModel: "D:/models/demo.gguf",
|
|
addr: "127.0.0.1:8080",
|
|
gpuLayers: 48,
|
|
ctxSize: 8192,
|
|
extraModels: [{ name: "demo.gguf", path: "D:/models/demo.gguf" }],
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("runtime settings form helpers", () => {
|
|
it("keeps the current model when it still exists", () => {
|
|
const current = "D:/models/demo.gguf";
|
|
const scanned = [{ name: "demo.gguf", path: current }];
|
|
const extra = [{ name: "other.gguf", path: "D:/models/other.gguf" }];
|
|
|
|
expect(chooseModel(current, scanned, extra)).toBe(current);
|
|
});
|
|
|
|
it("falls back to the first available model when the current one disappears", () => {
|
|
expect(
|
|
chooseModel(
|
|
"D:/models/missing.gguf",
|
|
[{ name: "demo.gguf", path: "D:/models/demo.gguf" }],
|
|
[],
|
|
),
|
|
).toBe("D:/models/demo.gguf");
|
|
});
|
|
|
|
it("deduplicates model entries by path while keeping first occurrence", () => {
|
|
expect(
|
|
dedupeModels([
|
|
{ name: "demo.gguf", path: "D:/models/demo.gguf" },
|
|
{ name: "duplicate.gguf", path: "D:/models/demo.gguf" },
|
|
{ name: "other.gguf", path: "D:/models/other.gguf" },
|
|
]),
|
|
).toEqual([
|
|
{ name: "demo.gguf", path: "D:/models/demo.gguf" },
|
|
{ name: "other.gguf", path: "D:/models/other.gguf" },
|
|
]);
|
|
});
|
|
|
|
it("builds a trimmed local patch from the form state", () => {
|
|
const snapshot = createDefaultRuntimeSnapshot();
|
|
const patch = createRuntimeConfigPatch(snapshot.config, createFormState(), 32);
|
|
|
|
expect(patch).toMatchObject({
|
|
connection_mode: "local",
|
|
server_url: "https://api.example.com",
|
|
api_key: "sk-demo",
|
|
model_path: "D:/models/demo.gguf",
|
|
models_dir: "D:/models",
|
|
gpu_layers: 32,
|
|
ctx_size: 8192,
|
|
extra_model_paths: ["D:/models/demo.gguf"],
|
|
});
|
|
});
|
|
|
|
it("keeps saved local values untouched when editing remote mode", () => {
|
|
const snapshot = createDefaultRuntimeSnapshot();
|
|
const patch = createRuntimeConfigPatch(
|
|
{
|
|
...snapshot.config,
|
|
model_path: "D:/models/keep.gguf",
|
|
models_dir: "D:/models",
|
|
extra_model_paths: ["D:/models/keep.gguf"],
|
|
},
|
|
createFormState({
|
|
mode: "remote",
|
|
serverUrl: " https://api.example.com/ ",
|
|
apiKey: " remote-key ",
|
|
modelsDir: "",
|
|
selectedModel: "",
|
|
extraModels: [],
|
|
}),
|
|
16,
|
|
);
|
|
|
|
expect(patch).toMatchObject({
|
|
connection_mode: "remote",
|
|
server_url: "https://api.example.com",
|
|
api_key: "remote-key",
|
|
model_path: "D:/models/keep.gguf",
|
|
models_dir: "D:/models",
|
|
extra_model_paths: ["D:/models/keep.gguf"],
|
|
});
|
|
});
|
|
|
|
it("explains why local runtime start is blocked", () => {
|
|
expect(
|
|
getLocalRuntimeStartReason({ mode: "local", binaryPath: null, selectedModel: "D:/models/demo.gguf" }),
|
|
).toContain("Build or bundle");
|
|
|
|
expect(
|
|
getLocalRuntimeStartReason({ mode: "local", binaryPath: "D:/bin/localllm.exe", selectedModel: "" }),
|
|
).toContain("Choose a model");
|
|
|
|
expect(
|
|
getLocalRuntimeStartReason({
|
|
mode: "local",
|
|
binaryPath: "D:/bin/localllm.exe",
|
|
selectedModel: "D:/models/demo.gguf",
|
|
}),
|
|
).toBe("");
|
|
});
|
|
|
|
it("normalizes model file labels from a path", () => {
|
|
expect(pathName("D:/models/demo.gguf")).toBe("demo.gguf");
|
|
expect(toModelFile("D:/models/demo.gguf")).toEqual({
|
|
name: "demo.gguf",
|
|
path: "D:/models/demo.gguf",
|
|
});
|
|
});
|
|
});
|