'use strict'; const test = require('node:test'); const assert = require('node:assert/strict'); const { parse } = require('../plugins/registry'); const { validate } = require('../plugins/validation'); const { discoverWeebCatalog, discoverWeebChapter } = require('../adapters/weebcentral'); const { adapters, adapterFor } = require('../adapters'); const seriesUrl = 'https://weebcentral.com/series/ABC/title'; const chapterUrl = 'https://weebcentral.com/chapters/XYZ'; const seriesHtml = '

Test Manga

'; const listHtml = 'Chapter 1Log. 2'; const chapterHtml = ''; const imagesHtml = ''; test('first-party registry rejects unknown plugins and malformed input', () => { assert.throws(() => parse('remote-module', 'catalog', { html: listHtml, url: seriesUrl }), /Unknown/); assert.throws(() => parse('weebcentral', 'catalog', { html: listHtml, url: 'file:///tmp/x' }), /Invalid source URL/); assert.throws(() => parse('weebcentral', 'catalog', { html: listHtml, url: 'https://other.example/series/a' }), /does not belong/); assert.equal(parse('weebcentral', 'catalog', { html: listHtml, url: seriesUrl }).chapters.length, 2); }); test('parser output cannot turn catalog and image links into local or unrelated requests', () => { assert.throws(() => validate('weebcentral', 'series', { title: 'X', listUrl: 'http://127.0.0.1:8091/api/state' }), /unexpected/); assert.throws(() => validate('weebcentral', 'catalog', { chapterLinks: 1, chapters: [{ id: 'X', number: '1', url: 'https://other.example/chapters/X' }] }), /unexpected/); assert.throws(() => validate('weebcentral', 'chapter', { manifestUrl: 'https://weebcentral.com/chapters/X/not-images' }), /manifest/); assert.throws(() => validate('weebcentral', 'pages', { pages: ['http://127.0.0.1:8080/private'] }), /unsafe/); assert.throws(() => validate('weebcentral', 'pages', { pages: ['https://localhost/private'] }), /unsafe/); assert.doesNotThrow(() => validate('weebcentral', 'pages', { pages: ['https://img.example/1.jpg'] })); }); test('in-process parser preserves WeebCentral catalog and page discovery', async () => { const requested = []; const http = { async get(url) { requested.push(url); const body = url === seriesUrl ? seriesHtml : url.endsWith('/full-chapter-list') ? listHtml : url === chapterUrl ? chapterHtml : imagesHtml; return { text: async () => body }; } }; const catalog = await discoverWeebCatalog(seriesUrl, http); assert.equal(catalog.title, 'Test Manga'); assert.deepEqual(catalog.chapters.map(chapter => chapter.number), ['1', '2']); const chapter = await discoverWeebChapter(catalog.chapters[0], http); assert.equal(chapter.pages.length, 2); assert.equal(chapter.added, '2026-01-01'); assert.equal(requested.length, 4); }); test('a new source is an explicit adapter, not another container or remote module', () => { assert.deepEqual(adapters.map(adapter => adapter.id), ['weebcentral', 'mangadex', 'mangapill']); for (const adapter of adapters) { assert.equal(typeof adapter.match, 'function'); assert.equal(typeof adapter.discover, 'function'); assert.equal(typeof adapter.run, 'function'); } assert.equal(adapterFor(seriesUrl)?.adapter.id, 'weebcentral'); assert.equal(adapterFor('https://unknown.example/series/ABC'), null); });