@siteping/adapter-kit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,484 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/testing.ts
21
+ var testing_exports = {};
22
+ __export(testing_exports, {
23
+ testSitepingStore: () => testSitepingStore
24
+ });
25
+ module.exports = __toCommonJS(testing_exports);
26
+
27
+ // ../core/src/testing.ts
28
+ var import_vitest = require("vitest");
29
+
30
+ // ../core/src/type-utils.ts
31
+ function isRecord(value) {
32
+ return typeof value === "object" && value !== null;
33
+ }
34
+ function hasOwn(value, key) {
35
+ return isRecord(value) && key in value;
36
+ }
37
+
38
+ // ../core/src/types.ts
39
+ var StoreNotFoundError = class extends Error {
40
+ code = "STORE_NOT_FOUND";
41
+ constructor(message = "Record not found") {
42
+ super(message);
43
+ this.name = "StoreNotFoundError";
44
+ }
45
+ };
46
+ var StoreDuplicateError = class extends Error {
47
+ code = "STORE_DUPLICATE";
48
+ constructor(message = "Duplicate record") {
49
+ super(message);
50
+ this.name = "StoreDuplicateError";
51
+ }
52
+ };
53
+ function hasErrorCode(error, code) {
54
+ return hasOwn(error, "code") && error.code === code;
55
+ }
56
+ function isStoreDuplicate(error) {
57
+ if (error instanceof StoreDuplicateError) return true;
58
+ return hasErrorCode(error, "P2002");
59
+ }
60
+
61
+ // ../core/src/testing.ts
62
+ function createInput(overrides) {
63
+ return {
64
+ projectName: "test-project",
65
+ type: "bug",
66
+ message: "Something is broken",
67
+ status: "open",
68
+ url: "https://example.com",
69
+ viewport: "1920x1080",
70
+ userAgent: "Mozilla/5.0",
71
+ authorName: "Alice",
72
+ authorEmail: "alice@test.com",
73
+ clientId: `client-${Date.now()}-${Math.random()}`,
74
+ annotations: [
75
+ {
76
+ cssSelector: "div.main",
77
+ xpath: "/html/body/div",
78
+ textSnippet: "Hello",
79
+ elementTag: "DIV",
80
+ elementId: "main",
81
+ textPrefix: "before",
82
+ textSuffix: "after",
83
+ fingerprint: "3:1:abc",
84
+ neighborText: "sibling",
85
+ xPct: 0.1,
86
+ yPct: 0.2,
87
+ wPct: 0.5,
88
+ hPct: 0.3,
89
+ scrollX: 0,
90
+ scrollY: 100,
91
+ viewportW: 1920,
92
+ viewportH: 1080,
93
+ devicePixelRatio: 2
94
+ }
95
+ ],
96
+ ...overrides
97
+ };
98
+ }
99
+ var MINIMAL_ANNOTATION = {
100
+ cssSelector: "div",
101
+ xpath: "/div",
102
+ textSnippet: "",
103
+ elementTag: "DIV",
104
+ textPrefix: "",
105
+ textSuffix: "",
106
+ fingerprint: "1:0:x",
107
+ neighborText: "",
108
+ xPct: 0,
109
+ yPct: 0,
110
+ wPct: 1,
111
+ hPct: 1,
112
+ scrollX: 0,
113
+ scrollY: 0,
114
+ viewportW: 1920,
115
+ viewportH: 1080,
116
+ devicePixelRatio: 1
117
+ };
118
+ function testSitepingStore(factory, options) {
119
+ const duplicateBehavior = options?.duplicateBehavior ?? "return";
120
+ const caseInsensitiveSearch = options?.caseInsensitiveSearch ?? true;
121
+ (0, import_vitest.describe)("SitepingStore conformance", () => {
122
+ let store;
123
+ (0, import_vitest.beforeEach)(async () => {
124
+ store = await factory();
125
+ });
126
+ (0, import_vitest.describe)("createFeedback", () => {
127
+ (0, import_vitest.it)("creates a feedback and returns a FeedbackRecord", async () => {
128
+ const record = await store.createFeedback(createInput());
129
+ (0, import_vitest.expect)(record.id).toBeDefined();
130
+ (0, import_vitest.expect)(record.projectName).toBe("test-project");
131
+ (0, import_vitest.expect)(record.type).toBe("bug");
132
+ (0, import_vitest.expect)(record.message).toBe("Something is broken");
133
+ (0, import_vitest.expect)(record.status).toBe("open");
134
+ (0, import_vitest.expect)(record.resolvedAt).toBeNull();
135
+ (0, import_vitest.expect)(record.createdAt).toBeInstanceOf(Date);
136
+ (0, import_vitest.expect)(record.updatedAt).toBeInstanceOf(Date);
137
+ });
138
+ (0, import_vitest.it)("creates annotations with feedbackId reference", async () => {
139
+ const record = await store.createFeedback(createInput());
140
+ (0, import_vitest.expect)(record.annotations).toHaveLength(1);
141
+ const [ann] = record.annotations;
142
+ (0, import_vitest.expect)(ann).toBeDefined();
143
+ (0, import_vitest.expect)(ann?.id).toBeDefined();
144
+ (0, import_vitest.expect)(ann?.feedbackId).toBe(record.id);
145
+ (0, import_vitest.expect)(ann?.cssSelector).toBe("div.main");
146
+ (0, import_vitest.expect)(ann?.xPct).toBe(0.1);
147
+ (0, import_vitest.expect)(ann?.elementId).toBe("main");
148
+ (0, import_vitest.expect)(ann?.createdAt).toBeInstanceOf(Date);
149
+ });
150
+ (0, import_vitest.it)("sets elementId to null when undefined in input", async () => {
151
+ const record = await store.createFeedback(createInput({ annotations: [{ ...MINIMAL_ANNOTATION }] }));
152
+ (0, import_vitest.expect)(record.annotations[0]?.elementId).toBeNull();
153
+ });
154
+ (0, import_vitest.it)("persists anchorKey when provided", async () => {
155
+ const record = await store.createFeedback(
156
+ createInput({
157
+ annotations: [
158
+ {
159
+ ...MINIMAL_ANNOTATION,
160
+ cssSelector: "section",
161
+ xpath: "/section",
162
+ textSnippet: "Services",
163
+ elementTag: "SECTION",
164
+ anchorKey: "order-card.services"
165
+ }
166
+ ]
167
+ })
168
+ );
169
+ (0, import_vitest.expect)(record.annotations[0]?.anchorKey).toBe("order-card.services");
170
+ });
171
+ (0, import_vitest.it)("persists anchorKey as null when omitted", async () => {
172
+ const record = await store.createFeedback(createInput());
173
+ (0, import_vitest.expect)(record.annotations[0]?.anchorKey).toBeNull();
174
+ });
175
+ (0, import_vitest.it)("persists screenshotUrl as null when no data URL is provided", async () => {
176
+ const record = await store.createFeedback(createInput());
177
+ (0, import_vitest.expect)(record.screenshotUrl).toBeNull();
178
+ });
179
+ (0, import_vitest.it)("persists the screenshot data URL inline when no external storage is configured", async () => {
180
+ const dataUrl = "data:image/jpeg;base64,/9j/4AAQ";
181
+ const record = await store.createFeedback(createInput({ screenshotDataUrl: dataUrl }));
182
+ (0, import_vitest.expect)(record.screenshotUrl).toBe(dataUrl);
183
+ });
184
+ (0, import_vitest.it)("persists screenshotRegion verbatim when provided", async () => {
185
+ const region = { xPct: 0.1234, yPct: 0.5678, wPct: 0.25, hPct: 0.125 };
186
+ const record = await store.createFeedback(createInput({ screenshotRegion: region }));
187
+ (0, import_vitest.expect)(record.screenshotRegion).toEqual(region);
188
+ });
189
+ (0, import_vitest.it)("persists screenshotRegion as null when omitted", async () => {
190
+ const record = await store.createFeedback(createInput());
191
+ (0, import_vitest.expect)(record.screenshotRegion).toBeNull();
192
+ });
193
+ (0, import_vitest.it)("persists diagnostics verbatim when provided", async () => {
194
+ const diagnostics = {
195
+ console: [{ level: "error", timestamp: "2026-01-01T00:00:00.000Z", message: "boom" }],
196
+ network: [
197
+ {
198
+ url: "https://api.test/things",
199
+ method: "GET",
200
+ status: 500,
201
+ durationMs: 123,
202
+ timestamp: "2026-01-01T00:00:01.000Z"
203
+ }
204
+ ]
205
+ };
206
+ const record = await store.createFeedback(createInput({ diagnostics }));
207
+ (0, import_vitest.expect)(record.diagnostics).toEqual(diagnostics);
208
+ });
209
+ (0, import_vitest.it)("persists diagnostics as null when omitted", async () => {
210
+ const record = await store.createFeedback(createInput());
211
+ (0, import_vitest.expect)(record.diagnostics).toBeNull();
212
+ });
213
+ if (duplicateBehavior === "return") {
214
+ (0, import_vitest.it)("deduplicates by clientId (idempotent)", async () => {
215
+ const input = createInput({ clientId: "same-id" });
216
+ const first = await store.createFeedback(input);
217
+ const second = await store.createFeedback(input);
218
+ (0, import_vitest.expect)(second.id).toBe(first.id);
219
+ const { total } = await store.getFeedbacks({ projectName: "test-project" });
220
+ (0, import_vitest.expect)(total).toBe(1);
221
+ });
222
+ } else {
223
+ (0, import_vitest.it)("throws StoreDuplicateError on duplicate clientId", async () => {
224
+ const input = createInput({ clientId: "same-id" });
225
+ await store.createFeedback(input);
226
+ await (0, import_vitest.expect)(store.createFeedback(input)).rejects.toSatisfy(isStoreDuplicate);
227
+ const { total } = await store.getFeedbacks({ projectName: "test-project" });
228
+ (0, import_vitest.expect)(total).toBe(1);
229
+ });
230
+ }
231
+ (0, import_vitest.it)("stores newest feedbacks first", async () => {
232
+ const a = await store.createFeedback(createInput({ message: "first" }));
233
+ const b = await store.createFeedback(createInput({ message: "second" }));
234
+ const { feedbacks } = await store.getFeedbacks({ projectName: "test-project" });
235
+ (0, import_vitest.expect)(feedbacks[0]?.id).toBe(b.id);
236
+ (0, import_vitest.expect)(feedbacks[1]?.id).toBe(a.id);
237
+ });
238
+ (0, import_vitest.it)("generates unique IDs across calls", async () => {
239
+ const a = await store.createFeedback(createInput());
240
+ const b = await store.createFeedback(createInput());
241
+ (0, import_vitest.expect)(a.id).not.toBe(b.id);
242
+ });
243
+ (0, import_vitest.it)("creates feedbacks with no annotations", async () => {
244
+ const record = await store.createFeedback(createInput({ annotations: [] }));
245
+ (0, import_vitest.expect)(record.annotations).toHaveLength(0);
246
+ });
247
+ });
248
+ (0, import_vitest.describe)("getFeedbacks", () => {
249
+ (0, import_vitest.it)("returns empty array when no feedbacks", async () => {
250
+ const result = await store.getFeedbacks({ projectName: "test-project" });
251
+ (0, import_vitest.expect)(result.feedbacks).toHaveLength(0);
252
+ (0, import_vitest.expect)(result.total).toBe(0);
253
+ });
254
+ (0, import_vitest.it)("filters by projectName", async () => {
255
+ await store.createFeedback(createInput({ projectName: "a" }));
256
+ await store.createFeedback(createInput({ projectName: "b" }));
257
+ const result = await store.getFeedbacks({ projectName: "a" });
258
+ (0, import_vitest.expect)(result.total).toBe(1);
259
+ (0, import_vitest.expect)(result.feedbacks[0]?.projectName).toBe("a");
260
+ });
261
+ (0, import_vitest.it)("filters by type", async () => {
262
+ await store.createFeedback(createInput({ type: "bug" }));
263
+ await store.createFeedback(createInput({ type: "question" }));
264
+ const result = await store.getFeedbacks({ projectName: "test-project", type: "bug" });
265
+ (0, import_vitest.expect)(result.feedbacks).toHaveLength(1);
266
+ (0, import_vitest.expect)(result.feedbacks[0]?.type).toBe("bug");
267
+ });
268
+ (0, import_vitest.it)("filters by status", async () => {
269
+ const fb = await store.createFeedback(createInput());
270
+ await store.updateFeedback(fb.id, { status: "resolved", resolvedAt: /* @__PURE__ */ new Date() });
271
+ await store.createFeedback(createInput());
272
+ const result = await store.getFeedbacks({ projectName: "test-project", status: "open" });
273
+ (0, import_vitest.expect)(result.feedbacks).toHaveLength(1);
274
+ });
275
+ (0, import_vitest.it)("filters by status in_progress", async () => {
276
+ const fb = await store.createFeedback(createInput());
277
+ await store.updateFeedback(fb.id, { status: "in_progress", resolvedAt: null });
278
+ await store.createFeedback(createInput());
279
+ const result = await store.getFeedbacks({ projectName: "test-project", status: "in_progress" });
280
+ (0, import_vitest.expect)(result.feedbacks).toHaveLength(1);
281
+ (0, import_vitest.expect)(result.feedbacks[0]?.status).toBe("in_progress");
282
+ });
283
+ (0, import_vitest.it)("filters by status wont_fix", async () => {
284
+ const fb = await store.createFeedback(createInput());
285
+ await store.updateFeedback(fb.id, { status: "wont_fix", resolvedAt: /* @__PURE__ */ new Date() });
286
+ await store.createFeedback(createInput());
287
+ const result = await store.getFeedbacks({ projectName: "test-project", status: "wont_fix" });
288
+ (0, import_vitest.expect)(result.feedbacks).toHaveLength(1);
289
+ (0, import_vitest.expect)(result.feedbacks[0]?.status).toBe("wont_fix");
290
+ });
291
+ (0, import_vitest.it)("filters by a statuses bucket (any of the listed values)", async () => {
292
+ const open = await store.createFeedback(createInput());
293
+ const prog = await store.createFeedback(createInput());
294
+ await store.updateFeedback(prog.id, { status: "in_progress", resolvedAt: null });
295
+ const resolved = await store.createFeedback(createInput());
296
+ await store.updateFeedback(resolved.id, { status: "resolved", resolvedAt: /* @__PURE__ */ new Date() });
297
+ const result = await store.getFeedbacks({
298
+ projectName: "test-project",
299
+ statuses: ["open", "in_progress"]
300
+ });
301
+ (0, import_vitest.expect)(result.total).toBe(2);
302
+ (0, import_vitest.expect)(result.feedbacks.map((f) => f.id).sort()).toEqual([open.id, prog.id].sort());
303
+ });
304
+ (0, import_vitest.it)("paginates a statuses bucket with the correct total", async () => {
305
+ for (let i = 0; i < 3; i++) {
306
+ const fb = await store.createFeedback(createInput());
307
+ await store.updateFeedback(fb.id, { status: "in_progress", resolvedAt: null });
308
+ }
309
+ const closed = await store.createFeedback(createInput());
310
+ await store.updateFeedback(closed.id, { status: "resolved", resolvedAt: /* @__PURE__ */ new Date() });
311
+ const page1 = await store.getFeedbacks({
312
+ projectName: "test-project",
313
+ statuses: ["open", "in_progress"],
314
+ page: 1,
315
+ limit: 2
316
+ });
317
+ (0, import_vitest.expect)(page1.total).toBe(3);
318
+ (0, import_vitest.expect)(page1.feedbacks).toHaveLength(2);
319
+ const page2 = await store.getFeedbacks({
320
+ projectName: "test-project",
321
+ statuses: ["open", "in_progress"],
322
+ page: 2,
323
+ limit: 2
324
+ });
325
+ (0, import_vitest.expect)(page2.total).toBe(3);
326
+ (0, import_vitest.expect)(page2.feedbacks).toHaveLength(1);
327
+ });
328
+ (0, import_vitest.it)("prefers statuses over status when both are set", async () => {
329
+ await store.createFeedback(createInput());
330
+ const prog = await store.createFeedback(createInput());
331
+ await store.updateFeedback(prog.id, { status: "in_progress", resolvedAt: null });
332
+ const result = await store.getFeedbacks({
333
+ projectName: "test-project",
334
+ status: "open",
335
+ statuses: ["open", "in_progress"]
336
+ });
337
+ (0, import_vitest.expect)(result.total).toBe(2);
338
+ });
339
+ (0, import_vitest.it)("ignores an empty statuses array (no status filter)", async () => {
340
+ await store.createFeedback(createInput());
341
+ const prog = await store.createFeedback(createInput());
342
+ await store.updateFeedback(prog.id, { status: "in_progress", resolvedAt: null });
343
+ const result = await store.getFeedbacks({ projectName: "test-project", statuses: [] });
344
+ (0, import_vitest.expect)(result.total).toBe(2);
345
+ });
346
+ (0, import_vitest.it)("filters by search (same-case substring)", async () => {
347
+ await store.createFeedback(createInput({ message: "Button is broken" }));
348
+ await store.createFeedback(createInput({ message: "Layout looks great" }));
349
+ const result = await store.getFeedbacks({ projectName: "test-project", search: "broken" });
350
+ (0, import_vitest.expect)(result.feedbacks).toHaveLength(1);
351
+ (0, import_vitest.expect)(result.feedbacks[0]?.message).toBe("Button is broken");
352
+ });
353
+ if (caseInsensitiveSearch) {
354
+ (0, import_vitest.it)("filters by search (case-insensitive)", async () => {
355
+ await store.createFeedback(createInput({ message: "Button is broken" }));
356
+ await store.createFeedback(createInput({ message: "Layout looks great" }));
357
+ const result = await store.getFeedbacks({ projectName: "test-project", search: "BROKEN" });
358
+ (0, import_vitest.expect)(result.feedbacks).toHaveLength(1);
359
+ (0, import_vitest.expect)(result.feedbacks[0]?.message).toBe("Button is broken");
360
+ });
361
+ }
362
+ (0, import_vitest.it)("filters by exact url", async () => {
363
+ await store.createFeedback(createInput({ url: "https://app.test/orders/42" }));
364
+ await store.createFeedback(createInput({ url: "https://app.test/dashboard" }));
365
+ const result = await store.getFeedbacks({ projectName: "test-project", url: "https://app.test/dashboard" });
366
+ (0, import_vitest.expect)(result.feedbacks).toHaveLength(1);
367
+ (0, import_vitest.expect)(result.feedbacks[0]?.url).toBe("https://app.test/dashboard");
368
+ });
369
+ (0, import_vitest.it)("filters by urlPattern", async () => {
370
+ await store.createFeedback(createInput({ url: "https://app.test/orders/42", urlPattern: "/orders/:id" }));
371
+ await store.createFeedback(createInput({ url: "https://app.test/orders/99", urlPattern: "/orders/:id" }));
372
+ await store.createFeedback(createInput({ url: "https://app.test/dashboard", urlPattern: "/dashboard" }));
373
+ const result = await store.getFeedbacks({ projectName: "test-project", urlPattern: "/orders/:id" });
374
+ (0, import_vitest.expect)(result.feedbacks).toHaveLength(2);
375
+ });
376
+ (0, import_vitest.it)("persists urlPattern as null when omitted from input", async () => {
377
+ const record = await store.createFeedback(createInput());
378
+ (0, import_vitest.expect)(record.urlPattern).toBeNull();
379
+ });
380
+ (0, import_vitest.it)("paginates correctly", async () => {
381
+ for (let i = 0; i < 5; i++) {
382
+ await store.createFeedback(createInput());
383
+ }
384
+ const page1 = await store.getFeedbacks({ projectName: "test-project", page: 1, limit: 2 });
385
+ (0, import_vitest.expect)(page1.feedbacks).toHaveLength(2);
386
+ (0, import_vitest.expect)(page1.total).toBe(5);
387
+ const page3 = await store.getFeedbacks({ projectName: "test-project", page: 3, limit: 2 });
388
+ (0, import_vitest.expect)(page3.feedbacks).toHaveLength(1);
389
+ });
390
+ (0, import_vitest.it)("caps limit at 100", async () => {
391
+ for (let i = 0; i < 105; i++) {
392
+ await store.createFeedback(createInput({ annotations: [] }));
393
+ }
394
+ const result = await store.getFeedbacks({ projectName: "test-project", limit: 200 });
395
+ (0, import_vitest.expect)(result.total).toBe(105);
396
+ (0, import_vitest.expect)(result.feedbacks).toHaveLength(100);
397
+ });
398
+ });
399
+ (0, import_vitest.describe)("findByClientId", () => {
400
+ (0, import_vitest.it)("returns the record when found", async () => {
401
+ const created = await store.createFeedback(createInput({ clientId: "find-me" }));
402
+ const found = await store.findByClientId("find-me");
403
+ (0, import_vitest.expect)(found).not.toBeNull();
404
+ (0, import_vitest.expect)(found?.id).toBe(created.id);
405
+ });
406
+ (0, import_vitest.it)("returns null when not found", async () => {
407
+ (0, import_vitest.expect)(await store.findByClientId("nope")).toBeNull();
408
+ });
409
+ });
410
+ (0, import_vitest.describe)("updateFeedback", () => {
411
+ (0, import_vitest.it)("updates status to resolved with resolvedAt", async () => {
412
+ const fb = await store.createFeedback(createInput());
413
+ const resolvedAt = /* @__PURE__ */ new Date();
414
+ const updated = await store.updateFeedback(fb.id, { status: "resolved", resolvedAt });
415
+ (0, import_vitest.expect)(updated.status).toBe("resolved");
416
+ (0, import_vitest.expect)(updated.resolvedAt).toEqual(resolvedAt);
417
+ (0, import_vitest.expect)(updated.updatedAt.getTime()).toBeGreaterThanOrEqual(fb.updatedAt.getTime());
418
+ });
419
+ (0, import_vitest.it)("updates status to in_progress with resolvedAt null", async () => {
420
+ const fb = await store.createFeedback(createInput());
421
+ const updated = await store.updateFeedback(fb.id, { status: "in_progress", resolvedAt: null });
422
+ (0, import_vitest.expect)(updated.status).toBe("in_progress");
423
+ (0, import_vitest.expect)(updated.resolvedAt).toBeNull();
424
+ });
425
+ (0, import_vitest.it)("updates status to wont_fix with the given resolvedAt", async () => {
426
+ const fb = await store.createFeedback(createInput());
427
+ const resolvedAt = /* @__PURE__ */ new Date();
428
+ const updated = await store.updateFeedback(fb.id, { status: "wont_fix", resolvedAt });
429
+ (0, import_vitest.expect)(updated.status).toBe("wont_fix");
430
+ (0, import_vitest.expect)(updated.resolvedAt).toEqual(resolvedAt);
431
+ });
432
+ (0, import_vitest.it)("throws StoreNotFoundError for unknown id", async () => {
433
+ await (0, import_vitest.expect)(store.updateFeedback("unknown", { status: "resolved", resolvedAt: /* @__PURE__ */ new Date() })).rejects.toThrow(
434
+ StoreNotFoundError
435
+ );
436
+ });
437
+ (0, import_vitest.it)("can reopen a closed feedback", async () => {
438
+ const fb = await store.createFeedback(createInput());
439
+ await store.updateFeedback(fb.id, { status: "resolved", resolvedAt: /* @__PURE__ */ new Date() });
440
+ const reopened = await store.updateFeedback(fb.id, { status: "open", resolvedAt: null });
441
+ (0, import_vitest.expect)(reopened.status).toBe("open");
442
+ (0, import_vitest.expect)(reopened.resolvedAt).toBeNull();
443
+ await store.updateFeedback(fb.id, { status: "wont_fix", resolvedAt: /* @__PURE__ */ new Date() });
444
+ const reopenedAgain = await store.updateFeedback(fb.id, { status: "open", resolvedAt: null });
445
+ (0, import_vitest.expect)(reopenedAgain.status).toBe("open");
446
+ (0, import_vitest.expect)(reopenedAgain.resolvedAt).toBeNull();
447
+ });
448
+ });
449
+ (0, import_vitest.describe)("deleteFeedback", () => {
450
+ (0, import_vitest.it)("removes the feedback", async () => {
451
+ const fb = await store.createFeedback(createInput());
452
+ await store.deleteFeedback(fb.id);
453
+ const { total } = await store.getFeedbacks({ projectName: "test-project" });
454
+ (0, import_vitest.expect)(total).toBe(0);
455
+ });
456
+ (0, import_vitest.it)("throws StoreNotFoundError for unknown id", async () => {
457
+ await (0, import_vitest.expect)(store.deleteFeedback("unknown")).rejects.toThrow(StoreNotFoundError);
458
+ });
459
+ });
460
+ (0, import_vitest.describe)("deleteAllFeedbacks", () => {
461
+ (0, import_vitest.it)("removes all feedbacks for a project but keeps others", async () => {
462
+ await store.createFeedback(createInput({ projectName: "delete-me" }));
463
+ await store.createFeedback(createInput({ projectName: "delete-me" }));
464
+ await store.createFeedback(createInput({ projectName: "keep-me" }));
465
+ await store.deleteAllFeedbacks("delete-me");
466
+ (0, import_vitest.expect)((await store.getFeedbacks({ projectName: "delete-me" })).total).toBe(0);
467
+ (0, import_vitest.expect)((await store.getFeedbacks({ projectName: "keep-me" })).total).toBe(1);
468
+ });
469
+ (0, import_vitest.it)("is a no-op when project has no feedbacks", async () => {
470
+ await (0, import_vitest.expect)(store.deleteAllFeedbacks("nonexistent")).resolves.toBeUndefined();
471
+ });
472
+ });
473
+ (0, import_vitest.describe)("verifyProjectOwnership", () => {
474
+ (0, import_vitest.it)("returns true for the owning project, false otherwise (when implemented)", async () => {
475
+ if (!store.verifyProjectOwnership) return;
476
+ const fb = await store.createFeedback(createInput({ projectName: "owner" }));
477
+ await (0, import_vitest.expect)(store.verifyProjectOwnership(fb.id, "owner")).resolves.toBe(true);
478
+ await (0, import_vitest.expect)(store.verifyProjectOwnership(fb.id, "intruder")).resolves.toBe(false);
479
+ await (0, import_vitest.expect)(store.verifyProjectOwnership("unknown-id", "owner")).resolves.toBe(false);
480
+ });
481
+ });
482
+ });
483
+ }
484
+ //# sourceMappingURL=testing.cjs.map