1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
// WebKit drops a push subscription after three pushes that show no
// notification, so on an Apple endpoint every path through the worker's push
// handler must end in one. Run: bun test tools/sw-push-quota.test.js
import { test, expect, mock } from 'bun:test';
let respond = async () => true;
mock.module('../src/nwc-respond.js', () => ({ respondFromBg: (...a) => respond(...a), tendRenewals: async () => {} }));
mock.module('../src/nwc-lock.js', () => ({ withRequestLock: async (_id, fn) => fn() }));
mock.module('../src/bg-autowithdraw.js', () => ({ bgAutoWithdraw: async () => {} }));
mock.module('../src/dm-inbox.js', () => ({
allInboxes: async () => [{ muted: ['m'.repeat(64)] }],
classifyDm: async () => ({ own: true }), shouldNotifyDm: () => false, classifyCall: async () => null,
}));
const listeners = {};
let endpoint = 'https://web.push.apple.com/abc';
let windows = [];
const shown = [], closed = [];
globalThis.self = {
location: { hostname: 'v3.coinos.io', origin: 'https://v3.coinos.io' },
addEventListener: (t, f) => { listeners[t] = f; },
clients: { matchAll: async () => windows },
registration: {
pushManager: { getSubscription: async () => ({ endpoint }) },
showNotification: async (title, o) => { shown.push({ title, ...o }); },
getNotifications: async ({ tag }) => shown.filter((n) => n.tag === tag).map((n) => ({ close: () => closed.push(n) })),
},
};
await import('../src/sw-nwc.js');
async function push(data) {
shown.length = 0; closed.length = 0;
let done;
listeners.push({ data: { json: () => data }, waitUntil: (p) => { done = p; } });
await done;
return shown.map((n) => n.title);
}
const nwc = { type: 'nwc', servicePubkey: 'a'.repeat(64), event: { id: 'e'.repeat(64), kind: 23194 } };
const hidden = { visibilityState: 'hidden', postMessage: () => {} };
const visible = { visibilityState: 'visible', postMessage: () => {} };
test('apple: every quiet path shows the stand-in', async () => {
endpoint = 'https://web.push.apple.com/abc'; windows = [];
respond = async () => true; // a lookup answered, or another device took it
expect(await push(nwc)).toEqual(['Coinos']);
expect(await push({ type: 'notify', reason: 'dm', wrap: {} })).toEqual(['Coinos']);
expect(await push({ type: 'notify', reason: 'reaction', from: 'm'.repeat(64) })).toEqual(['Coinos']);
expect(await push({ type: 'notify', reason: 'call', wrapId: 'x' })).toEqual(['Coinos']);
expect(await push({ type: 'weird' })).toEqual(['Coinos']);
expect(closed.length).toBe(0);
});
test('apple: an app on screen gets the stand-in closed at once', async () => {
windows = [visible];
expect(await push({ type: 'notify', reason: 'payment', amountSat: 5 })).toEqual(['Coinos']);
expect(closed.length).toBe(1);
});
test('apple: a suspended background app does not swallow the request', async () => {
windows = [hidden];
let asked = 0;
respond = async () => { asked++; return { notify: { title: 'Payment sent', body: '' } }; };
expect(await push(nwc)).toEqual(['Payment sent']);
expect(asked).toBe(1);
});
test('elsewhere: quiet stays quiet', async () => {
endpoint = 'https://fcm.googleapis.com/fcm/send/abc'; windows = [];
respond = async () => true;
expect(await push(nwc)).toEqual([]);
windows = [hidden];
let asked = 0;
respond = async () => { asked++; return true; };
expect(await push(nwc)).toEqual([]);
expect(asked).toBe(0); // the open (if hidden) window answers on Android
windows = [];
respond = async () => false;
expect(await push(nwc)).toEqual(['Payment request']);
});
|