test(danmaku): assert openCommentXml withRelated contract

This commit is contained in:
noelorin 2026-09-27 00:04:58 +08:00
parent 5b095d677f
commit 07c7108789
2 changed files with 66 additions and 3 deletions

View File

@ -17,8 +17,8 @@
| `@app/models` | Drizzle 表定义(唯一 schema 源) | drizzle-orm | | `@app/models` | Drizzle 表定义(唯一 schema 源) | drizzle-orm |
| `@app/db` | libSQL/Turso 客户端单例 + drizzle-kit | models | | `@app/db` | libSQL/Turso 客户端单例 + drizzle-kit | models |
| `@app/dao` | 数据访问层(`userDao` / `mountDao` / `mediaItemDao` 等) | db, models | | `@app/dao` | 数据访问层(`userDao` / `mountDao` / `mediaItemDao` 等) | db, models |
| `@app/trpc` | tRPC 路由 + service 业务逻辑(调用 DAO) | types, dao | | `@app/trpc` | tRPC 路由 + service 业务逻辑(调用 DAO) | types, dao, danmaku |
| `@app/web` | Astro 前端 + React islands + 本地 tRPC HTTP 适配 | trpc, types, design-tokens, seo-geo, i18n, ui | | `@app/web` | Astro 前端 + React islands + 本地 tRPC HTTP 适配 | trpc, types, danmaku(`./browser`), design-tokens, seo-geo, i18n, ui |
| `@app/ui` | shadcn 风格基础 UI 组件(源码直出 + Storybook) | design-tokens, react (peer) | | `@app/ui` | shadcn 风格基础 UI 组件(源码直出 + Storybook) | design-tokens, react (peer) |
| `@app/design-tokens` | 共享 CSS 设计令牌(`tokens.css`) | — | | `@app/design-tokens` | 共享 CSS 设计令牌(`tokens.css`) | — |
| `@app/seo-geo` | SEO/GEO 纯函数(meta、sitemap、JSON-D、llms.txt) | zod, schema-dts, escape-html, fast-xml-builder, serialize-javascript, mdast-util-to-markdown | | `@app/seo-geo` | SEO/GEO 纯函数(meta、sitemap、JSON-D、llms.txt) | zod, schema-dts, escape-html, fast-xml-builder, serialize-javascript, mdast-util-to-markdown |

View File

@ -1,5 +1,12 @@
import { afterEach, describe, expect, it, vi } from "vitest"; import { afterEach, describe, expect, it, vi } from "vitest";
import { hasOpenCredentials, openApiBase, openHeaders, readOpenCredentials } from "./client.js"; import {
hasOpenCredentials,
openApiBase,
openCommentXml,
openHeaders,
openMatch,
readOpenCredentials,
} from "./client.js";
afterEach(() => { afterEach(() => {
vi.unstubAllEnvs(); vi.unstubAllEnvs();
@ -50,3 +57,59 @@ describe("openHeaders", () => {
expect(openHeaders("/api/v2/match", null)).toEqual({ "Content-Type": "application/json" }); 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",
});
});
});