返回 DeepSeek-Reasonix
admin.test.ts
根目录 / workers / crash-report / src / registry / routes / admin.test.ts
1 import { afterEach, describe, expect, it, vi } from "vitest";
2 import registryApp from "../app";
3 import type { Bindings } from "../env";
4 import type { PackageRow } from "../types";
5
6 const oldRevision: PackageRow = {
7 id: 42,
8 kind: "plugin",
9 scope_handle: "publisher",
10 name: "devkit",
11 slug: "publisher/devkit",
12 summary: "Developer tools",
13 description: "",
14 source: "https://github.com/o/r",
15 install_kind: "plugin",
16 homepage: "",
17 repo_url: "https://github.com/o/r",
18 tags: "tool",
19 latest_version: "2.7.1",
20 install_count: 0,
21 star_count: 0,
22 verified: 0,
23 status: "pending",
24 publisher_id: 7,
25 created_at: "2026-07-22T00:00:00.000Z",
26 updated_at: "2026-07-22T00:30:00.000Z",
27 };
28
29 function approvalDB(current: PackageRow, approved: PackageRow | null) {
30 const statements: { sql: string; values: unknown[] }[] = [];
31 const db = {
32 prepare(sql: string) {
33 let values: unknown[] = [];
34 const statement = {
35 bind(...bound: unknown[]) {
36 values = bound;
37 return statement;
38 },
39 async first<T>() {
40 statements.push({ sql, values });
41 if (sql.startsWith("UPDATE packages SET status")) return approved as T | null;
42 if (sql.startsWith("SELECT * FROM packages")) return current as T;
43 return null;
44 },
45 async run() {
46 statements.push({ sql, values });
47 return { meta: { changes: 1 } };
48 },
49 };
50 return statement;
51 },
52 };
53 return { db: db as unknown as D1Database, statements };
54 }
55
56 function bindings(db: D1Database): Bindings {
57 return {
58 DB: db,
59 ACCOUNTS_ORIGIN: "https://id.reasonix.test",
60 APP_ORIGIN: "https://reasonix.test",
61 ALLOWED_ORIGINS: "https://reasonix.test",
62 };
63 }
64
65 function approvalRequest(body: object): Request {
66 return new Request("https://registry.reasonix.test/v1/admin/packages/publisher/devkit/approve", {
67 method: "POST",
68 headers: { cookie: "rxid=test", "content-type": "application/json" },
69 body: JSON.stringify(body),
70 });
71 }
72
73 afterEach(() => vi.unstubAllGlobals());
74
75 describe("admin package approval", () => {
76 it("fails closed when an older review page omits the revision", async () => {
77 vi.stubGlobal(
78 "fetch",
79 vi.fn(async () =>
80 Response.json({ user: { id: 1, handle: "admin", role: "admin", emailVerified: true } }),
81 ),
82 );
83 const { db, statements } = approvalDB(oldRevision, null);
84
85 const response = await registryApp.fetch(approvalRequest({}), bindings(db));
86
87 expect(response.status).toBe(400);
88 await expect(response.json()).resolves.toEqual({
89 error: {
90 code: "invalid_review_revision",
91 message: "Approval requires the reviewed package revision.",
92 },
93 });
94 expect(statements).toHaveLength(0);
95 });
96
97 it("rejects a stale review after the publisher submits a newer version", async () => {
98 vi.stubGlobal(
99 "fetch",
100 vi.fn(async () =>
101 Response.json({ user: { id: 1, handle: "admin", role: "admin", emailVerified: true } }),
102 ),
103 );
104 const current = { ...oldRevision, latest_version: "2.7.2", updated_at: "2026-07-22T00:45:00.000Z" };
105 const { db, statements } = approvalDB(current, null);
106
107 const response = await registryApp.fetch(
108 approvalRequest({
109 expectedVersion: oldRevision.latest_version,
110 expectedUpdatedAt: oldRevision.updated_at,
111 expectedStatus: oldRevision.status,
112 }),
113 bindings(db),
114 );
115
116 expect(response.status).toBe(409);
117 await expect(response.json()).resolves.toEqual({
118 error: {
119 code: "stale_review",
120 message: "Package changed since it was reviewed. Refresh and review the latest version.",
121 },
122 });
123 expect(statements.some(({ sql }) => sql.startsWith("INSERT INTO events"))).toBe(false);
124 });
125
126 it("publishes when the submitted review revision still matches", async () => {
127 vi.stubGlobal(
128 "fetch",
129 vi.fn(async () =>
130 Response.json({ user: { id: 1, handle: "admin", role: "admin", emailVerified: true } }),
131 ),
132 );
133 const approved = { ...oldRevision, status: "active", updated_at: "2026-07-22T01:00:00.000Z" };
134 const { db, statements } = approvalDB(oldRevision, approved);
135
136 const response = await registryApp.fetch(
137 approvalRequest({
138 expectedVersion: oldRevision.latest_version,
139 expectedUpdatedAt: oldRevision.updated_at,
140 expectedStatus: oldRevision.status,
141 }),
142 bindings(db),
143 );
144
145 expect(response.status).toBe(200);
146 const body = (await response.json()) as { package: { status: string; latestVersion: string } };
147 expect(body.package).toMatchObject({ status: "active", latestVersion: "2.7.1" });
148 expect(statements.some(({ sql }) => sql.startsWith("INSERT INTO events"))).toBe(true);
149 });
150 });
151
151 lines TYPESCRIPT