76 lines
3.3 KiB
TypeScript
76 lines
3.3 KiB
TypeScript
import { createMockFetch, TNODE_HOME_HTML, TNODE_HOME_NO_TOKEN, TNODE_SEARCH_OK } from "@umt/testkit";
|
|
import { describe, expect, it } from "vitest";
|
|
import { createSiteRateLimiter } from "../rate-limit.ts";
|
|
import { createSpecializedSiteAdapter } from "./adapter.ts";
|
|
import { adapterWith, expectSiteError, testDef, withTorrent } from "./testing.ts";
|
|
import { parseTnodeToken, tnodeSearchParams, tnodeSpider } from "./tnode.ts";
|
|
|
|
const def = testDef("TNodeSpider", { domain: "https://tnode.example/" });
|
|
|
|
describe("TNodeSpider(MP tnode.py)", () => {
|
|
it("凭据变更后不复用旧CSRF令牌", async () => {
|
|
let cookie = "session=first";
|
|
const mock = createMockFetch({
|
|
"GET /": { body: TNODE_HOME_HTML },
|
|
"POST /api/torrent/advancedSearch": { body: TNODE_SEARCH_OK },
|
|
});
|
|
const adapter = createSpecializedSiteAdapter(def, tnodeSpider, {
|
|
fetch: mock.fetch,
|
|
secrets: {
|
|
async resolve() {
|
|
return cookie;
|
|
},
|
|
},
|
|
limiter: createSiteRateLimiter({ minIntervalMs: 0 }),
|
|
});
|
|
await adapter.list(1);
|
|
await adapter.list(1);
|
|
cookie = "session=second";
|
|
await adapter.list(1);
|
|
expect(mock.requests.filter((request) => request.url.pathname === "/")).toHaveLength(2);
|
|
});
|
|
it("__get_params / token 解析", () => {
|
|
expect(tnodeSearchParams("tt0923811", 1)).toMatchObject({
|
|
page: 2,
|
|
size: 100,
|
|
type: "imdbid",
|
|
keyword: "tt0923811",
|
|
});
|
|
expect(tnodeSearchParams(null)).toMatchObject({ type: "title", keyword: "", category: [501, 502, 503, 504] });
|
|
expect(parseTnodeToken(TNODE_HOME_HTML)).toBe("CSRF-TOKEN-XYZ");
|
|
});
|
|
|
|
it("列表解析:CSRF token 缓存、系数直取、时间戳转 UTC", async () => {
|
|
const m = createMockFetch({
|
|
"GET /": { body: TNODE_HOME_HTML },
|
|
"POST /api/torrent/advancedSearch": { body: TNODE_SEARCH_OK },
|
|
});
|
|
const a = adapterWith(def, tnodeSpider, m.fetch);
|
|
const page = await a.search("eva");
|
|
await a.list(1);
|
|
expect(m.requests.filter((r) => r.url.pathname === "/")).toHaveLength(1);
|
|
expect(m.requests[1]!.headers["x-csrf-token"]).toBe("CSRF-TOKEN-XYZ");
|
|
const [x, y] = page.items;
|
|
expect(x).toMatchObject({
|
|
siteReleaseId: "3301",
|
|
promotion: { type: "free", downloadFactor: 0, uploadFactor: 1 },
|
|
uploadedAt: "2024-09-20T00:00:00.000Z",
|
|
imdbId: "tt0923811",
|
|
detailUrl: "https://tnode.example/torrent/info/3301",
|
|
hr: "unknown",
|
|
});
|
|
expect(y!.promotion.type).toBe("2x50%");
|
|
const t = withTorrent(m.fetch, (u) => u.pathname.startsWith("/api/torrent/download/"));
|
|
await adapterWith(def, tnodeSpider, t.fetch).downloadTarget("3301");
|
|
expect(t.hits[0]!.headers.get("cookie")).toBe("c=COOKIESECRET");
|
|
});
|
|
|
|
it("取不到 token → auth_expired;缺 Cookie → auth_missing;搜索 HTTP 403 → auth_invalid", async () => {
|
|
const m = createMockFetch({ "GET /": { body: TNODE_HOME_NO_TOKEN } });
|
|
await expectSiteError(adapterWith(def, tnodeSpider, m.fetch).list(1), "auth_expired");
|
|
await expectSiteError(adapterWith(def, tnodeSpider, m.fetch, {}).list(1), "auth_missing");
|
|
const f = createMockFetch({ "GET /": { body: TNODE_HOME_HTML }, "/api/torrent/advancedSearch": { status: 403 } });
|
|
await expectSiteError(adapterWith(def, tnodeSpider, f.fetch).list(1), "auth_invalid");
|
|
});
|
|
});
|