@skillsmith/core 0.4.10 → 0.4.11

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.
Files changed (123) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/src/analysis/__tests__/incremental.test.d.ts +1 -1
  3. package/dist/src/analysis/__tests__/incremental.test.js +1 -1
  4. package/dist/src/analysis/__tests__/integration.test.d.ts +1 -1
  5. package/dist/src/analysis/__tests__/integration.test.js +1 -1
  6. package/dist/src/analysis/__tests__/performance.test.d.ts +1 -1
  7. package/dist/src/analysis/__tests__/performance.test.js +1 -1
  8. package/dist/src/analysis/adapters/__tests__/python.test.d.ts +1 -1
  9. package/dist/src/analysis/adapters/__tests__/python.test.js +1 -1
  10. package/dist/src/analysis/adapters/__tests__/typescript.test.d.ts +1 -1
  11. package/dist/src/analysis/adapters/__tests__/typescript.test.js +1 -1
  12. package/dist/src/analysis/adapters/base.d.ts +1 -1
  13. package/dist/src/analysis/adapters/base.js +1 -1
  14. package/dist/src/analysis/adapters/factory.d.ts +1 -1
  15. package/dist/src/analysis/adapters/factory.js +1 -1
  16. package/dist/src/analysis/adapters/go.d.ts +1 -1
  17. package/dist/src/analysis/adapters/go.js +1 -1
  18. package/dist/src/analysis/adapters/index.d.ts +1 -1
  19. package/dist/src/analysis/adapters/index.js +1 -1
  20. package/dist/src/analysis/adapters/java-parsers.d.ts +1 -1
  21. package/dist/src/analysis/adapters/java-parsers.js +1 -1
  22. package/dist/src/analysis/adapters/java.d.ts +1 -1
  23. package/dist/src/analysis/adapters/java.js +1 -1
  24. package/dist/src/analysis/adapters/python-frameworks.d.ts +1 -1
  25. package/dist/src/analysis/adapters/python-frameworks.js +1 -1
  26. package/dist/src/analysis/adapters/python.d.ts +1 -1
  27. package/dist/src/analysis/adapters/python.js +1 -1
  28. package/dist/src/analysis/adapters/rust-parsers.d.ts +1 -1
  29. package/dist/src/analysis/adapters/rust-parsers.js +1 -1
  30. package/dist/src/analysis/adapters/rust.d.ts +1 -1
  31. package/dist/src/analysis/adapters/rust.js +1 -1
  32. package/dist/src/analysis/adapters/typescript.d.ts +1 -1
  33. package/dist/src/analysis/adapters/typescript.js +1 -1
  34. package/dist/src/analysis/aggregator.d.ts +1 -1
  35. package/dist/src/analysis/aggregator.js +1 -1
  36. package/dist/src/analysis/cache.d.ts +1 -1
  37. package/dist/src/analysis/cache.js +1 -1
  38. package/dist/src/analysis/file-streamer.d.ts +1 -1
  39. package/dist/src/analysis/file-streamer.js +1 -1
  40. package/dist/src/analysis/incremental-parser.d.ts +1 -1
  41. package/dist/src/analysis/incremental-parser.js +1 -1
  42. package/dist/src/analysis/incremental.d.ts +1 -1
  43. package/dist/src/analysis/incremental.js +1 -1
  44. package/dist/src/analysis/index.d.ts +1 -1
  45. package/dist/src/analysis/index.js +1 -1
  46. package/dist/src/analysis/language-detector.d.ts +1 -1
  47. package/dist/src/analysis/language-detector.js +1 -1
  48. package/dist/src/analysis/memory-monitor.d.ts +1 -1
  49. package/dist/src/analysis/memory-monitor.js +1 -1
  50. package/dist/src/analysis/metrics.d.ts +1 -1
  51. package/dist/src/analysis/metrics.js +1 -1
  52. package/dist/src/analysis/router.d.ts +1 -1
  53. package/dist/src/analysis/router.js +1 -1
  54. package/dist/src/analysis/tree-cache.d.ts +1 -1
  55. package/dist/src/analysis/tree-cache.js +1 -1
  56. package/dist/src/analysis/tree-sitter/manager.d.ts +1 -1
  57. package/dist/src/analysis/tree-sitter/manager.js +1 -1
  58. package/dist/src/analysis/types.d.ts +1 -1
  59. package/dist/src/analysis/types.js +1 -1
  60. package/dist/src/analysis/worker-pool.d.ts +1 -1
  61. package/dist/src/analysis/worker-pool.js +1 -1
  62. package/dist/src/analysis/worker-types.d.ts +1 -1
  63. package/dist/src/analysis/worker-types.js +1 -1
  64. package/dist/src/analysis/worker-utils.d.ts +1 -1
  65. package/dist/src/analysis/worker-utils.js +1 -1
  66. package/dist/src/config/index.d.ts +49 -1
  67. package/dist/src/config/index.d.ts.map +1 -1
  68. package/dist/src/config/index.js +166 -3
  69. package/dist/src/config/index.js.map +1 -1
  70. package/dist/src/config/index.test.d.ts +11 -0
  71. package/dist/src/config/index.test.d.ts.map +1 -0
  72. package/dist/src/config/index.test.js +288 -0
  73. package/dist/src/config/index.test.js.map +1 -0
  74. package/dist/src/db/quarantine-approvals-schema.d.ts +37 -0
  75. package/dist/src/db/quarantine-approvals-schema.d.ts.map +1 -0
  76. package/dist/src/db/quarantine-approvals-schema.js +71 -0
  77. package/dist/src/db/quarantine-approvals-schema.js.map +1 -0
  78. package/dist/src/index.d.ts +1 -1
  79. package/dist/src/index.d.ts.map +1 -1
  80. package/dist/src/index.js +2 -2
  81. package/dist/src/index.js.map +1 -1
  82. package/dist/src/repositories/quarantine/ApprovalRepository.d.ts +148 -0
  83. package/dist/src/repositories/quarantine/ApprovalRepository.d.ts.map +1 -0
  84. package/dist/src/repositories/quarantine/ApprovalRepository.js +212 -0
  85. package/dist/src/repositories/quarantine/ApprovalRepository.js.map +1 -0
  86. package/dist/src/repositories/quarantine/index.d.ts +2 -0
  87. package/dist/src/repositories/quarantine/index.d.ts.map +1 -1
  88. package/dist/src/repositories/quarantine/index.js +1 -0
  89. package/dist/src/repositories/quarantine/index.js.map +1 -1
  90. package/dist/src/security/audit-types.d.ts +1 -1
  91. package/dist/src/security/audit-types.d.ts.map +1 -1
  92. package/dist/src/security/audit-types.js.map +1 -1
  93. package/dist/src/security/scanner/SecurityScanner.formatters.js +1 -1
  94. package/dist/src/security/scanner/SecurityScanner.formatters.js.map +1 -1
  95. package/dist/src/services/quarantine/QuarantineService.d.ts +17 -9
  96. package/dist/src/services/quarantine/QuarantineService.d.ts.map +1 -1
  97. package/dist/src/services/quarantine/QuarantineService.js +114 -56
  98. package/dist/src/services/quarantine/QuarantineService.js.map +1 -1
  99. package/dist/tests/RawUrlSourceAdapter.security.test.js +2 -1
  100. package/dist/tests/RawUrlSourceAdapter.security.test.js.map +1 -1
  101. package/dist/tests/SecurityScanner.test.js +2 -2
  102. package/dist/tests/SecurityScanner.test.js.map +1 -1
  103. package/dist/tests/adapters-factory.test.d.ts +1 -1
  104. package/dist/tests/adapters-factory.test.js +1 -1
  105. package/dist/tests/integration/QuarantineService.test.js +49 -1
  106. package/dist/tests/integration/QuarantineService.test.js.map +1 -1
  107. package/dist/tests/integration/neural/e2e-learning.test.d.ts +1 -1
  108. package/dist/tests/integration/neural/e2e-learning.test.js +1 -1
  109. package/dist/tests/integration/neural/personalization.test.d.ts +1 -1
  110. package/dist/tests/integration/neural/personalization.test.js +1 -1
  111. package/dist/tests/integration/neural/preference-learner.test.d.ts +1 -1
  112. package/dist/tests/integration/neural/preference-learner.test.js +1 -1
  113. package/dist/tests/integration/neural/privacy.test.d.ts +1 -1
  114. package/dist/tests/integration/neural/privacy.test.js +1 -1
  115. package/dist/tests/integration/neural/signal-collection.test.d.ts +1 -1
  116. package/dist/tests/integration/neural/signal-collection.test.js +1 -1
  117. package/dist/tests/language-detector.test.d.ts +1 -1
  118. package/dist/tests/language-detector.test.js +1 -1
  119. package/dist/tests/unit/approval-repository.test.d.ts +9 -0
  120. package/dist/tests/unit/approval-repository.test.d.ts.map +1 -0
  121. package/dist/tests/unit/approval-repository.test.js +509 -0
  122. package/dist/tests/unit/approval-repository.test.js.map +1 -0
  123. package/package.json +1 -1
