mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-12 19:42:54 +00:00
refactor(nodes): share default selection and tighten node.list fallback
This commit is contained in:
@@ -1,6 +1,14 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const gatewayMocks = vi.hoisted(() => ({
|
||||
callGatewayTool: vi.fn(),
|
||||
}));
|
||||
vi.mock("./gateway.js", () => ({
|
||||
callGatewayTool: (...args: unknown[]) => gatewayMocks.callGatewayTool(...args),
|
||||
}));
|
||||
|
||||
import type { NodeListNode } from "./nodes-utils.js";
|
||||
import { resolveNodeIdFromList } from "./nodes-utils.js";
|
||||
import { listNodes, resolveNodeIdFromList } from "./nodes-utils.js";
|
||||
|
||||
function node(overrides: Partial<NodeListNode> & { nodeId: string }): NodeListNode {
|
||||
return {
|
||||
@@ -11,14 +19,18 @@ function node(overrides: Partial<NodeListNode> & { nodeId: string }): NodeListNo
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
gatewayMocks.callGatewayTool.mockReset();
|
||||
});
|
||||
|
||||
describe("resolveNodeIdFromList defaults", () => {
|
||||
it("falls back to first connected canvas-capable node when multiple non-Mac candidates exist", () => {
|
||||
it("falls back to most recently connected node when multiple non-Mac candidates exist", () => {
|
||||
const nodes: NodeListNode[] = [
|
||||
node({ nodeId: "ios-1", platform: "ios" }),
|
||||
node({ nodeId: "android-1", platform: "android" }),
|
||||
node({ nodeId: "ios-1", platform: "ios", connectedAtMs: 1 }),
|
||||
node({ nodeId: "android-1", platform: "android", connectedAtMs: 2 }),
|
||||
];
|
||||
|
||||
expect(resolveNodeIdFromList(nodes, undefined, true)).toBe("ios-1");
|
||||
expect(resolveNodeIdFromList(nodes, undefined, true)).toBe("android-1");
|
||||
});
|
||||
|
||||
it("preserves local Mac preference when exactly one local Mac candidate exists", () => {
|
||||
@@ -29,4 +41,45 @@ describe("resolveNodeIdFromList defaults", () => {
|
||||
|
||||
expect(resolveNodeIdFromList(nodes, undefined, true)).toBe("mac-1");
|
||||
});
|
||||
|
||||
it("uses stable nodeId ordering when connectedAtMs is unavailable", () => {
|
||||
const nodes: NodeListNode[] = [
|
||||
node({ nodeId: "z-node", platform: "ios", connectedAtMs: undefined }),
|
||||
node({ nodeId: "a-node", platform: "android", connectedAtMs: undefined }),
|
||||
];
|
||||
|
||||
expect(resolveNodeIdFromList(nodes, undefined, true)).toBe("a-node");
|
||||
});
|
||||
});
|
||||
|
||||
describe("listNodes", () => {
|
||||
it("falls back to node.pair.list only when node.list is unavailable", async () => {
|
||||
gatewayMocks.callGatewayTool
|
||||
.mockRejectedValueOnce(new Error("unknown method: node.list"))
|
||||
.mockResolvedValueOnce({
|
||||
pending: [],
|
||||
paired: [{ nodeId: "pair-1", displayName: "Pair 1", platform: "ios", remoteIp: "1.2.3.4" }],
|
||||
});
|
||||
|
||||
await expect(listNodes({})).resolves.toEqual([
|
||||
{
|
||||
nodeId: "pair-1",
|
||||
displayName: "Pair 1",
|
||||
platform: "ios",
|
||||
remoteIp: "1.2.3.4",
|
||||
},
|
||||
]);
|
||||
expect(gatewayMocks.callGatewayTool).toHaveBeenNthCalledWith(1, "node.list", {}, {});
|
||||
expect(gatewayMocks.callGatewayTool).toHaveBeenNthCalledWith(2, "node.pair.list", {}, {});
|
||||
});
|
||||
|
||||
it("rethrows unexpected node.list failures without fallback", async () => {
|
||||
gatewayMocks.callGatewayTool.mockRejectedValueOnce(
|
||||
new Error("gateway closed (1008): unauthorized"),
|
||||
);
|
||||
|
||||
await expect(listNodes({})).rejects.toThrow("gateway closed (1008): unauthorized");
|
||||
expect(gatewayMocks.callGatewayTool).toHaveBeenCalledTimes(1);
|
||||
expect(gatewayMocks.callGatewayTool).toHaveBeenCalledWith("node.list", {}, {});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user