Discovery final review fixes: lean publish, robust scoring, cheap prune (BL-0051)
- Cache prune counts directory entries first and only reads files when over the cap; tests for cross-kind prune, schema-mismatch and corrupt entries. - Scoring tolerates malformed manga.json and cached records (non-list tags, object or null relations, non-object items); a tag without a rank no longer passes the top-tag filter; authors without an integer AniList id are dropped. - discovery.json publishes only candidates some feed shows; examined/clipped still describe the pool. Tests that asserted the published count equals the pool size now check the pool via candidatePool() and the published set against the feeds. - The post-harvest rescore failure test now breaks discovery.json right before the publish (via a scraper stand-in) instead of relying on tags: 5, and a following successful harvest clears the stale lastError. - Consent text names everything sent to AniList; paused label mentions admin harvests; the UI test asserts the XSS seed instead of skipping it. - Slot-mix test proves the fresh/outside reservations matter. - Docs: privacy note, fresh fallback reason, per-period cap, dry-run wording, published-candidate rule; plan/spec hygiene and spec status. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
613db96355
commit
cd6a30a76b
18 files changed
+250
-65
No files matched your search
+12
-11
@@ -15,19 +15,20 @@ module.exports = async function testDiscoveryUI(url, cookie, { xssTitle } = {})
|
||||
await page.goto(url + '/discover.html');
|
||||
await page.getByRole('heading', { name: 'Discover', exact: true }).waitFor();
|
||||
await page.getByText('Turn on personalized discovery').waitFor();
|
||||
await page.getByText('the server sends AniList the IDs of titles you liked, without login').waitFor();
|
||||
await page.getByText('Off by default. When on, the server sends AniList the IDs of titles you liked, read or own, '
|
||||
+ 'their authors\' IDs and your top tags, without login, to find related work. Your feed and feedback stay private to this server.').waitFor();
|
||||
await page.getByLabel('Personalized discovery').check();
|
||||
// Review focus 5: the seeded feed is the first one shown, before any run replaces it.
|
||||
if (xssTitle) {
|
||||
const seeded = page.locator('.discovery-card').filter({ hasText: 'AniList readers who liked Moon Garden' });
|
||||
await seeded.waitFor();
|
||||
assert.equal(await seeded.locator('h2').textContent(), xssTitle);
|
||||
assert.equal(await page.locator('.discovery-card h2 img').count(), 0);
|
||||
await seeded.getByText('Why this is here · evidence').click();
|
||||
const details = await seeded.locator('details li').allTextContents();
|
||||
assert.ok(details.some(text => text.includes('New: started 2025')), `All reasons listed: ${details}`);
|
||||
assert.ok(details.some(text => /AniList recommendation.* · fetched \S+/.test(text)), `Evidence fetch date: ${details}`);
|
||||
}
|
||||
// The XSS check must never be skipped silently: the Compose driver always seeds the title.
|
||||
assert.ok(xssTitle, 'The discovery UI test needs the seeded XSS title (run it through tools/test-compose.js --discovery-ui)');
|
||||
const seeded = page.locator('.discovery-card').filter({ hasText: 'AniList readers who liked Moon Garden' });
|
||||
await seeded.waitFor();
|
||||
assert.equal(await seeded.locator('h2').textContent(), xssTitle);
|
||||
assert.equal(await page.locator('.discovery-card h2 img').count(), 0);
|
||||
await seeded.getByText('Why this is here · evidence').click();
|
||||
const details = await seeded.locator('details li').allTextContents();
|
||||
assert.ok(details.some(text => text.includes('New: started 2025')), `All reasons listed: ${details}`);
|
||||
assert.ok(details.some(text => /AniList recommendation.* · fetched \S+/.test(text)), `Evidence fetch date: ${details}`);
|
||||
await page.getByRole('button', { name: 'Run discovery now' }).click();
|
||||
// The seeded store already says "success"; wait until the run's own feed replaced the seeded card.
|
||||
await page.waitForFunction(title => /Status: success/.test(document.querySelector('.discovery-admin')?.textContent || '')
|
||||
|
||||
Reference in new issue
Block a user