mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-11 12:58:34 +00:00
test: tighten camera tool assertions
This commit is contained in:
@@ -65,6 +65,32 @@ async function executeNodes(
|
||||
type NodesToolResult = Awaited<ReturnType<typeof executeNodes>>;
|
||||
type GatewayMockResult = Record<string, unknown> | null | undefined;
|
||||
|
||||
function requireRecord(value: unknown, label: string): Record<string, unknown> {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
throw new Error(`expected ${label}`);
|
||||
}
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function expectInvokeParams(
|
||||
invokeParams: unknown,
|
||||
expected: {
|
||||
command: string;
|
||||
nodeId?: string;
|
||||
params?: Record<string, unknown>;
|
||||
},
|
||||
) {
|
||||
const record = requireRecord(invokeParams, "node.invoke params");
|
||||
expect(record.command).toBe(expected.command);
|
||||
if (expected.nodeId !== undefined) {
|
||||
expect(record.nodeId).toBe(expected.nodeId);
|
||||
}
|
||||
const params = requireRecord(record.params, `${expected.command} params`);
|
||||
for (const [key, value] of Object.entries(expected.params ?? {})) {
|
||||
expect(params[key]).toEqual(value);
|
||||
}
|
||||
}
|
||||
|
||||
function mockNodeList(params?: { commands?: string[]; remoteIp?: string }) {
|
||||
return {
|
||||
nodes: [
|
||||
@@ -98,15 +124,15 @@ function expectFirstMediaUrl(result: NodesToolResult): string {
|
||||
}
|
||||
|
||||
function expectFirstTextContains(result: NodesToolResult, expectedText: string) {
|
||||
expect(result.content?.[0]).toMatchObject({
|
||||
type: "text",
|
||||
text: expect.stringContaining(expectedText),
|
||||
});
|
||||
const first = result.content?.[0];
|
||||
expect(first?.type).toBe("text");
|
||||
const text = first?.type === "text" ? first.text : "";
|
||||
expect(text).toContain(expectedText);
|
||||
}
|
||||
|
||||
function parseFirstTextJson(result: NodesToolResult): unknown {
|
||||
const first = result.content?.[0];
|
||||
expect(first).toMatchObject({ type: "text" });
|
||||
expect(first?.type).toBe("text");
|
||||
const text = first?.type === "text" ? first.text : "";
|
||||
return JSON.parse(text);
|
||||
}
|
||||
@@ -138,7 +164,7 @@ function setupPhotosLatestMock(params?: { remoteIp?: string }) {
|
||||
setupNodeInvokeMock({
|
||||
...(params?.remoteIp ? { remoteIp: params.remoteIp } : {}),
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
expectInvokeParams(invokeParams, {
|
||||
command: "photos.latest",
|
||||
params: PHOTOS_LATEST_DEFAULT_PARAMS,
|
||||
});
|
||||
@@ -162,7 +188,7 @@ describe("nodes camera_snap", () => {
|
||||
it("uses front/high-quality defaults when params are omitted", async () => {
|
||||
setupNodeInvokeMock({
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
expectInvokeParams(invokeParams, {
|
||||
command: "camera.snap",
|
||||
params: {
|
||||
facing: "front",
|
||||
@@ -224,7 +250,7 @@ describe("nodes camera_snap", () => {
|
||||
it("passes deviceId when provided", async () => {
|
||||
setupNodeInvokeMock({
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
expectInvokeParams(invokeParams, {
|
||||
command: "camera.snap",
|
||||
params: { deviceId: "cam-123" },
|
||||
});
|
||||
@@ -343,7 +369,7 @@ describe("nodes photos_latest", () => {
|
||||
it("returns empty content/details when no photos are available", async () => {
|
||||
setupNodeInvokeMock({
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
expectInvokeParams(invokeParams, {
|
||||
command: "photos.latest",
|
||||
params: {
|
||||
limit: 1,
|
||||
@@ -380,11 +406,9 @@ describe("nodes photos_latest", () => {
|
||||
expect(result.content ?? []).toStrictEqual([]);
|
||||
const details =
|
||||
(result.details as { photos?: Array<Record<string, unknown>> } | undefined)?.photos ?? [];
|
||||
expect(details[0]).toMatchObject({
|
||||
width: 1,
|
||||
height: 1,
|
||||
createdAt: "2026-03-04T00:00:00Z",
|
||||
});
|
||||
expect(details[0]?.width).toBe(1);
|
||||
expect(details[0]?.height).toBe(1);
|
||||
expect(details[0]?.createdAt).toBe("2026-03-04T00:00:00Z");
|
||||
expect(expectFirstMediaUrl(result)).toMatch(/openclaw-camera-snap-.*\.jpg$/);
|
||||
});
|
||||
|
||||
@@ -403,7 +427,7 @@ describe("nodes notifications_list", () => {
|
||||
setupNodeInvokeMock({
|
||||
commands: ["notifications.list"],
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
expectInvokeParams(invokeParams, {
|
||||
nodeId: NODE_ID,
|
||||
command: "notifications.list",
|
||||
params: {},
|
||||
@@ -425,7 +449,7 @@ describe("nodes notifications_list", () => {
|
||||
});
|
||||
|
||||
expectFirstTextContains(result, '"notifications"');
|
||||
expect(parseFirstTextJson(result)).toMatchObject({
|
||||
expect(parseFirstTextJson(result)).toStrictEqual({
|
||||
enabled: true,
|
||||
connected: true,
|
||||
count: 1,
|
||||
@@ -439,7 +463,7 @@ describe("nodes notifications_action", () => {
|
||||
setupNodeInvokeMock({
|
||||
commands: ["notifications.actions"],
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
expectInvokeParams(invokeParams, {
|
||||
nodeId: NODE_ID,
|
||||
command: "notifications.actions",
|
||||
params: {
|
||||
@@ -459,7 +483,7 @@ describe("nodes notifications_action", () => {
|
||||
});
|
||||
|
||||
expectFirstTextContains(result, '"dismiss"');
|
||||
expect(parseFirstTextJson(result)).toMatchObject({
|
||||
expect(parseFirstTextJson(result)).toStrictEqual({
|
||||
ok: true,
|
||||
key: "n1",
|
||||
action: "dismiss",
|
||||
@@ -470,7 +494,7 @@ describe("nodes notifications_action", () => {
|
||||
setupNodeInvokeMock({
|
||||
commands: ["notifications.actions"],
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
expectInvokeParams(invokeParams, {
|
||||
nodeId: NODE_ID,
|
||||
command: "notifications.actions",
|
||||
params: {
|
||||
@@ -491,7 +515,7 @@ describe("nodes notifications_action", () => {
|
||||
notificationReplyText: " On it ",
|
||||
});
|
||||
|
||||
expect(parseFirstTextJson(result)).toMatchObject({
|
||||
expect(parseFirstTextJson(result)).toStrictEqual({
|
||||
ok: true,
|
||||
key: "n2",
|
||||
action: "reply",
|
||||
@@ -504,7 +528,7 @@ describe("nodes location_get", () => {
|
||||
setupNodeInvokeMock({
|
||||
commands: ["location.get"],
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
expectInvokeParams(invokeParams, {
|
||||
nodeId: NODE_ID,
|
||||
command: "location.get",
|
||||
params: {
|
||||
@@ -532,7 +556,7 @@ describe("nodes location_get", () => {
|
||||
locationTimeoutMs: 4_500,
|
||||
});
|
||||
|
||||
expect(parseFirstTextJson(result)).toMatchObject({
|
||||
expect(parseFirstTextJson(result)).toStrictEqual({
|
||||
latitude: 37.3346,
|
||||
longitude: -122.009,
|
||||
accuracyMeters: 18,
|
||||
@@ -546,7 +570,7 @@ describe("nodes device_status and device_info", () => {
|
||||
setupNodeInvokeMock({
|
||||
commands: ["device.status", "device.info"],
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
expectInvokeParams(invokeParams, {
|
||||
nodeId: NODE_ID,
|
||||
command: "device.status",
|
||||
params: {},
|
||||
@@ -571,7 +595,7 @@ describe("nodes device_status and device_info", () => {
|
||||
setupNodeInvokeMock({
|
||||
commands: ["device.status", "device.info"],
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
expectInvokeParams(invokeParams, {
|
||||
nodeId: NODE_ID,
|
||||
command: "device.info",
|
||||
params: {},
|
||||
@@ -597,7 +621,7 @@ describe("nodes device_status and device_info", () => {
|
||||
setupNodeInvokeMock({
|
||||
commands: ["device.permissions"],
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
expectInvokeParams(invokeParams, {
|
||||
nodeId: NODE_ID,
|
||||
command: "device.permissions",
|
||||
params: {},
|
||||
@@ -626,25 +650,21 @@ describe("nodes device_status and device_info", () => {
|
||||
});
|
||||
|
||||
expectFirstTextContains(result, '"permissions"');
|
||||
expect(parseFirstTextJson(result)).toMatchObject({
|
||||
permissions: {
|
||||
sms: {
|
||||
status: "denied",
|
||||
promptable: true,
|
||||
capabilities: {
|
||||
send: { status: "denied", promptable: true },
|
||||
read: { status: "granted", promptable: false },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const parsed = requireRecord(parseFirstTextJson(result), "device permissions payload");
|
||||
const permissions = requireRecord(parsed.permissions, "permissions");
|
||||
const sms = requireRecord(permissions.sms, "sms permissions");
|
||||
expect(sms.status).toBe("denied");
|
||||
expect(sms.promptable).toBe(true);
|
||||
const capabilities = requireRecord(sms.capabilities, "sms capabilities");
|
||||
expect(capabilities.send).toStrictEqual({ status: "denied", promptable: true });
|
||||
expect(capabilities.read).toStrictEqual({ status: "granted", promptable: false });
|
||||
});
|
||||
|
||||
it("invokes device.health and returns payload", async () => {
|
||||
setupNodeInvokeMock({
|
||||
commands: ["device.health"],
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
expectInvokeParams(invokeParams, {
|
||||
nodeId: NODE_ID,
|
||||
command: "device.health",
|
||||
params: {},
|
||||
@@ -671,7 +691,7 @@ describe("nodes invoke", () => {
|
||||
it("allows metadata-only camera.list via generic invoke", async () => {
|
||||
setupNodeInvokeMock({
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
expectInvokeParams(invokeParams, {
|
||||
command: "camera.list",
|
||||
params: {},
|
||||
});
|
||||
@@ -689,10 +709,9 @@ describe("nodes invoke", () => {
|
||||
invokeCommand: "camera.list",
|
||||
});
|
||||
|
||||
expect(result.details).toMatchObject({
|
||||
payload: {
|
||||
devices: [{ id: "cam-back", name: "Back Camera" }],
|
||||
},
|
||||
const details = requireRecord(result.details, "camera.list details");
|
||||
expect(details.payload).toStrictEqual({
|
||||
devices: [{ id: "cam-back", name: "Back Camera" }],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -710,7 +729,7 @@ describe("nodes invoke", () => {
|
||||
it("allows media invoke commands when explicitly enabled", async () => {
|
||||
setupNodeInvokeMock({
|
||||
onInvoke: (invokeParams) => {
|
||||
expect(invokeParams).toMatchObject({
|
||||
expectInvokeParams(invokeParams, {
|
||||
command: "photos.latest",
|
||||
params: { limit: 1 },
|
||||
});
|
||||
@@ -732,10 +751,9 @@ describe("nodes invoke", () => {
|
||||
{ allowMediaInvokeCommands: true },
|
||||
);
|
||||
|
||||
expect(result.details).toMatchObject({
|
||||
payload: {
|
||||
photos: [{ format: "jpg", base64: "aGVsbG8=", width: 1, height: 1 }],
|
||||
},
|
||||
const details = requireRecord(result.details, "photos.latest invoke details");
|
||||
expect(details.payload).toStrictEqual({
|
||||
photos: [{ format: "jpg", base64: "aGVsbG8=", width: 1, height: 1 }],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user