@@ -0,0 +1,509 @@
1
+ /**
2
+ * SMI-2277: ApprovalRepository Unit Tests
3
+ *
4
+ * Tests for the database-persisted multi-approval workflow state.
5
+ * Verifies that approval state survives across repository instances
6
+ * (simulating service restarts).
7
+ */
8
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
9
+ import { ApprovalRepository } from '../../src/repositories/quarantine/ApprovalRepository.js';
10
+ import { createDatabaseSync } from '../../src/db/createDatabase.js';
11
+ describe('SMI-2277: ApprovalRepository', () => {
12
+ let db;
13
+ let repo;
14
+ beforeEach(() => {
15
+ db = createDatabaseSync(':memory:');
16
+ repo = new ApprovalRepository(db);
17
+ });
18
+ afterEach(() => {
19
+ db.close();
20
+ });
21
+ // ==========================================================================
22
+ // Recording Approvals
23
+ // ==========================================================================
24
+ describe('recordApproval', () => {
25
+ it('should record an approval with generated ID', () => {
26
+ const entry = repo.recordApproval({
27
+ skillId: 'quarantine-123',
28
+ reviewerId: 'user-456',
29
+ reviewerEmail: 'reviewer@example.com',
30
+ decision: 'approved',
31
+ reason: 'Code verified safe',
32
+ });
33
+ expect(entry.id).toBeDefined();
34
+ expect(entry.skillId).toBe('quarantine-123');
35
+ expect(entry.reviewerId).toBe('user-456');
36
+ expect(entry.reviewerEmail).toBe('reviewer@example.com');
37
+ expect(entry.decision).toBe('approved');
38
+ expect(entry.reason).toBe('Code verified safe');
39
+ expect(entry.isComplete).toBe(false);
40
+ expect(entry.requiredApprovals).toBe(2);
41
+ expect(entry.createdAt).toBeDefined();
42
+ });
43
+ it('should record a rejection', () => {
44
+ const entry = repo.recordApproval({
45
+ skillId: 'quarantine-123',
46
+ reviewerId: 'user-456',
47
+ reviewerEmail: 'reviewer@example.com',
48
+ decision: 'rejected',
49
+ reason: 'Confirmed malicious',
50
+ });
51
+ expect(entry.decision).toBe('rejected');
52
+ });
53
+ it('should record approval without reason', () => {
54
+ const entry = repo.recordApproval({
55
+ skillId: 'quarantine-123',
56
+ reviewerId: 'user-456',
57
+ reviewerEmail: 'reviewer@example.com',
58
+ decision: 'approved',
59
+ });
60
+ expect(entry.reason).toBeNull();
61
+ });
62
+ it('should respect custom requiredApprovals', () => {
63
+ const entry = repo.recordApproval({
64
+ skillId: 'quarantine-123',
65
+ reviewerId: 'user-456',
66
+ reviewerEmail: 'reviewer@example.com',
67
+ decision: 'approved',
68
+ requiredApprovals: 3,
69
+ });
70
+ expect(entry.requiredApprovals).toBe(3);
71
+ });
72
+ it('should allow different reviewers for the same skill', () => {
73
+ repo.recordApproval({
74
+ skillId: 'quarantine-123',
75
+ reviewerId: 'user-1',
76
+ reviewerEmail: 'reviewer1@example.com',
77
+ decision: 'approved',
78
+ });
79
+ repo.recordApproval({
80
+ skillId: 'quarantine-123',
81
+ reviewerId: 'user-2',
82
+ reviewerEmail: 'reviewer2@example.com',
83
+ decision: 'approved',
84
+ });
85
+ const approvals = repo.getApprovals('quarantine-123');
86
+ expect(approvals).toHaveLength(2);
87
+ });
88
+ });
89
+ // ==========================================================================
90
+ // Querying Approvals
91
+ // ==========================================================================
92
+ describe('getApprovals', () => {
93
+ it('should return empty array for unknown skill', () => {
94
+ const approvals = repo.getApprovals('nonexistent');
95
+ expect(approvals).toEqual([]);
96
+ });
97
+ it('should return all approvals for a skill in order', () => {
98
+ repo.recordApproval({
99
+ skillId: 'quarantine-123',
100
+ reviewerId: 'user-1',
101
+ reviewerEmail: 'reviewer1@example.com',
102
+ decision: 'approved',
103
+ reason: 'First',
104
+ });
105
+ repo.recordApproval({
106
+ skillId: 'quarantine-123',
107
+ reviewerId: 'user-2',
108
+ reviewerEmail: 'reviewer2@example.com',
109
+ decision: 'approved',
110
+ reason: 'Second',
111
+ });
112
+ const approvals = repo.getApprovals('quarantine-123');
113
+ expect(approvals).toHaveLength(2);
114
+ expect(approvals[0].reason).toBe('First');
115
+ expect(approvals[1].reason).toBe('Second');
116
+ });
117
+ it('should not return approvals for different skills', () => {
118
+ repo.recordApproval({
119
+ skillId: 'quarantine-111',
120
+ reviewerId: 'user-1',
121
+ reviewerEmail: 'reviewer1@example.com',
122
+ decision: 'approved',
123
+ });
124
+ repo.recordApproval({
125
+ skillId: 'quarantine-222',
126
+ reviewerId: 'user-2',
127
+ reviewerEmail: 'reviewer2@example.com',
128
+ decision: 'approved',
129
+ });
130
+ const approvals = repo.getApprovals('quarantine-111');
131
+ expect(approvals).toHaveLength(1);
132
+ expect(approvals[0].skillId).toBe('quarantine-111');
133
+ });
134
+ });
135
+ describe('getPendingApprovals', () => {
136
+ it('should only return non-complete approvals', () => {
137
+ repo.recordApproval({
138
+ skillId: 'quarantine-123',
139
+ reviewerId: 'user-1',
140
+ reviewerEmail: 'reviewer1@example.com',
141
+ decision: 'approved',
142
+ });
143
+ repo.recordApproval({
144
+ skillId: 'quarantine-123',
145
+ reviewerId: 'user-2',
146
+ reviewerEmail: 'reviewer2@example.com',
147
+ decision: 'approved',
148
+ });
149
+ // Mark as complete
150
+ repo.markComplete('quarantine-123');
151
+ const pending = repo.getPendingApprovals('quarantine-123');
152
+ expect(pending).toHaveLength(0);
153
+ // All approvals (including complete) should still be retrievable
154
+ const all = repo.getApprovals('quarantine-123');
155
+ expect(all).toHaveLength(2);
156
+ });
157
+ });
158
+ // ==========================================================================
159
+ // Reviewer Uniqueness
160
+ // ==========================================================================
161
+ describe('hasReviewerApproved', () => {
162
+ it('should return false for unknown reviewer', () => {
163
+ expect(repo.hasReviewerApproved('quarantine-123', 'user-unknown')).toBe(false);
164
+ });
165
+ it('should return true for existing reviewer', () => {
166
+ repo.recordApproval({
167
+ skillId: 'quarantine-123',
168
+ reviewerId: 'user-456',
169
+ reviewerEmail: 'reviewer@example.com',
170
+ decision: 'approved',
171
+ });
172
+ expect(repo.hasReviewerApproved('quarantine-123', 'user-456')).toBe(true);
173
+ });
174
+ it('should return false after approvals are cleared', () => {
175
+ repo.recordApproval({
176
+ skillId: 'quarantine-123',
177
+ reviewerId: 'user-456',
178
+ reviewerEmail: 'reviewer@example.com',
179
+ decision: 'approved',
180
+ });
181
+ repo.clearApprovals('quarantine-123');
182
+ expect(repo.hasReviewerApproved('quarantine-123', 'user-456')).toBe(false);
183
+ });
184
+ it('should scope check to specific skill', () => {
185
+ repo.recordApproval({
186
+ skillId: 'quarantine-111',
187
+ reviewerId: 'user-456',
188
+ reviewerEmail: 'reviewer@example.com',
189
+ decision: 'approved',
190
+ });
191
+ expect(repo.hasReviewerApproved('quarantine-111', 'user-456')).toBe(true);
192
+ expect(repo.hasReviewerApproved('quarantine-222', 'user-456')).toBe(false);
193
+ });
194
+ });
195
+ // ==========================================================================
196
+ // Completion Detection
197
+ // ==========================================================================
198
+ describe('isComplete', () => {
199
+ it('should return false with zero approvals', () => {
200
+ expect(repo.isComplete('quarantine-123')).toBe(false);
201
+ });
202
+ it('should return false with one approval (default requires 2)', () => {
203
+ repo.recordApproval({
204
+ skillId: 'quarantine-123',
205
+ reviewerId: 'user-1',
206
+ reviewerEmail: 'reviewer1@example.com',
207
+ decision: 'approved',
208
+ });
209
+ expect(repo.isComplete('quarantine-123')).toBe(false);
210
+ });
211
+ it('should return true when required count is reached', () => {
212
+ repo.recordApproval({
213
+ skillId: 'quarantine-123',
214
+ reviewerId: 'user-1',
215
+ reviewerEmail: 'reviewer1@example.com',
216
+ decision: 'approved',
217
+ });
218
+ repo.recordApproval({
219
+ skillId: 'quarantine-123',
220
+ reviewerId: 'user-2',
221
+ reviewerEmail: 'reviewer2@example.com',
222
+ decision: 'approved',
223
+ });
224
+ expect(repo.isComplete('quarantine-123')).toBe(true);
225
+ });
226
+ it('should respect custom required count', () => {
227
+ repo.recordApproval({
228
+ skillId: 'quarantine-123',
229
+ reviewerId: 'user-1',
230
+ reviewerEmail: 'reviewer1@example.com',
231
+ decision: 'approved',
232
+ });
233
+ repo.recordApproval({
234
+ skillId: 'quarantine-123',
235
+ reviewerId: 'user-2',
236
+ reviewerEmail: 'reviewer2@example.com',
237
+ decision: 'approved',
238
+ });
239
+ // With required count of 3, should not be complete
240
+ expect(repo.isComplete('quarantine-123', 3)).toBe(false);
241
+ });
242
+ it('should only count approved decisions, not rejected', () => {
243
+ repo.recordApproval({
244
+ skillId: 'quarantine-123',
245
+ reviewerId: 'user-1',
246
+ reviewerEmail: 'reviewer1@example.com',
247
+ decision: 'approved',
248
+ });
249
+ repo.recordApproval({
250
+ skillId: 'quarantine-123',
251
+ reviewerId: 'user-2',
252
+ reviewerEmail: 'reviewer2@example.com',
253
+ decision: 'rejected',
254
+ });
255
+ expect(repo.isComplete('quarantine-123')).toBe(false);
256
+ });
257
+ });
258
+ // ==========================================================================
259
+ // Marking Complete
260
+ // ==========================================================================
261
+ describe('markComplete', () => {
262
+ it('should mark pending approvals as complete', () => {
263
+ repo.recordApproval({
264
+ skillId: 'quarantine-123',
265
+ reviewerId: 'user-1',
266
+ reviewerEmail: 'reviewer1@example.com',
267
+ decision: 'approved',
268
+ });
269
+ repo.recordApproval({
270
+ skillId: 'quarantine-123',
271
+ reviewerId: 'user-2',
272
+ reviewerEmail: 'reviewer2@example.com',
273
+ decision: 'approved',
274
+ });
275
+ const updated = repo.markComplete('quarantine-123');
276
+ expect(updated).toBe(2);
277
+ const approvals = repo.getApprovals('quarantine-123');
278
+ expect(approvals.every((a) => a.isComplete)).toBe(true);
279
+ });
280
+ it('should return 0 for unknown skill', () => {
281
+ const updated = repo.markComplete('nonexistent');
282
+ expect(updated).toBe(0);
283
+ });
284
+ });
285
+ // ==========================================================================
286
+ // Cleanup
287
+ // ==========================================================================
288
+ describe('clearApprovals', () => {
289
+ it('should delete all approvals for a skill', () => {
290
+ repo.recordApproval({
291
+ skillId: 'quarantine-123',
292
+ reviewerId: 'user-1',
293
+ reviewerEmail: 'reviewer1@example.com',
294
+ decision: 'approved',
295
+ });
296
+ repo.recordApproval({
297
+ skillId: 'quarantine-123',
298
+ reviewerId: 'user-2',
299
+ reviewerEmail: 'reviewer2@example.com',
300
+ decision: 'approved',
301
+ });
302
+ const deleted = repo.clearApprovals('quarantine-123');
303
+ expect(deleted).toBe(2);
304
+ const approvals = repo.getApprovals('quarantine-123');
305
+ expect(approvals).toHaveLength(0);
306
+ });
307
+ it('should not affect other skills', () => {
308
+ repo.recordApproval({
309
+ skillId: 'quarantine-111',
310
+ reviewerId: 'user-1',
311
+ reviewerEmail: 'reviewer1@example.com',
312
+ decision: 'approved',
313
+ });
314
+ repo.recordApproval({
315
+ skillId: 'quarantine-222',
316
+ reviewerId: 'user-2',
317
+ reviewerEmail: 'reviewer2@example.com',
318
+ decision: 'approved',
319
+ });
320
+ repo.clearApprovals('quarantine-111');
321
+ expect(repo.getApprovals('quarantine-111')).toHaveLength(0);
322
+ expect(repo.getApprovals('quarantine-222')).toHaveLength(1);
323
+ });
324
+ it('should return 0 for unknown skill', () => {
325
+ const deleted = repo.clearApprovals('nonexistent');
326
+ expect(deleted).toBe(0);
327
+ });
328
+ });
329
+ // ==========================================================================
330
+ // Workflow Start Time
331
+ // ==========================================================================
332
+ describe('getWorkflowStartTime', () => {
333
+ it('should return null for unknown skill', () => {
334
+ expect(repo.getWorkflowStartTime('nonexistent')).toBeNull();
335
+ });
336
+ it('should return earliest pending approval time', () => {
337
+ repo.recordApproval({
338
+ skillId: 'quarantine-123',
339
+ reviewerId: 'user-1',
340
+ reviewerEmail: 'reviewer1@example.com',
341
+ decision: 'approved',
342
+ });
343
+ const startTime = repo.getWorkflowStartTime('quarantine-123');
344
+ expect(startTime).toBeDefined();
345
+ expect(startTime).not.toBeNull();
346
+ });
347
+ it('should return null after approvals are cleared', () => {
348
+ repo.recordApproval({
349
+ skillId: 'quarantine-123',
350
+ reviewerId: 'user-1',
351
+ reviewerEmail: 'reviewer1@example.com',
352
+ decision: 'approved',
353
+ });
354
+ repo.clearApprovals('quarantine-123');
355
+ expect(repo.getWorkflowStartTime('quarantine-123')).toBeNull();
356
+ });
357
+ });
358
+ // ==========================================================================
359
+ // Pending Approval Count
360
+ // ==========================================================================
361
+ describe('getPendingApprovalCount', () => {
362
+ it('should return 0 for unknown skill', () => {
363
+ expect(repo.getPendingApprovalCount('nonexistent')).toBe(0);
364
+ });
365
+ it('should count only approved pending entries', () => {
366
+ repo.recordApproval({
367
+ skillId: 'quarantine-123',
368
+ reviewerId: 'user-1',
369
+ reviewerEmail: 'reviewer1@example.com',
370
+ decision: 'approved',
371
+ });
372
+ repo.recordApproval({
373
+ skillId: 'quarantine-123',
374
+ reviewerId: 'user-2',
375
+ reviewerEmail: 'reviewer2@example.com',
376
+ decision: 'rejected',
377
+ });
378
+ expect(repo.getPendingApprovalCount('quarantine-123')).toBe(1);
379
+ });
380
+ it('should not count completed approvals', () => {
381
+ repo.recordApproval({
382
+ skillId: 'quarantine-123',
383
+ reviewerId: 'user-1',
384
+ reviewerEmail: 'reviewer1@example.com',
385
+ decision: 'approved',
386
+ });
387
+ repo.markComplete('quarantine-123');
388
+ expect(repo.getPendingApprovalCount('quarantine-123')).toBe(0);
389
+ });
390
+ });
391
+ // ==========================================================================
392
+ // Duplicate Reviewer Enforcement (unique index)
393
+ // ==========================================================================
394
+ describe('duplicate reviewer enforcement', () => {
395
+ it('should reject duplicate reviewer for same pending skill', () => {
396
+ repo.recordApproval({
397
+ skillId: 'quarantine-123',
398
+ reviewerId: 'user-1',
399
+ reviewerEmail: 'reviewer1@example.com',
400
+ decision: 'approved',
401
+ });
402
+ expect(() => repo.recordApproval({
403
+ skillId: 'quarantine-123',
404
+ reviewerId: 'user-1',
405
+ reviewerEmail: 'reviewer1@example.com',
406
+ decision: 'approved',
407
+ })).toThrow(); // UNIQUE constraint violation
408
+ });
409
+ it('should allow same reviewer after previous round is completed', () => {
410
+ repo.recordApproval({
411
+ skillId: 'quarantine-123',
412
+ reviewerId: 'user-1',
413
+ reviewerEmail: 'reviewer1@example.com',
414
+ decision: 'approved',
415
+ });
416
+ // Complete the first round
417
+ repo.markComplete('quarantine-123');
418
+ // Same reviewer can now approve in a new round
419
+ const entry = repo.recordApproval({
420
+ skillId: 'quarantine-123',
421
+ reviewerId: 'user-1',
422
+ reviewerEmail: 'reviewer1@example.com',
423
+ decision: 'approved',
424
+ });
425
+ expect(entry.reviewerId).toBe('user-1');
426
+ });
427
+ it('should allow same reviewer for different skills', () => {
428
+ repo.recordApproval({
429
+ skillId: 'quarantine-111',
430
+ reviewerId: 'user-1',
431
+ reviewerEmail: 'reviewer1@example.com',
432
+ decision: 'approved',
433
+ });
434
+ // Different skill, same reviewer — should work
435
+ const entry = repo.recordApproval({
436
+ skillId: 'quarantine-222',
437
+ reviewerId: 'user-1',
438
+ reviewerEmail: 'reviewer1@example.com',
439
+ decision: 'approved',
440
+ });
441
+ expect(entry.skillId).toBe('quarantine-222');
442
+ });
443
+ });
444
+ // ==========================================================================
445
+ // Completed At Timestamp
446
+ // ==========================================================================
447
+ describe('completedAt timestamp', () => {
448
+ it('should be null for pending approvals', () => {
449
+ const entry = repo.recordApproval({
450
+ skillId: 'quarantine-123',
451
+ reviewerId: 'user-1',
452
+ reviewerEmail: 'reviewer1@example.com',
453
+ decision: 'approved',
454
+ });
455
+ expect(entry.completedAt).toBeNull();
456
+ });
457
+ it('should be set when markComplete is called', () => {
458
+ repo.recordApproval({
459
+ skillId: 'quarantine-123',
460
+ reviewerId: 'user-1',
461
+ reviewerEmail: 'reviewer1@example.com',
462
+ decision: 'approved',
463
+ });
464
+ repo.markComplete('quarantine-123');
465
+ const approvals = repo.getApprovals('quarantine-123');
466
+ expect(approvals[0].completedAt).toBeDefined();
467
+ expect(approvals[0].completedAt).not.toBeNull();
468
+ });
469
+ });
470
+ // ==========================================================================
471
+ // Persistence (simulates restart)
472
+ // ==========================================================================
473
+ describe('persistence across instances', () => {
474
+ it('should retain approvals when a new repository is created', () => {
475
+ // First "session" - record an approval
476
+ repo.recordApproval({
477
+ skillId: 'quarantine-123',
478
+ reviewerId: 'user-1',
479
+ reviewerEmail: 'reviewer1@example.com',
480
+ decision: 'approved',
481
+ });
482
+ // Simulate service restart: create new repository from same db
483
+ const repo2 = new ApprovalRepository(db);
484
+ const approvals = repo2.getApprovals('quarantine-123');
485
+ expect(approvals).toHaveLength(1);
486
+ expect(approvals[0].reviewerId).toBe('user-1');
487
+ });
488
+ it('should allow completing multi-approval across instances', () => {
489
+ // First reviewer approves
490
+ repo.recordApproval({
491
+ skillId: 'quarantine-123',
492
+ reviewerId: 'user-1',
493
+ reviewerEmail: 'reviewer1@example.com',
494
+ decision: 'approved',
495
+ });
496
+ // Simulate restart
497
+ const repo2 = new ApprovalRepository(db);
498
+ // Second reviewer approves in new instance
499
+ repo2.recordApproval({
500
+ skillId: 'quarantine-123',
501
+ reviewerId: 'user-2',
502
+ reviewerEmail: 'reviewer2@example.com',
503
+ decision: 'approved',
504
+ });
505
+ expect(repo2.isComplete('quarantine-123')).toBe(true);
506
+ });
507
+ });
508
+ });
509
+ //# sourceMappingURL=approval-repository.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval-repository.test.js","sourceRoot":"","sources":["../../../tests/unit/approval-repository.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillsmith/core",
3
- "version": "0.4.10",
3
+ "version": "0.4.11",
4
4
  "description": "Core types and utilities for Skillsmith",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",