Add iOS node push-token registration foundation

This commit is contained in:
William Valentin
2026-02-16 12:47:34 -08:00
parent bea4c54f3b
commit 58c4b0b9bb
19 changed files with 448 additions and 7 deletions
+59
View File
@@ -238,6 +238,7 @@ describe('GatewayServer integration', () => {
expect(methods).toContain('canvas.list');
expect(methods).toContain('system.nodes');
expect(methods).toContain('node.status.set');
expect(methods).toContain('node.push_token.set');
});
it('supports canvas artifact lifecycle via gateway RPC', async () => {
@@ -641,6 +642,7 @@ describe('GatewayServer node registration and capability negotiation', () => {
allowedRoles: ['companion'],
featureGates: { 'ui.canvas': true },
locationEnabled: true,
pushEnabled: true,
},
});
await nodeServer.start();
@@ -809,4 +811,61 @@ describe('GatewayServer node registration and capability negotiation', () => {
}
}
});
it('supports node.push_token.set and exposes masked push summary via system.nodes', async () => {
if (!LISTEN_ALLOWED) {
return;
}
const ws = await new Promise<WebSocket>((resolve, reject) => {
const c = new WebSocket(`ws://127.0.0.1:${NODE_PORT}`);
c.on('open', () => resolve(c));
c.on('error', reject);
});
try {
const registered = await sendAndReceive(ws, {
id: 30,
method: 'node.register',
params: {
nodeId: 'node-ios',
role: 'companion',
protocolVersion: 1,
capabilities: ['notifications'],
},
});
expect(((registered as GatewayResponse).result as { registered: boolean }).registered).toBe(true);
const push = await sendAndReceive(ws, {
id: 31,
method: 'node.push_token.set',
params: {
provider: 'apns',
token: 'abcd1234abcd1234abcd1234abcd1234',
topic: 'com.example.flynn',
environment: 'sandbox',
},
});
const preview = ((push as GatewayResponse).result as {
push: { tokenPreview: string };
}).push.tokenPreview;
expect(preview).toContain('abcd1234');
const nodes = await sendAndReceive(ws, {
id: 32,
method: 'system.nodes',
params: { role: 'companion', limit: 10 },
});
const list = ((nodes as GatewayResponse).result as {
nodes: Array<{ nodeId: string; push?: { tokenPreview: string } }>;
}).nodes;
const iosNode = list.find((entry) => entry.nodeId === 'node-ios');
expect(iosNode?.push?.tokenPreview).toContain('abcd1234');
expect(iosNode?.push?.tokenPreview).not.toContain('abcd1234abcd1234abcd1234');
} finally {
if (ws.readyState === WebSocket.OPEN) {
ws.close();
}
}
});
});