// Copyright (C) 2026 Danilo M. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // Run: node desktop/modules/assistant/test_config.js // Config.js is a QML library, so its .pragma line is stripped before node // evaluates it. const fs = require("fs"); const assert = require("assert"); const src = fs.readFileSync(__dirname + "/Config.js", "utf8") .replace(/^\.pragma library$/m, ""); const C = new Function(src + "\nreturn { scalars, setScalar, env, setEnv, quote };")(); const KEYS = ["llm", "system", "tool_rounds", "temperature", "thinking"]; const file = [ "# desktop-assistant configuration.", "# Style name = weight; prose with an equals sign", "", '# llm = "http://localhost:8181/v1" # llama-server, OpenAI API', '# system = "You are a voice assistant. ..."', "temperature = 0.6 # per request; unset = the preset's", "", "# [whisper]", "# port = 8182", "", "[env]", 'SEARXNG_URL = "http://search.example.org" # a # inside a comment', 'MD_EDITOR = "typora"', '# WEATHER_ENV = "/home/you/x"', "", ].join("\n"); // Only active keys have values; prose and commented keys do not. assert.deepStrictEqual(C.scalars(file, KEYS), { temperature: 0.6 }); assert.deepStrictEqual(C.env(file), [ { key: "SEARXNG_URL", value: "http://search.example.org" }, { key: "MD_EDITOR", value: "typora" }, ]); // Setting an active key keeps its inline comment. let t = C.setScalar(file, "temperature", "0.2"); assert.strictEqual(t.split("\n")[5], "temperature = 0.2 # per request; unset = the preset's"); // Unsetting comments it out in place, and it reads as unset. t = C.setScalar(file, "temperature", null); assert.strictEqual(t.split("\n")[5], "# temperature = 0.6 # per request; unset = the preset's"); assert.deepStrictEqual(C.scalars(t, KEYS), {}); // Setting a commented key activates its line rather than adding one. t = C.setScalar(file, "llm", C.quote("http://box:8080/v1")); assert.strictEqual(t.split("\n")[3], 'llm = "http://box:8080/v1" # llama-server, OpenAI API'); assert.strictEqual(t.split("\n").length, file.split("\n").length); // A key nowhere in the file lands before the first table, not inside it. t = C.setScalar(file, "thinking", "false"); const lines = t.split("\n"); assert.ok(lines.indexOf("thinking = false") < lines.indexOf("[env]")); assert.strictEqual(C.scalars(t, KEYS).thinking, false); // The prose line with "=" is never treated as a key. assert.strictEqual(C.setScalar(file, "name", null), file); // Strings round-trip with quotes, backslashes and newlines. const tricky = 'Say "hi"\\now\nline two\ttab'; t = C.setScalar(file, "system", C.quote(tricky)); assert.strictEqual(C.scalars(t, KEYS).system, tricky); assert.strictEqual(t.split("\n").length, file.split("\n").length); // env: replace, remove, add after the last active entry. t = C.setEnv(file, "MD_EDITOR", "xdg-open"); assert.deepStrictEqual(C.env(t)[1], { key: "MD_EDITOR", value: "xdg-open" }); t = C.setEnv(file, "SEARXNG_URL", null); assert.deepStrictEqual(C.env(t).map(e => e.key), ["MD_EDITOR"]); t = C.setEnv(file, "CALENDAR_DIR", "/home/you/cal"); assert.deepStrictEqual(C.env(t).map(e => e.key), ["SEARXNG_URL", "MD_EDITOR", "CALENDAR_DIR"]); assert.strictEqual(t.split("\n")[13], 'CALENDAR_DIR = "/home/you/cal"'); // No [env] table yet: one is appended. t = C.setEnv("temperature = 0.6\n", "X", "1"); assert.deepStrictEqual(C.env(t), [{ key: "X", value: "1" }]); assert.ok(/\n\[env\]\nX = "1"\n$/.test(t), JSON.stringify(t)); // An env key in another table is not the env one. assert.deepStrictEqual(C.env('[voice]\nlang = "it"\n'), []); console.log("test_config: ok");