pnpm-catalog-updates 1.0.2 → 1.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.
- package/README.md +15 -0
- package/dist/index.js +22031 -10684
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
- package/src/cli/__tests__/commandRegistrar.test.ts +248 -0
- package/src/cli/commandRegistrar.ts +785 -0
- package/src/cli/commands/__tests__/aiCommand.test.ts +161 -0
- package/src/cli/commands/__tests__/analyzeCommand.test.ts +283 -0
- package/src/cli/commands/__tests__/checkCommand.test.ts +435 -0
- package/src/cli/commands/__tests__/graphCommand.test.ts +312 -0
- package/src/cli/commands/__tests__/initCommand.test.ts +317 -0
- package/src/cli/commands/__tests__/rollbackCommand.test.ts +400 -0
- package/src/cli/commands/__tests__/securityCommand.test.ts +467 -0
- package/src/cli/commands/__tests__/themeCommand.test.ts +166 -0
- package/src/cli/commands/__tests__/updateCommand.test.ts +720 -0
- package/src/cli/commands/__tests__/workspaceCommand.test.ts +286 -0
- package/src/cli/commands/aiCommand.ts +163 -0
- package/src/cli/commands/analyzeCommand.ts +219 -0
- package/src/cli/commands/checkCommand.ts +91 -98
- package/src/cli/commands/graphCommand.ts +475 -0
- package/src/cli/commands/initCommand.ts +64 -54
- package/src/cli/commands/rollbackCommand.ts +334 -0
- package/src/cli/commands/securityCommand.ts +165 -100
- package/src/cli/commands/themeCommand.ts +148 -0
- package/src/cli/commands/updateCommand.ts +215 -263
- package/src/cli/commands/workspaceCommand.ts +73 -0
- package/src/cli/constants/cliChoices.ts +93 -0
- package/src/cli/formatters/__tests__/__snapshots__/outputFormatter.test.ts.snap +557 -0
- package/src/cli/formatters/__tests__/ciFormatter.test.ts +526 -0
- package/src/cli/formatters/__tests__/outputFormatter.test.ts +448 -0
- package/src/cli/formatters/__tests__/progressBar.test.ts +709 -0
- package/src/cli/formatters/ciFormatter.ts +964 -0
- package/src/cli/formatters/colorUtils.ts +145 -0
- package/src/cli/formatters/outputFormatter.ts +615 -332
- package/src/cli/formatters/progressBar.ts +43 -52
- package/src/cli/formatters/versionFormatter.ts +132 -0
- package/src/cli/handlers/aiAnalysisHandler.ts +205 -0
- package/src/cli/handlers/changelogHandler.ts +113 -0
- package/src/cli/handlers/index.ts +9 -0
- package/src/cli/handlers/installHandler.ts +130 -0
- package/src/cli/index.ts +175 -726
- package/src/cli/interactive/InteractiveOptionsCollector.ts +387 -0
- package/src/cli/interactive/interactivePrompts.ts +189 -83
- package/src/cli/interactive/optionUtils.ts +89 -0
- package/src/cli/themes/colorTheme.ts +43 -16
- package/src/cli/utils/cliOutput.ts +118 -0
- package/src/cli/utils/commandHelpers.ts +249 -0
- package/src/cli/validators/commandValidator.ts +321 -336
- package/src/cli/validators/index.ts +37 -2
- package/src/cli/options/globalOptions.ts +0 -437
- package/src/cli/options/index.ts +0 -5
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI Formatter Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
6
|
+
|
|
7
|
+
// Mock @pcu/utils
|
|
8
|
+
vi.mock('@pcu/utils', () => ({
|
|
9
|
+
t: (key: string, params?: Record<string, unknown>) => {
|
|
10
|
+
if (params) {
|
|
11
|
+
let result = key
|
|
12
|
+
for (const [k, v] of Object.entries(params)) {
|
|
13
|
+
result = result.replace(`{{${k}}}`, String(v))
|
|
14
|
+
}
|
|
15
|
+
return result
|
|
16
|
+
}
|
|
17
|
+
return key
|
|
18
|
+
},
|
|
19
|
+
}))
|
|
20
|
+
|
|
21
|
+
const { CIFormatter } = await import('../ciFormatter.js')
|
|
22
|
+
|
|
23
|
+
describe('CIFormatter', () => {
|
|
24
|
+
// Test data fixtures
|
|
25
|
+
const mockOutdatedReport = {
|
|
26
|
+
hasUpdates: true,
|
|
27
|
+
totalOutdated: 3,
|
|
28
|
+
catalogs: [
|
|
29
|
+
{
|
|
30
|
+
catalogName: 'default',
|
|
31
|
+
outdatedDependencies: [
|
|
32
|
+
{
|
|
33
|
+
packageName: 'lodash',
|
|
34
|
+
currentVersion: '4.17.20',
|
|
35
|
+
latestVersion: '4.17.21',
|
|
36
|
+
updateType: 'patch',
|
|
37
|
+
isSecurityUpdate: false,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
packageName: 'react',
|
|
41
|
+
currentVersion: '17.0.0',
|
|
42
|
+
latestVersion: '18.0.0',
|
|
43
|
+
updateType: 'major',
|
|
44
|
+
isSecurityUpdate: false,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
packageName: 'axios',
|
|
48
|
+
currentVersion: '0.21.0',
|
|
49
|
+
latestVersion: '1.0.0',
|
|
50
|
+
updateType: 'major',
|
|
51
|
+
isSecurityUpdate: true,
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const mockEmptyOutdatedReport = {
|
|
59
|
+
hasUpdates: false,
|
|
60
|
+
totalOutdated: 0,
|
|
61
|
+
catalogs: [],
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const mockUpdateResult = {
|
|
65
|
+
success: true,
|
|
66
|
+
totalUpdated: 2,
|
|
67
|
+
totalErrors: 0,
|
|
68
|
+
updatedDependencies: [
|
|
69
|
+
{
|
|
70
|
+
packageName: 'lodash',
|
|
71
|
+
catalogName: 'default',
|
|
72
|
+
fromVersion: '4.17.20',
|
|
73
|
+
toVersion: '4.17.21',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
packageName: 'react',
|
|
77
|
+
catalogName: 'default',
|
|
78
|
+
fromVersion: '17.0.0',
|
|
79
|
+
toVersion: '18.0.0',
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
errors: [],
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const mockUpdateResultWithErrors = {
|
|
86
|
+
success: false,
|
|
87
|
+
totalUpdated: 1,
|
|
88
|
+
totalErrors: 1,
|
|
89
|
+
updatedDependencies: [
|
|
90
|
+
{
|
|
91
|
+
packageName: 'lodash',
|
|
92
|
+
catalogName: 'default',
|
|
93
|
+
fromVersion: '4.17.20',
|
|
94
|
+
toVersion: '4.17.21',
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
errors: [
|
|
98
|
+
{
|
|
99
|
+
packageName: 'react',
|
|
100
|
+
catalogName: 'default',
|
|
101
|
+
error: 'Network timeout',
|
|
102
|
+
fatal: false,
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const mockUpdatePlan = {
|
|
108
|
+
totalUpdates: 2,
|
|
109
|
+
hasConflicts: false,
|
|
110
|
+
conflicts: [],
|
|
111
|
+
updates: [
|
|
112
|
+
{
|
|
113
|
+
packageName: 'lodash',
|
|
114
|
+
catalogName: 'default',
|
|
115
|
+
currentVersion: '4.17.20',
|
|
116
|
+
newVersion: '4.17.21',
|
|
117
|
+
updateType: 'patch',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
packageName: 'react',
|
|
121
|
+
catalogName: 'default',
|
|
122
|
+
currentVersion: '17.0.0',
|
|
123
|
+
newVersion: '18.0.0',
|
|
124
|
+
updateType: 'major',
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const mockUpdatePlanWithConflicts = {
|
|
130
|
+
totalUpdates: 2,
|
|
131
|
+
hasConflicts: true,
|
|
132
|
+
conflicts: [
|
|
133
|
+
{
|
|
134
|
+
packageName: 'react',
|
|
135
|
+
versions: ['17.0.0', '18.0.0'],
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
updates: [
|
|
139
|
+
{
|
|
140
|
+
packageName: 'react',
|
|
141
|
+
catalogName: 'default',
|
|
142
|
+
currentVersion: '17.0.0',
|
|
143
|
+
newVersion: '18.0.0',
|
|
144
|
+
updateType: 'major',
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const mockValidationReport = {
|
|
150
|
+
isValid: true,
|
|
151
|
+
errors: [],
|
|
152
|
+
warnings: ['Consider pinning version for lodash'],
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const mockInvalidValidationReport = {
|
|
156
|
+
isValid: false,
|
|
157
|
+
errors: ['Missing pnpm-workspace.yaml', 'Invalid catalog configuration'],
|
|
158
|
+
warnings: ['Consider pinning version for lodash'],
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const mockSecurityReport = {
|
|
162
|
+
summary: {
|
|
163
|
+
totalVulnerabilities: 2,
|
|
164
|
+
critical: 1,
|
|
165
|
+
high: 1,
|
|
166
|
+
moderate: 0,
|
|
167
|
+
low: 0,
|
|
168
|
+
},
|
|
169
|
+
vulnerabilities: [
|
|
170
|
+
{
|
|
171
|
+
id: 'VULN-001',
|
|
172
|
+
package: 'axios',
|
|
173
|
+
title: 'Server-Side Request Forgery',
|
|
174
|
+
severity: 'critical',
|
|
175
|
+
overview: 'SSRF vulnerability in axios',
|
|
176
|
+
cwe: 918,
|
|
177
|
+
fixAvailable: '1.0.0',
|
|
178
|
+
url: 'https://nvd.nist.gov/vuln/detail/CVE-2021-12345',
|
|
179
|
+
installedVersion: '0.21.0',
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
id: 'VULN-002',
|
|
183
|
+
package: 'lodash',
|
|
184
|
+
title: 'Prototype Pollution',
|
|
185
|
+
severity: 'high',
|
|
186
|
+
overview: 'Prototype pollution in lodash',
|
|
187
|
+
cwe: 1321,
|
|
188
|
+
fixAvailable: '4.17.21',
|
|
189
|
+
url: 'https://nvd.nist.gov/vuln/detail/CVE-2021-54321',
|
|
190
|
+
installedVersion: '4.17.20',
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
metadata: {
|
|
194
|
+
scanDate: '2024-01-01T00:00:00.000Z',
|
|
195
|
+
},
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const mockEmptySecurityReport = {
|
|
199
|
+
summary: {
|
|
200
|
+
totalVulnerabilities: 0,
|
|
201
|
+
critical: 0,
|
|
202
|
+
high: 0,
|
|
203
|
+
moderate: 0,
|
|
204
|
+
low: 0,
|
|
205
|
+
},
|
|
206
|
+
vulnerabilities: [],
|
|
207
|
+
metadata: {
|
|
208
|
+
scanDate: '2024-01-01T00:00:00.000Z',
|
|
209
|
+
},
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
describe('GitHub Actions format', () => {
|
|
213
|
+
let formatter: InstanceType<typeof CIFormatter>
|
|
214
|
+
|
|
215
|
+
beforeEach(() => {
|
|
216
|
+
formatter = new CIFormatter('github')
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
describe('formatOutdatedReport', () => {
|
|
220
|
+
it('should format outdated report with GitHub annotations', () => {
|
|
221
|
+
const output = formatter.formatOutdatedReport(mockOutdatedReport)
|
|
222
|
+
|
|
223
|
+
expect(output).toContain('::warning')
|
|
224
|
+
expect(output).toContain('::notice')
|
|
225
|
+
expect(output).toContain('::group::')
|
|
226
|
+
expect(output).toContain('lodash')
|
|
227
|
+
expect(output).toContain('react')
|
|
228
|
+
expect(output).toContain('axios')
|
|
229
|
+
expect(output).toContain('[SECURITY]')
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
it('should format notice when no updates found', () => {
|
|
233
|
+
const output = formatter.formatOutdatedReport(mockEmptyOutdatedReport)
|
|
234
|
+
|
|
235
|
+
expect(output).toContain('::notice')
|
|
236
|
+
expect(output).toContain('format.allUpToDate')
|
|
237
|
+
})
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
describe('formatUpdateResult', () => {
|
|
241
|
+
it('should format successful update result', () => {
|
|
242
|
+
const output = formatter.formatUpdateResult(mockUpdateResult)
|
|
243
|
+
|
|
244
|
+
expect(output).toContain('::notice')
|
|
245
|
+
expect(output).toContain('format.updatedCount')
|
|
246
|
+
expect(output).toContain('::group::')
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
it('should format update result with errors', () => {
|
|
250
|
+
const output = formatter.formatUpdateResult(mockUpdateResultWithErrors)
|
|
251
|
+
|
|
252
|
+
expect(output).toContain('::error')
|
|
253
|
+
expect(output).toContain('Network timeout')
|
|
254
|
+
})
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
describe('formatUpdatePlan', () => {
|
|
258
|
+
it('should format update plan', () => {
|
|
259
|
+
const output = formatter.formatUpdatePlan(mockUpdatePlan)
|
|
260
|
+
|
|
261
|
+
expect(output).toContain('::notice')
|
|
262
|
+
expect(output).toContain('format.plannedUpdates')
|
|
263
|
+
expect(output).toContain('lodash')
|
|
264
|
+
expect(output).toContain('react')
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
it('should format plan with conflicts', () => {
|
|
268
|
+
const output = formatter.formatUpdatePlan(mockUpdatePlanWithConflicts)
|
|
269
|
+
|
|
270
|
+
expect(output).toContain('::warning')
|
|
271
|
+
expect(output).toContain('Version conflict')
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
it('should format empty plan', () => {
|
|
275
|
+
const output = formatter.formatUpdatePlan({
|
|
276
|
+
totalUpdates: 0,
|
|
277
|
+
hasConflicts: false,
|
|
278
|
+
conflicts: [],
|
|
279
|
+
updates: [],
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
expect(output).toContain('::notice')
|
|
283
|
+
expect(output).toContain('format.noUpdatesPlanned')
|
|
284
|
+
})
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
describe('formatValidationReport', () => {
|
|
288
|
+
it('should format valid workspace', () => {
|
|
289
|
+
const output = formatter.formatValidationReport(mockValidationReport)
|
|
290
|
+
|
|
291
|
+
expect(output).toContain('::notice')
|
|
292
|
+
expect(output).toContain('format.valid')
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
it('should format invalid workspace with errors', () => {
|
|
296
|
+
const output = formatter.formatValidationReport(mockInvalidValidationReport)
|
|
297
|
+
|
|
298
|
+
expect(output).toContain('::error')
|
|
299
|
+
expect(output).toContain('format.invalid')
|
|
300
|
+
expect(output).toContain('Missing pnpm-workspace.yaml')
|
|
301
|
+
})
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
describe('formatSecurityReport', () => {
|
|
305
|
+
it('should format security report with vulnerabilities', () => {
|
|
306
|
+
const output = formatter.formatSecurityReport(mockSecurityReport)
|
|
307
|
+
|
|
308
|
+
expect(output).toContain('::error')
|
|
309
|
+
expect(output).toContain('critical')
|
|
310
|
+
expect(output).toContain('axios')
|
|
311
|
+
expect(output).toContain('lodash')
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
it('should format notice when no vulnerabilities', () => {
|
|
315
|
+
const output = formatter.formatSecurityReport(mockEmptySecurityReport)
|
|
316
|
+
|
|
317
|
+
expect(output).toContain('::notice')
|
|
318
|
+
expect(output).toContain('format.noVulnsFound')
|
|
319
|
+
})
|
|
320
|
+
})
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
describe('GitLab CI format', () => {
|
|
324
|
+
let formatter: InstanceType<typeof CIFormatter>
|
|
325
|
+
|
|
326
|
+
beforeEach(() => {
|
|
327
|
+
formatter = new CIFormatter('gitlab')
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
describe('formatOutdatedReport', () => {
|
|
331
|
+
it('should format outdated report as GitLab code quality JSON', () => {
|
|
332
|
+
const output = formatter.formatOutdatedReport(mockOutdatedReport)
|
|
333
|
+
const parsed = JSON.parse(output)
|
|
334
|
+
|
|
335
|
+
expect(Array.isArray(parsed)).toBe(true)
|
|
336
|
+
expect(parsed.length).toBe(3)
|
|
337
|
+
expect(parsed[0]).toHaveProperty('description')
|
|
338
|
+
expect(parsed[0]).toHaveProperty('check_name', 'outdated-dependency')
|
|
339
|
+
expect(parsed[0]).toHaveProperty('fingerprint')
|
|
340
|
+
expect(parsed[0]).toHaveProperty('severity')
|
|
341
|
+
expect(parsed[0]).toHaveProperty('location')
|
|
342
|
+
})
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
describe('formatUpdateResult', () => {
|
|
346
|
+
it('should format update errors as GitLab code quality JSON', () => {
|
|
347
|
+
const output = formatter.formatUpdateResult(mockUpdateResultWithErrors)
|
|
348
|
+
const parsed = JSON.parse(output)
|
|
349
|
+
|
|
350
|
+
expect(Array.isArray(parsed)).toBe(true)
|
|
351
|
+
expect(parsed.length).toBe(1)
|
|
352
|
+
expect(parsed[0].check_name).toBe('update-error')
|
|
353
|
+
})
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
describe('formatSecurityReport', () => {
|
|
357
|
+
it('should format as GitLab Security Report', () => {
|
|
358
|
+
const output = formatter.formatSecurityReport(mockSecurityReport)
|
|
359
|
+
const parsed = JSON.parse(output)
|
|
360
|
+
|
|
361
|
+
expect(parsed.version).toBe('15.0.0')
|
|
362
|
+
expect(parsed.vulnerabilities).toHaveLength(2)
|
|
363
|
+
expect(parsed.scan).toBeDefined()
|
|
364
|
+
expect(parsed.scan.scanner.id).toBe('pcu-security')
|
|
365
|
+
})
|
|
366
|
+
})
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
describe('JUnit XML format', () => {
|
|
370
|
+
let formatter: InstanceType<typeof CIFormatter>
|
|
371
|
+
|
|
372
|
+
beforeEach(() => {
|
|
373
|
+
formatter = new CIFormatter('junit')
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
describe('formatOutdatedReport', () => {
|
|
377
|
+
it('should format as JUnit XML', () => {
|
|
378
|
+
const output = formatter.formatOutdatedReport(mockOutdatedReport)
|
|
379
|
+
|
|
380
|
+
expect(output).toContain('<?xml version="1.0"')
|
|
381
|
+
expect(output).toContain('<testsuites')
|
|
382
|
+
expect(output).toContain('<testsuite')
|
|
383
|
+
expect(output).toContain('<testcase')
|
|
384
|
+
expect(output).toContain('lodash')
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
it('should mark major updates as failures', () => {
|
|
388
|
+
const output = formatter.formatOutdatedReport(mockOutdatedReport)
|
|
389
|
+
|
|
390
|
+
expect(output).toContain('<failure')
|
|
391
|
+
expect(output).toContain('major update')
|
|
392
|
+
})
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
describe('formatUpdateResult', () => {
|
|
396
|
+
it('should format as JUnit XML', () => {
|
|
397
|
+
const output = formatter.formatUpdateResult(mockUpdateResult)
|
|
398
|
+
|
|
399
|
+
expect(output).toContain('<?xml version="1.0"')
|
|
400
|
+
expect(output).toContain('pcu-update')
|
|
401
|
+
expect(output).toContain('failures="0"')
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
it('should include failures for errors', () => {
|
|
405
|
+
const output = formatter.formatUpdateResult(mockUpdateResultWithErrors)
|
|
406
|
+
|
|
407
|
+
expect(output).toContain('failures="1"')
|
|
408
|
+
expect(output).toContain('<failure')
|
|
409
|
+
expect(output).toContain('Network timeout')
|
|
410
|
+
})
|
|
411
|
+
})
|
|
412
|
+
|
|
413
|
+
describe('formatSecurityReport', () => {
|
|
414
|
+
it('should format vulnerabilities as test failures', () => {
|
|
415
|
+
const output = formatter.formatSecurityReport(mockSecurityReport)
|
|
416
|
+
|
|
417
|
+
expect(output).toContain('<?xml version="1.0"')
|
|
418
|
+
expect(output).toContain('pcu-security')
|
|
419
|
+
expect(output).toContain('<failure')
|
|
420
|
+
expect(output).toContain('SecurityVulnerability')
|
|
421
|
+
})
|
|
422
|
+
})
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
describe('SARIF format', () => {
|
|
426
|
+
let formatter: InstanceType<typeof CIFormatter>
|
|
427
|
+
|
|
428
|
+
beforeEach(() => {
|
|
429
|
+
formatter = new CIFormatter('sarif')
|
|
430
|
+
})
|
|
431
|
+
|
|
432
|
+
describe('formatOutdatedReport', () => {
|
|
433
|
+
it('should format as valid SARIF JSON', () => {
|
|
434
|
+
const output = formatter.formatOutdatedReport(mockOutdatedReport)
|
|
435
|
+
const parsed = JSON.parse(output)
|
|
436
|
+
|
|
437
|
+
expect(parsed.$schema).toContain('sarif-schema')
|
|
438
|
+
expect(parsed.version).toBe('2.1.0')
|
|
439
|
+
expect(parsed.runs).toHaveLength(1)
|
|
440
|
+
expect(parsed.runs[0].tool.driver.name).toBe('pcu-check')
|
|
441
|
+
expect(parsed.runs[0].results.length).toBeGreaterThan(0)
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
it('should include rules for each update type', () => {
|
|
445
|
+
const output = formatter.formatOutdatedReport(mockOutdatedReport)
|
|
446
|
+
const parsed = JSON.parse(output)
|
|
447
|
+
|
|
448
|
+
const rules = parsed.runs[0].tool.driver.rules
|
|
449
|
+
expect(rules.some((r: { id: string }) => r.id === 'outdated-major')).toBe(true)
|
|
450
|
+
expect(rules.some((r: { id: string }) => r.id === 'outdated-patch')).toBe(true)
|
|
451
|
+
})
|
|
452
|
+
})
|
|
453
|
+
|
|
454
|
+
describe('formatUpdateResult', () => {
|
|
455
|
+
it('should format as valid SARIF JSON', () => {
|
|
456
|
+
const output = formatter.formatUpdateResult(mockUpdateResult)
|
|
457
|
+
const parsed = JSON.parse(output)
|
|
458
|
+
|
|
459
|
+
expect(parsed.runs[0].tool.driver.name).toBe('pcu-update')
|
|
460
|
+
expect(parsed.runs[0].results.length).toBe(2)
|
|
461
|
+
})
|
|
462
|
+
})
|
|
463
|
+
|
|
464
|
+
describe('formatUpdatePlan', () => {
|
|
465
|
+
it('should format as valid SARIF JSON', () => {
|
|
466
|
+
const output = formatter.formatUpdatePlan(mockUpdatePlan)
|
|
467
|
+
const parsed = JSON.parse(output)
|
|
468
|
+
|
|
469
|
+
expect(parsed.runs[0].tool.driver.name).toBe('pcu-update-plan')
|
|
470
|
+
})
|
|
471
|
+
|
|
472
|
+
it('should include conflict results', () => {
|
|
473
|
+
const output = formatter.formatUpdatePlan(mockUpdatePlanWithConflicts)
|
|
474
|
+
const parsed = JSON.parse(output)
|
|
475
|
+
|
|
476
|
+
const conflictResults = parsed.runs[0].results.filter(
|
|
477
|
+
(r: { ruleId: string }) => r.ruleId === 'version-conflict'
|
|
478
|
+
)
|
|
479
|
+
expect(conflictResults.length).toBeGreaterThan(0)
|
|
480
|
+
})
|
|
481
|
+
})
|
|
482
|
+
|
|
483
|
+
describe('formatValidationReport', () => {
|
|
484
|
+
it('should format as valid SARIF JSON', () => {
|
|
485
|
+
const output = formatter.formatValidationReport(mockInvalidValidationReport)
|
|
486
|
+
const parsed = JSON.parse(output)
|
|
487
|
+
|
|
488
|
+
expect(parsed.runs[0].tool.driver.name).toBe('pcu-validation')
|
|
489
|
+
expect(
|
|
490
|
+
parsed.runs[0].results.some((r: { ruleId: string }) => r.ruleId === 'validation-error')
|
|
491
|
+
).toBe(true)
|
|
492
|
+
})
|
|
493
|
+
})
|
|
494
|
+
|
|
495
|
+
describe('formatSecurityReport', () => {
|
|
496
|
+
it('should format as valid SARIF JSON', () => {
|
|
497
|
+
const output = formatter.formatSecurityReport(mockSecurityReport)
|
|
498
|
+
const parsed = JSON.parse(output)
|
|
499
|
+
|
|
500
|
+
expect(parsed.runs[0].tool.driver.name).toBe('pcu-security')
|
|
501
|
+
expect(parsed.runs[0].results.length).toBe(2)
|
|
502
|
+
})
|
|
503
|
+
|
|
504
|
+
it('should map severity levels correctly', () => {
|
|
505
|
+
const output = formatter.formatSecurityReport(mockSecurityReport)
|
|
506
|
+
const parsed = JSON.parse(output)
|
|
507
|
+
|
|
508
|
+
const criticalResult = parsed.runs[0].results.find(
|
|
509
|
+
(r: { properties: { severity: string } }) => r.properties.severity === 'critical'
|
|
510
|
+
)
|
|
511
|
+
expect(criticalResult.level).toBe('error')
|
|
512
|
+
})
|
|
513
|
+
})
|
|
514
|
+
})
|
|
515
|
+
|
|
516
|
+
describe('default format fallback', () => {
|
|
517
|
+
it('should return JSON for unknown format', () => {
|
|
518
|
+
// @ts-expect-error Testing unknown format
|
|
519
|
+
const formatter = new CIFormatter('unknown')
|
|
520
|
+
const output = formatter.formatOutdatedReport(mockOutdatedReport)
|
|
521
|
+
const parsed = JSON.parse(output)
|
|
522
|
+
|
|
523
|
+
expect(parsed).toEqual(mockOutdatedReport)
|
|
524
|
+
})
|
|
525
|
+
})
|
|
526
|
+
})
|