110 lines
3.3 KiB
JavaScript
110 lines
3.3 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { briefing, getHot, getRedditConfig, getToken } from '../apis/sources/reddit.mjs';
|
|
|
|
test('Reddit reports missing OAuth credentials without network access', async () => {
|
|
let calls = 0;
|
|
const data = await briefing({
|
|
env: {},
|
|
delayMs: 0,
|
|
fetchImpl: async () => {
|
|
calls++;
|
|
throw new Error('unexpected network access');
|
|
},
|
|
});
|
|
|
|
assert.equal(calls, 0);
|
|
assert.equal(data.status, 'no_credentials');
|
|
assert.equal(data.error, 'missing_reddit_oauth_credentials');
|
|
assert.deepEqual(data.missing, ['REDDIT_CLIENT_ID', 'REDDIT_CLIENT_SECRET']);
|
|
});
|
|
|
|
test('Reddit hot posts require OAuth token and never use public JSON fallback', async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
let calledUrl = null;
|
|
globalThis.fetch = async url => {
|
|
calledUrl = url;
|
|
throw new Error('unexpected public fallback');
|
|
};
|
|
|
|
try {
|
|
const data = await getHot('worldnews');
|
|
assert.equal(calledUrl, null);
|
|
assert.equal(data.status, 'no_credentials');
|
|
assert.equal(data.error, 'reddit_oauth_required');
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test('Reddit classifies OAuth HTTP failure without exposing secrets', async () => {
|
|
const result = await getToken({
|
|
env: { REDDIT_CLIENT_ID: 'client-id', REDDIT_CLIENT_SECRET: 'client-secret' },
|
|
fetchImpl: async () => ({
|
|
ok: false,
|
|
status: 401,
|
|
text: async () => 'invalid client',
|
|
}),
|
|
});
|
|
|
|
assert.equal(result.ok, false);
|
|
assert.equal(result.status, 'auth_failed');
|
|
assert.equal(result.error, 'reddit_oauth_http_401');
|
|
assert.doesNotMatch(JSON.stringify(result), /client-secret/);
|
|
});
|
|
|
|
test('Reddit fetches hot posts through oauth.reddit.com when configured', async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
const urls = [];
|
|
globalThis.fetch = async url => {
|
|
urls.push(String(url));
|
|
if (String(url).includes('/api/v1/access_token')) {
|
|
return {
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => ({ access_token: 'test-token' }),
|
|
};
|
|
}
|
|
return {
|
|
ok: true,
|
|
status: 200,
|
|
headers: { get: () => 'application/json' },
|
|
text: async () => JSON.stringify({
|
|
data: {
|
|
children: [
|
|
{
|
|
data: {
|
|
title: 'Market stress headline',
|
|
score: 42,
|
|
num_comments: 7,
|
|
url: 'https://example.test/post',
|
|
created_utc: 1700000000,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
}),
|
|
};
|
|
};
|
|
|
|
try {
|
|
const data = await briefing({
|
|
env: { REDDIT_CLIENT_ID: 'client-id', REDDIT_CLIENT_SECRET: 'client-secret' },
|
|
subreddits: ['worldnews'],
|
|
delayMs: 0,
|
|
});
|
|
|
|
assert.equal(data.status, 'ok');
|
|
assert.equal(data.subreddits.worldnews[0].title, 'Market stress headline');
|
|
assert.ok(urls.some(url => url === 'https://www.reddit.com/api/v1/access_token'));
|
|
assert.ok(urls.some(url => url.startsWith('https://oauth.reddit.com/r/worldnews/hot')));
|
|
assert.equal(urls.some(url => url.includes('hot.json')), false);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test('Reddit config reports partial credential state', () => {
|
|
assert.deepEqual(getRedditConfig({ REDDIT_CLIENT_ID: 'id' }).missing, ['REDDIT_CLIENT_SECRET']);
|
|
});
|