unrip/test/notification-client.test.mjs
philipp 551050beb3
All checks were successful
deploy / deploy (push) Successful in 46s
Add internal ntfy utility service
Proof: npm test; PYTHONPATH=. python3 test/render_release_manifest_test.py; PYTHONPATH=. python3 test/repo_deployments_test.py; PYTHONPATH=. python3 test/ntfy_manifest_test.py; kubectl kustomize deploy/k8s/base.

Assumptions: ntfy should start as an internal ClusterIP utility so repo-owned services can publish without exposing an unauthenticated public notification endpoint; mobile delivery needs a separate authenticated ingress or external endpoint decision.

Still fake: No public ntfy ingress or operator mobile subscription exists yet; no existing runtime path emits ntfy notifications by default; ntfy cache storage is ephemeral emptyDir.
2026-04-15 21:23:41 +02:00

99 lines
3.7 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import {
buildNtfyPublishRequest,
buildNtfyPublishUrl,
createNtfyNotificationClient,
} from '../src/core/notification-client.mjs';
import { loadConfig } from '../src/lib/config.mjs';
test('ntfy publish request uses repo configured base url and topic', () => {
const request = buildNtfyPublishRequest({
baseUrl: 'http://ntfy.utility.svc.cluster.local/',
topic: 'unrip alerts',
title: 'Quote lifecycle',
message: 'quote abc submitted, awaiting settlement',
priority: 'high',
tags: ['warning', 'unrip'],
click: 'https://dashboard.example/strategy',
});
assert.equal(request.ok, true);
assert.equal(request.url, 'http://ntfy.utility.svc.cluster.local/unrip%20alerts');
assert.equal(request.init.method, 'POST');
assert.equal(request.init.body, 'quote abc submitted, awaiting settlement');
assert.equal(request.init.headers.Title, 'Quote lifecycle');
assert.equal(request.init.headers.Priority, 'high');
assert.equal(request.init.headers.Tags, 'warning,unrip');
assert.equal(request.init.headers.Click, 'https://dashboard.example/strategy');
});
test('ntfy notification client skips when endpoint or message is not configured', async () => {
assert.equal(buildNtfyPublishUrl({ baseUrl: '', topic: 'unrip' }), null);
assert.deepEqual(
buildNtfyPublishRequest({ baseUrl: 'http://ntfy.utility.svc.cluster.local', topic: 'unrip', message: '' }),
{ ok: false, skipped: true, reason: 'message_empty' },
);
const client = createNtfyNotificationClient({ baseUrl: '', topic: 'unrip' });
assert.equal(client.isConfigured(), false);
assert.deepEqual(await client.publish({ message: 'hello' }), {
ok: false,
skipped: true,
reason: 'ntfy_not_configured',
});
});
test('ntfy notification client posts text and returns response status', async () => {
const requests = [];
const client = createNtfyNotificationClient({
baseUrl: 'http://ntfy.utility.svc.cluster.local',
topic: 'unrip',
timeoutMs: 1234,
fetchImpl: async (url, init) => {
requests.push({ url, init });
return {
ok: true,
status: 200,
async text() { return '{"id":"msg-1"}'; },
};
},
});
const result = await client.publish({ message: 'request completed', title: 'unrip' });
assert.equal(result.ok, true);
assert.equal(result.status, 200);
assert.equal(requests[0].url, 'http://ntfy.utility.svc.cluster.local/unrip');
assert.equal(requests[0].init.body, 'request completed');
assert.equal(requests[0].init.headers.Title, 'unrip');
assert.ok(requests[0].init.signal);
});
test('config exposes ntfy notification defaults and environment overrides', () => {
const previous = {
baseUrl: process.env.NOTIFICATION_NTFY_BASE_URL,
topic: process.env.NOTIFICATION_NTFY_TOPIC,
timeout: process.env.NOTIFICATION_NTFY_TIMEOUT_MS,
};
process.env.NOTIFICATION_NTFY_BASE_URL = 'http://ntfy.utility.svc.cluster.local';
process.env.NOTIFICATION_NTFY_TOPIC = 'unrip';
process.env.NOTIFICATION_NTFY_TIMEOUT_MS = '4321';
try {
const config = loadConfig({ envPath: '/tmp/unrip-missing-env-for-notification-test' });
assert.equal(config.notificationNtfyBaseUrl, 'http://ntfy.utility.svc.cluster.local');
assert.equal(config.notificationNtfyTopic, 'unrip');
assert.equal(config.notificationNtfyTimeoutMs, 4321);
} finally {
restoreEnv('NOTIFICATION_NTFY_BASE_URL', previous.baseUrl);
restoreEnv('NOTIFICATION_NTFY_TOPIC', previous.topic);
restoreEnv('NOTIFICATION_NTFY_TIMEOUT_MS', previous.timeout);
}
});
function restoreEnv(key, value) {
if (value == null) delete process.env[key];
else process.env[key] = value;
}