Consolidate MangaCircus app, scraper, and Compose baseline
This commit is contained in:
1 parent
cfb2d79103
commit
eef4ffa07c
103 files changed
+9316
-172
No files matched your search
@@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
const test = require('node:test'), assert = require('node:assert/strict'), fs = require('fs'), os = require('os'), path = require('path');
|
||||
const { Catalog, normalize, collectionViews } = require('../catalog');
|
||||
const input = () => ({ collections: [{ id: 'franchise', title: 'Franchise', members: [{ mangaId: 'a', role: 'main', readingOrder: 1 }, { mangaId: 'b', role: 'sequel', readingOrder: 2 }] }], relations: [{ from: 'a', to: 'b', type: 'sequel', state: 'confirmed', evidence: [] }] });
|
||||
test('collections persist without changing manga identities and reject stale edits', () => {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'mc-catalog-'));
|
||||
try {
|
||||
const c = new Catalog(dir); c.update({ ...input(), revision: 0 }, new Set(['a', 'b']), 'admin');
|
||||
assert.equal(new Catalog(dir).state.revision, 1);
|
||||
assert.throws(() => c.update({ ...input(), revision: 0 }, new Set(['a', 'b']), 'admin'), { code: 409 });
|
||||
assert.deepEqual(c.state.collections[0].members.map(m => m.mangaId), ['a', 'b']);
|
||||
assert.throws(() => normalize({ ...input(), collections: [...input().collections, { id: 'other', title: 'Other', members: [{ mangaId: 'a' }] }] }, new Set(['a', 'b'])), /already belongs/);
|
||||
assert.throws(() => normalize({ ...input(), relations: [{ from: 'a', to: 'a', type: 'sequel' }] }, new Set(['a', 'b'])), /Invalid relation/);
|
||||
} finally { fs.rmSync(dir, { recursive: true, force: true }); }
|
||||
});
|
||||
test('collections aggregate progress once and preserve absent members', () => {
|
||||
const normalized = normalize(input(), new Set(['a', 'b']));
|
||||
const views = collectionViews(normalized, [{ id: 'a', title: 'A', chapters: 3, read: 2, unread: 1, tags: ['Fantasy'], status: 'completed', reading: { activeMs: 30 } }, { id: 'b', title: 'B', chapters: 2, read: 1, unread: 1, tags: ['Fantasy'], status: 'ongoing', reading: { activeMs: 20 } }]);
|
||||
assert.equal(views[0].chapters, 5); assert.equal(views[0].read, 3); assert.equal(views[0].reading.activeMs, 50);
|
||||
assert.equal(views[0].status, 'ongoing'); assert.deepEqual(views[0].tags, ['Fantasy']);
|
||||
assert.equal(normalize(normalized, new Set(['a']), normalized).collections[0].members.length, 2);
|
||||
});
|
||||
test('metadata suggestions retain manual and rejected decisions', () => {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'mc-suggestions-'));
|
||||
try {
|
||||
const c = new Catalog(dir);
|
||||
const mangas = [{ id: 'a', meta: { 'x-anilist': { id: 10, relations: [{ id: 11, type: 'SEQUEL' }] } } }, { id: 'b', meta: { 'x-anilist': { id: 11 } } }];
|
||||
const relations = c.suggest(mangas); assert.equal(relations.length, 1); assert.equal(relations[0].state, 'suggested');
|
||||
c.update({ revision: 0, collections: [], relations: [{ ...relations[0], state: 'rejected' }] }, new Set(['a', 'b']), 'admin');
|
||||
assert.equal(c.suggest(mangas).length, 1); assert.equal(c.suggest(mangas)[0].state, 'rejected');
|
||||
} finally { fs.rmSync(dir, { recursive: true, force: true }); }
|
||||
});
|
||||
Reference in new issue
Block a user