app-template/packages/danmaku/src/client.test.ts

116 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { afterEach, describe, expect, it, vi } from "vitest";
import {
hasOpenCredentials,
openApiBase,
openCommentXml,
openHeaders,
openMatch,
readOpenCredentials,
} from "./client.js";
afterEach(() => {
vi.unstubAllEnvs();
vi.unstubAllGlobals();
});
describe("readOpenCredentials", () => {
it("reads from injected env", () => {
expect(
readOpenCredentials({ OPEN_DANMAKU_APP_ID: "id", OPEN_DANMAKU_APP_SECRET: "s" }),
).toEqual({
appId: "id",
appSecret: "s",
});
});
it("returns null when missing", () => {
expect(readOpenCredentials({})).toBeNull();
expect(readOpenCredentials({ OPEN_DANMAKU_APP_ID: "id" })).toBeNull();
});
it("defaults to process.env", () => {
vi.stubEnv("OPEN_DANMAKU_APP_ID", "pid");
vi.stubEnv("OPEN_DANMAKU_APP_SECRET", "ps");
expect(readOpenCredentials()).toEqual({ appId: "pid", appSecret: "ps" });
expect(hasOpenCredentials()).toBe(true);
});
});
describe("openApiBase", () => {
it("strips trailing slashes", () => {
vi.stubEnv("OPEN_DANMAKU_API_BASE", "https://example.test///");
expect(openApiBase()).toBe("https://example.test");
});
it("defaults to dandanplay api", () => {
vi.stubEnv("OPEN_DANMAKU_API_BASE", undefined);
expect(openApiBase()).toBe("https://api.dandanplay.net");
});
});
describe("openHeaders", () => {
it("signs request when credentials present", () => {
const h = openHeaders("/api/v2/match", { appId: "id", appSecret: "s" });
expect(h["Content-Type"]).toBe("application/json");
expect(h["X-AppId"]).toBe("id");
expect(h["X-Timestamp"]).toMatch(/^\d+$/);
expect(h["X-Signature"]).toBeTruthy();
});
it("only content-type without credentials", () => {
expect(openHeaders("/api/v2/match", null)).toEqual({ "Content-Type": "application/json" });
});
});
function stubFetchJson(json: unknown) {
const fetchMock = vi.fn().mockResolvedValue({
ok: true,
status: 200,
json: async () => json,
});
vi.stubGlobal("fetch", fetchMock);
return fetchMock;
}
describe("openCommentXml 请求契约", () => {
it("请求路径含 ?withRelated=true(关联弹幕计入)", async () => {
const fetchMock = stubFetchJson({ success: true, comments: [] });
await openCommentXml(555);
expect(fetchMock).toHaveBeenCalledTimes(1);
const url = String(fetchMock.mock.calls[0]?.[0]);
expect(url).toBe("https://api.dandanplay.net/api/v2/comment/555?withRelated=true");
});
it("comments 序列化为 xml,HTTP 失败返回 null", async () => {
const fetchMock = stubFetchJson({
success: true,
comments: [{ p: "12.5,1,0,0,0,0,,", m: "hi" }],
});
await expect(openCommentXml(555)).resolves.toContain("<d ");
expect(fetchMock).toHaveBeenCalledTimes(1);
fetchMock.mockResolvedValue({ ok: false, status: 500, json: async () => ({}) });
await expect(openCommentXml(555)).resolves.toBeNull();
});
});
describe("openMatch 请求契约", () => {
it("POST /api/v2/match,matchMode 随 hash 切换", async () => {
const fetchMock = stubFetchJson({ success: true, isMatched: true, matches: [] });
await openMatch("Show - 02", 100, "abc");
const [url1, init1] = fetchMock.mock.calls[0] ?? [];
expect(String(url1)).toBe("https://api.dandanplay.net/api/v2/match");
expect(init1?.method).toBe("POST");
expect(JSON.parse(String(init1?.body))).toEqual({
fileName: "Show - 02",
fileHash: "abc",
fileSize: 100,
matchMode: "hashAndFileName",
});
await openMatch("Show - 02", 100, null);
const init2 = fetchMock.mock.calls[1]?.[1];
expect(JSON.parse(String(init2?.body))).toMatchObject({
fileHash: "",
matchMode: "fileNameOnly",
});
});
});