import test from "node:test"; import assert from "node:assert/strict"; import { DEFAULT_SETTINGS, MAX_MODEL_CHARS } from "../lib/defaults.js"; import { mergeSettings } from "../lib/settings.js"; test("empty object yields the defaults", () => { assert.deepEqual(mergeSettings({}), DEFAULT_SETTINGS); }); test("null and undefined yield the defaults", () => { assert.deepEqual(mergeSettings(null), DEFAULT_SETTINGS); assert.deepEqual(mergeSettings(undefined), DEFAULT_SETTINGS); }); test("a non-object yields the defaults", () => { assert.deepEqual(mergeSettings("nonsense"), DEFAULT_SETTINGS); assert.deepEqual(mergeSettings(42), DEFAULT_SETTINGS); }); test("valid stored values are preserved", () => { assert.deepEqual( mergeSettings({ model: "auto", temporaryChat: true, webSearch: true, openIn: "new-window", promptTemplate: "Explain: {query}", }), { model: "auto", temporaryChat: true, webSearch: true, openIn: "new-window", promptTemplate: "Explain: {query}", }, ); }); test("missing keys fall back individually", () => { const merged = mergeSettings({ model: "auto" }); assert.equal(merged.model, "auto"); assert.equal(merged.temporaryChat, DEFAULT_SETTINGS.temporaryChat); assert.equal(merged.promptTemplate, DEFAULT_SETTINGS.promptTemplate); }); test("unknown keys are dropped", () => { const merged = mergeSettings({ model: "auto", nope: "gone", openInn: "typo" }); assert.deepEqual(Object.keys(merged).sort(), Object.keys(DEFAULT_SETTINGS).sort()); assert.equal("nope" in merged, false); }); test("wrong types fall back to the default", () => { const merged = mergeSettings({ model: 123, temporaryChat: "yes", webSearch: 1, promptTemplate: [], }); assert.equal(merged.model, DEFAULT_SETTINGS.model); assert.equal(merged.temporaryChat, DEFAULT_SETTINGS.temporaryChat); assert.equal(merged.webSearch, DEFAULT_SETTINGS.webSearch); assert.equal(merged.promptTemplate, DEFAULT_SETTINGS.promptTemplate); }); test("an out-of-range openIn falls back to the default", () => { assert.equal(mergeSettings({ openIn: "teleport" }).openIn, DEFAULT_SETTINGS.openIn); assert.equal(mergeSettings({ openIn: "" }).openIn, DEFAULT_SETTINGS.openIn); }); test("each documented openIn value is accepted", () => { for (const value of ["new-tab", "background-tab", "new-window"]) { assert.equal(mergeSettings({ openIn: value }).openIn, value); } }); test("mergeSettings does not mutate DEFAULT_SETTINGS", () => { mergeSettings({ model: "mutated" }); assert.equal(DEFAULT_SETTINGS.model, ""); }); // An unbounded model string can, on its own, push the finished URL over // MAX_URL_CHARS (lib/build-url.js), which silently drops `q` — the user's // selection vanishes with no signal. mergeSettings() must bound `model` the // same way it already bounds an out-of-range openIn: fall back to the default. test("a model at exactly MAX_MODEL_CHARS is accepted", () => { const model = "m".repeat(MAX_MODEL_CHARS); assert.equal(mergeSettings({ model }).model, model); }); test("a model one character over MAX_MODEL_CHARS falls back to the default", () => { const model = "m".repeat(MAX_MODEL_CHARS + 1); assert.equal(mergeSettings({ model }).model, DEFAULT_SETTINGS.model); });