aboutsummaryrefslogtreecommitdiff
path: root/test/integrations.test.ts
diff options
context:
space:
mode:
authorElizabeth Hunt <me@liz.coffee>2025-12-14 22:43:24 -0800
committerElizabeth Hunt <me@liz.coffee>2025-12-14 22:43:24 -0800
commitcdb1a57614068fcfefa145bc6df45c9def7ccc6a (patch)
tree92cadbecda8658c143b7625d5925e3411976a892 /test/integrations.test.ts
parent6d318665a08c0d4564d8de23cc39425d2c0bac49 (diff)
downloadposthook-cdb1a57614068fcfefa145bc6df45c9def7ccc6a.tar.gz
posthook-cdb1a57614068fcfefa145bc6df45c9def7ccc6a.zip
Updates
Diffstat (limited to 'test/integrations.test.ts')
-rw-r--r--test/integrations.test.ts128
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');
+ });
+});