const test = require('node:test'); const assert = require('node:assert'); const { isDiscordWebhook, formatDurationShort, summarizePerHosterFromBatch, buildWebhookRequest } = require('../lib/webhook-notify'); const SAMPLE_SUMMARY = { total: 10, succeeded: 8, failed: 2, files: [ { name: 'a.mkv', results: [ { hoster: 'voe.sx', status: 'done' }, { hoster: 'byse.sx', status: 'error', error: 'x' } ] }, { name: 'b.mkv', results: [ { hoster: 'voe.sx', status: 'done' }, { hoster: 'byse.sx', status: 'done' } ] } ] }; test('isDiscordWebhook recognizes discord URLs incl. ptb/canary/discordapp', () => { assert.ok(isDiscordWebhook('https://discord.com/api/webhooks/123/abc')); assert.ok(isDiscordWebhook('https://discordapp.com/api/webhooks/123/abc')); assert.ok(isDiscordWebhook('https://ptb.discord.com/api/webhooks/123/abc')); assert.ok(isDiscordWebhook('https://canary.discord.com/api/webhooks/123/abc')); assert.strictEqual(isDiscordWebhook('https://example.com/hook'), false); assert.strictEqual(isDiscordWebhook(''), false); assert.strictEqual(isDiscordWebhook(null), false); }); test('formatDurationShort formats h/m/s tiers', () => { assert.strictEqual(formatDurationShort(45), '45s'); assert.strictEqual(formatDurationShort(125), '2m 5s'); assert.strictEqual(formatDurationShort(3 * 3600 + 12 * 60), '3h 12m'); assert.strictEqual(formatDurationShort(-5), '0s'); assert.strictEqual(formatDurationShort(undefined), '0s'); }); test('summarizePerHosterFromBatch counts ok/fail per hoster', () => { const s = summarizePerHosterFromBatch(SAMPLE_SUMMARY); assert.deepStrictEqual(s['voe.sx'], { ok: 2, fail: 0 }); assert.deepStrictEqual(s['byse.sx'], { ok: 1, fail: 1 }); }); test('summarizePerHosterFromBatch handles malformed input', () => { assert.deepStrictEqual(summarizePerHosterFromBatch(null), {}); assert.deepStrictEqual(summarizePerHosterFromBatch({}), {}); assert.deepStrictEqual(summarizePerHosterFromBatch({ files: [{ results: null }] }), {}); }); test('buildWebhookRequest produces Discord content body for discord URLs', () => { const req = buildWebhookRequest('https://discord.com/api/webhooks/1/x', SAMPLE_SUMMARY, { durationSec: 3700, appVersion: '3.3.59', machineName: 'srv-1' }); assert.strictEqual(req.method, 'POST'); assert.strictEqual(req.headers['Content-Type'], 'application/json'); const body = JSON.parse(req.body); assert.ok(typeof body.content === 'string'); assert.match(body.content, /Batch fertig/); assert.match(body.content, /srv-1/); assert.match(body.content, /8 ok/); assert.match(body.content, /2 Fehler/); assert.match(body.content, /1h 1m/); assert.match(body.content, /voe\.sx: 2\/2/); }); test('buildWebhookRequest produces raw JSON payload for generic URLs', () => { const req = buildWebhookRequest('https://example.com/hook', SAMPLE_SUMMARY, { durationSec: 60, appVersion: '3.3.59', timestamp: '2026-06-09T00:00:00Z' }); const body = JSON.parse(req.body); assert.strictEqual(body.event, 'batch-done'); assert.strictEqual(body.total, 10); assert.strictEqual(body.succeeded, 8); assert.strictEqual(body.failed, 2); assert.strictEqual(body.durationSec, 60); assert.strictEqual(body.version, '3.3.59'); assert.deepStrictEqual(body.perHoster['byse.sx'], { ok: 1, fail: 1 }); }); test('buildWebhookRequest tolerates empty summary', () => { const req = buildWebhookRequest('https://example.com/hook', null, {}); const body = JSON.parse(req.body); assert.strictEqual(body.total, 0); assert.strictEqual(body.succeeded, 0); });