mirror of https://github.com/openclaw/openclaw.git
refactor: share node wake test apns fixtures
This commit is contained in:
parent
3ccf5f9dc8
commit
e351a86290
|
|
@ -59,6 +59,92 @@ type TestNodeSession = {
|
|||
};
|
||||
|
||||
const WAKE_WAIT_TIMEOUT_MS = 3_001;
|
||||
const DEFAULT_RELAY_CONFIG = {
|
||||
baseUrl: "https://relay.example.com",
|
||||
timeoutMs: 1000,
|
||||
} as const;
|
||||
type WakeResultOverrides = Partial<{
|
||||
ok: boolean;
|
||||
status: number;
|
||||
reason: string;
|
||||
tokenSuffix: string;
|
||||
topic: string;
|
||||
environment: "sandbox" | "production";
|
||||
transport: "direct" | "relay";
|
||||
}>;
|
||||
|
||||
function directRegistration(nodeId: string) {
|
||||
return {
|
||||
nodeId,
|
||||
transport: "direct" as const,
|
||||
token: "abcd1234abcd1234abcd1234abcd1234",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "sandbox" as const,
|
||||
updatedAtMs: 1,
|
||||
};
|
||||
}
|
||||
|
||||
function relayRegistration(nodeId: string) {
|
||||
return {
|
||||
nodeId,
|
||||
transport: "relay" as const,
|
||||
relayHandle: "relay-handle-123",
|
||||
sendGrant: "send-grant-123",
|
||||
installationId: "install-123",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "production" as const,
|
||||
distribution: "official" as const,
|
||||
updatedAtMs: 1,
|
||||
tokenDebugSuffix: "abcd1234",
|
||||
};
|
||||
}
|
||||
|
||||
function mockDirectWakeConfig(nodeId: string, overrides: WakeResultOverrides = {}) {
|
||||
mocks.loadApnsRegistration.mockResolvedValue(directRegistration(nodeId));
|
||||
mocks.resolveApnsAuthConfigFromEnv.mockResolvedValue({
|
||||
ok: true,
|
||||
value: {
|
||||
teamId: "TEAM123",
|
||||
keyId: "KEY123",
|
||||
privateKey: "-----BEGIN PRIVATE KEY-----\nabc\n-----END PRIVATE KEY-----", // pragma: allowlist secret
|
||||
},
|
||||
});
|
||||
mocks.sendApnsBackgroundWake.mockResolvedValue({
|
||||
ok: true,
|
||||
status: 200,
|
||||
tokenSuffix: "1234abcd",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "sandbox",
|
||||
transport: "direct",
|
||||
...overrides,
|
||||
});
|
||||
}
|
||||
|
||||
function mockRelayWakeConfig(nodeId: string, overrides: WakeResultOverrides = {}) {
|
||||
mocks.loadConfig.mockReturnValue({
|
||||
gateway: {
|
||||
push: {
|
||||
apns: {
|
||||
relay: DEFAULT_RELAY_CONFIG,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
mocks.loadApnsRegistration.mockResolvedValue(relayRegistration(nodeId));
|
||||
mocks.resolveApnsRelayConfigFromEnv.mockReturnValue({
|
||||
ok: true,
|
||||
value: DEFAULT_RELAY_CONFIG,
|
||||
});
|
||||
mocks.sendApnsBackgroundWake.mockResolvedValue({
|
||||
ok: true,
|
||||
status: 200,
|
||||
tokenSuffix: "abcd1234",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "production",
|
||||
transport: "relay",
|
||||
...overrides,
|
||||
});
|
||||
}
|
||||
|
||||
function makeNodeInvokeParams(overrides?: Partial<Record<string, unknown>>) {
|
||||
return {
|
||||
|
|
@ -157,33 +243,6 @@ async function ackPending(nodeId: string, ids: string[]) {
|
|||
return respond;
|
||||
}
|
||||
|
||||
function mockSuccessfulWakeConfig(nodeId: string) {
|
||||
mocks.loadApnsRegistration.mockResolvedValue({
|
||||
nodeId,
|
||||
transport: "direct",
|
||||
token: "abcd1234abcd1234abcd1234abcd1234",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "sandbox",
|
||||
updatedAtMs: 1,
|
||||
});
|
||||
mocks.resolveApnsAuthConfigFromEnv.mockResolvedValue({
|
||||
ok: true,
|
||||
value: {
|
||||
teamId: "TEAM123",
|
||||
keyId: "KEY123",
|
||||
privateKey: "-----BEGIN PRIVATE KEY-----\nabc\n-----END PRIVATE KEY-----", // pragma: allowlist secret
|
||||
},
|
||||
});
|
||||
mocks.sendApnsBackgroundWake.mockResolvedValue({
|
||||
ok: true,
|
||||
status: 200,
|
||||
tokenSuffix: "1234abcd",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "sandbox",
|
||||
transport: "direct",
|
||||
});
|
||||
}
|
||||
|
||||
describe("node.invoke APNs wake path", () => {
|
||||
beforeEach(() => {
|
||||
mocks.loadConfig.mockClear();
|
||||
|
|
@ -227,18 +286,7 @@ describe("node.invoke APNs wake path", () => {
|
|||
});
|
||||
|
||||
it("does not throttle repeated relay wake attempts when relay config is missing", async () => {
|
||||
mocks.loadApnsRegistration.mockResolvedValue({
|
||||
nodeId: "ios-node-relay-no-auth",
|
||||
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",
|
||||
});
|
||||
mocks.loadApnsRegistration.mockResolvedValue(relayRegistration("ios-node-relay-no-auth"));
|
||||
mocks.resolveApnsRelayConfigFromEnv.mockReturnValue({
|
||||
ok: false,
|
||||
error: "relay config missing",
|
||||
|
|
@ -265,7 +313,7 @@ describe("node.invoke APNs wake path", () => {
|
|||
|
||||
it("wakes and retries invoke after the node reconnects", async () => {
|
||||
vi.useFakeTimers();
|
||||
mockSuccessfulWakeConfig("ios-node-reconnect");
|
||||
mockDirectWakeConfig("ios-node-reconnect");
|
||||
|
||||
let connected = false;
|
||||
const session: TestNodeSession = { nodeId: "ios-node-reconnect", commands: ["camera.capture"] };
|
||||
|
|
@ -308,30 +356,12 @@ describe("node.invoke APNs wake path", () => {
|
|||
});
|
||||
|
||||
it("clears stale registrations after an invalid device token wake failure", async () => {
|
||||
mocks.loadApnsRegistration.mockResolvedValue({
|
||||
nodeId: "ios-node-stale",
|
||||
transport: "direct",
|
||||
token: "abcd1234abcd1234abcd1234abcd1234",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "sandbox",
|
||||
updatedAtMs: 1,
|
||||
});
|
||||
mocks.resolveApnsAuthConfigFromEnv.mockResolvedValue({
|
||||
ok: true,
|
||||
value: {
|
||||
teamId: "TEAM123",
|
||||
keyId: "KEY123",
|
||||
privateKey: "-----BEGIN PRIVATE KEY-----\nabc\n-----END PRIVATE KEY-----", // pragma: allowlist secret
|
||||
},
|
||||
});
|
||||
mocks.sendApnsBackgroundWake.mockResolvedValue({
|
||||
const registration = directRegistration("ios-node-stale");
|
||||
mocks.loadApnsRegistration.mockResolvedValue(registration);
|
||||
mockDirectWakeConfig("ios-node-stale", {
|
||||
ok: false,
|
||||
status: 400,
|
||||
reason: "BadDeviceToken",
|
||||
tokenSuffix: "1234abcd",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "sandbox",
|
||||
transport: "direct",
|
||||
});
|
||||
mocks.shouldClearStoredApnsRegistration.mockReturnValue(true);
|
||||
|
||||
|
|
@ -350,57 +380,16 @@ describe("node.invoke APNs wake path", () => {
|
|||
expect(call?.[2]?.message).toBe("node not connected");
|
||||
expect(mocks.clearApnsRegistrationIfCurrent).toHaveBeenCalledWith({
|
||||
nodeId: "ios-node-stale",
|
||||
registration: {
|
||||
nodeId: "ios-node-stale",
|
||||
transport: "direct",
|
||||
token: "abcd1234abcd1234abcd1234abcd1234",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "sandbox",
|
||||
updatedAtMs: 1,
|
||||
},
|
||||
registration,
|
||||
});
|
||||
});
|
||||
|
||||
it("does not clear relay registrations from wake failures", async () => {
|
||||
mocks.loadConfig.mockReturnValue({
|
||||
gateway: {
|
||||
push: {
|
||||
apns: {
|
||||
relay: {
|
||||
baseUrl: "https://relay.example.com",
|
||||
timeoutMs: 1000,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
mocks.loadApnsRegistration.mockResolvedValue({
|
||||
nodeId: "ios-node-relay",
|
||||
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",
|
||||
});
|
||||
mocks.resolveApnsRelayConfigFromEnv.mockReturnValue({
|
||||
ok: true,
|
||||
value: {
|
||||
baseUrl: "https://relay.example.com",
|
||||
timeoutMs: 1000,
|
||||
},
|
||||
});
|
||||
mocks.sendApnsBackgroundWake.mockResolvedValue({
|
||||
const registration = relayRegistration("ios-node-relay");
|
||||
mockRelayWakeConfig("ios-node-relay", {
|
||||
ok: false,
|
||||
status: 410,
|
||||
reason: "Unregistered",
|
||||
tokenSuffix: "abcd1234",
|
||||
topic: "ai.openclaw.ios",
|
||||
environment: "production",
|
||||
transport: "relay",
|
||||
});
|
||||
mocks.shouldClearStoredApnsRegistration.mockReturnValue(false);
|
||||
|
||||
|
|
@ -420,26 +409,12 @@ describe("node.invoke APNs wake path", () => {
|
|||
expect(mocks.resolveApnsRelayConfigFromEnv).toHaveBeenCalledWith(process.env, {
|
||||
push: {
|
||||
apns: {
|
||||
relay: {
|
||||
baseUrl: "https://relay.example.com",
|
||||
timeoutMs: 1000,
|
||||
},
|
||||
relay: DEFAULT_RELAY_CONFIG,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(mocks.shouldClearStoredApnsRegistration).toHaveBeenCalledWith({
|
||||
registration: {
|
||||
nodeId: "ios-node-relay",
|
||||
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",
|
||||
},
|
||||
registration,
|
||||
result: {
|
||||
ok: false,
|
||||
status: 410,
|
||||
|
|
@ -455,7 +430,7 @@ describe("node.invoke APNs wake path", () => {
|
|||
|
||||
it("forces one retry wake when the first wake still fails to reconnect", async () => {
|
||||
vi.useFakeTimers();
|
||||
mockSuccessfulWakeConfig("ios-node-throttle");
|
||||
mockDirectWakeConfig("ios-node-throttle");
|
||||
|
||||
const nodeRegistry = {
|
||||
get: vi.fn(() => undefined),
|
||||
|
|
|
|||
Loading…
Reference in New Issue