test: clear apns push broad matchers

This commit is contained in:
Peter Steinberger
2026-05-10 12:32:59 +01:00
parent 5ec84b3040
commit 3278f640ce

View File

@@ -164,6 +164,36 @@ async function closeServer(server: HttpServer | http2.Http2SecureServer): Promis
});
}
function requireRecord(value: unknown, label: string): Record<string, unknown> {
expect(typeof value).toBe("object");
expect(value).not.toBeNull();
if (typeof value !== "object" || value === null) {
throw new Error(`${label} was not an object`);
}
return value as Record<string, unknown>;
}
function expectRecordFields(record: Record<string, unknown>, fields: Record<string, unknown>) {
for (const [key, value] of Object.entries(fields)) {
expect(record[key]).toEqual(value);
}
}
function expectNoProperties(record: Record<string, unknown>, keys: string[]) {
for (const key of keys) {
expect(record).not.toHaveProperty(key);
}
}
function requireSendRequest(send: ReturnType<typeof vi.fn>, label = "APNs send request") {
const request = send.mock.calls[0]?.[0];
return requireRecord(request, label);
}
function requirePayload(sendRequest: Record<string, unknown>) {
return requireRecord(sendRequest.payload, "APNs payload");
}
async function startFakeApnsServer(): Promise<{
port: number;
requests: CapturedApnsRequest[];
@@ -266,19 +296,20 @@ describe("push APNs send semantics", () => {
});
expect(send).toHaveBeenCalledTimes(1);
const sent = send.mock.calls[0]?.[0];
expect(sent?.pushType).toBe("alert");
expect(sent?.priority).toBe("10");
expect(sent?.payload).toMatchObject({
aps: {
alert: { title: "Wake", body: "Ping" },
sound: "default",
},
openclaw: {
kind: "push.test",
nodeId: "ios-node-alert",
},
const sent = requireSendRequest(send);
expect(sent.pushType).toBe("alert");
expect(sent.priority).toBe("10");
const payload = requirePayload(sent);
expect(payload.aps).toEqual({
alert: { title: "Wake", body: "Ping" },
sound: "default",
});
const openclawPayload = requireRecord(payload.openclaw, "openclaw payload");
expectRecordFields(openclawPayload, {
kind: "push.test",
nodeId: "ios-node-alert",
});
expect(typeof openclawPayload.ts).toBe("number");
expect(result.ok).toBe(true);
expect(result.status).toBe(200);
expect(result.transport).toBe("direct");
@@ -312,7 +343,7 @@ describe("push APNs send semantics", () => {
timeoutMs: 2_500,
});
expect(result).toMatchObject({
expectRecordFields(requireRecord(result, "APNs result"), {
ok: true,
status: 200,
apnsId: "proxied-apns-id",
@@ -358,23 +389,23 @@ describe("push APNs send semantics", () => {
});
expect(send).toHaveBeenCalledTimes(1);
const sent = send.mock.calls[0]?.[0];
expect(sent?.pushType).toBe("background");
expect(sent?.priority).toBe("5");
expect(sent?.payload).toMatchObject({
aps: {
"content-available": 1,
},
openclaw: {
kind: "node.wake",
reason: "node.invoke",
nodeId: "ios-node-wake",
},
const sent = requireSendRequest(send);
expect(sent.pushType).toBe("background");
expect(sent.priority).toBe("5");
const payload = requirePayload(sent);
expect(payload.aps).toEqual({
"content-available": 1,
});
const sentPayload = sent?.payload as { aps?: { alert?: unknown; sound?: unknown } } | undefined;
const aps = sentPayload?.aps;
expect(aps?.alert).toBeUndefined();
expect(aps?.sound).toBeUndefined();
const openclawPayload = requireRecord(payload.openclaw, "openclaw payload");
expectRecordFields(openclawPayload, {
kind: "node.wake",
reason: "node.invoke",
nodeId: "ios-node-wake",
});
expect(typeof openclawPayload.ts).toBe("number");
const aps = requireRecord(payload.aps, "APNs aps payload");
expect(aps.alert).toBeUndefined();
expect(aps.sound).toBeUndefined();
expect(result.ok).toBe(true);
expect(result.environment).toBe("production");
expect(result.transport).toBe("direct");
@@ -400,33 +431,32 @@ describe("push APNs send semantics", () => {
});
expect(send).toHaveBeenCalledTimes(1);
const sent = send.mock.calls[0]?.[0];
expect(sent?.pushType).toBe("alert");
expect(sent?.payload).toMatchObject({
aps: {
alert: {
title: "Exec approval required",
body: "Open OpenClaw to review this request.",
},
sound: "default",
category: "openclaw.exec-approval",
"content-available": 1,
},
openclaw: {
kind: "exec.approval.requested",
approvalId: "approval-123",
const sent = requireSendRequest(send);
expect(sent.pushType).toBe("alert");
const payload = requirePayload(sent);
expect(payload.aps).toEqual({
alert: {
title: "Exec approval required",
body: "Open OpenClaw to review this request.",
},
sound: "default",
category: "openclaw.exec-approval",
"content-available": 1,
});
expect(sent?.payload).not.toMatchObject({
openclaw: {
host: expect.anything(),
nodeId: expect.anything(),
agentId: expect.anything(),
commandText: expect.anything(),
allowedDecisions: expect.anything(),
expiresAtMs: expect.anything(),
},
const openclawPayload = requireRecord(payload.openclaw, "openclaw payload");
expectRecordFields(openclawPayload, {
kind: "exec.approval.requested",
approvalId: "approval-123",
});
expect(typeof openclawPayload.ts).toBe("number");
expectNoProperties(openclawPayload, [
"host",
"nodeId",
"agentId",
"commandText",
"allowedDecisions",
"expiresAtMs",
]);
expect(result.ok).toBe(true);
expect(result.transport).toBe("direct");
});
@@ -451,17 +481,18 @@ describe("push APNs send semantics", () => {
});
expect(send).toHaveBeenCalledTimes(1);
const sent = send.mock.calls[0]?.[0];
expect(sent?.pushType).toBe("background");
expect(sent?.payload).toMatchObject({
aps: {
"content-available": 1,
},
openclaw: {
kind: "exec.approval.resolved",
approvalId: "approval-123",
},
const sent = requireSendRequest(send);
expect(sent.pushType).toBe("background");
const payload = requirePayload(sent);
expect(payload.aps).toEqual({
"content-available": 1,
});
const openclawPayload = requireRecord(payload.openclaw, "openclaw payload");
expectRecordFields(openclawPayload, {
kind: "exec.approval.resolved",
approvalId: "approval-123",
});
expect(typeof openclawPayload.ts).toBe("number");
expect(result.ok).toBe(true);
expect(result.transport).toBe("direct");
});
@@ -488,7 +519,7 @@ describe("push APNs send semantics", () => {
});
expect(send.mock.calls[0]?.[0]?.timeoutMs).toBe(1000);
expect(result).toMatchObject({
expectRecordFields(requireRecord(result, "APNs result"), {
ok: false,
status: 400,
apnsId: "apns-direct-fail-id",
@@ -542,12 +573,11 @@ describe("push APNs send semantics", () => {
});
const sent = send.mock.calls[0]?.[0];
expect(sent?.payload).toMatchObject({
openclaw: {
kind: "node.wake",
reason: "node.invoke",
nodeId: "ios-node-wake-default-reason",
},
const payload = requirePayload(requireRecord(sent, "APNs send request"));
expectRecordFields(requireRecord(payload.openclaw, "openclaw payload"), {
kind: "node.wake",
reason: "node.invoke",
nodeId: "ios-node-wake-default-reason",
});
});
@@ -574,24 +604,23 @@ describe("push APNs send semantics", () => {
});
expect(send).toHaveBeenCalledTimes(1);
const sent = send.mock.calls[0]?.[0];
expect(sent).toMatchObject({
const sent = requireSendRequest(send);
expectRecordFields(sent, {
relayConfig,
sendGrant: "send-grant-123",
relayHandle: "relay-handle-12345678",
gatewayDeviceId: "gateway-device-1",
pushType: "alert",
priority: "10",
payload: {
aps: {
alert: { title: "Wake", body: "Ping" },
sound: "default",
},
},
});
expect(sent?.signature).toBeTypeOf("string");
expect(sent?.signature).not.toBe("");
expect(result).toMatchObject({
const payload = requirePayload(sent);
expect(requireRecord(payload.aps, "APNs aps payload")).toEqual({
alert: { title: "Wake", body: "Ping" },
sound: "default",
});
expect(sent.signature).toBeTypeOf("string");
expect(sent.signature).not.toBe("");
expectRecordFields(requireRecord(result, "APNs result"), {
ok: true,
status: 202,
apnsId: "relay-alert-id",
@@ -623,24 +652,25 @@ describe("push APNs send semantics", () => {
});
expect(send).toHaveBeenCalledTimes(1);
const sent = send.mock.calls[0]?.[0];
expect(sent).toMatchObject({
const sent = requireSendRequest(send);
expectRecordFields(sent, {
relayConfig,
sendGrant: "send-grant-123",
relayHandle: "relay-handle-12345678",
gatewayDeviceId: "gateway-device-1",
pushType: "background",
priority: "5",
payload: {
aps: { "content-available": 1 },
openclaw: {
kind: "node.wake",
reason: "queue.retry",
nodeId: "ios-node-relay-wake",
},
},
});
expect(result).toMatchObject({
const payload = requirePayload(sent);
expect(payload.aps).toEqual({ "content-available": 1 });
const openclawPayload = requireRecord(payload.openclaw, "openclaw payload");
expectRecordFields(openclawPayload, {
kind: "node.wake",
reason: "queue.retry",
nodeId: "ios-node-relay-wake",
});
expect(typeof openclawPayload.ts).toBe("number");
expectRecordFields(requireRecord(result, "APNs result"), {
ok: false,
status: 429,
reason: "TooManyRequests",
@@ -671,30 +701,30 @@ describe("push APNs send semantics", () => {
});
const sent = send.mock.calls[0]?.[0];
expect(sent?.payload).toMatchObject({
aps: {
alert: {
title: "Exec approval required",
body: "Open OpenClaw to review this request.",
},
category: "openclaw.exec-approval",
"content-available": 1,
},
openclaw: {
kind: "exec.approval.requested",
approvalId: "approval-relay-1",
const payload = requirePayload(requireRecord(sent, "APNs send request"));
expect(payload.aps).toEqual({
alert: {
title: "Exec approval required",
body: "Open OpenClaw to review this request.",
},
sound: "default",
category: "openclaw.exec-approval",
"content-available": 1,
});
expect(sent?.payload).not.toMatchObject({
openclaw: {
commandText: expect.anything(),
host: expect.anything(),
nodeId: expect.anything(),
allowedDecisions: expect.anything(),
expiresAtMs: expect.anything(),
},
const openclawPayload = requireRecord(payload.openclaw, "openclaw payload");
expectRecordFields(openclawPayload, {
kind: "exec.approval.requested",
approvalId: "approval-relay-1",
});
expect(result).toMatchObject({
expect(typeof openclawPayload.ts).toBe("number");
expectNoProperties(openclawPayload, [
"commandText",
"host",
"nodeId",
"allowedDecisions",
"expiresAtMs",
]);
expectRecordFields(requireRecord(result, "APNs result"), {
ok: true,
status: 202,
environment: "production",