Discover page: refresh, harvest admin and seed reasons; docs (BL-0051)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0f87fbe948
commit
613db96355
10 files changed
+265
-58
No files matched your search
+23
-2
@@ -18,6 +18,23 @@ if (peerGatewayQa) {
|
||||
if (process.platform === 'win32' && !process.env.OPENSSL_BIN) process.env.OPENSSL_BIN = 'C:\\Program Files\\Git\\usr\\bin\\openssl.exe';
|
||||
new PeeringState(peerData).generateIdentity('ComposeQA');
|
||||
}
|
||||
const discoveryQa = process.argv.includes('--discovery-ui');
|
||||
// Review focus: an untrusted AniList title in the QA admin's first feed must render as text, never markup.
|
||||
const XSS_TITLE = '<img src=x onerror=alert(1)>';
|
||||
if (discoveryQa) {
|
||||
const fetchedAt = '2026-10-20T10:00:00.000Z';
|
||||
const evidence = [{ id: 'recommendation:990001', kind: 'recommendation', label: 'AniList recommendation',
|
||||
note: 'Fixture recommendation', url: 'https://anilist.co/manga/990001', fetchedAt }];
|
||||
const reasons = ['AniList readers who liked Moon Garden recommend this (42).', 'New: started 2025, ongoing and trending.'];
|
||||
fs.writeFileSync(path.join(data, 'discovery.json'), JSON.stringify({ schema: 1, revision: 1,
|
||||
candidates: { 'anilist:990001': { id: 'anilist:990001', title: XSS_TITLE, year: 2025, status: 'RELEASING', cover: null,
|
||||
seed: 'recs', fetchedAt, evidence, metadataUrl: 'https://anilist.co/manga/990001' } },
|
||||
feeds: { qa: { generatedAt: fetchedAt, items: [{ candidateId: 'anilist:990001', reason: reasons[0], reasons, evidence,
|
||||
evidenceIds: evidence.map(entry => entry.id), scoreParts: { communityRec: 3, fresh: 2 }, score: 5, period: 'Related works', group: 'recs' }] } },
|
||||
lastRun: { status: 'success', startedAt: fetchedAt, finishedAt: fetchedAt, candidateCount: 1, examined: 1, clipped: 0 },
|
||||
// Nightly off, so only the test's own clicks start harvests.
|
||||
harvest: null, settings: { harvestEnabled: false, harvestAt: '03:30' }, nightly: { lastDate: null } }));
|
||||
}
|
||||
const pausedConfirmQa = process.argv.includes('--paused-confirm');
|
||||
const readingGateQa = process.argv.includes('--reading-limit-ui');
|
||||
if (readingGateQa) fs.writeFileSync(path.join(data, 'reading-quiz.json'), JSON.stringify({ version: 1, manga: {
|
||||
@@ -45,6 +62,8 @@ for (const [index, name] of ['Moon Garden', 'Before the Rain', 'Iron Harbor'].en
|
||||
fs.writeFileSync(path.join(library, name, 'manga.json'), JSON.stringify({ title: name,
|
||||
direction: directionQa && index === 1 ? 'ltr' : process.argv.includes('--reading-ui') && index === 0 ? 'ttb' : 'rtl',
|
||||
...(directionQa && index === 0 ? { 'x-reading-direction': { version: 1, value: 'rtl', source: 'fallback' } } : {}),
|
||||
// AniList IDs make these owned titles harvest anchors; the unreachable endpoint turns their requests into upstream errors.
|
||||
...(discoveryQa ? { 'x-anilist': { id: 990101 + index } } : {}),
|
||||
status: index ? 'completed' : 'ongoing', tags: ['Fantasy', 'Demo'] }));
|
||||
}
|
||||
const env = { ...process.env, LIBRARY_PATH: library, DATA_PATH: data, SCRAPER_DATA_PATH: path.join(data, 'scraper'),
|
||||
@@ -53,6 +72,8 @@ const env = { ...process.env, LIBRARY_PATH: library, DATA_PATH: data, SCRAPER_DA
|
||||
...(readingGateQa ? { READING_WINDOWS_JSON: '{"0":[],"1":[],"2":[],"3":[],"4":[],"5":[],"6":[]}',
|
||||
READING_TIMEZONE: 'Europe/Berlin', READING_QUIZ_UNLOCK_MINUTES: '60', READING_QUIZ_CATALOG_PATH: '/data/reading-quiz.json' }
|
||||
: { READING_WINDOWS_JSON: '', READING_QUIZ_CATALOG_PATH: '' }),
|
||||
// A closed local port: every harvest request fails fast and AniList is never contacted.
|
||||
...(discoveryQa ? { DISCOVERY_ANILIST_URL: 'http://127.0.0.1:9/' } : {}),
|
||||
...(peerGatewayQa ? { PEER_DATA_PATH: path.join(data, 'peering'), PEER_USAGE_PATH: path.join(data, 'peering-usage'), PEER_PORT: process.env.QA_PEER_PORT || '8193' } : {}) };
|
||||
const args = ['compose', '-p', project, '-f', path.join(root, 'docker-compose.yml'), ...(peerGatewayQa ? ['--profile', 'peering'] : [])];
|
||||
const docker = (...command) => new Promise((resolve, reject) => {
|
||||
@@ -215,10 +236,10 @@ let keep = false;
|
||||
const image = await api('/lib/' + encodeURIComponent('Moon Garden') + '/cover.png'); assert.equal(image.status, 200);
|
||||
state = await (await api('/api/admin/scraper')).json(); assert.equal(state.paused, true);
|
||||
if (process.argv.includes('--settings-ui')) await require('./test-settings-ui')(url, cookie);
|
||||
if (process.argv.includes('--discovery-ui')) {
|
||||
if (discoveryQa) {
|
||||
const login = await api('/api/login', { name: 'qa', password: 'disposable-local-test' });
|
||||
assert.equal(login.status, 200);
|
||||
await require('./test-discovery-ui')(url, login.headers.get('set-cookie').split(';')[0]);
|
||||
await require('./test-discovery-ui')(url, login.headers.get('set-cookie').split(';')[0], { xssTitle: XSS_TITLE });
|
||||
}
|
||||
if (process.argv.includes('--peer-ui')) {
|
||||
const login = await api('/api/login', { name: 'qa', password: 'disposable-local-test' });
|
||||
|
||||
Reference in new issue
Block a user