All checks were successful
deploy / deploy (push) Successful in 46s
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.
99 lines
3.7 KiB
JavaScript
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;
|
|
}
|