Files
MangaCircus/test/discovery.test.js
T

49 lines
3.1 KiB
JavaScript

'use strict';
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const { generate } = require('../discovery/engine');
const { DiscoveryStore } = require('../discovery/store');
test('discovery is bounded, diversified, opt-in, evidence-linked and user-specific', () => {
const { loadCatalog } = require('../greatest');
const greatest = loadCatalog();
const users = { alice: { name: 'alice', settings: { discoveryEnabled: true } },
bob: { name: 'bob', settings: { discoveryEnabled: true } },
off: { name: 'off', settings: {} } };
const db = { users, greatestRatings: { alice: { [greatest.entries[0].id]: 'flame' },
bob: { [greatest.entries[20].id]: 'flame' } }, discoveryFeedback: { alice: {} }, progress: {} };
const result = generate({ db, library: [], greatest, cacheDir: path.join(os.tmpdir(), 'nonexistent-discovery-cache') });
assert.equal(Object.keys(result.candidates).length, 50);
assert.ok(result.feeds.alice.items.length > 0 && result.feeds.alice.items.length <= 20);
assert.ok(result.feeds.bob.items.length > 0);
assert.equal(result.feeds.off.disabled, true);
assert.ok(new Set(Object.values(result.candidates).map(c => c.period)).size >= 10, 'An early catalog page must not crowd out later eras');
assert.ok(result.feeds.alice.items.every(item => item.evidenceIds.every(id => item.evidence.some(e => e.id === id))));
assert.ok(result.feeds.alice.items.some(item => item.evidence.some(e => e.kind === 'personal')));
assert.ok(!result.feeds.bob.items.some(item => item.evidence.some(e => e.label?.includes(greatest.entries[0].title) && e.kind === 'personal')));
assert.notDeepEqual(result.feeds.alice.items.map(x => x.candidateId), result.feeds.bob.items.map(x => x.candidateId));
const first = result.feeds.alice.items[0].candidateId;
db.discoveryFeedback.alice[first] = 'dismissed';
const rerun = generate({ db, library: [], greatest, cacheDir: path.join(os.tmpdir(), 'nonexistent-discovery-cache') });
assert.ok(!rerun.feeds.alice.items.some(item => item.candidateId === first));
assert.equal(rerun.feeds.bob.items.length, result.feeds.bob.items.length);
});
test('discovery store publishes atomically and refuses to overwrite corrupt data', t => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'mc-discovery-store-'));
t.after(() => fs.rmSync(dir, { recursive: true, force: true }));
const store = new DiscoveryStore(dir);
assert.equal(store.load().revision, 0);
const value = store.publish({ candidates: {}, feeds: {}, startedAt: new Date().toISOString(), examined: 0, clipped: 0 });
assert.equal(value.revision, 1);
store.publish({ candidates: {}, feeds: {}, startedAt: new Date().toISOString(), examined: 0, clipped: 0 });
assert.equal(store.load().revision, 2);
assert.ok(fs.existsSync(store.file + '.prev'));
fs.writeFileSync(store.file, '{broken');
assert.throws(() => store.publish({ candidates: {}, feeds: {} }), /refusing to overwrite/);
assert.equal(fs.readFileSync(store.file, 'utf8'), '{broken');
});