monal-rebuild/test/push.test.ts
pijukka cd29bb589b Web push: subscribe via Push 2.0, notifications and badge in sw.js
After every login (and when notifications are turned on) the app creates
or reuses a per-device VAPID key, subscribes with the browser's
PushManager and registers the subscription with the server's mod_push2;
logging out or turning notifications off unregisters it. sw.js shows each
push as a notification (sender, text, calls, attachments) and counts it
onto the icon badge; the app resets that count when it runs.

Match profile "important": mod_push2's "archived" profiles never match
(it looks for "stana-id"). Tested end to end against a local Prosody with
mod_push2 and the blunix relay module: decrypted payload, valid VAPID JWT,
also with the session hibernated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 23:47:03 +02:00

41 lines
1.9 KiB
TypeScript

import { test } from "node:test";
import assert from "node:assert/strict";
import { createVapidKey, enableElement, fromBase64, toBase64 } from "../src/xmpp/push.ts";
const bytes = (n: number, v: number) => new Uint8Array(n).fill(v).buffer;
test("base64 round trip, also from base64url", () => {
const data = Uint8Array.from([0, 1, 250, 251, 252, 253, 254, 255]);
assert.deepEqual(fromBase64(toBase64(data)), data);
assert.deepEqual(fromBase64("-_8"), Uint8Array.from([251, 255]));
});
test("VAPID key: P-256 public key and PKCS#8 private key", async () => {
const key = await createVapidKey();
const pub = fromBase64(key.publicRaw);
assert.equal(pub.length, 65);
assert.equal(pub[0], 4); // uncompressed point
assert.ok(fromBase64(key.privatePkcs8).length > 100);
});
test("enable request for mod_push2", async () => {
const vapid = await createVapidKey();
const el = enableElement(
"push.0ff.space",
{ endpoint: "https://web.push.apple.com/QGuXmPmr5a", p256dh: bytes(65, 7), auth: bytes(16, 9) },
vapid,
);
assert.equal(el.attrs.xmlns, "urn:xmpp:push2:0");
assert.equal(el.getChildText("service"), "push.0ff.space");
assert.equal(el.getChildText("client"), "https://web.push.apple.com/QGuXmPmr5a");
const match = el.getChild("match")!;
assert.equal(match.attrs.profile, "urn:xmpp:push2:match:important");
const send = match.getChild("send", "urn:xmpp:push2:send:sce+rfc8291+rfc8292:0")!;
// mod_push2 appends "==" itself, so no padding here.
assert.equal(send.getChildText("ua-public"), toBase64(bytes(65, 7)).replace(/=+$/, ""));
assert.equal(send.getChildText("auth-secret"), toBase64(bytes(16, 9)).replace(/=+$/, ""));
assert.equal(send.getChildText("jwt-alg"), "ES256");
assert.equal(send.getChildText("jwt-key"), vapid.privatePkcs8);
const aud = send.getChildren("jwt-claim").find((c) => c.attrs.name === "aud");
assert.equal(aud?.text(), "https://web.push.apple.com");
});