Files
immich-folio/lib/__tests__/proofWebhook.test.ts

77 lines
2.7 KiB
TypeScript
Raw Normal View History

import { describe, it, expect, vi, afterEach } from 'vitest';
import { buildWebhookPayload, notifySubmitted, webhookUrl } from '../proofWebhook';
import type { ProofSession } from '../proofSessions';
const session: ProofSession = {
id: 'abc123',
token: 'x'.repeat(32),
clientName: 'Anna & Ben',
albumId: '11111111-1111-1111-1111-111111111111',
createdAt: '2026-01-01T00:00:00Z',
download: 'none',
downloadsUsed: 0,
selection: ['a', 'b'],
submittedAt: '2026-02-01T00:00:00Z',
};
afterEach(() => {
vi.unstubAllEnvs();
vi.unstubAllGlobals();
vi.restoreAllMocks();
});
describe('webhookUrl', () => {
it('accepts http(s) only', () => {
expect(webhookUrl('https://ntfy.example/x')).toBe('https://ntfy.example/x');
expect(webhookUrl('ftp://x')).toBeNull();
expect(webhookUrl('not a url')).toBeNull();
expect(webhookUrl('')).toBeNull();
});
});
describe('buildWebhookPayload', () => {
it('speaks Discord, Slack and Gotify at once, and never carries the client token', () => {
const payload = buildWebhookPayload(session, 'Wedding', 'https://folio.example');
expect(payload.content).toContain('Anna & Ben');
expect(payload.text).toContain('2 photos');
expect(payload.title).toBeTruthy();
expect(payload.message).toContain('"Wedding"');
expect(payload.proofing.adminUrl).toBe('https://folio.example/admin/proofing?session=abc123');
expect(JSON.stringify(payload)).not.toContain(session.token);
});
it('works without a site URL', () => {
expect(buildWebhookPayload(session, 'Wedding', null).proofing.adminUrl).toBeNull();
});
});
describe('notifySubmitted', () => {
it('does nothing without a URL', async () => {
const fetchMock = vi.fn();
vi.stubGlobal('fetch', fetchMock);
await notifySubmitted(session, 'Wedding', null);
expect(fetchMock).not.toHaveBeenCalled();
});
it('posts JSON to the configured URL', async () => {
vi.stubEnv('PROOFING_WEBHOOK_URL', 'https://hooks.example/abc');
const fetchMock = vi.fn(async () => new Response('ok'));
vi.stubGlobal('fetch', fetchMock);
await notifySubmitted(session, 'Wedding', null);
const [url, init] = fetchMock.mock.calls[0] as unknown as [string, RequestInit];
expect(url).toBe('https://hooks.example/abc');
expect(JSON.parse(init.body as string).event).toBe('proofing.submitted');
});
it('swallows a failing webhook', async () => {
vi.stubEnv('PROOFING_WEBHOOK_URL', 'https://hooks.example/abc');
vi.stubGlobal(
'fetch',
vi.fn(async () => Promise.reject(new Error('down'))),
);
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
await expect(notifySubmitted(session, 'Wedding', null)).resolves.toBeUndefined();
expect(warn).toHaveBeenCalled();
});
});