Files
Elydora-Node-SDK/test-support/qwen.mjs
T

189 lines
5.4 KiB
JavaScript

import assert from "node:assert/strict";
import { spawn } from "node:child_process";
import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { parse } from "jsonc-parser";
const pluginModuleUrl = pathToFileURL(
path.resolve("dist/plugins/qwen.js"),
).href;
export const registryModuleUrl = pathToFileURL(
path.resolve("dist/plugins/registry.js"),
).href;
export const cliPath = path.resolve("dist/cli.js");
export const expectedShell =
process.platform === "win32" ? "powershell" : "bash";
export function runNode(args, env, input = "", cwd) {
return new Promise((resolve, reject) => {
const child = spawn(process.execPath, args, {
cwd,
env,
stdio: ["pipe", "pipe", "pipe"],
});
let stdout = "";
let stderr = "";
child.stdout.on("data", (chunk) => {
stdout += chunk;
});
child.stderr.on("data", (chunk) => {
stderr += chunk;
});
child.once("error", reject);
child.once("close", (code) => resolve({ code, stdout, stderr }));
child.stdin.end(input);
});
}
export function childEnv(fixture, overrides = {}) {
const env = {
...process.env,
HOME: fixture.homeDir,
USERPROFILE: fixture.homeDir,
...overrides,
};
if (!Object.hasOwn(overrides, "QWEN_HOME")) delete env.QWEN_HOME;
for (const [key, value] of Object.entries(env)) {
if (value === undefined) delete env[key];
}
return env;
}
export function runPlugin(fixture, method, argument, overrides = {}) {
const script = `
import { qwenPlugin } from ${JSON.stringify(pluginModuleUrl)};
const argument = JSON.parse(process.env.ELYDORA_TEST_ARGUMENT);
const result = await qwenPlugin[process.env.ELYDORA_TEST_METHOD](argument);
if (result !== undefined) console.log(JSON.stringify(result));
`;
return runNode(
["--input-type=module", "--eval", script],
childEnv(fixture, {
...overrides,
ELYDORA_TEST_ARGUMENT: JSON.stringify(argument),
ELYDORA_TEST_METHOD: method,
}),
"",
fixture.workspaceDir,
);
}
function quoteShell(value) {
return process.platform === "win32"
? `'${value.replaceAll("'", "''")}'`
: `'${value.replaceAll("'", `'"'"'`)}'`;
}
export function generatedCommand(scriptPath) {
const invocation = `${quoteShell(process.execPath)} ${quoteShell(scriptPath)}`;
return process.platform === "win32"
? `& ${invocation}; exit $LASTEXITCODE`
: invocation;
}
export function runHook(handler, input) {
const command = process.platform === "win32" ? "powershell" : "bash";
const args =
process.platform === "win32"
? ["-NoProfile", "-NonInteractive", "-Command", handler.command]
: ["-c", handler.command];
return new Promise((resolve, reject) => {
const child = spawn(command, args, { stdio: ["pipe", "pipe", "pipe"] });
let stdout = "";
let stderr = "";
child.stdout.on("data", (chunk) => {
stdout += chunk;
});
child.stderr.on("data", (chunk) => {
stderr += chunk;
});
child.once("error", reject);
child.once("close", (code) => resolve({ code, stdout, stderr }));
child.stdin.end(input);
});
}
export function parseSettings(raw) {
const errors = [];
const value = parse(raw, errors, {
allowTrailingComma: false,
disallowComments: false,
});
assert.deepEqual(errors, []);
return value;
}
export function managedHandler(settings, event, scriptPath) {
const command = generatedCommand(scriptPath);
for (const group of settings.hooks?.[event] ?? []) {
const handler = group.hooks.find(
(candidate) => candidate.command === command,
);
if (handler) return handler;
}
return undefined;
}
export async function createFixture({
existingSettings,
guardSource,
hookSource,
} = {}) {
const rootDir = await mkdtemp(path.join(os.tmpdir(), "elydora-qwen-"));
const homeDir = path.join(rootDir, "home with spaces and 'quote");
const workspaceDir = path.join(homeDir, "workspace");
const agentDir = path.join(homeDir, ".elydora", "agent-1");
const configPath = path.join(homeDir, ".qwen", "settings.json");
const guardScriptPath = path.join(agentDir, "guard.js");
const hookScriptPath = path.join(agentDir, "hook.js");
await mkdir(workspaceDir, { recursive: true });
await mkdir(agentDir, { recursive: true });
await writeFile(
guardScriptPath,
guardSource ??
"process.stderr.write('Agent is frozen by Elydora.'); process.exit(2);\n",
);
await writeFile(hookScriptPath, hookSource ?? "process.exit(0);\n");
await writeFile(
path.join(agentDir, "config.json"),
JSON.stringify({ agent_id: "agent-1", agent_name: "qwen" }),
);
if (existingSettings !== undefined) {
await mkdir(path.dirname(configPath), { recursive: true });
await writeFile(
configPath,
typeof existingSettings === "string"
? existingSettings
: `${JSON.stringify(existingSettings, null, 2)}\n`,
);
}
return {
agentDir,
configPath,
guardScriptPath,
homeDir,
hookScriptPath,
rootDir,
workspaceDir,
async install(overrides = {}) {
return runPlugin(
this,
"install",
{
agentName: "qwen",
agentId: "agent-1",
guardScriptPath,
hookScriptPath,
},
overrides,
);
},
async close() {
await rm(rootDir, { recursive: true, force: true });
},
};
}