Files
UHD_Media_Tool/packages/adapters/src/sites/spiders/haidan.test.ts
T
rootandClaude Opus 5.5 d9f2af2f3c feat(adapters): port MoviePilot open site framework (7 spiders, 20 user parsers) + MovieClaw v0.27.0 site definitions + mp-sync tools (W1-3c)
- sites/spiders: Haidan/HDDolby/RousiPro/SunnyPT/TNode/TorrentLeech/Yema (+mTorrent wrapper), specialized SiteAdapter, indirect download resolved at download time
- sites/user-parsers: SiteParserBase + all 20 MP SiteSchema parsers (lxml XPath via xpath+xmldom), refreshSiteUserData with schema fallback
- sites/definitions: zod SiteDefinition schemas, 23 MovieClaw v0.27.0 (MIT) definitions via scripts/mp-sync/movieclaw-import.mjs, registry getSiteDefinition/listSiteDefinitions, NOTICE
- scripts/mp-sync: ledger.json (upstream sha256), check.mjs, build-ledger.mjs; docs/porting/mp-sync-ledger.md
- license-and-provenance: add copied-mit kind, re-anchor MovieClaw rows to v0.27.0

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-27 18:39:27 +08:00

59 lines
3.1 KiB
TypeScript

import { createMockFetch, HAIDAN_SEARCH_ERROR, HAIDAN_SEARCH_OK } from "@umt/testkit";
import { describe, expect, it } from "vitest";
import { haidanSearchQuery, haidanSpider } from "./haidan.ts";
import { adapterWith, expectSiteError, testDef, withTorrent } from "./testing.ts";
const def = testDef("Haidan", { domain: "https://www.haidan.video/" });
describe("HaiDanSpider(MP haidan.py)", () => {
it("__get_params:isapi/search_area/空关键词/分类逗号拼接", () => {
expect(haidanSearchQuery(null)).toBe("isapi=1&search_area=0&search=&search_mode=0&cat=");
expect(haidanSearchQuery("tt1160419", "tv")).toBe(
"isapi=1&search_area=4&search=tt1160419&search_mode=0&cat=402%2C403%2C404%2C405",
);
});
it("列表解析:字段映射、分类投影、促销系数表、passkey 脱敏", async () => {
const m = createMockFetch({ "/torrents.php": { body: HAIDAN_SEARCH_OK } });
const page = await adapterWith(def, haidanSpider, m.fetch).search("Dune", { mtype: "movie" });
expect(m.requests[0]!.headers.cookie).toBe("c=COOKIESECRET");
expect(m.requests[0]!.url.searchParams.get("cat")).toBe("401,404,405");
const by = Object.fromEntries(page.items.map((i) => [i.siteReleaseId, i]));
expect(by["30001"]).toMatchObject({
title: "Dune.Part.Two.2024.2160p.UHD.BluRay.REMUX.HDR.HEVC.Atmos-HDH",
subtitle: "沙丘2 | 4K 原盘 REMUX",
category: { id: "401" },
sizeBytes: 76543210987,
seeders: 88,
leechers: 3,
snatched: 412,
promotion: { type: "free", downloadFactor: 0, uploadFactor: 1 },
hr: "unknown",
uploadedAt: "2024-09-20T00:00:00.000Z",
detailUrl: "https://www.haidan.video/details.php?group_id=9001&torrent_id=30001",
raw: expect.objectContaining({ category: "电影" }),
});
expect(by["30002"]!.promotion).toMatchObject({ type: "2x50%", downloadFactor: 0.5, uploadFactor: 2 });
expect(by["30002"]!.raw.category).toBe("电视剧");
expect(by["30003"]!.raw.category).toBe("音乐");
expect(JSON.stringify(page)).not.toContain("HDPASSKEY123");
expect(page.totalPages).toBeNull();
// 不翻页:第 2 页直接为空
expect((await adapterWith(def, haidanSpider, m.fetch).list(2)).items).toEqual([]);
});
it("业务错误 / 缺 Cookie / 下载沿用缓存直链带 Cookie", async () => {
const bad = createMockFetch({ "/torrents.php": { body: HAIDAN_SEARCH_ERROR } });
await expectSiteError(adapterWith(def, haidanSpider, bad.fetch).list(1), "auth_invalid");
await expectSiteError(adapterWith(def, haidanSpider, bad.fetch, {}).list(1), "auth_missing");
const m = createMockFetch({ "/torrents.php": { body: HAIDAN_SEARCH_OK } });
const t = withTorrent(m.fetch, (u) => u.pathname === "/download.php");
const a = adapterWith(def, haidanSpider, t.fetch);
await a.list(1);
const target = await a.downloadTarget("30001");
expect(target).toMatchObject({ kind: "torrent", infoHash: t.infoHash });
expect(t.hits[0]!.url.searchParams.get("passkey")).toBe("HDPASSKEY123");
expect(t.hits[0]!.headers.get("cookie")).toBe("c=COOKIESECRET");
});
});