Files
Elydora-Open-Source/sdks/node/test/qwen-plugin.test.mjs
T

399 lines
13 KiB
JavaScript

import assert from "node:assert/strict";
import { mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import test from "node:test";
import {
childEnv,
cliPath,
createFixture,
expectedShell,
generatedCommand,
managedHandler,
parseSettings,
registryModuleUrl,
runHook,
runNode,
runPlugin,
} from "../test-support/qwen.mjs";
test("Qwen Code is registered in the SDK and CLI", async () => {
const { SUPPORTED_AGENTS } = await import(registryModuleUrl);
assert.deepEqual(SUPPORTED_AGENTS.get("qwen"), {
name: "Qwen Code",
configDir: "~/.qwen",
configFile: "settings.json",
});
const fixture = await createFixture();
try {
const result = await runNode(
["--no-warnings", cliPath, "status"],
childEnv(fixture),
"",
fixture.workspaceDir,
);
assert.equal(result.code, 0, result.stderr);
assert.match(result.stdout, /Qwen Code \(qwen\)/);
} finally {
await fixture.close();
}
});
test("Qwen install preserves JSONC settings and is idempotent", async () => {
const existing = [
"{",
" // Keep this user preference.",
' "theme": "GitHub",',
' "hooks": {',
' "SessionStart": [{ "hooks": [{ "type": "command", "command": "session-hook" }] }],',
' "PreToolUse": [{ "matcher": "read_file", "hooks": [{ "type": "command", "command": "user-hook" }] }]',
" }",
"}",
"",
].join("\r\n");
const fixture = await createFixture({ existingSettings: existing });
const workspaceSettings = path.join(
fixture.workspaceDir,
".qwen",
"settings.json",
);
await mkdir(path.dirname(workspaceSettings), { recursive: true });
await writeFile(workspaceSettings, '{ "owner": "workspace" }\n');
try {
const first = await fixture.install();
assert.equal(first.code, 0, first.stderr);
assert.match(first.stdout, /run \/hooks/i);
const second = await fixture.install();
assert.equal(second.code, 0, second.stderr);
const raw = await readFile(fixture.configPath, "utf-8");
const settings = parseSettings(raw);
assert.match(raw, /Keep this user preference/);
assert.match(raw, /\r\n/);
assert.equal(settings.theme, "GitHub");
assert.equal(
settings.hooks.SessionStart[0].hooks[0].command,
"session-hook",
);
assert.equal(settings.hooks.PreToolUse.length, 2);
assert.equal(settings.hooks.PostToolUse.length, 1);
for (const [event, scriptPath] of [
["PreToolUse", fixture.guardScriptPath],
["PostToolUse", fixture.hookScriptPath],
]) {
const handler = managedHandler(settings, event, scriptPath);
assert.deepEqual(Object.keys(handler).sort(), [
"command",
"shell",
"timeout",
"type",
]);
assert.equal(handler.type, "command");
assert.equal(handler.shell, expectedShell);
assert.equal(handler.timeout, 10_000);
}
assert.equal(
await readFile(workspaceSettings, "utf-8"),
'{ "owner": "workspace" }\n',
);
} finally {
await fixture.close();
}
});
test("Qwen resolves QWEN_HOME with official user env precedence", async () => {
const fixture = await createFixture();
const firstHome = path.join(fixture.rootDir, "first qwen home");
const secondHome = path.join(fixture.rootDir, "second qwen home");
await mkdir(path.join(fixture.homeDir, ".qwen"), { recursive: true });
await writeFile(
path.join(fixture.homeDir, ".qwen", ".env"),
`QWEN_HOME=${firstHome}\n`,
);
await writeFile(
path.join(fixture.homeDir, ".env"),
`QWEN_HOME=${secondHome}\n`,
);
try {
const result = await fixture.install();
assert.equal(result.code, 0, result.stderr);
const selected = path.join(firstHome, "settings.json");
assert.ok(
managedHandler(
parseSettings(await readFile(selected, "utf-8")),
"PreToolUse",
fixture.guardScriptPath,
),
);
await assert.rejects(readFile(path.join(secondHome, "settings.json")), {
code: "ENOENT",
});
await assert.rejects(readFile(fixture.configPath), { code: "ENOENT" });
const status = await runPlugin(fixture, "status", null);
assert.equal(JSON.parse(status.stdout).configPath, selected);
} finally {
await fixture.close();
}
});
test("Qwen resolves explicit relative, tilde, and empty QWEN_HOME values", async () => {
for (const [value, expectedPath] of [
[
"relative-qwen",
(fixture) =>
path.join(fixture.workspaceDir, "relative-qwen", "settings.json"),
],
[
"~/custom-qwen",
(fixture) => path.join(fixture.homeDir, "custom-qwen", "settings.json"),
],
["", (fixture) => fixture.configPath],
]) {
const fixture = await createFixture();
await mkdir(path.join(fixture.homeDir, ".qwen"), { recursive: true });
await writeFile(
path.join(fixture.homeDir, ".qwen", ".env"),
`QWEN_HOME=${path.join(fixture.rootDir, "ignored-qwen-home")}\n`,
);
try {
const result = await fixture.install({ QWEN_HOME: value });
assert.equal(result.code, 0, result.stderr);
assert.ok(
parseSettings(await readFile(expectedPath(fixture), "utf-8")).hooks,
);
} finally {
await fixture.close();
}
}
});
test("Qwen hooks block freezes and forward official input", async () => {
const capturePath = path.join(
os.tmpdir(),
`elydora-qwen-event-${process.pid}-${Date.now()}.json`,
);
const hookSource = `
const fs = require('node:fs');
const chunks = [];
process.stdin.on('data', (chunk) => chunks.push(chunk));
process.stdin.on('end', () => fs.writeFileSync(${JSON.stringify(capturePath)}, Buffer.concat(chunks)));
`;
const fixture = await createFixture({ hookSource });
try {
const installed = await fixture.install();
assert.equal(installed.code, 0, installed.stderr);
const settings = parseSettings(await readFile(fixture.configPath, "utf-8"));
const guard = managedHandler(
settings,
"PreToolUse",
fixture.guardScriptPath,
);
const audit = managedHandler(
settings,
"PostToolUse",
fixture.hookScriptPath,
);
const preInput = JSON.stringify({
session_id: "session-1",
transcript_path: "transcript.jsonl",
cwd: fixture.workspaceDir,
hook_event_name: "PreToolUse",
timestamp: "2026-07-19T00:00:00.000Z",
tool_name: "run_shell_command",
tool_input: { command: "echo test" },
});
const guardResult = await runHook(guard, preInput);
assert.equal(guardResult.code, 2, guardResult.stderr);
assert.match(guardResult.stderr, /Agent is frozen by Elydora/);
const postInput = preInput.replace("PreToolUse", "PostToolUse");
const auditResult = await runHook(audit, postInput);
assert.equal(auditResult.code, 0, auditResult.stderr);
assert.equal(await readFile(capturePath, "utf-8"), postInput);
} finally {
await fixture.close();
await rm(capturePath, { force: true });
}
});
test("Qwen status requires enabled hooks and complete runtimes", async () => {
const fixture = await createFixture();
try {
assert.equal((await fixture.install()).code, 0);
let status = JSON.parse((await runPlugin(fixture, "status", null)).stdout);
assert.equal(status.installed, true);
const settings = parseSettings(await readFile(fixture.configPath, "utf-8"));
await writeFile(
fixture.configPath,
`${JSON.stringify({ ...settings, disableAllHooks: true }, null, 2)}\n`,
);
status = JSON.parse((await runPlugin(fixture, "status", null)).stdout);
assert.equal(status.installed, false);
assert.equal(status.hookConfigured, false);
await writeFile(
fixture.configPath,
`${JSON.stringify({ ...settings, disableAllHooks: false }, null, 2)}\n`,
);
await rm(fixture.guardScriptPath);
status = JSON.parse((await runPlugin(fixture, "status", null)).stdout);
assert.equal(status.installed, false);
assert.equal(status.hookConfigured, true);
assert.equal(status.hookScriptExists, false);
} finally {
await fixture.close();
}
});
test("Qwen uninstall removes exact ownership and preserves user hooks", async () => {
const fixture = await createFixture({
existingSettings: { $version: 4, owner: "user" },
});
try {
assert.equal((await fixture.install()).code, 0);
const settings = parseSettings(await readFile(fixture.configPath, "utf-8"));
const managed = settings.hooks.PreToolUse.at(-1);
managed.hooks.push({ type: "command", command: "user-command" });
settings.hooks.PreToolUse.push({
matcher: "*",
hooks: [
{
type: "command",
command: generatedCommand(
path.join(fixture.homeDir, ".elydora", "agent-10", "guard.js"),
),
shell: expectedShell,
timeout: 10_000,
},
],
});
await writeFile(
fixture.configPath,
`${JSON.stringify(settings, null, 2)}\n`,
);
const before = await readFile(fixture.configPath, "utf-8");
assert.equal(
(await runPlugin(fixture, "uninstall", "other-agent")).code,
0,
);
assert.equal(await readFile(fixture.configPath, "utf-8"), before);
const uninstallId = process.platform === "win32" ? "AGENT-1" : "agent-1";
const result = await runPlugin(fixture, "uninstall", uninstallId);
assert.equal(result.code, 0, result.stderr);
const remaining = parseSettings(
await readFile(fixture.configPath, "utf-8"),
);
assert.equal(remaining.$version, 4);
assert.equal(remaining.owner, "user");
assert.equal(remaining.hooks.PreToolUse.length, 2);
assert.equal(
remaining.hooks.PreToolUse[0].hooks[0].command,
"user-command",
);
assert.match(JSON.stringify(remaining), /agent-10/);
assert.equal(remaining.hooks.PostToolUse, undefined);
} finally {
await fixture.close();
}
});
test("Qwen removes an Elydora-owned settings file on uninstall", async () => {
const fixture = await createFixture();
try {
assert.equal((await fixture.install()).code, 0);
assert.match(
await readFile(fixture.configPath, "utf-8"),
/^\/\/ Managed by Elydora/,
);
const result = await runPlugin(fixture, "uninstall", "agent-1");
assert.equal(result.code, 0, result.stderr);
await assert.rejects(readFile(fixture.configPath), { code: "ENOENT" });
} finally {
await fixture.close();
}
});
test("Qwen rejects malformed settings before writes", async () => {
const cases = [
"{ malformed",
"[]",
'{ "owner": true, }',
'{ "hooks": {}, "hooks": {} }',
'{ "disableAllHooks": "yes" }',
'{ "hooks": [] }',
'{ "hooks": { "UnknownEvent": [] } }',
'{ "hooks": { "PreToolUse": null } }',
'{ "hooks": { "PreToolUse": [null] } }',
'{ "hooks": { "PreToolUse": [{ "matcher": "[", "hooks": [] }] } }',
'{ "hooks": { "PreToolUse": [{ "sequential": "yes", "hooks": [] }] } }',
'{ "hooks": { "PreToolUse": [{ "hooks": null }] } }',
'{ "hooks": { "PreToolUse": [{ "hooks": [{ "type": "command" }] }] } }',
'{ "hooks": { "PreToolUse": [{ "hooks": [{ "type": "http" }] }] } }',
'{ "hooks": { "PreToolUse": [{ "hooks": [{ "type": "function", "command": "x" }] }] } }',
'{ "hooks": { "PreToolUse": [{ "hooks": [{ "type": "command", "command": "x", "timeout": "ten" }] }] } }',
];
for (const existingSettings of cases) {
const fixture = await createFixture({ existingSettings });
try {
const result = await fixture.install();
assert.equal(result.code, 1, `${existingSettings}\n${result.stderr}`);
assert.match(result.stderr, /Qwen (Code settings|hooks)/i);
assert.equal(
await readFile(fixture.configPath, "utf-8"),
existingSettings,
);
const names = await readdir(path.dirname(fixture.configPath));
assert.equal(
names.some((name) => name.endsWith(".tmp")),
false,
);
} finally {
await fixture.close();
}
}
});
test("Qwen fails on unreadable home env routing and missing runtimes", async () => {
const envFixture = await createFixture();
await mkdir(path.join(envFixture.homeDir, ".qwen", ".env"), {
recursive: true,
});
try {
const result = await envFixture.install();
assert.equal(result.code, 1);
assert.match(result.stderr, /Qwen home environment/i);
await assert.rejects(readFile(envFixture.configPath), { code: "ENOENT" });
} finally {
await envFixture.close();
}
const runtimeFixture = await createFixture();
await rm(runtimeFixture.guardScriptPath);
try {
const result = await runtimeFixture.install();
assert.equal(result.code, 1);
assert.match(result.stderr, /runtime is missing/i);
await assert.rejects(readFile(runtimeFixture.configPath), {
code: "ENOENT",
});
} finally {
await runtimeFixture.close();
}
});
test("Qwen status surfaces malformed runtime metadata and leaves no staging files", async () => {
const fixture = await createFixture();
try {
assert.equal((await fixture.install()).code, 0);
assert.equal(
(await readdir(path.dirname(fixture.configPath))).some((name) =>
name.endsWith(".tmp"),
),
false,
);
await writeFile(path.join(fixture.agentDir, "config.json"), "{ malformed");
const status = await runPlugin(fixture, "status", null);
assert.equal(status.code, 1);
assert.match(status.stderr, /parse Elydora runtime config/i);
} finally {
await fixture.close();
}
});