fix(cli): keep nodes list aligned with nodes status (#72619)

* fix(cli): keep nodes list aligned with nodes status

* fix(clownfish): address review for ghcrawl-156588-autonomous-smoke (1)

* fix(cli): keep nodes list aligned with nodes status
This commit is contained in:
Vincent Koc
2026-04-27 02:39:33 -07:00
committed by GitHub
parent af03f9248d
commit a50edbdc60
4 changed files with 295 additions and 47 deletions

View File

@@ -62,6 +62,183 @@ describe("cli program (nodes basics)", () => {
program = createProgram();
});
it("runs nodes list with the effective paired node view while preserving paired metadata", async () => {
const now = Date.now();
callGateway.mockImplementation(async (...args: unknown[]) => {
const opts = (args[0] ?? {}) as { method?: string };
if (opts.method === "node.pair.list") {
return {
pending: [{ requestId: "r1", nodeId: "pending-node", ts: now - 10_000 }],
paired: [
{
nodeId: "paired-store",
displayName: "Stale paired name",
remoteIp: "10.0.0.1",
token: "paired-token",
lastConnectedAtMs: now - 5_000,
},
{
nodeId: "pair-only",
displayName: "Pair Only",
token: "pair-only-token",
},
],
};
}
if (opts.method === "node.list") {
return {
nodes: [
{
nodeId: "paired-store",
displayName: "Effective paired name",
remoteIp: "10.0.0.2",
connected: true,
connectedAtMs: now - 1_000,
},
{
nodeId: "catalog-only",
displayName: "Catalog Only",
remoteIp: "10.0.0.3",
paired: true,
connected: false,
},
{
nodeId: "effective-only-unknown",
displayName: "Effective Only Unknown",
connected: true,
},
{
nodeId: "unpaired-live",
displayName: "Unpaired Live",
paired: false,
connected: true,
},
],
};
}
return { ok: true };
});
await runProgram(["nodes", "list", "--json"]);
expect(callGateway).toHaveBeenCalledWith(expect.objectContaining({ method: "node.pair.list" }));
expect(callGateway).toHaveBeenCalledWith(expect.objectContaining({ method: "node.list" }));
expect(runtime.writeJson).toHaveBeenCalledWith({
pending: [{ requestId: "r1", nodeId: "pending-node", ts: now - 10_000 }],
paired: [
expect.objectContaining({
nodeId: "paired-store",
displayName: "Effective paired name",
remoteIp: "10.0.0.2",
lastConnectedAtMs: now - 5_000,
connected: true,
}),
expect.objectContaining({
nodeId: "catalog-only",
displayName: "Catalog Only",
paired: true,
}),
expect.objectContaining({
nodeId: "pair-only",
displayName: "Pair Only",
}),
],
});
expect(JSON.stringify(runtime.writeJson.mock.calls[0]?.[0])).not.toContain("paired-token");
expect(JSON.stringify(runtime.writeJson.mock.calls[0]?.[0])).not.toContain("pair-only-token");
const output = getRuntimeOutput();
expect(output).toContain("Pending: 1 · Paired: 3");
expect(output).not.toContain("Effective Only Unknown");
expect(output).not.toContain("unpaired-live");
});
it("runs unfiltered nodes list with pairing data when node.list is unavailable", async () => {
callGateway.mockImplementation(async (...args: unknown[]) => {
const opts = (args[0] ?? {}) as { method?: string };
if (opts.method === "node.pair.list") {
return {
pending: [],
paired: [
{
nodeId: "pairing-scoped",
displayName: "Pairing Scoped",
remoteIp: "10.0.0.9",
},
],
};
}
if (opts.method === "node.list") {
throw new Error("unauthorized");
}
return { ok: true };
});
await runProgram(["nodes", "list"]);
const output = getRuntimeOutput();
expect(output).toContain("Pending: 0 · Paired: 1");
expect(output).toContain("Pairing Scoped");
});
it("sanitizes untrusted nodes list table fields while preserving JSON values", async () => {
const now = Date.now();
callGateway.mockImplementation(async (...args: unknown[]) => {
const opts = (args[0] ?? {}) as { method?: string };
if (opts.method === "node.pair.list") {
return {
pending: [
{
requestId: "request\u001b[2K-1",
nodeId: "pending-node",
displayName: "Pending\u001b[1A\nNode",
remoteIp: "10.0.0.4\rrewritten",
ts: now - 1_000,
},
],
paired: [
{
nodeId: "paired-node",
displayName: "Paired\u001b[2K\nNode",
remoteIp: "10.0.0.5\rrewritten",
},
],
};
}
if (opts.method === "node.list") {
throw new Error("older gateway");
}
return { ok: true };
});
await runProgram(["nodes", "list"]);
const output = getRuntimeOutput();
expect(output).not.toContain("\u001b");
expect(output).not.toContain("[2K");
expect(output).toContain("Pending\\nNode");
expect(output).toContain("Paired\\nNode");
expect(output).toContain("10.0.0.5\\rrewritten");
runtime.log.mockClear();
await runProgram(["nodes", "list", "--json"]);
expect(runtime.writeJson).toHaveBeenCalledWith({
pending: [
expect.objectContaining({
requestId: "request\u001b[2K-1",
displayName: "Pending\u001b[1A\nNode",
}),
],
paired: [
expect.objectContaining({
nodeId: "paired-node",
displayName: "Paired\u001b[2K\nNode",
remoteIp: "10.0.0.5\rrewritten",
}),
],
});
});
it("runs nodes list --connected and filters to connected nodes", async () => {
const now = Date.now();
callGateway.mockImplementation(async (...args: unknown[]) => {