diff options
Diffstat (limited to 'test/integrations.test.ts')
| -rw-r--r-- | test/integrations.test.ts | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/test/integrations.test.ts b/test/integrations.test.ts new file mode 100644 index 0000000..310945a --- /dev/null +++ b/test/integrations.test.ts @@ -0,0 +1,128 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { verifyHCaptcha } from '../src/integrations/hcaptcha.js'; +import { sendNtfyNotification } from '../src/integrations/ntfy.js'; +import type { NtfyConfig, StoredRequest } from '../src/types/index.js'; + +describe('verifyHCaptcha', () => { + beforeEach(() => { + vi.stubGlobal('fetch', vi.fn()); + }); + + afterEach(() => { + vi.unstubAllGlobals(); + }); + + it('returns success boolean when response is ok', async () => { + const fetchMock = vi.mocked(fetch); + fetchMock.mockResolvedValue({ + ok: true, + statusText: 'OK', + json: async () => ({ success: true }), + } as Response); + + const result = await verifyHCaptcha('token', 'secret'); + expect(result.left().present()).toBe(false); + expect(result.right().get()).toBe(true); + + expect(fetchMock).toHaveBeenCalledTimes(1); + const [url, init] = fetchMock.mock.calls[0]!; + expect(url).toBe('https://hcaptcha.com/siteverify'); + expect(init?.method).toBe('POST'); + expect(init?.headers).toEqual({ 'Content-Type': 'application/x-www-form-urlencoded' }); + expect(init?.body).toBeInstanceOf(URLSearchParams); + expect((init?.body as URLSearchParams).get('secret')).toBe('secret'); + expect((init?.body as URLSearchParams).get('response')).toBe('token'); + }); + + it('returns error when response is not ok', async () => { + const fetchMock = vi.mocked(fetch); + fetchMock.mockResolvedValue({ + ok: false, + statusText: 'Bad Request', + } as Response); + + const result = await verifyHCaptcha('token', 'secret'); + expect(result.left().present()).toBe(true); + expect(result.left().get().message).toBe('hCaptcha verification failed: Bad Request'); + }); +}); + +describe('sendNtfyNotification', () => { + beforeEach(() => { + vi.stubGlobal('fetch', vi.fn()); + }); + + afterEach(() => { + vi.unstubAllGlobals(); + }); + + it('is a no-op when not enabled or misconfigured', async () => { + const fetchMock = vi.mocked(fetch); + + const config: NtfyConfig = { enabled: false }; + const request: StoredRequest = { + timestamp: 1, + uuid: 'uuid', + routeName: 'route1', + method: 'POST', + headers: {}, + body: {}, + }; + + const result = await sendNtfyNotification(config, request); + expect(result.left().present()).toBe(false); + expect(fetchMock).not.toHaveBeenCalled(); + }); + + it('posts a notification to the configured server/topic', async () => { + const fetchMock = vi.mocked(fetch); + fetchMock.mockResolvedValue({ ok: true, statusText: 'OK' } as Response); + + const config: NtfyConfig = { enabled: true, server: 'https://ntfy.example.com', topic: 'topic1' }; + const request: StoredRequest = { + timestamp: Date.parse('2020-01-01T00:00:00.000Z'), + uuid: 'uuid', + routeName: 'route1', + method: 'POST', + headers: {}, + body: {}, + }; + + const result = await sendNtfyNotification(config, request); + expect(result.left().present()).toBe(false); + + expect(fetchMock).toHaveBeenCalledTimes(1); + const [url, init] = fetchMock.mock.calls[0]!; + expect(url).toBe('https://ntfy.example.com/topic1'); + expect(init?.method).toBe('POST'); + expect(init?.headers).toEqual({ + Title: 'Webhook received: route1', + Tags: 'webhook,posthook', + Priority: '3', + }); + + expect(init?.body).toContain('Method: POST'); + expect(init?.body).toContain('Timestamp: 2020-01-01T00:00:00.000Z'); + expect(init?.body).toContain('UUID: uuid'); + }); + + it('returns an error when ntfy responds with non-2xx', async () => { + const fetchMock = vi.mocked(fetch); + fetchMock.mockResolvedValue({ ok: false, statusText: 'Unauthorized' } as Response); + + const config: NtfyConfig = { enabled: true, server: 'https://ntfy.example.com', topic: 'topic1' }; + const request: StoredRequest = { + timestamp: 1, + uuid: 'uuid', + routeName: 'route1', + method: 'POST', + headers: {}, + body: {}, + }; + + const result = await sendNtfyNotification(config, request); + expect(result.left().present()).toBe(true); + expect(result.left().get().message).toBe('ntfy notification failed: Unauthorized'); + }); +}); |
