feat: add webchat pwa push subscription support

This commit is contained in:
William Valentin
2026-02-18 10:46:55 -08:00
parent 02fa604c7c
commit 8234cc93f3
17 changed files with 743 additions and 2 deletions
+93
View File
@@ -554,6 +554,99 @@ describe('GatewayServer request body limits', () => {
});
});
describe('GatewayServer WebChat push endpoints', () => {
const PUSH_PORT = 18894;
let pushServer: GatewayServer;
beforeAll(async () => {
if (!LISTEN_ALLOWED) {
return;
}
pushServer = new GatewayServer({
port: PUSH_PORT,
sessionManager: mockSessionManager as unknown as GatewayServerConfig['sessionManager'],
modelClient: mockModelClient,
systemPrompt: 'Test prompt',
toolRegistry: mockToolRegistry as unknown as GatewayServerConfig['toolRegistry'],
toolExecutor: mockToolExecutor as unknown as GatewayServerConfig['toolExecutor'],
auth: { token: 'push-secret' },
authHttp: true,
uiDir: resolve(import.meta.dirname, 'ui'),
webchatPush: {
enabled: true,
vapidPublicKey: 'BO_test_public_key',
maxSubscriptions: 2,
},
});
await pushServer.start();
});
afterAll(async () => {
if (!LISTEN_ALLOWED) {
return;
}
await pushServer.stop();
});
it('returns push public key metadata when authenticated', async () => {
if (!LISTEN_ALLOWED) {
return;
}
const res = await fetch(`http://127.0.0.1:${PUSH_PORT}/webchat/push/public-key`, {
headers: { Authorization: 'Bearer push-secret' },
});
expect(res.status).toBe(200);
const body = await res.json() as { enabled: boolean; vapidPublicKey: string | null };
expect(body.enabled).toBe(true);
expect(body.vapidPublicKey).toBe('BO_test_public_key');
});
it('stores and deletes webchat push subscriptions', async () => {
if (!LISTEN_ALLOWED) {
return;
}
const headers = { Authorization: 'Bearer push-secret', 'Content-Type': 'application/json' };
const payload = {
endpoint: 'https://example.invalid/sub/1',
keys: {
p256dh: 'p256dh-sample',
auth: 'auth-sample',
},
userAgent: 'vitest',
};
const putRes = await fetch(`http://127.0.0.1:${PUSH_PORT}/webchat/push/subscriptions`, {
method: 'POST',
headers,
body: JSON.stringify(payload),
});
expect(putRes.status).toBe(200);
const putBody = await putRes.json() as { stored: boolean; count: number };
expect(putBody.stored).toBe(true);
expect(putBody.count).toBe(1);
const listRes = await fetch(`http://127.0.0.1:${PUSH_PORT}/webchat/push/subscriptions`, {
headers: { Authorization: 'Bearer push-secret' },
});
expect(listRes.status).toBe(200);
const listBody = await listRes.json() as { count: number; maxSubscriptions: number };
expect(listBody.count).toBe(1);
expect(listBody.maxSubscriptions).toBe(2);
const delRes = await fetch(`http://127.0.0.1:${PUSH_PORT}/webchat/push/subscriptions`, {
method: 'DELETE',
headers,
body: JSON.stringify({ endpoint: payload.endpoint }),
});
expect(delRes.status).toBe(200);
const delBody = await delRes.json() as { removed: boolean; count: number };
expect(delBody.removed).toBe(true);
expect(delBody.count).toBe(0);
});
});
describe('GatewayServer WebSocket ingress rate limiting', () => {
const RATE_PORT = 18895;
let rateServer: GatewayServer;