96 lines
3.0 KiB
JavaScript
96 lines
3.0 KiB
JavaScript
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/);
|
|
});
|