import test from 'node:test'; import assert from 'node:assert/strict'; import { authenticate, briefing, resetAcledSessionCache } from '../apis/sources/acled.mjs'; function jsonResponse(status, body, ok = status >= 200 && status < 300) { return { ok, status, headers: { getSetCookie: () => [] }, json: async () => body, text: async () => JSON.stringify(body), }; } test('ACLED reports missing credentials without network access', async () => { resetAcledSessionCache(); let calls = 0; const data = await briefing({ env: {}, fetchImpl: async () => { calls++; throw new Error('unexpected network access'); }, }); assert.equal(calls, 0); assert.equal(data.status, 'no_credentials'); assert.equal(data.error, 'missing_acled_credentials'); assert.deepEqual(data.missing, ['ACLED_EMAIL', 'ACLED_PASSWORD']); }); test('ACLED accepts ACLED_USER as email alias and returns empty valid result', async () => { resetAcledSessionCache(); const urls = []; const data = await briefing({ env: { ACLED_USER: 'analyst@example.test', ACLED_PASSWORD: 'secret' }, fetchImpl: async url => { urls.push(String(url)); if (String(url).includes('/oauth/token')) { return jsonResponse(200, { access_token: 'token' }); } return jsonResponse(200, { status: 200, data: [] }); }, }); assert.equal(data.status, 'ok'); assert.equal(data.totalEvents, 0); assert.ok(urls.some(url => url.includes('/oauth/token'))); assert.ok(urls.some(url => url.includes('/api/acled/read'))); }); test('ACLED classifies auth failure without exposing credentials', async () => { resetAcledSessionCache(); const result = await authenticate({ env: { ACLED_EMAIL: 'analyst@example.test', ACLED_PASSWORD: 'super-secret' }, fetchImpl: async url => { if (String(url).includes('/oauth/token')) { return jsonResponse(401, { error: 'invalid_grant' }, false); } return { ok: false, status: 403, headers: { getSetCookie: () => [] }, text: async () => 'forbidden', }; }, }); assert.equal(result.status, 'auth_failed'); assert.equal(result.error, 'acled_auth_failed'); assert.equal(result.diagnostics.length, 2); assert.doesNotMatch(JSON.stringify(result), /super-secret/); }); test('ACLED classifies data access denied distinctly from auth failure', async () => { resetAcledSessionCache(); const data = await briefing({ env: { ACLED_EMAIL: 'analyst@example.test', ACLED_PASSWORD: 'secret' }, fetchImpl: async url => { if (String(url).includes('/oauth/token')) { return jsonResponse(200, { access_token: 'token' }); } return { ok: false, status: 403, headers: { getSetCookie: () => [] }, text: async () => 'terms not accepted', }; }, }); assert.equal(data.status, 'access_denied'); assert.equal(data.error, 'acled_data_http_403'); assert.match(data.hint, /Accept ACLED terms/); });