test / launch.test.ts
test / launch.test.ts
// Starting the browser: never a second copy when the profile is already open, and never twice
// within the cooldown, even from a server LM Studio restarted in between.
import { test } from "node:test";
import assert from "node:assert/strict";
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import { Bridge, BrowserUnavailable } from "../src/bridge";
import { browserCandidates, mcpHost, windowsFolders, type Config } from "../src/config";
import { lastLaunchAt, profileDir, profileInUse, recordLaunch } from "../src/profile";
const tmp = (p: string) => fs.mkdtempSync(path.join(os.tmpdir(), p));
function profilesRoot(lockPid: number | null): { root: string; dir: string } {
const root = tmp("ss-profiles-");
fs.writeFileSync(
path.join(root, "profiles.ini"),
"[Profile1]\nName=default-release\nIsRelative=1\nPath=Profiles/abc.default\n\n[Profile0]\nName=llm-browser\nIsRelative=1\nPath=Profiles/u86w.llm-browser\nDefault=1\n",
);
const dir = path.join(root, "Profiles", "u86w.llm-browser");
fs.mkdirSync(dir, { recursive: true });
if (lockPid !== null) fs.symlinkSync(`127.0.0.1:+${lockPid}`, path.join(dir, "lock"));
return { root, dir };
}
test("the profile folder is found by name in profiles.ini", () => {
const { root, dir } = profilesRoot(null);
assert.equal(profileDir("llm-browser", { SIMPLESEARCH_PROFILES_DIR: root }), dir);
assert.equal(profileDir("missing", { SIMPLESEARCH_PROFILES_DIR: root }), null);
});
test("an open profile is detected from its lock", { skip: process.platform === "win32" }, () => {
assert.equal(profileInUse(profilesRoot(process.pid).dir), true, "a live owner holds it");
assert.equal(profileInUse(profilesRoot(null).dir), false, "no lock, not open");
assert.equal(profileInUse(profilesRoot(2 ** 22 - 3).dir), false, "a stale lock from a dead process");
});
test("launches are recorded for every copy of the server", () => {
const data = tmp("ss-data-");
assert.equal(lastLaunchAt(data), 0);
recordLaunch(data, 12345);
assert.equal(lastLaunchAt(data), 12345);
});
async function bridgeFor(opts: { profilesRoot: string; data: string; browser: string }): Promise<Bridge> {
process.env.SIMPLESEARCH_PROFILES_DIR = opts.profilesRoot;
const cfg = {
host: mcpHost("simplesearch.js"),
secret: "c0ffee".repeat(10) + "abcd",
port: 20000 + Math.floor(Math.random() * 20000),
browserPath: opts.browser,
browserPathSource: "env",
profile: "llm-browser",
engines: ["duckduckgo"],
ruleUpdates: "approve",
dataDir: opts.data,
researchBudget: 24000,
researchReads: 6,
enforceBudget: true,
problems: [],
} as unknown as Config;
const b = new Bridge(cfg, () => {});
await b.start();
return b;
}
function fakeBrowser(): { exe: string; ran: () => boolean } {
const dir = tmp("ss-fakezen-");
const marker = path.join(dir, "ran");
const exe = path.join(dir, "zen");
fs.writeFileSync(exe, `#!/bin/sh\ntouch "${marker}"\n`, { mode: 0o755 });
return { exe, ran: () => fs.existsSync(marker) };
}
const wait = (ms: number) => new Promise((r) => setTimeout(r, ms));
test("an already open profile is waited for, not started again", { skip: process.platform === "win32" }, async () => {
const { root } = profilesRoot(process.pid);
const zen = fakeBrowser();
const b = await bridgeFor({ profilesRoot: root, data: tmp("ss-data-"), browser: zen.exe });
try {
await assert.rejects(b.ensureConnected(300), (e: unknown) => e instanceof BrowserUnavailable && /profile is open, but the simpleSearch Bridge extension/.test((e as Error).message));
await wait(300);
assert.equal(zen.ran(), false);
} finally {
b.close();
}
});
test("a browser started moments ago by another copy of the server is not started again", { skip: process.platform === "win32" }, async () => {
const { root } = profilesRoot(null);
const data = tmp("ss-data-");
recordLaunch(data, Date.now() - 5000);
const zen = fakeBrowser();
const b = await bridgeFor({ profilesRoot: root, data, browser: zen.exe });
try {
await assert.rejects(b.ensureConnected(300), (e: unknown) => e instanceof BrowserUnavailable && e.starting && /still loading/.test((e as Error).message));
await wait(300);
assert.equal(zen.ran(), false);
} finally {
b.close();
}
});
test("a closed profile with no recent start is started, once", { skip: process.platform === "win32" }, async () => {
const { root } = profilesRoot(null);
const data = tmp("ss-data-");
const zen = fakeBrowser();
const b = await bridgeFor({ profilesRoot: root, data, browser: zen.exe });
try {
await assert.rejects(b.ensureConnected(300), (e: unknown) => e instanceof BrowserUnavailable && e.starting);
await wait(300);
assert.equal(zen.ran(), true);
assert.ok(Date.now() - lastLaunchAt(data) < 10_000, "the start is recorded");
} finally {
b.close();
}
});
test("Zen is looked for in its standard folders even when LM Studio leaves out the usual variables", () => {
const home = "C:\\Users\\someone";
const found = browserCandidates("win32", {}, home);
assert.ok(found.includes("C:\\Program Files\\Zen Browser\\zen.exe"));
assert.ok(found.includes("C:\\Users\\someone\\AppData\\Local\\Programs\\Zen Browser\\zen.exe"));
const set = browserCandidates("win32", { ProgramFiles: "D:\\Apps", LOCALAPPDATA: "E:\\Local" }, home);
assert.equal(set[0], "D:\\Apps\\Zen Browser\\zen.exe");
assert.ok(set.includes("E:\\Local\\Programs\\Zen Browser\\zen.exe"));
assert.equal(windowsFolders({}, home).appData.replace(/\//g, "\\"), "C:\\Users\\someone\\AppData\\Roaming");
});
test("a call soon after one that gave up waiting fails within seconds, for the same reason", { skip: process.platform === "win32" }, async () => {
const { root } = profilesRoot(process.pid);
const b = await bridgeFor({ profilesRoot: root, data: tmp("ss-data-"), browser: fakeBrowser().exe });
try {
await assert.rejects(b.ensureConnected(300), /did not connect/);
const started = Date.now();
await assert.rejects(b.ensureConnected(40_000), (e: unknown) => /^Still not connected\. The "llm-browser" profile is open/.test((e as Error).message));
assert.ok(Date.now() - started < 5000, "the second call did not wait the full time");
} finally {
b.close();
}
});
// Starting the browser: never a second copy when the profile is already open, and never twice
// within the cooldown, even from a server LM Studio restarted in between.
import { test } from "node:test";
import assert from "node:assert/strict";
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import { Bridge, BrowserUnavailable } from "../src/bridge";
import { browserCandidates, mcpHost, windowsFolders, type Config } from "../src/config";
import { lastLaunchAt, profileDir, profileInUse, recordLaunch } from "../src/profile";
const tmp = (p: string) => fs.mkdtempSync(path.join(os.tmpdir(), p));
function profilesRoot(lockPid: number | null): { root: string; dir: string } {
const root = tmp("ss-profiles-");
fs.writeFileSync(
path.join(root, "profiles.ini"),
"[Profile1]\nName=default-release\nIsRelative=1\nPath=Profiles/abc.default\n\n[Profile0]\nName=llm-browser\nIsRelative=1\nPath=Profiles/u86w.llm-browser\nDefault=1\n",
);
const dir = path.join(root, "Profiles", "u86w.llm-browser");
fs.mkdirSync(dir, { recursive: true });
if (lockPid !== null) fs.symlinkSync(`127.0.0.1:+${lockPid}`, path.join(dir, "lock"));
return { root, dir };
}
test("the profile folder is found by name in profiles.ini", () => {
const { root, dir } = profilesRoot(null);
assert.equal(profileDir("llm-browser", { SIMPLESEARCH_PROFILES_DIR: root }), dir);
assert.equal(profileDir("missing", { SIMPLESEARCH_PROFILES_DIR: root }), null);
});
test("an open profile is detected from its lock", { skip: process.platform === "win32" }, () => {
assert.equal(profileInUse(profilesRoot(process.pid).dir), true, "a live owner holds it");
assert.equal(profileInUse(profilesRoot(null).dir), false, "no lock, not open");
assert.equal(profileInUse(profilesRoot(2 ** 22 - 3).dir), false, "a stale lock from a dead process");
});
test("launches are recorded for every copy of the server", () => {
const data = tmp("ss-data-");
assert.equal(lastLaunchAt(data), 0);
recordLaunch(data, 12345);
assert.equal(lastLaunchAt(data), 12345);
});
async function bridgeFor(opts: { profilesRoot: string; data: string; browser: string }): Promise<Bridge> {
process.env.SIMPLESEARCH_PROFILES_DIR = opts.profilesRoot;
const cfg = {
host: mcpHost("simplesearch.js"),
secret: "c0ffee".repeat(10) + "abcd",
port: 20000 + Math.floor(Math.random() * 20000),
browserPath: opts.browser,
browserPathSource: "env",
profile: "llm-browser",
engines: ["duckduckgo"],
ruleUpdates: "approve",
dataDir: opts.data,
researchBudget: 24000,
researchReads: 6,
enforceBudget: true,
problems: [],
} as unknown as Config;
const b = new Bridge(cfg, () => {});
await b.start();
return b;
}
function fakeBrowser(): { exe: string; ran: () => boolean } {
const dir = tmp("ss-fakezen-");
const marker = path.join(dir, "ran");
const exe = path.join(dir, "zen");
fs.writeFileSync(exe, `#!/bin/sh\ntouch "${marker}"\n`, { mode: 0o755 });
return { exe, ran: () => fs.existsSync(marker) };
}
const wait = (ms: number) => new Promise((r) => setTimeout(r, ms));
test("an already open profile is waited for, not started again", { skip: process.platform === "win32" }, async () => {
const { root } = profilesRoot(process.pid);
const zen = fakeBrowser();
const b = await bridgeFor({ profilesRoot: root, data: tmp("ss-data-"), browser: zen.exe });
try {
await assert.rejects(b.ensureConnected(300), (e: unknown) => e instanceof BrowserUnavailable && /profile is open, but the simpleSearch Bridge extension/.test((e as Error).message));
await wait(300);
assert.equal(zen.ran(), false);
} finally {
b.close();
}
});
test("a browser started moments ago by another copy of the server is not started again", { skip: process.platform === "win32" }, async () => {
const { root } = profilesRoot(null);
const data = tmp("ss-data-");
recordLaunch(data, Date.now() - 5000);
const zen = fakeBrowser();
const b = await bridgeFor({ profilesRoot: root, data, browser: zen.exe });
try {
await assert.rejects(b.ensureConnected(300), (e: unknown) => e instanceof BrowserUnavailable && e.starting && /still loading/.test((e as Error).message));
await wait(300);
assert.equal(zen.ran(), false);
} finally {
b.close();
}
});
test("a closed profile with no recent start is started, once", { skip: process.platform === "win32" }, async () => {
const { root } = profilesRoot(null);
const data = tmp("ss-data-");
const zen = fakeBrowser();
const b = await bridgeFor({ profilesRoot: root, data, browser: zen.exe });
try {
await assert.rejects(b.ensureConnected(300), (e: unknown) => e instanceof BrowserUnavailable && e.starting);
await wait(300);
assert.equal(zen.ran(), true);
assert.ok(Date.now() - lastLaunchAt(data) < 10_000, "the start is recorded");
} finally {
b.close();
}
});
test("Zen is looked for in its standard folders even when LM Studio leaves out the usual variables", () => {
const home = "C:\\Users\\someone";
const found = browserCandidates("win32", {}, home);
assert.ok(found.includes("C:\\Program Files\\Zen Browser\\zen.exe"));
assert.ok(found.includes("C:\\Users\\someone\\AppData\\Local\\Programs\\Zen Browser\\zen.exe"));
const set = browserCandidates("win32", { ProgramFiles: "D:\\Apps", LOCALAPPDATA: "E:\\Local" }, home);
assert.equal(set[0], "D:\\Apps\\Zen Browser\\zen.exe");
assert.ok(set.includes("E:\\Local\\Programs\\Zen Browser\\zen.exe"));
assert.equal(windowsFolders({}, home).appData.replace(/\//g, "\\"), "C:\\Users\\someone\\AppData\\Roaming");
});
test("a call soon after one that gave up waiting fails within seconds, for the same reason", { skip: process.platform === "win32" }, async () => {
const { root } = profilesRoot(process.pid);
const b = await bridgeFor({ profilesRoot: root, data: tmp("ss-data-"), browser: fakeBrowser().exe });
try {
await assert.rejects(b.ensureConnected(300), /did not connect/);
const started = Date.now();
await assert.rejects(b.ensureConnected(40_000), (e: unknown) => /^Still not connected\. The "llm-browser" profile is open/.test((e as Error).message));
assert.ok(Date.now() - started < 5000, "the second call did not wait the full time");
} finally {
b.close();
}
});