mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-23 09:42:53 +00:00
* fix(daemon): keep lifecycle operations on the selected instance Restart reconstructed launch inputs and address fallback could redirect a home-selected shutdown to another daemon. Let supervisors publish bound endpoints, retain their worker restart contract, and share captured local lifecycle ownership between CLI and Desktop. * test(daemon): verify bounded desktop readiness and cancellable artifacts * test(cli): verify reload target hints and durable offline pairing * test(daemon): align lifecycle fixtures with ownership and targeting * fix(daemon): preserve reachability when status details time out Provider discovery can outlast a status request after authentication succeeds. Keep local connection facts separate from unavailable details, while explicit remote queries retain their error outcome. Migrate CLI and packaged Desktop fixtures to persistent managed configuration and explicit targets. Isolate generic Git observation from forge discovery and reuse disposable CLI homes on Windows. * test(daemon): observe restart through the worker status RPC The bounded CLI status observation may omit worker details while an authenticated connection remains healthy. Exercise the app-style restart through its full RPC and retain the replacement observation, with a real slow provider probe covering the distinction.
174 lines
7.5 KiB
TypeScript
174 lines
7.5 KiB
TypeScript
#!/usr/bin/env npx tsx
|
|
import { resolveCliVersion } from "../../src/version.js";
|
|
import { readPluginManifest } from "../../../server/src/server/plugins/manifest.js";
|
|
|
|
import assert from "node:assert/strict";
|
|
import { execFile } from "node:child_process";
|
|
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import path from "node:path";
|
|
import { pathToFileURL } from "node:url";
|
|
import { promisify } from "node:util";
|
|
import { connectToDaemon } from "../../src/utils/client.ts";
|
|
import { createE2ETestContext } from "../helpers/test-daemon.ts";
|
|
|
|
const pluginSource = `export default function contribute(plugin: unknown) {
|
|
void plugin;
|
|
return () => undefined;
|
|
}`;
|
|
|
|
const execFileAsync = promisify(execFile);
|
|
|
|
async function git(cwd: string, args: string[]): Promise<void> {
|
|
await execFileAsync("git", args, { cwd });
|
|
}
|
|
|
|
async function main(): Promise<void> {
|
|
const directory = await mkdtemp(path.join(tmpdir(), "paseo-plugin-cli-e2e-"));
|
|
const gitDirectory = await mkdtemp(path.join(tmpdir(), "paseo-plugin-git-cli-e2e-"));
|
|
const context = await createE2ETestContext({ timeout: 45_000 });
|
|
try {
|
|
const scaffold = path.join(context.workDir, "authored-plugin");
|
|
const init = await context.paseo(["plugin", "init", scaffold, "--json"]);
|
|
assert.equal(init.exitCode, 0, init.stderr);
|
|
const manifestPath = path.join(scaffold, "paseo-plugin.json");
|
|
const manifest = await readPluginManifest(scaffold);
|
|
assert.deepEqual(manifest.requirements, { paseo: `>=${resolveCliVersion()}` });
|
|
await writeFile(
|
|
path.join(directory, "paseo-plugin.json"),
|
|
JSON.stringify({ id: "cli-e2e", requirements: { paseo: `>=${resolveCliVersion()}` } }),
|
|
);
|
|
await writeFile(path.join(directory, "index.server.ts"), pluginSource);
|
|
|
|
const install = await context.paseo(["plugin", "install", directory, "--json"]);
|
|
assert.equal(install.exitCode, 0, install.stderr);
|
|
assert.equal(JSON.parse(install.stdout).id, "cli-e2e");
|
|
|
|
const client = await connectToDaemon({
|
|
target: { kind: "endpoint", host: `127.0.0.1:${context.port}` },
|
|
});
|
|
await client.patchDaemonConfig({ pluginsEnabled: true });
|
|
await client.close();
|
|
|
|
await writeFile(
|
|
manifestPath,
|
|
JSON.stringify({ ...manifest, requirements: { paseo: ">=999.0.0" } }),
|
|
);
|
|
const incompatibleInstall = await context.paseo(["plugin", "install", scaffold, "--json"]);
|
|
assert.equal(incompatibleInstall.exitCode, 1);
|
|
assert.match(incompatibleInstall.stderr, /requires Paseo >=999.0.0/);
|
|
const afterRejection = await context.paseo(["plugin", "ls", "--json"]);
|
|
assert.equal(afterRejection.exitCode, 0, afterRejection.stderr);
|
|
assert.deepEqual(
|
|
JSON.parse(afterRejection.stdout).map((plugin: { id: string }) => plugin.id),
|
|
["cli-e2e"],
|
|
);
|
|
await writeFile(manifestPath, JSON.stringify(manifest));
|
|
const scaffoldInstall = await context.paseo(["plugin", "install", scaffold, "--json"]);
|
|
assert.equal(scaffoldInstall.exitCode, 0, scaffoldInstall.stderr);
|
|
assert.equal(JSON.parse(scaffoldInstall.stdout).status, "running");
|
|
|
|
await git(gitDirectory, ["init", "-b", "main"]);
|
|
await git(gitDirectory, ["config", "user.name", "Paseo Tests"]);
|
|
await git(gitDirectory, ["config", "user.email", "paseo@example.test"]);
|
|
await writeFile(
|
|
path.join(gitDirectory, "paseo-plugin.json"),
|
|
JSON.stringify({ id: "git-cli-e2e", requirements: { paseo: `>=${resolveCliVersion()}` } }),
|
|
);
|
|
await writeFile(path.join(gitDirectory, "index.server.ts"), pluginSource);
|
|
await git(gitDirectory, ["add", "-A"]);
|
|
await git(gitDirectory, ["commit", "-m", "initial"]);
|
|
|
|
const gitInstall = await context.paseo([
|
|
"plugin",
|
|
"add",
|
|
pathToFileURL(gitDirectory).href,
|
|
"--json",
|
|
]);
|
|
assert.equal(gitInstall.exitCode, 0, gitInstall.stderr);
|
|
assert.equal(JSON.parse(gitInstall.stdout).source, "git");
|
|
|
|
await writeFile(
|
|
path.join(gitDirectory, "index.server.ts"),
|
|
`${pluginSource}\nconst updated = true;\n`,
|
|
);
|
|
await git(gitDirectory, ["add", "-A"]);
|
|
await git(gitDirectory, ["commit", "-m", "update"]);
|
|
const status = await context.paseo(["plugin", "status", "git-cli-e2e", "--json"]);
|
|
assert.equal(status.exitCode, 0, status.stderr);
|
|
assert.equal(JSON.parse(status.stdout)[0].status, "running");
|
|
assert.equal(JSON.parse(status.stdout)[0].commit, JSON.parse(gitInstall.stdout).commit);
|
|
|
|
const update = await context.paseo(["plugin", "update", "git-cli-e2e", "--json"]);
|
|
assert.equal(update.exitCode, 0, update.stderr);
|
|
assert.equal(JSON.parse(update.stdout)[0].updated, true);
|
|
|
|
const installedCommit = JSON.parse(update.stdout)[0].currentCommit;
|
|
const buildMarker = path.join(context.workDir, "incompatible-build-ran");
|
|
await writeFile(
|
|
path.join(gitDirectory, "paseo-plugin.json"),
|
|
JSON.stringify({
|
|
id: "git-cli-e2e",
|
|
requirements: { paseo: ">=999.0.0" },
|
|
build: [
|
|
[
|
|
process.execPath,
|
|
"-e",
|
|
'require("node:fs").writeFileSync(process.argv[1], "ran")',
|
|
buildMarker,
|
|
],
|
|
],
|
|
}),
|
|
);
|
|
await git(gitDirectory, ["add", "-A"]);
|
|
await git(gitDirectory, ["commit", "-m", "requires a future Paseo"]);
|
|
const incompatibleUpdate = await context.paseo(["plugin", "update", "git-cli-e2e", "--json"]);
|
|
assert.equal(incompatibleUpdate.exitCode, 1);
|
|
assert.match(incompatibleUpdate.stderr, /requires Paseo >=999.0.0/);
|
|
await assert.rejects(readFile(buildMarker), { code: "ENOENT" });
|
|
const retained = await context.paseo(["plugin", "ls", "git-cli-e2e", "--json"]);
|
|
assert.equal(retained.exitCode, 0, retained.stderr);
|
|
assert.equal(JSON.parse(retained.stdout)[0].commit, installedCommit);
|
|
assert.equal(JSON.parse(retained.stdout)[0].status, "running");
|
|
const incompatibleAdd = await context.paseo([
|
|
"plugin",
|
|
"add",
|
|
pathToFileURL(gitDirectory).href,
|
|
"--id",
|
|
"future-plugin",
|
|
"--json",
|
|
]);
|
|
assert.equal(incompatibleAdd.exitCode, 1);
|
|
assert.match(incompatibleAdd.stderr, /requires Paseo >=999.0.0/);
|
|
await assert.rejects(readFile(buildMarker), { code: "ENOENT" });
|
|
|
|
const reload = await context.paseo(["plugin", "reload", "cli-e2e", "--json"]);
|
|
assert.equal(reload.exitCode, 0, reload.stderr);
|
|
assert.equal(JSON.parse(reload.stdout).status, "running");
|
|
|
|
const disable = await context.paseo(["plugin", "disable", "cli-e2e", "--json"]);
|
|
assert.equal(disable.exitCode, 0, disable.stderr);
|
|
assert.equal(JSON.parse(disable.stdout).status, "disabled");
|
|
|
|
const enable = await context.paseo(["plugin", "enable", "cli-e2e", "--json"]);
|
|
assert.equal(enable.exitCode, 0, enable.stderr);
|
|
assert.equal(JSON.parse(enable.stdout).status, "running");
|
|
|
|
const remove = await context.paseo(["plugin", "remove", "cli-e2e", "--json"]);
|
|
assert.equal(remove.exitCode, 0, remove.stderr);
|
|
const removeGit = await context.paseo(["plugin", "remove", "git-cli-e2e", "--json"]);
|
|
assert.equal(removeGit.exitCode, 0, removeGit.stderr);
|
|
const removeScaffold = await context.paseo(["plugin", "remove", "authored-plugin", "--json"]);
|
|
assert.equal(removeScaffold.exitCode, 0, removeScaffold.stderr);
|
|
const list = await context.paseo(["plugin", "ls", "--json"]);
|
|
assert.equal(list.exitCode, 0, list.stderr);
|
|
assert.deepEqual(JSON.parse(list.stdout), []);
|
|
} finally {
|
|
await context.stop();
|
|
await rm(directory, { recursive: true, force: true });
|
|
await rm(gitDirectory, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
await main();
|