More cards
This commit is contained in:
Executable
+82
@@ -0,0 +1,82 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { WikipediaArticleSource } from '../server/wiki.ts';
|
||||
|
||||
function page(pageid: number, patch: Record<string, unknown> = {}) {
|
||||
return { pageid, ns: 0, title: `Article ${pageid}`, extract: `An encyclopedia article with enough explanatory text for a useful collectible card. ${pageid}.`,
|
||||
lastrevid: pageid + 100, thumbnail: { source: `https://upload.wikimedia.org/wikipedia/commons/${pageid}.jpg` },
|
||||
categories: [{ title: 'Category:Science' }], ...patch };
|
||||
}
|
||||
function json(value: unknown, headers: Record<string, string> = {}) {
|
||||
return new Response(JSON.stringify(value), { headers: { 'Content-Type': 'application/json', ...headers } });
|
||||
}
|
||||
function mockFetch(handler: (url: URL, init: RequestInit) => Response | Promise<Response>) {
|
||||
const calls: { url: URL; init: RequestInit }[] = [];
|
||||
const fetcher: typeof fetch = async (input, init) => {
|
||||
const url = new URL(String(input));
|
||||
calls.push({ url, init: init! });
|
||||
return handler(url, init!);
|
||||
};
|
||||
return { fetcher, calls };
|
||||
}
|
||||
|
||||
test('random article lookup retries batches with no pictures and selects a pictured page', async () => {
|
||||
let attempt = 0;
|
||||
const { fetcher, calls } = mockFetch(() => {
|
||||
attempt++;
|
||||
return attempt === 1
|
||||
? json({ query: { pages: [page(10, { thumbnail: undefined }), page(11, { thumbnail: undefined })] } })
|
||||
: json({ query: { pages: [page(12)] } });
|
||||
});
|
||||
const article = await new WikipediaArticleSource(fetcher).random();
|
||||
assert.equal(attempt, 2);
|
||||
assert.equal(article.pageId, 12);
|
||||
assert.equal(article.source, 'live');
|
||||
assert.equal(article.image, 'https://upload.wikimedia.org/wikipedia/commons/12.jpg');
|
||||
assert.equal(article.url, 'https://en.wikipedia.org/?curid=12');
|
||||
assert.equal(calls.length, 2);
|
||||
assert.equal(calls[0].url.origin, 'https://en.wikipedia.org');
|
||||
assert.equal(calls[0].url.searchParams.get('generator'), 'random');
|
||||
assert.equal(calls[0].url.searchParams.get('grnlimit'), '10');
|
||||
assert.equal(calls[0].url.searchParams.get('piprop'), 'thumbnail');
|
||||
assert.equal(calls[0].init.redirect, 'error');
|
||||
});
|
||||
|
||||
test('only HTTPS Wikimedia pictures are eligible and random lookup has a bounded retry count', async () => {
|
||||
const { fetcher, calls } = mockFetch(() => json({ query: { pages: [
|
||||
page(20, { thumbnail: { source: 'https://evil.example/tracker.jpg' } }),
|
||||
page(21, { thumbnail: { source: 'http://upload.wikimedia.org/image.jpg' } }),
|
||||
] } }));
|
||||
await assert.rejects(new WikipediaArticleSource(fetcher).random(), /three attempts/);
|
||||
assert.equal(calls.length, 3);
|
||||
});
|
||||
|
||||
test('random article fetch rejects unsafe response types, redirects, and oversized payloads without fallback', async () => {
|
||||
const failures = [
|
||||
() => new Response('{}', { headers: { 'Content-Type': 'text/html' } }),
|
||||
() => new Response(null, { status: 302, headers: { Location: 'https://evil.example' } }),
|
||||
() => new Response(' '.repeat(512 * 1024 + 1), { headers: { 'Content-Type': 'application/json' } }),
|
||||
() => { throw new Error('offline'); },
|
||||
];
|
||||
for (const fail of failures) {
|
||||
const { fetcher, calls } = mockFetch(() => fail());
|
||||
await assert.rejects(new WikipediaArticleSource(fetcher).random());
|
||||
assert.equal(calls.length, 3);
|
||||
}
|
||||
});
|
||||
|
||||
test('bad page records are skipped while later valid candidates are considered', async () => {
|
||||
const { fetcher } = mockFetch(() => json({ query: { pages: [
|
||||
page(30, { pageid: Number.MAX_SAFE_INTEGER + 1 }), page(31, { extract: 'short' }), page(32),
|
||||
] } }));
|
||||
const article = await new WikipediaArticleSource(fetcher).random();
|
||||
assert.equal(article.pageId, 32);
|
||||
});
|
||||
|
||||
test('a previously selected Wikipedia page may be selected again', async () => {
|
||||
const { fetcher } = mockFetch(() => json({ query: { pages: [page(40)] } }));
|
||||
const source = new WikipediaArticleSource(fetcher);
|
||||
const first = await source.random();
|
||||
const second = await source.random();
|
||||
assert.equal(first.pageId, second.pageId);
|
||||
});
|
||||
Reference in New Issue
Block a user