142 lines
4.3 KiB
TypeScript
142 lines
4.3 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
detectNpmInstallation,
|
|
runUpgrade,
|
|
type UpgradeHost,
|
|
type UpgradeOutput,
|
|
type NpmInstallationHost,
|
|
} from "../src/application/Upgrade.js";
|
|
|
|
class FakeUpgradeHost implements UpgradeHost {
|
|
latest: string | undefined = "0.6.0";
|
|
installationResult: { readonly prefix: string } | undefined = {
|
|
prefix: "/fixture",
|
|
};
|
|
installSucceeds = true;
|
|
installs = 0;
|
|
output: UpgradeOutput | undefined;
|
|
|
|
latestVersion = (): Promise<string | undefined> =>
|
|
Promise.resolve(this.latest);
|
|
installation = (): Promise<{ readonly prefix: string } | undefined> =>
|
|
Promise.resolve(this.installationResult);
|
|
installLatest = (
|
|
_installation: { readonly prefix: string },
|
|
output: UpgradeOutput,
|
|
): Promise<boolean> => {
|
|
this.installs += 1;
|
|
this.output = output;
|
|
return Promise.resolve(this.installSucceeds);
|
|
};
|
|
}
|
|
|
|
describe("CLI upgrade", () => {
|
|
it("reports an already-current release without mutation", async () => {
|
|
const host = new FakeUpgradeHost();
|
|
|
|
expect(await runUpgrade("0.6.0", host)).toEqual({
|
|
status: "current",
|
|
currentVersion: "0.6.0",
|
|
latestVersion: "0.6.0",
|
|
installMethod: "npm",
|
|
});
|
|
expect(host.installs).toBe(0);
|
|
});
|
|
|
|
it("upgrades an outdated global npm installation", async () => {
|
|
const host = new FakeUpgradeHost();
|
|
|
|
expect(await runUpgrade("0.5.0", host)).toEqual({
|
|
status: "upgraded",
|
|
previousVersion: "0.5.0",
|
|
latestVersion: "0.6.0",
|
|
versionCheck: "available",
|
|
installMethod: "npm",
|
|
command: "npm install --global rea-agents@latest",
|
|
clientRestartRequired: true,
|
|
remediation:
|
|
"Rerun rea setup to refresh registrations and the skill, then restart clients that may retain an older MCP server.",
|
|
});
|
|
expect(host.installs).toBe(1);
|
|
});
|
|
|
|
it("continues with npm latest when the registry check is unavailable", async () => {
|
|
const host = new FakeUpgradeHost();
|
|
host.latest = undefined;
|
|
|
|
expect(await runUpgrade("0.5.0", host)).toMatchObject({
|
|
status: "upgraded",
|
|
latestVersion: null,
|
|
versionCheck: "unavailable",
|
|
});
|
|
expect(host.installs).toBe(1);
|
|
});
|
|
|
|
it("preserves stdout for an explicitly structured CLI response", async () => {
|
|
const host = new FakeUpgradeHost();
|
|
|
|
await runUpgrade("0.5.0", host, "structured");
|
|
|
|
expect(host.output).toBe("structured");
|
|
});
|
|
|
|
it("refuses to mutate an unrelated installation", async () => {
|
|
const host = new FakeUpgradeHost();
|
|
host.installationResult = undefined;
|
|
|
|
expect(await runUpgrade("0.5.0", host)).toMatchObject({
|
|
status: "failed",
|
|
reason: "unknown-install-method",
|
|
remediation:
|
|
"Update manually with: npm install --global rea-agents@latest",
|
|
});
|
|
expect(host.installs).toBe(0);
|
|
});
|
|
|
|
it("reports npm installation failure", async () => {
|
|
const host = new FakeUpgradeHost();
|
|
host.installSucceeds = false;
|
|
|
|
expect(await runUpgrade("0.5.0", host)).toMatchObject({
|
|
status: "failed",
|
|
reason: "install",
|
|
remediation:
|
|
"REA could not update through npm. Check npm registry access and global install permissions, then run: npm install --global rea-agents@latest",
|
|
});
|
|
expect(host.installs).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe("npm installation detection", () => {
|
|
const host = (root: string, prefix = "/usr/local"): NpmInstallationHost => ({
|
|
canonicalPath: (path) => Promise.resolve(path),
|
|
globalRoot: () => Promise.resolve(root),
|
|
globalPrefix: () => Promise.resolve(prefix),
|
|
});
|
|
|
|
it("returns the prefix for the global package that owns the running CLI", async () => {
|
|
await expect(
|
|
detectNpmInstallation(
|
|
"/usr/local/lib/node_modules/rea-agents",
|
|
host("/usr/local/lib/node_modules"),
|
|
),
|
|
).resolves.toEqual({ prefix: "/usr/local" });
|
|
});
|
|
|
|
it("infers the prefix used by the curl installer's custom npm layout", async () => {
|
|
await expect(
|
|
detectNpmInstallation(
|
|
"/home/user/.local/lib/node_modules/rea-agents",
|
|
host("/usr/local/lib/node_modules"),
|
|
),
|
|
).resolves.toEqual({ prefix: "/home/user/.local" });
|
|
});
|
|
|
|
it("rejects a source checkout when a different global copy exists", async () => {
|
|
await expect(
|
|
detectNpmInstallation("/work/rea", host("/usr/local/lib/node_modules")),
|
|
).resolves.toBeUndefined();
|
|
});
|
|
});
|