Files
intelligence-terminal/test/fetch-utils.test.mjs
MrSphay 5b176851c8
All checks were successful
Codex Template Compliance / template-compliance (pull_request) Successful in 5s
Build / test-and-image (pull_request) Successful in 50s
fix: classify acled auth diagnostics
2026-05-17 14:03:18 +02:00

163 lines
4.9 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { safeFetch, safeFetchText, getFetchMetrics } from '../apis/utils/fetch.mjs';
test('safeFetch reports HTML as degraded JSON response', async () => {
const originalFetch = globalThis.fetch;
globalThis.fetch = async () => ({
ok: true,
status: 200,
headers: { get: () => 'text/html' },
text: async () => '<html>not json</html>',
});
try {
const data = await safeFetch('https://example.test/json', { retries: 0, source: 'unit' });
assert.match(data.error, /Expected JSON/);
assert.ok(getFetchMetrics().bySource.unit.requests >= 1);
} finally {
globalThis.fetch = originalFetch;
}
});
test('safeFetchText returns text and byte count', async () => {
const originalFetch = globalThis.fetch;
globalThis.fetch = async () => ({
ok: true,
status: 200,
text: async () => 'hello',
});
try {
const data = await safeFetchText('https://example.test/rss', { retries: 0, source: 'rss-unit' });
assert.equal(data.text, 'hello');
assert.equal(data.bytes, 5);
} finally {
globalThis.fetch = originalFetch;
}
});
function jsonResponse(payload, ok = true, status = 200) {
return {
ok,
status,
headers: { getSetCookie: () => [], get: () => 'application/json' },
text: async () => JSON.stringify(payload),
json: async () => payload,
};
}
function textResponse(text, ok = false, status = 500) {
return {
ok,
status,
headers: { getSetCookie: () => [], get: () => 'text/plain' },
text: async () => text,
json: async () => JSON.parse(text),
};
}
async function withAcledEnv(mockFetch, fn) {
const originalFetch = globalThis.fetch;
const saved = {
ACLED_EMAIL: process.env.ACLED_EMAIL,
ACLED_USER: process.env.ACLED_USER,
ACLED_USERNAME: process.env.ACLED_USERNAME,
ACLED_PASSWORD: process.env.ACLED_PASSWORD,
};
globalThis.fetch = mockFetch;
delete process.env.ACLED_EMAIL;
delete process.env.ACLED_USER;
delete process.env.ACLED_USERNAME;
delete process.env.ACLED_PASSWORD;
const acled = await import('../apis/sources/acled.mjs');
acled.resetAcledSessionForTests();
try {
return await fn(acled);
} finally {
globalThis.fetch = originalFetch;
for (const [key, value] of Object.entries(saved)) {
if (value === undefined) delete process.env[key];
else process.env[key] = value;
}
acled.resetAcledSessionForTests();
}
}
test('ACLED credentialed OAuth success returns live events and supports ACLED_USER', async () => {
const responses = [
jsonResponse({ access_token: 'secret-token' }),
jsonResponse({
status: 200,
data: [{
event_date: '2026-05-17',
event_type: 'Protests',
sub_event_type: 'Peaceful protest',
country: 'Example',
region: 'Example Region',
location: 'Example City',
fatalities: '0',
latitude: '1.23',
longitude: '4.56',
}],
}),
];
await withAcledEnv(async () => responses.shift(), async ({ briefing }) => {
process.env.ACLED_USER = 'operator@example.test';
process.env.ACLED_PASSWORD = 'password';
const data = await briefing();
assert.equal(data.status, 'live');
assert.equal(data.totalEvents, 1);
assert.equal(data.topCountries.Example.count, 1);
});
});
test('ACLED rejected credentials return auth_failed diagnostics', async () => {
const responses = [
textResponse('invalid credentials', false, 401),
textResponse('forbidden', false, 403),
];
await withAcledEnv(async () => responses.shift(), async ({ briefing }) => {
process.env.ACLED_EMAIL = 'operator@example.test';
process.env.ACLED_PASSWORD = 'wrong-password';
const data = await briefing();
assert.equal(data.status, 'auth_failed');
assert.match(data.error, /All ACLED auth methods failed/);
});
});
test('ACLED token endpoint failure returns api_failed diagnostics', async () => {
const responses = [
textResponse('temporary outage', false, 503),
textResponse('temporary outage', false, 503),
];
await withAcledEnv(async () => responses.shift(), async ({ briefing }) => {
process.env.ACLED_EMAIL = 'operator@example.test';
process.env.ACLED_PASSWORD = 'password';
const data = await briefing();
assert.equal(data.status, 'api_failed');
assert.match(data.error, /All ACLED auth methods failed/);
});
});
test('ACLED valid empty response is live with zero events', async () => {
const responses = [
jsonResponse({ access_token: 'secret-token' }),
jsonResponse({ status: 200, data: [] }),
];
await withAcledEnv(async () => responses.shift(), async ({ briefing }) => {
process.env.ACLED_EMAIL = 'operator@example.test';
process.env.ACLED_PASSWORD = 'password';
const data = await briefing();
assert.equal(data.status, 'live');
assert.equal(data.totalEvents, 0);
assert.match(data.message, /valid empty/);
});
});