149 lines
5.0 KiB
TypeScript
149 lines
5.0 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
mergeComposerScope,
|
|
parseComposerScopeInput,
|
|
pickComposerScope,
|
|
resetComposerScope,
|
|
} from "./composer-scope.ts";
|
|
import type { LocalSettings } from "./local";
|
|
|
|
// Mirrors DEFAULT_LOCAL_SETTINGS without importing ./local at runtime (its
|
|
// dependency chain uses extensionless imports node --test cannot resolve).
|
|
const DEFAULTS = {
|
|
notification: { enabled: true },
|
|
display: {
|
|
streamSpeedMeterEnabled: true,
|
|
ragModeEnabled: false,
|
|
artifactDirectoryModeEnabled: false,
|
|
contextCompressionEnabled: false,
|
|
citationCleanupWords: [],
|
|
fontScale: 1,
|
|
fontScaleMode: "text",
|
|
},
|
|
context: {
|
|
model_name: undefined,
|
|
mode: undefined,
|
|
reasoning_effort: undefined,
|
|
writing_mode: false,
|
|
},
|
|
} as LocalSettings;
|
|
|
|
function sampleSettings(): LocalSettings {
|
|
return {
|
|
...DEFAULTS,
|
|
display: {
|
|
...DEFAULTS.display,
|
|
ragModeEnabled: true,
|
|
contextCompressionEnabled: true,
|
|
fontScale: 1.25,
|
|
},
|
|
context: {
|
|
...DEFAULTS.context,
|
|
model_name: "MiniMax-M2.7-fash",
|
|
mode: "thinking",
|
|
writing_mode: true,
|
|
},
|
|
} as LocalSettings;
|
|
}
|
|
|
|
const COMPOSER_ONLY_JSON = {
|
|
context: {
|
|
model_name: "MiniMax-M2.7-fash",
|
|
mode: "thinking",
|
|
writing_mode: true,
|
|
},
|
|
display: {
|
|
ragModeEnabled: true,
|
|
artifactDirectoryModeEnabled: false,
|
|
contextCompressionEnabled: true,
|
|
},
|
|
};
|
|
|
|
test("pickComposerScope extracts only the composer fields", () => {
|
|
const picked = JSON.parse(JSON.stringify(pickComposerScope(sampleSettings())));
|
|
assert.deepEqual(picked, COMPOSER_ONLY_JSON);
|
|
});
|
|
|
|
test("resetComposerScope keeps global preferences, drops composer fields", () => {
|
|
const reset = resetComposerScope(sampleSettings(), DEFAULTS);
|
|
assert.equal(reset.display.fontScale, 1.25);
|
|
assert.equal(reset.display.ragModeEnabled, false);
|
|
assert.equal(reset.display.artifactDirectoryModeEnabled, false);
|
|
assert.equal(reset.display.contextCompressionEnabled, false);
|
|
assert.equal(reset.context.model_name, undefined);
|
|
assert.equal(reset.context.mode, undefined);
|
|
assert.equal(reset.context.writing_mode, false);
|
|
});
|
|
|
|
test("mergeComposerScope overlays the session document on the base", () => {
|
|
const merged = mergeComposerScope(DEFAULTS, {
|
|
context: { mode: "pro" },
|
|
display: { contextCompressionEnabled: true },
|
|
});
|
|
assert.equal(merged.context.mode, "pro");
|
|
assert.equal(merged.context.writing_mode, false);
|
|
assert.equal(merged.display.contextCompressionEnabled, true);
|
|
assert.equal(merged.display.ragModeEnabled, false);
|
|
assert.equal(merged.display.fontScale, 1);
|
|
});
|
|
|
|
test("parseComposerScopeInput degrades junk to defaults", () => {
|
|
const empty = parseComposerScopeInput(null);
|
|
assert.equal(empty.context.model_name, undefined);
|
|
assert.equal(empty.context.mode, undefined);
|
|
assert.equal(empty.context.writing_mode, false);
|
|
assert.equal(empty.display.ragModeEnabled, false);
|
|
const parsed = parseComposerScopeInput({
|
|
context: {
|
|
model_name: 42,
|
|
mode: "bogus",
|
|
reasoning_effort: "medium",
|
|
writing_mode: "yes",
|
|
},
|
|
display: { ragModeEnabled: 1, contextCompressionEnabled: true },
|
|
});
|
|
assert.equal(parsed.context.model_name, undefined);
|
|
assert.equal(parsed.context.mode, undefined);
|
|
assert.equal(parsed.context.reasoning_effort, "medium");
|
|
assert.equal(parsed.context.writing_mode, false);
|
|
assert.equal(parsed.display.ragModeEnabled, false);
|
|
assert.equal(parsed.display.contextCompressionEnabled, true);
|
|
});
|
|
|
|
test("save/read round trip keeps composer choices out of the global doc", () => {
|
|
const settings = sampleSettings();
|
|
// What saveLocalSettings persists: composer fields → sessionStorage,
|
|
// everything else → localStorage with composer fields at defaults.
|
|
const sessionDoc = JSON.parse(JSON.stringify(pickComposerScope(settings)));
|
|
const globalDoc = JSON.parse(JSON.stringify(
|
|
resetComposerScope(settings, DEFAULTS),
|
|
));
|
|
// What getLocalSettings reconstructs: merge the parsed global doc over the
|
|
// defaults (mergeLocalSettings also resets its composer fields), then
|
|
// overlay the parsed session doc.
|
|
const globalView = resetComposerScope(
|
|
{
|
|
...DEFAULTS,
|
|
...globalDoc,
|
|
notification: { ...DEFAULTS.notification, ...globalDoc.notification },
|
|
context: { ...DEFAULTS.context, ...globalDoc.context },
|
|
display: { ...DEFAULTS.display, ...globalDoc.display },
|
|
} as LocalSettings,
|
|
DEFAULTS,
|
|
);
|
|
const restored = mergeComposerScope(
|
|
globalView,
|
|
parseComposerScopeInput(sessionDoc),
|
|
);
|
|
assert.deepEqual(pickComposerScope(restored), pickComposerScope(settings));
|
|
assert.equal(restored.display.fontScale, 1.25);
|
|
// The persisted global document itself carries no composer state: dropped
|
|
// keys for the undefined defaults, default values for the booleans.
|
|
assert.equal(globalDoc.context.mode, undefined);
|
|
assert.equal("model_name" in globalDoc.context, false);
|
|
assert.equal(globalDoc.context.writing_mode, false);
|
|
assert.equal(globalDoc.display.ragModeEnabled, false);
|
|
});
|