mirror of
https://github.com/morluto/rea
synced 2026-10-25 22:08:11 -04:00
89f5e489ad
Replay the complete real Apple dylib-use output through the native provider boundary and retain rejection of an empty repeated options field. Align skill-install Result assertions and CDP initiator prerequisites with current contracts. The producing parser fix is already retained from main. Refs #1970 Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
198 lines
7.3 KiB
TypeScript
198 lines
7.3 KiB
TypeScript
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
import { join, resolve } from "node:path";
|
|
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { parse as parseJsonc } from "jsonc-parser";
|
|
|
|
import { readClientRegistrationStatuses } from "../../../src/application/ClientRegistrationStatus.js";
|
|
import { configureClientConfiguration } from "../../../src/application/SetupClientConfiguration.js";
|
|
import { systemSetupHost } from "../../../src/application/SetupHost.js";
|
|
import { MANAGED_SKILL_FILES } from "../../../src/application/SetupSkill.js";
|
|
import { supportedClients } from "../../../src/application/SupportedClients.js";
|
|
import { systemUninstallHost } from "../../../src/application/Uninstall.js";
|
|
import { PRODUCT_IDENTITY } from "../../../src/identity.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
import {
|
|
clearClientLocationEnvironment,
|
|
NPX_REGISTRATION_COMMAND,
|
|
} from "../../fixtures/clientEnvironment.js";
|
|
|
|
const command = NPX_REGISTRATION_COMMAND;
|
|
|
|
beforeEach(clearClientLocationEnvironment);
|
|
afterEach(() => vi.unstubAllEnvs());
|
|
|
|
const qwenPath = (
|
|
home: string,
|
|
platform: NodeJS.Platform,
|
|
env: Parameters<typeof supportedClients>[2] = {},
|
|
) =>
|
|
supportedClients(home, platform, env).find(
|
|
({ name }) => name === "qwen_code",
|
|
);
|
|
|
|
describe("Qwen Code configuration paths", () => {
|
|
it("resolves the user settings file and marker directory", () => {
|
|
expect(qwenPath("/home/a", "linux")).toEqual({
|
|
name: "qwen_code",
|
|
displayName: "Qwen Code",
|
|
configPath: join("/home/a", ".qwen", "settings.json"),
|
|
markerPath: join("/home/a", ".qwen"),
|
|
format: "json",
|
|
});
|
|
expect(qwenPath("/Users/a", "darwin")?.configPath).toBe(
|
|
join("/Users/a", ".qwen", "settings.json"),
|
|
);
|
|
});
|
|
|
|
it.each([
|
|
["", join("/home/a", ".qwen")],
|
|
["~", "/home/a"],
|
|
["~/profiles/work", join("/home/a", "profiles", "work")],
|
|
["~\\profiles\\work", join("/home/a", "profiles", "work")],
|
|
["profiles/work", resolve("profiles/work")],
|
|
[" profile ", resolve(" profile ")],
|
|
["$PROFILE", resolve("$PROFILE")],
|
|
])("matches Qwen's QWEN_HOME resolution for %j", (override, directory) => {
|
|
vi.stubEnv("QWEN_HOME", override);
|
|
const client = qwenPath("/home/a", process.platform, {
|
|
QWEN_HOME: override,
|
|
});
|
|
expect(
|
|
supportedClients("/home/a").find(({ name }) => name === "qwen_code"),
|
|
).toEqual(client);
|
|
expect(client).toMatchObject({
|
|
configPath: join(directory, "settings.json"),
|
|
markerPath: directory,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Qwen Code client lifecycle", () => {
|
|
const qwenClient = async () => {
|
|
const home = await createTestTempDirectory("rea-qwen-");
|
|
const client = qwenPath(home, process.platform, {});
|
|
if (client?.markerPath === undefined)
|
|
throw new Error("missing Qwen Code client");
|
|
await mkdir(client.markerPath, { recursive: true });
|
|
return { home, client };
|
|
};
|
|
|
|
it("registers rea in mcpServers while preserving unrelated settings and comments", async () => {
|
|
const { home, client } = await qwenClient();
|
|
await writeFile(
|
|
client.configPath,
|
|
`{
|
|
// Keep this note.
|
|
"model": { "name": "qwen3-coder-plus" },
|
|
"mcpServers": { "other": { "command": "other-server" } }
|
|
}
|
|
`,
|
|
);
|
|
expect(
|
|
(await configureClientConfiguration(client, {}, command)).status,
|
|
).toBe("configured");
|
|
const text = await readFile(client.configPath, "utf8");
|
|
expect(text).toContain("// Keep this note.");
|
|
expect(parseJsonc(text)).toEqual({
|
|
model: { name: "qwen3-coder-plus" },
|
|
mcpServers: {
|
|
other: { command: "other-server" },
|
|
rea: { command: "npx", args: command.slice(1) },
|
|
},
|
|
});
|
|
expect(await configureClientConfiguration(client, {}, command)).toEqual({
|
|
status: "unchanged",
|
|
});
|
|
expect((await systemUninstallHost(home).removeClient(client)).status).toBe(
|
|
"removed",
|
|
);
|
|
const removedText = await readFile(client.configPath, "utf8");
|
|
const removed = parseJsonc(removedText);
|
|
expect(removed).toEqual({
|
|
model: { name: "qwen3-coder-plus" },
|
|
mcpServers: { other: { command: "other-server" } },
|
|
});
|
|
expect(removedText).toContain("// Keep this note.");
|
|
});
|
|
|
|
it("reports a package-runner registration as aligned", async () => {
|
|
const { home, client } = await qwenClient();
|
|
expect(
|
|
(await configureClientConfiguration(client, {}, command)).status,
|
|
).toBe("configured");
|
|
const entry = resolve("scripts/rea.mjs");
|
|
const status = (
|
|
await readClientRegistrationStatuses(home, entry, { environment: {} })
|
|
).find(({ client: name }) => name === "qwen_code");
|
|
expect(status).toMatchObject({ state: "aligned" });
|
|
});
|
|
});
|
|
|
|
describe("Qwen Code profile setup", () => {
|
|
it.each([false, true])(
|
|
"installs and removes the shared skill with a custom home: %s",
|
|
async (customHome) => {
|
|
const home = await createTestTempDirectory("rea-qwen-profile-");
|
|
const profile = join(home, customHome ? "qwen-profile" : ".qwen");
|
|
const environment = {
|
|
HOME: home,
|
|
USERPROFILE: home,
|
|
...(customHome ? { QWEN_HOME: profile } : {}),
|
|
};
|
|
await mkdir(profile);
|
|
const setup = systemSetupHost(undefined, environment);
|
|
const client = (await setup.detectedClients()).find(
|
|
({ name }) => name === "qwen_code",
|
|
);
|
|
if (client === undefined) throw new Error("missing Qwen Code client");
|
|
expect(client.configPath).toBe(join(profile, "settings.json"));
|
|
expect(await setup.configureClient(client, {}, command)).toEqual({
|
|
status: "configured",
|
|
});
|
|
expect(await setup.installSkill([client.name])).toEqual({
|
|
ok: true,
|
|
value: "installed",
|
|
});
|
|
const skill = join(home, ".agents", "skills", PRODUCT_IDENTITY.skillName);
|
|
await writeFile(join(skill, "user-notes.txt"), "user-authored");
|
|
expect(await readFile(join(skill, "SKILL.md"), "utf8")).toContain(
|
|
"reverse-engineer-anything",
|
|
);
|
|
expect(await setup.clientNeedsConfigure(client, {}, command)).toBe(false);
|
|
expect(await setup.skillNeedsInstall([client.name])).toBe(false);
|
|
expect(await setup.installSkill([client.name])).toEqual({
|
|
ok: true,
|
|
value: "unchanged",
|
|
});
|
|
expect(
|
|
(
|
|
await readClientRegistrationStatuses(
|
|
home,
|
|
resolve("scripts/rea.mjs"),
|
|
{ environment },
|
|
)
|
|
).find(({ client: name }) => name === "qwen_code"),
|
|
).toMatchObject({ state: "aligned" });
|
|
const uninstall = systemUninstallHost(home, undefined, environment);
|
|
const registered = (await uninstall.clients()).find(
|
|
({ name }) => name === "qwen_code",
|
|
);
|
|
expect(registered).toEqual(client);
|
|
expect((await uninstall.removeClient(client)).status).toBe("removed");
|
|
expect((await uninstall.removeSkill()).status).toBe("removed");
|
|
for (const relativePath of MANAGED_SKILL_FILES)
|
|
await expect(access(join(skill, relativePath))).rejects.toMatchObject({
|
|
code: "ENOENT",
|
|
});
|
|
expect(await readFile(join(skill, "user-notes.txt"), "utf8")).toBe(
|
|
"user-authored",
|
|
);
|
|
if (customHome)
|
|
await expect(access(join(home, ".qwen"))).rejects.toMatchObject({
|
|
code: "ENOENT",
|
|
});
|
|
},
|
|
);
|
|
});
|