Sessions 5-7a: 955 tests, deployment ready
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
process.env.VAPID_PUBLIC_KEY = 'BTestPublicKey_______________________________________________________________';
|
||||
process.env.VAPID_PRIVATE_KEY = 'TestPrivateKey_______________________________';
|
||||
process.env.VAPID_SUBJECT = 'mailto:test@vyndr.app';
|
||||
|
||||
const mockSendNotification = jest.fn();
|
||||
const mockSetVapidDetails = jest.fn();
|
||||
|
||||
jest.mock('web-push', () => ({
|
||||
setVapidDetails: (...args) => mockSetVapidDetails(...args),
|
||||
sendNotification: (...args) => mockSendNotification(...args),
|
||||
}));
|
||||
|
||||
// Builds a fluent supabase mock where each chained call records intent and
|
||||
// the terminal promise yields {data, error}. Good enough for what webPush
|
||||
// actually does (.from().select().eq() / .delete().eq()).
|
||||
function makeSupabase({ rows = [], error = null } = {}) {
|
||||
const deleted = [];
|
||||
const builder = (table) => {
|
||||
const ctx = { table, filters: [] };
|
||||
const proxy = {
|
||||
_ctx: ctx,
|
||||
select: () => proxy,
|
||||
eq: (col, val) => {
|
||||
ctx.filters.push([col, val]);
|
||||
return ctx.action === 'delete' ? Promise.resolve({ error: null }) : proxy;
|
||||
},
|
||||
contains: () => proxy,
|
||||
delete: () => {
|
||||
ctx.action = 'delete';
|
||||
deleted.push(ctx);
|
||||
return proxy;
|
||||
},
|
||||
then: (resolve) => resolve({ data: rows, error }),
|
||||
};
|
||||
return proxy;
|
||||
};
|
||||
return {
|
||||
from: jest.fn().mockImplementation(builder),
|
||||
_deleted: deleted,
|
||||
};
|
||||
}
|
||||
|
||||
const mockSupabase = { current: makeSupabase() };
|
||||
jest.mock('../../src/utils/supabase', () => ({
|
||||
getSupabaseServiceClient: () => mockSupabase.current,
|
||||
}));
|
||||
|
||||
const webPush = require('../../src/services/distribution/webPush');
|
||||
|
||||
beforeEach(() => {
|
||||
mockSendNotification.mockReset();
|
||||
mockSetVapidDetails.mockReset();
|
||||
mockSupabase.current = makeSupabase();
|
||||
});
|
||||
|
||||
describe('webPush.configured', () => {
|
||||
test('returns true when both VAPID keys present', () => {
|
||||
expect(webPush.configured()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('webPush.sendPushToUser', () => {
|
||||
test('returns sent=0 when user has no subscriptions', async () => {
|
||||
mockSupabase.current = makeSupabase({ rows: [] });
|
||||
const result = await webPush.sendPushToUser('user-1', { title: 'hi' });
|
||||
expect(result).toMatchObject({ ok: true, sent: 0 });
|
||||
expect(mockSendNotification).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('sends to every subscription and counts successes', async () => {
|
||||
mockSupabase.current = makeSupabase({
|
||||
rows: [
|
||||
{ id: 'a', endpoint: 'https://a', keys_p256dh: 'p1', keys_auth: 'k1' },
|
||||
{ id: 'b', endpoint: 'https://b', keys_p256dh: 'p2', keys_auth: 'k2' },
|
||||
],
|
||||
});
|
||||
mockSendNotification.mockResolvedValue({ statusCode: 201 });
|
||||
const result = await webPush.sendPushToUser('user-1', { title: 'hi' });
|
||||
expect(result.ok).toBe(true);
|
||||
expect(result.sent).toBe(2);
|
||||
expect(mockSendNotification).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
test('prunes subscription that returns 410 Gone', async () => {
|
||||
mockSupabase.current = makeSupabase({
|
||||
rows: [{ id: 'dead', endpoint: 'https://dead', keys_p256dh: 'p', keys_auth: 'k' }],
|
||||
});
|
||||
const err = Object.assign(new Error('Gone'), { statusCode: 410 });
|
||||
mockSendNotification.mockRejectedValue(err);
|
||||
const result = await webPush.sendPushToUser('user-1', { title: 'hi' });
|
||||
expect(result.pruned).toBe(1);
|
||||
expect(result.sent).toBe(0);
|
||||
expect(mockSupabase.current._deleted.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('non-410 failure does not prune, counts as failed', async () => {
|
||||
mockSupabase.current = makeSupabase({
|
||||
rows: [{ id: 'flaky', endpoint: 'https://flaky', keys_p256dh: 'p', keys_auth: 'k' }],
|
||||
});
|
||||
const err = Object.assign(new Error('boom'), { statusCode: 500 });
|
||||
mockSendNotification.mockRejectedValue(err);
|
||||
const result = await webPush.sendPushToUser('user-1', { title: 'hi' });
|
||||
expect(result.failed).toBe(1);
|
||||
expect(result.sent).toBe(0);
|
||||
expect(mockSupabase.current._deleted.length).toBe(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user