返回 DeepSeek-Reasonix
packages.test.ts
根目录 / workers / crash-report / src / registry / db / packages.test.ts
1 import { describe, expect, it } from "vitest";
2 import type { PackageRow, RegistryUser } from "../types";
3 import { PublishSchema } from "../lib/validation";
4 import { PackageRepo } from "./packages";
5
6 const now = "2026-07-22T00:00:00.000Z";
7 const user: RegistryUser = {
8 id: 7,
9 handle: "publisher",
10 role: "member",
11 emailVerified: true,
12 };
13
14 const existing: PackageRow = {
15 id: 42,
16 kind: "mcp",
17 scope_handle: "publisher",
18 name: "devkit",
19 slug: "publisher/devkit",
20 summary: "old",
21 description: "",
22 source: "https://github.com/o/r",
23 install_kind: "auto",
24 homepage: "",
25 repo_url: "https://github.com/o/r",
26 tags: "tool",
27 latest_version: "2.7.0",
28 status: "pending",
29 verified: 0,
30 publisher_id: 7,
31 install_count: 0,
32 star_count: 0,
33 created_at: now,
34 updated_at: now,
35 };
36
37 function fakePackageDB(reads: PackageRow[]) {
38 const updates: { sql: string; values: unknown[] }[] = [];
39 let packageReads = 0;
40 const db = {
41 prepare(sql: string) {
42 let values: unknown[] = [];
43 const statement = {
44 bind(...bound: unknown[]) {
45 values = bound;
46 return statement;
47 },
48 async first<T>() {
49 if (sql.startsWith("SELECT * FROM packages")) {
50 const row = reads[Math.min(packageReads, reads.length - 1)];
51 packageReads += 1;
52 return row as T;
53 }
54 return null;
55 },
56 async run() {
57 if (sql.startsWith("UPDATE packages SET")) updates.push({ sql, values });
58 return { meta: { changes: 1 } };
59 },
60 };
61 return statement;
62 },
63 };
64 return { db: db as unknown as D1Database, updates };
65 }
66
67 function pluginInput() {
68 return PublishSchema.parse({
69 kind: "plugin",
70 installKind: "plugin",
71 name: "devkit",
72 source: "https://github.com/o/r",
73 repoUrl: "https://github.com/o/r",
74 version: "2.7.1",
75 });
76 }
77
78 describe("PackageRepo.publish", () => {
79 it("persists a kind change when an owned pending package is republished as a plugin", async () => {
80 const updated: PackageRow = { ...existing, kind: "plugin", install_kind: "plugin", latest_version: "2.7.1" };
81 const { db, updates } = fakePackageDB([existing, updated]);
82 const result = await new PackageRepo(db).publish(user, pluginInput(), now);
83
84 expect(result.created).toBe(false);
85 expect(result.row.kind).toBe("plugin");
86 expect(updates).toHaveLength(1);
87 expect(updates[0].sql).toContain("SET kind = ?1");
88 expect(updates[0].values[0]).toBe("plugin");
89 expect(updates[0].values[4]).toBe("plugin");
90 expect(updates[0].values[10]).toBe("pending");
91 expect(updates[0].values[11]).toBe(0);
92 expect(updates[0].values[12]).toBe(existing.id);
93 });
94
95 it("returns an active verified package to review when its kind changes", async () => {
96 const active: PackageRow = { ...existing, status: "active", verified: 1 };
97 const requeued: PackageRow = {
98 ...active,
99 kind: "plugin",
100 install_kind: "plugin",
101 latest_version: "2.7.1",
102 status: "pending",
103 verified: 0,
104 };
105 const { db, updates } = fakePackageDB([active, requeued]);
106
107 const result = await new PackageRepo(db).publish(user, pluginInput(), now);
108
109 expect(result.row.status).toBe("pending");
110 expect(result.row.verified).toBe(0);
111 expect(updates[0].values[10]).toBe("pending");
112 expect(updates[0].values[11]).toBe(0);
113 });
114
115 it("returns a same-kind active package update to review", async () => {
116 const active: PackageRow = { ...existing, status: "active", verified: 1 };
117 const updated: PackageRow = {
118 ...active,
119 summary: "new summary",
120 source: "https://github.com/o/r2",
121 repo_url: "https://github.com/o/r2",
122 install_kind: "mcp",
123 latest_version: "2.7.1",
124 status: "pending",
125 verified: 0,
126 };
127 const { db, updates } = fakePackageDB([active, updated]);
128 const input = PublishSchema.parse({
129 kind: "mcp",
130 name: "devkit",
131 summary: "new summary",
132 source: "https://github.com/o/r2",
133 repoUrl: "https://github.com/o/r2",
134 version: "2.7.1",
135 });
136
137 const result = await new PackageRepo(db).publish(user, input, now);
138
139 expect(result.row.status).toBe("pending");
140 expect(result.row.verified).toBe(0);
141 expect(updates[0].values[4]).toBe("mcp");
142 expect(updates[0].values[10]).toBe("pending");
143 expect(updates[0].values[11]).toBe(0);
144 });
145
146 it("returns a hidden package update to review and clears verification", async () => {
147 const hidden: PackageRow = { ...existing, status: "hidden", verified: 1 };
148 const updated: PackageRow = {
149 ...hidden,
150 kind: "plugin",
151 install_kind: "plugin",
152 latest_version: "2.7.1",
153 status: "pending",
154 verified: 0,
155 };
156 const { db, updates } = fakePackageDB([hidden, updated]);
157
158 const result = await new PackageRepo(db).publish(user, pluginInput(), now);
159
160 expect(result.row.status).toBe("pending");
161 expect(result.row.verified).toBe(0);
162 expect(updates[0].values[10]).toBe("pending");
163 expect(updates[0].values[11]).toBe(0);
164 });
165
166 it("returns a rejected package update to review", async () => {
167 const rejected: PackageRow = { ...existing, status: "rejected", verified: 0 };
168 const requeued: PackageRow = { ...rejected, latest_version: "2.7.1", status: "pending" };
169 const { db, updates } = fakePackageDB([rejected, requeued]);
170 const input = PublishSchema.parse({
171 kind: "mcp",
172 name: "devkit",
173 source: "https://github.com/o/r",
174 repoUrl: "https://github.com/o/r",
175 version: "2.7.1",
176 });
177
178 const result = await new PackageRepo(db).publish(user, input, now);
179
180 expect(result.row.status).toBe("pending");
181 expect(updates[0].values[10]).toBe("pending");
182 expect(updates[0].values[11]).toBe(0);
183 });
184
185 it("preserves status and verification for trusted admin updates", async () => {
186 const admin: RegistryUser = { ...user, role: "admin" };
187 const active: PackageRow = { ...existing, status: "active", verified: 1 };
188 const updated: PackageRow = { ...active, install_kind: "mcp", latest_version: "2.7.1" };
189 const { db, updates } = fakePackageDB([active, updated]);
190 const input = PublishSchema.parse({
191 kind: "mcp",
192 name: "devkit",
193 source: "https://github.com/o/r",
194 repoUrl: "https://github.com/o/r",
195 version: "2.7.1",
196 });
197
198 const result = await new PackageRepo(db).publish(admin, input, now);
199
200 expect(result.row.status).toBe("active");
201 expect(result.row.verified).toBe(1);
202 expect(updates[0].values[10]).toBe("active");
203 expect(updates[0].values[11]).toBe(1);
204 });
205 });
206
207 describe("PackageRepo.setStatusIfCurrent", () => {
208 it("approves only the exact package revision the admin reviewed", async () => {
209 const approvedAt = "2026-07-22T01:00:00.000Z";
210 const approved: PackageRow = { ...existing, status: "active", updated_at: approvedAt };
211 const statements: { sql: string; values: unknown[] }[] = [];
212 const db = {
213 prepare(sql: string) {
214 let values: unknown[] = [];
215 const statement = {
216 bind(...bound: unknown[]) {
217 values = bound;
218 return statement;
219 },
220 async first<T>() {
221 statements.push({ sql, values });
222 return approved as T;
223 },
224 };
225 return statement;
226 },
227 } as unknown as D1Database;
228
229 const row = await new PackageRepo(db).setStatusIfCurrent(
230 existing.slug,
231 "active",
232 existing.latest_version,
233 existing.updated_at,
234 existing.status,
235 approvedAt,
236 );
237
238 expect(row).toEqual(approved);
239 expect(statements[0].sql).toContain("latest_version = ?4 AND updated_at = ?5 AND status = ?6");
240 expect(statements[0].sql).toContain("RETURNING *");
241 expect(statements[0].values).toEqual([
242 "active",
243 approvedAt,
244 existing.slug,
245 existing.latest_version,
246 existing.updated_at,
247 existing.status,
248 ]);
249 });
250
251 it("returns null when a newer package revision no longer matches", async () => {
252 const statements: { sql: string; values: unknown[] }[] = [];
253 const db = {
254 prepare(sql: string) {
255 let values: unknown[] = [];
256 const statement = {
257 bind(...bound: unknown[]) {
258 values = bound;
259 return statement;
260 },
261 async first<T>() {
262 statements.push({ sql, values });
263 return null as T | null;
264 },
265 };
266 return statement;
267 },
268 } as unknown as D1Database;
269
270 const row = await new PackageRepo(db).setStatusIfCurrent(
271 existing.slug,
272 "active",
273 existing.latest_version,
274 existing.updated_at,
275 existing.status,
276 "2026-07-22T01:00:00.000Z",
277 );
278
279 expect(row).toBeNull();
280 expect(statements).toHaveLength(1);
281 });
282 });
283
283 lines TYPESCRIPT