Files
inkling/tests/unit/NotificationService.test.ts
altair823 c9ccf6433f feat(notify): NotificationService with 4 rotating reward copies
Task 15 of the slice plan. Strategy §4.1 immediate-reward
toast. celebrate(noteId) deterministically picks one of the
4 reward copies via SHA-256(noteId)[0] % 4, then forwards to
the injected send() callback (which Task 30 wires to a real
electron Notification). Skips silently when isSupported() is
false (denied OS permission), and swallows send() errors so
that capture path never fails because of a notification quirk.

Verification: `npx vitest run tests/unit/NotificationService.test.ts`
3 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 12:11:44 +09:00

42 lines
1.3 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { NotificationService, REWARD_COPIES } from '@main/services/NotificationService.js';
describe('NotificationService', () => {
it('rotates copy deterministically by noteId hash', () => {
const fired: string[] = [];
const svc = new NotificationService({
isSupported: () => true,
send: (body) => { fired.push(body); }
});
const id1 = '00000000-0000-7000-8000-000000000001';
svc.celebrate(id1);
svc.celebrate(id1);
expect(fired).toHaveLength(2);
expect(fired[0]).toBe(fired[1]);
expect(REWARD_COPIES).toContain(fired[0]);
});
it('different ids select different (eventually all 4)', () => {
const fired: string[] = [];
const svc = new NotificationService({
isSupported: () => true,
send: (body) => { fired.push(body); }
});
for (let i = 0; i < 32; i++) {
svc.celebrate(`id-${i}`);
}
const distinct = new Set(fired);
expect(distinct.size).toBe(REWARD_COPIES.length);
});
it('skips silently when notifications unsupported', () => {
const fired: string[] = [];
const svc = new NotificationService({
isSupported: () => false,
send: (body) => { fired.push(body); }
});
svc.celebrate('id-1');
expect(fired).toHaveLength(0);
});
});