refactor: deduplicate push test fixtures

This commit is contained in:
Peter Steinberger
2026-03-13 16:40:56 +00:00
parent fb40b09157
commit b6b5e5caac

View File

@@ -21,6 +21,8 @@ vi.mock("../../infra/push-apns.js", () => ({
})); }));
import { import {
type ApnsPushResult,
type ApnsRegistration,
clearApnsRegistrationIfCurrent, clearApnsRegistrationIfCurrent,
loadApnsRegistration, loadApnsRegistration,
normalizeApnsEnvironment, normalizeApnsEnvironment,
@@ -32,6 +34,63 @@ import {
type RespondCall = [boolean, unknown?, { code: number; message: string }?]; type RespondCall = [boolean, unknown?, { code: number; message: string }?];
const DEFAULT_DIRECT_REGISTRATION = {
nodeId: "ios-node-1",
transport: "direct",
token: "abcd",
topic: "ai.openclaw.ios",
environment: "sandbox",
updatedAtMs: 1,
} as const;
const DEFAULT_RELAY_REGISTRATION = {
nodeId: "ios-node-1",
transport: "relay",
relayHandle: "relay-handle-123",
sendGrant: "send-grant-123",
installationId: "install-123",
topic: "ai.openclaw.ios",
environment: "production",
distribution: "official",
updatedAtMs: 1,
tokenDebugSuffix: "abcd1234",
} as const;
function directRegistration(
overrides: Partial<Extract<ApnsRegistration, { transport: "direct" }>> = {},
): Extract<ApnsRegistration, { transport: "direct" }> {
return { ...DEFAULT_DIRECT_REGISTRATION, ...overrides };
}
function relayRegistration(
overrides: Partial<Extract<ApnsRegistration, { transport: "relay" }>> = {},
): Extract<ApnsRegistration, { transport: "relay" }> {
return { ...DEFAULT_RELAY_REGISTRATION, ...overrides };
}
function mockDirectAuth() {
vi.mocked(resolveApnsAuthConfigFromEnv).mockResolvedValue({
ok: true,
value: {
teamId: "TEAM123",
keyId: "KEY123",
privateKey: "-----BEGIN PRIVATE KEY-----\nabc\n-----END PRIVATE KEY-----", // pragma: allowlist secret
},
});
}
function apnsResult(overrides: Partial<ApnsPushResult>): ApnsPushResult {
return {
ok: true,
status: 200,
tokenSuffix: "1234abcd",
topic: "ai.openclaw.ios",
environment: "sandbox",
transport: "direct",
...overrides,
};
}
function createInvokeParams(params: Record<string, unknown>) { function createInvokeParams(params: Record<string, unknown>) {
const respond = vi.fn(); const respond = vi.fn();
return { return {
@@ -85,31 +144,10 @@ describe("push.test handler", () => {
}); });
it("sends push test when registration and auth are available", async () => { it("sends push test when registration and auth are available", async () => {
vi.mocked(loadApnsRegistration).mockResolvedValue({ vi.mocked(loadApnsRegistration).mockResolvedValue(directRegistration());
nodeId: "ios-node-1", mockDirectAuth();
transport: "direct",
token: "abcd",
topic: "ai.openclaw.ios",
environment: "sandbox",
updatedAtMs: 1,
});
vi.mocked(resolveApnsAuthConfigFromEnv).mockResolvedValue({
ok: true,
value: {
teamId: "TEAM123",
keyId: "KEY123",
privateKey: "-----BEGIN PRIVATE KEY-----\nabc\n-----END PRIVATE KEY-----", // pragma: allowlist secret
},
});
vi.mocked(normalizeApnsEnvironment).mockReturnValue(null); vi.mocked(normalizeApnsEnvironment).mockReturnValue(null);
vi.mocked(sendApnsAlert).mockResolvedValue({ vi.mocked(sendApnsAlert).mockResolvedValue(apnsResult({}));
ok: true,
status: 200,
tokenSuffix: "1234abcd",
topic: "ai.openclaw.ios",
environment: "sandbox",
transport: "direct",
});
const { respond, invoke } = createInvokeParams({ const { respond, invoke } = createInvokeParams({
nodeId: "ios-node-1", nodeId: "ios-node-1",
@@ -137,18 +175,9 @@ describe("push.test handler", () => {
}, },
}, },
}); });
vi.mocked(loadApnsRegistration).mockResolvedValue({ vi.mocked(loadApnsRegistration).mockResolvedValue(
nodeId: "ios-node-1", relayRegistration({ installationId: "install-1" }),
transport: "relay", );
relayHandle: "relay-handle-123",
sendGrant: "send-grant-123",
installationId: "install-1",
topic: "ai.openclaw.ios",
environment: "production",
distribution: "official",
updatedAtMs: 1,
tokenDebugSuffix: "abcd1234",
});
vi.mocked(resolveApnsRelayConfigFromEnv).mockReturnValue({ vi.mocked(resolveApnsRelayConfigFromEnv).mockReturnValue({
ok: true, ok: true,
value: { value: {
@@ -157,14 +186,13 @@ describe("push.test handler", () => {
}, },
}); });
vi.mocked(normalizeApnsEnvironment).mockReturnValue(null); vi.mocked(normalizeApnsEnvironment).mockReturnValue(null);
vi.mocked(sendApnsAlert).mockResolvedValue({ vi.mocked(sendApnsAlert).mockResolvedValue(
ok: true, apnsResult({
status: 200, tokenSuffix: "abcd1234",
tokenSuffix: "abcd1234", environment: "production",
topic: "ai.openclaw.ios", transport: "relay",
environment: "production", }),
transport: "relay", );
});
const { respond, invoke } = createInvokeParams({ const { respond, invoke } = createInvokeParams({
nodeId: "ios-node-1", nodeId: "ios-node-1",
@@ -192,32 +220,17 @@ describe("push.test handler", () => {
}); });
it("clears stale registrations after invalid token push-test failures", async () => { it("clears stale registrations after invalid token push-test failures", async () => {
vi.mocked(loadApnsRegistration).mockResolvedValue({ const registration = directRegistration();
nodeId: "ios-node-1", vi.mocked(loadApnsRegistration).mockResolvedValue(registration);
transport: "direct", mockDirectAuth();
token: "abcd",
topic: "ai.openclaw.ios",
environment: "sandbox",
updatedAtMs: 1,
});
vi.mocked(resolveApnsAuthConfigFromEnv).mockResolvedValue({
ok: true,
value: {
teamId: "TEAM123",
keyId: "KEY123",
privateKey: "-----BEGIN PRIVATE KEY-----\nabc\n-----END PRIVATE KEY-----", // pragma: allowlist secret
},
});
vi.mocked(normalizeApnsEnvironment).mockReturnValue(null); vi.mocked(normalizeApnsEnvironment).mockReturnValue(null);
vi.mocked(sendApnsAlert).mockResolvedValue({ vi.mocked(sendApnsAlert).mockResolvedValue(
ok: false, apnsResult({
status: 400, ok: false,
reason: "BadDeviceToken", status: 400,
tokenSuffix: "1234abcd", reason: "BadDeviceToken",
topic: "ai.openclaw.ios", }),
environment: "sandbox", );
transport: "direct",
});
vi.mocked(shouldClearStoredApnsRegistration).mockReturnValue(true); vi.mocked(shouldClearStoredApnsRegistration).mockReturnValue(true);
const { invoke } = createInvokeParams({ const { invoke } = createInvokeParams({
@@ -229,30 +242,13 @@ describe("push.test handler", () => {
expect(clearApnsRegistrationIfCurrent).toHaveBeenCalledWith({ expect(clearApnsRegistrationIfCurrent).toHaveBeenCalledWith({
nodeId: "ios-node-1", nodeId: "ios-node-1",
registration: { registration,
nodeId: "ios-node-1",
transport: "direct",
token: "abcd",
topic: "ai.openclaw.ios",
environment: "sandbox",
updatedAtMs: 1,
},
}); });
}); });
it("does not clear relay registrations after invalidation-shaped failures", async () => { it("does not clear relay registrations after invalidation-shaped failures", async () => {
vi.mocked(loadApnsRegistration).mockResolvedValue({ const registration = relayRegistration();
nodeId: "ios-node-1", vi.mocked(loadApnsRegistration).mockResolvedValue(registration);
transport: "relay",
relayHandle: "relay-handle-123",
sendGrant: "send-grant-123",
installationId: "install-123",
topic: "ai.openclaw.ios",
environment: "production",
distribution: "official",
updatedAtMs: 1,
tokenDebugSuffix: "abcd1234",
});
vi.mocked(resolveApnsRelayConfigFromEnv).mockReturnValue({ vi.mocked(resolveApnsRelayConfigFromEnv).mockReturnValue({
ok: true, ok: true,
value: { value: {
@@ -261,15 +257,15 @@ describe("push.test handler", () => {
}, },
}); });
vi.mocked(normalizeApnsEnvironment).mockReturnValue(null); vi.mocked(normalizeApnsEnvironment).mockReturnValue(null);
vi.mocked(sendApnsAlert).mockResolvedValue({ const result = apnsResult({
ok: false, ok: false,
status: 410, status: 410,
reason: "Unregistered", reason: "Unregistered",
tokenSuffix: "abcd1234", tokenSuffix: "abcd1234",
topic: "ai.openclaw.ios",
environment: "production", environment: "production",
transport: "relay", transport: "relay",
}); });
vi.mocked(sendApnsAlert).mockResolvedValue(result);
vi.mocked(shouldClearStoredApnsRegistration).mockReturnValue(false); vi.mocked(shouldClearStoredApnsRegistration).mockReturnValue(false);
const { invoke } = createInvokeParams({ const { invoke } = createInvokeParams({
@@ -280,59 +276,25 @@ describe("push.test handler", () => {
await invoke(); await invoke();
expect(shouldClearStoredApnsRegistration).toHaveBeenCalledWith({ expect(shouldClearStoredApnsRegistration).toHaveBeenCalledWith({
registration: { registration,
nodeId: "ios-node-1", result,
transport: "relay",
relayHandle: "relay-handle-123",
sendGrant: "send-grant-123",
installationId: "install-123",
topic: "ai.openclaw.ios",
environment: "production",
distribution: "official",
updatedAtMs: 1,
tokenDebugSuffix: "abcd1234",
},
result: {
ok: false,
status: 410,
reason: "Unregistered",
tokenSuffix: "abcd1234",
topic: "ai.openclaw.ios",
environment: "production",
transport: "relay",
},
overrideEnvironment: null, overrideEnvironment: null,
}); });
expect(clearApnsRegistrationIfCurrent).not.toHaveBeenCalled(); expect(clearApnsRegistrationIfCurrent).not.toHaveBeenCalled();
}); });
it("does not clear direct registrations when push.test overrides the environment", async () => { it("does not clear direct registrations when push.test overrides the environment", async () => {
vi.mocked(loadApnsRegistration).mockResolvedValue({ const registration = directRegistration();
nodeId: "ios-node-1", vi.mocked(loadApnsRegistration).mockResolvedValue(registration);
transport: "direct", mockDirectAuth();
token: "abcd",
topic: "ai.openclaw.ios",
environment: "sandbox",
updatedAtMs: 1,
});
vi.mocked(resolveApnsAuthConfigFromEnv).mockResolvedValue({
ok: true,
value: {
teamId: "TEAM123",
keyId: "KEY123",
privateKey: "-----BEGIN PRIVATE KEY-----\nabc\n-----END PRIVATE KEY-----", // pragma: allowlist secret
},
});
vi.mocked(normalizeApnsEnvironment).mockReturnValue("production"); vi.mocked(normalizeApnsEnvironment).mockReturnValue("production");
vi.mocked(sendApnsAlert).mockResolvedValue({ const result = apnsResult({
ok: false, ok: false,
status: 400, status: 400,
reason: "BadDeviceToken", reason: "BadDeviceToken",
tokenSuffix: "1234abcd",
topic: "ai.openclaw.ios",
environment: "production", environment: "production",
transport: "direct",
}); });
vi.mocked(sendApnsAlert).mockResolvedValue(result);
vi.mocked(shouldClearStoredApnsRegistration).mockReturnValue(false); vi.mocked(shouldClearStoredApnsRegistration).mockReturnValue(false);
const { invoke } = createInvokeParams({ const { invoke } = createInvokeParams({
@@ -344,23 +306,8 @@ describe("push.test handler", () => {
await invoke(); await invoke();
expect(shouldClearStoredApnsRegistration).toHaveBeenCalledWith({ expect(shouldClearStoredApnsRegistration).toHaveBeenCalledWith({
registration: { registration,
nodeId: "ios-node-1", result,
transport: "direct",
token: "abcd",
topic: "ai.openclaw.ios",
environment: "sandbox",
updatedAtMs: 1,
},
result: {
ok: false,
status: 400,
reason: "BadDeviceToken",
tokenSuffix: "1234abcd",
topic: "ai.openclaw.ios",
environment: "production",
transport: "direct",
},
overrideEnvironment: "production", overrideEnvironment: "production",
}); });
expect(clearApnsRegistrationIfCurrent).not.toHaveBeenCalled(); expect(clearApnsRegistrationIfCurrent).not.toHaveBeenCalled();