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,400 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rollback Command Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { BackupInfo } from '@pcu/core'
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
7
|
+
|
|
8
|
+
// Mock BackupService using vi.hoisted for proper class mocking
|
|
9
|
+
const backupServiceMocks = vi.hoisted(() => ({
|
|
10
|
+
listBackups: vi.fn(),
|
|
11
|
+
restoreFromBackup: vi.fn(),
|
|
12
|
+
deleteAllBackups: vi.fn(),
|
|
13
|
+
verifyRestoredFile: vi.fn(),
|
|
14
|
+
}))
|
|
15
|
+
|
|
16
|
+
vi.mock('@pcu/core', () => {
|
|
17
|
+
return {
|
|
18
|
+
BackupService: class MockBackupService {
|
|
19
|
+
listBackups = backupServiceMocks.listBackups
|
|
20
|
+
restoreFromBackup = backupServiceMocks.restoreFromBackup
|
|
21
|
+
deleteAllBackups = backupServiceMocks.deleteAllBackups
|
|
22
|
+
verifyRestoredFile = backupServiceMocks.verifyRestoredFile
|
|
23
|
+
},
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
// Mock @pcu/utils
|
|
28
|
+
vi.mock('@pcu/utils', () => ({
|
|
29
|
+
logger: {
|
|
30
|
+
error: vi.fn(),
|
|
31
|
+
warn: vi.fn(),
|
|
32
|
+
info: vi.fn(),
|
|
33
|
+
debug: vi.fn(),
|
|
34
|
+
},
|
|
35
|
+
t: (key: string, params?: Record<string, unknown>) => {
|
|
36
|
+
if (params) {
|
|
37
|
+
return `${key} ${JSON.stringify(params)}`
|
|
38
|
+
}
|
|
39
|
+
return key
|
|
40
|
+
},
|
|
41
|
+
}))
|
|
42
|
+
|
|
43
|
+
// Mock inquirer
|
|
44
|
+
const inquirerMocks = vi.hoisted(() => ({
|
|
45
|
+
prompt: vi.fn(),
|
|
46
|
+
}))
|
|
47
|
+
|
|
48
|
+
vi.mock('inquirer', () => ({
|
|
49
|
+
default: {
|
|
50
|
+
prompt: inquirerMocks.prompt,
|
|
51
|
+
},
|
|
52
|
+
}))
|
|
53
|
+
|
|
54
|
+
// Mock chalk
|
|
55
|
+
vi.mock('chalk', () => ({
|
|
56
|
+
default: {
|
|
57
|
+
blue: (text: string) => `[blue]${text}`,
|
|
58
|
+
gray: (text: string) => `[gray]${text}`,
|
|
59
|
+
cyan: (text: string) => `[cyan]${text}`,
|
|
60
|
+
green: (text: string) => `[green]${text}`,
|
|
61
|
+
yellow: (text: string) => `[yellow]${text}`,
|
|
62
|
+
},
|
|
63
|
+
}))
|
|
64
|
+
|
|
65
|
+
// Mock StyledText - QUAL-006/QUAL-016: Updated to include all methods used
|
|
66
|
+
vi.mock('../../themes/colorTheme.js', () => ({
|
|
67
|
+
StyledText: {
|
|
68
|
+
iconWarning: (text?: string) => `[warning]${text ?? ''}`,
|
|
69
|
+
iconSuccess: (text?: string) => `[success]${text ?? ''}`,
|
|
70
|
+
iconError: (text?: string) => `[error]${text ?? ''}`,
|
|
71
|
+
error: (text: string) => `[error]${text}`,
|
|
72
|
+
muted: (text: string) => `[muted]${text}`,
|
|
73
|
+
info: (text: string) => `[info]${text}`,
|
|
74
|
+
warning: (text: string) => `[warning]${text}`,
|
|
75
|
+
success: (text: string) => `[success]${text}`,
|
|
76
|
+
highlight: (text: string) => `[highlight]${text}`,
|
|
77
|
+
accent: (text: string) => `[accent]${text}`,
|
|
78
|
+
},
|
|
79
|
+
}))
|
|
80
|
+
|
|
81
|
+
// Mock cliOutput - QUAL-006: Added for unified output helpers
|
|
82
|
+
vi.mock('../../utils/cliOutput.js', () => ({
|
|
83
|
+
cliOutput: {
|
|
84
|
+
print: vi.fn((...args: unknown[]) => console.log(...args)),
|
|
85
|
+
error: vi.fn((...args: unknown[]) => console.error(...args)),
|
|
86
|
+
warn: vi.fn((...args: unknown[]) => console.warn(...args)),
|
|
87
|
+
},
|
|
88
|
+
}))
|
|
89
|
+
|
|
90
|
+
// Mock commandHelpers
|
|
91
|
+
vi.mock('../../utils/commandHelpers.js', () => ({
|
|
92
|
+
handleCommandError: vi.fn((error: unknown, _options?: unknown) => {
|
|
93
|
+
console.error(`[error]error.unknown`)
|
|
94
|
+
console.error(`[error]${String(error)}`)
|
|
95
|
+
return false
|
|
96
|
+
}),
|
|
97
|
+
}))
|
|
98
|
+
|
|
99
|
+
const { RollbackCommand } = await import('../rollbackCommand.js')
|
|
100
|
+
|
|
101
|
+
describe('RollbackCommand', () => {
|
|
102
|
+
let command: InstanceType<typeof RollbackCommand>
|
|
103
|
+
let consoleSpy: ReturnType<typeof vi.spyOn>
|
|
104
|
+
let consoleErrorSpy: ReturnType<typeof vi.spyOn>
|
|
105
|
+
|
|
106
|
+
const mockBackups: BackupInfo[] = [
|
|
107
|
+
{
|
|
108
|
+
path: '/workspace/pnpm-workspace.yaml.backup.2024-01-15T10-30-00-000Z',
|
|
109
|
+
timestamp: new Date('2024-01-15T10:30:00.000Z'),
|
|
110
|
+
size: 2048,
|
|
111
|
+
formattedTime: '01/15/2024, 10:30:00',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
path: '/workspace/pnpm-workspace.yaml.backup.2024-01-14T09-00-00-000Z',
|
|
115
|
+
timestamp: new Date('2024-01-14T09:00:00.000Z'),
|
|
116
|
+
size: 1536,
|
|
117
|
+
formattedTime: '01/14/2024, 09:00:00',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
path: '/workspace/pnpm-workspace.yaml.backup.2024-01-13T08-00-00-000Z',
|
|
121
|
+
timestamp: new Date('2024-01-13T08:00:00.000Z'),
|
|
122
|
+
size: 1024,
|
|
123
|
+
formattedTime: '01/13/2024, 08:00:00',
|
|
124
|
+
},
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
beforeEach(() => {
|
|
128
|
+
vi.clearAllMocks()
|
|
129
|
+
command = new RollbackCommand()
|
|
130
|
+
consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
|
|
131
|
+
consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
132
|
+
|
|
133
|
+
// Setup default mock for verifyRestoredFile
|
|
134
|
+
backupServiceMocks.verifyRestoredFile.mockResolvedValue({
|
|
135
|
+
success: true,
|
|
136
|
+
isValidYaml: true,
|
|
137
|
+
hasCatalogStructure: true,
|
|
138
|
+
catalogs: ['default'],
|
|
139
|
+
dependencyCount: 5,
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
afterEach(() => {
|
|
144
|
+
consoleSpy.mockRestore()
|
|
145
|
+
consoleErrorSpy.mockRestore()
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
describe('execute with --list option', () => {
|
|
149
|
+
it('should list available backups', async () => {
|
|
150
|
+
backupServiceMocks.listBackups.mockResolvedValue(mockBackups)
|
|
151
|
+
|
|
152
|
+
await command.execute({ list: true })
|
|
153
|
+
|
|
154
|
+
expect(backupServiceMocks.listBackups).toHaveBeenCalledWith(
|
|
155
|
+
expect.stringContaining('pnpm-workspace.yaml')
|
|
156
|
+
)
|
|
157
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
158
|
+
expect.stringContaining('command.rollback.availableBackups')
|
|
159
|
+
)
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('should show message when no backups exist', async () => {
|
|
163
|
+
backupServiceMocks.listBackups.mockResolvedValue([])
|
|
164
|
+
|
|
165
|
+
await command.execute({ list: true })
|
|
166
|
+
|
|
167
|
+
expect(consoleSpy).toHaveBeenCalledWith('[warning]command.rollback.noBackups')
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it('should show verbose information when verbose option is set', async () => {
|
|
171
|
+
backupServiceMocks.listBackups.mockResolvedValue(mockBackups)
|
|
172
|
+
|
|
173
|
+
await command.execute({ list: true, verbose: true })
|
|
174
|
+
|
|
175
|
+
// Verbose mode shows path and size
|
|
176
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Path:'))
|
|
177
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Size:'))
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('should use custom workspace path', async () => {
|
|
181
|
+
backupServiceMocks.listBackups.mockResolvedValue([])
|
|
182
|
+
|
|
183
|
+
await command.execute({ list: true, workspace: '/custom/path' })
|
|
184
|
+
|
|
185
|
+
expect(backupServiceMocks.listBackups).toHaveBeenCalledWith(
|
|
186
|
+
'/custom/path/pnpm-workspace.yaml'
|
|
187
|
+
)
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
describe('execute with --latest option', () => {
|
|
192
|
+
it('should restore from latest backup when confirmed', async () => {
|
|
193
|
+
backupServiceMocks.listBackups.mockResolvedValue(mockBackups)
|
|
194
|
+
backupServiceMocks.restoreFromBackup.mockResolvedValue(
|
|
195
|
+
'/workspace/pnpm-workspace.yaml.backup.pre-restore'
|
|
196
|
+
)
|
|
197
|
+
inquirerMocks.prompt.mockResolvedValue({ confirmed: true })
|
|
198
|
+
|
|
199
|
+
await command.execute({ latest: true })
|
|
200
|
+
|
|
201
|
+
expect(backupServiceMocks.restoreFromBackup).toHaveBeenCalledWith(
|
|
202
|
+
expect.stringContaining('pnpm-workspace.yaml'),
|
|
203
|
+
mockBackups[0]?.path
|
|
204
|
+
)
|
|
205
|
+
expect(consoleSpy).toHaveBeenCalledWith('[success]command.rollback.success')
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
it('should display auto-backup note and pre-restore backup path', async () => {
|
|
209
|
+
const preRestoreBackupPath = '/workspace/pnpm-workspace.yaml.backup.2024-01-16T12-00-00-000Z'
|
|
210
|
+
backupServiceMocks.listBackups.mockResolvedValue(mockBackups)
|
|
211
|
+
backupServiceMocks.restoreFromBackup.mockResolvedValue(preRestoreBackupPath)
|
|
212
|
+
inquirerMocks.prompt.mockResolvedValue({ confirmed: true })
|
|
213
|
+
|
|
214
|
+
await command.execute({ latest: true })
|
|
215
|
+
|
|
216
|
+
// Should show auto-backup note before confirmation
|
|
217
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
218
|
+
expect.stringContaining('command.rollback.autoBackupNote')
|
|
219
|
+
)
|
|
220
|
+
// Should show pre-restore backup path after restore
|
|
221
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
222
|
+
expect.stringContaining('command.rollback.preRestoreBackupCreated')
|
|
223
|
+
)
|
|
224
|
+
// Should show safety note
|
|
225
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
226
|
+
expect.stringContaining('command.rollback.safetyNote')
|
|
227
|
+
)
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
it('should not restore when user cancels', async () => {
|
|
231
|
+
backupServiceMocks.listBackups.mockResolvedValue(mockBackups)
|
|
232
|
+
inquirerMocks.prompt.mockResolvedValue({ confirmed: false })
|
|
233
|
+
|
|
234
|
+
await command.execute({ latest: true })
|
|
235
|
+
|
|
236
|
+
expect(backupServiceMocks.restoreFromBackup).not.toHaveBeenCalled()
|
|
237
|
+
expect(consoleSpy).toHaveBeenCalledWith('[warning]command.rollback.cancelled')
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
it('should show message when no backups exist', async () => {
|
|
241
|
+
backupServiceMocks.listBackups.mockResolvedValue([])
|
|
242
|
+
|
|
243
|
+
await command.execute({ latest: true })
|
|
244
|
+
|
|
245
|
+
expect(backupServiceMocks.restoreFromBackup).not.toHaveBeenCalled()
|
|
246
|
+
expect(consoleSpy).toHaveBeenCalledWith('[warning]command.rollback.noBackups')
|
|
247
|
+
})
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
describe('execute with --delete-all option', () => {
|
|
251
|
+
it('should delete all backups when confirmed', async () => {
|
|
252
|
+
backupServiceMocks.listBackups.mockResolvedValue(mockBackups)
|
|
253
|
+
backupServiceMocks.deleteAllBackups.mockResolvedValue(3)
|
|
254
|
+
inquirerMocks.prompt.mockResolvedValue({ confirmed: true })
|
|
255
|
+
|
|
256
|
+
await command.execute({ deleteAll: true })
|
|
257
|
+
|
|
258
|
+
expect(backupServiceMocks.deleteAllBackups).toHaveBeenCalledWith(
|
|
259
|
+
expect.stringContaining('pnpm-workspace.yaml')
|
|
260
|
+
)
|
|
261
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
262
|
+
expect.stringContaining('[success]command.rollback.deletedBackups')
|
|
263
|
+
)
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
it('should not delete when user cancels', async () => {
|
|
267
|
+
backupServiceMocks.listBackups.mockResolvedValue(mockBackups)
|
|
268
|
+
inquirerMocks.prompt.mockResolvedValue({ confirmed: false })
|
|
269
|
+
|
|
270
|
+
await command.execute({ deleteAll: true })
|
|
271
|
+
|
|
272
|
+
expect(backupServiceMocks.deleteAllBackups).not.toHaveBeenCalled()
|
|
273
|
+
expect(consoleSpy).toHaveBeenCalledWith('[warning]command.rollback.cancelled')
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
it('should show message when no backups exist', async () => {
|
|
277
|
+
backupServiceMocks.listBackups.mockResolvedValue([])
|
|
278
|
+
|
|
279
|
+
await command.execute({ deleteAll: true })
|
|
280
|
+
|
|
281
|
+
expect(backupServiceMocks.deleteAllBackups).not.toHaveBeenCalled()
|
|
282
|
+
expect(consoleSpy).toHaveBeenCalledWith('[warning]command.rollback.noBackups')
|
|
283
|
+
})
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
describe('execute with interactive mode (default)', () => {
|
|
287
|
+
it('should restore selected backup when confirmed', async () => {
|
|
288
|
+
backupServiceMocks.listBackups.mockResolvedValue(mockBackups)
|
|
289
|
+
backupServiceMocks.restoreFromBackup.mockResolvedValue(
|
|
290
|
+
'/workspace/pnpm-workspace.yaml.backup.pre-restore'
|
|
291
|
+
)
|
|
292
|
+
inquirerMocks.prompt
|
|
293
|
+
.mockResolvedValueOnce({ selectedBackup: mockBackups[1] })
|
|
294
|
+
.mockResolvedValueOnce({ confirmed: true })
|
|
295
|
+
|
|
296
|
+
await command.execute({})
|
|
297
|
+
|
|
298
|
+
expect(backupServiceMocks.restoreFromBackup).toHaveBeenCalledWith(
|
|
299
|
+
expect.stringContaining('pnpm-workspace.yaml'),
|
|
300
|
+
mockBackups[1]?.path
|
|
301
|
+
)
|
|
302
|
+
expect(consoleSpy).toHaveBeenCalledWith('[success]command.rollback.success')
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it('should display auto-backup note and pre-restore backup path in interactive mode', async () => {
|
|
306
|
+
const preRestoreBackupPath = '/workspace/pnpm-workspace.yaml.backup.2024-01-16T12-00-00-000Z'
|
|
307
|
+
backupServiceMocks.listBackups.mockResolvedValue(mockBackups)
|
|
308
|
+
backupServiceMocks.restoreFromBackup.mockResolvedValue(preRestoreBackupPath)
|
|
309
|
+
inquirerMocks.prompt
|
|
310
|
+
.mockResolvedValueOnce({ selectedBackup: mockBackups[0] })
|
|
311
|
+
.mockResolvedValueOnce({ confirmed: true })
|
|
312
|
+
|
|
313
|
+
await command.execute({})
|
|
314
|
+
|
|
315
|
+
// Should show auto-backup note before confirmation
|
|
316
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
317
|
+
expect.stringContaining('command.rollback.autoBackupNote')
|
|
318
|
+
)
|
|
319
|
+
// Should show pre-restore backup path after restore
|
|
320
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
321
|
+
expect.stringContaining('command.rollback.preRestoreBackupCreated')
|
|
322
|
+
)
|
|
323
|
+
// Should show safety note
|
|
324
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
325
|
+
expect.stringContaining('command.rollback.safetyNote')
|
|
326
|
+
)
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
it('should not restore when user cancels after selection', async () => {
|
|
330
|
+
backupServiceMocks.listBackups.mockResolvedValue(mockBackups)
|
|
331
|
+
inquirerMocks.prompt
|
|
332
|
+
.mockResolvedValueOnce({ selectedBackup: mockBackups[0] })
|
|
333
|
+
.mockResolvedValueOnce({ confirmed: false })
|
|
334
|
+
|
|
335
|
+
await command.execute({})
|
|
336
|
+
|
|
337
|
+
expect(backupServiceMocks.restoreFromBackup).not.toHaveBeenCalled()
|
|
338
|
+
expect(consoleSpy).toHaveBeenCalledWith('[warning]command.rollback.cancelled')
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
it('should show message when no backups exist', async () => {
|
|
342
|
+
backupServiceMocks.listBackups.mockResolvedValue([])
|
|
343
|
+
|
|
344
|
+
await command.execute({})
|
|
345
|
+
|
|
346
|
+
expect(inquirerMocks.prompt).not.toHaveBeenCalled()
|
|
347
|
+
expect(consoleSpy).toHaveBeenCalledWith('[warning]command.rollback.noBackups')
|
|
348
|
+
})
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
describe('error handling', () => {
|
|
352
|
+
it('should handle and rethrow errors', async () => {
|
|
353
|
+
const error = new Error('Backup service failed')
|
|
354
|
+
backupServiceMocks.listBackups.mockRejectedValue(error)
|
|
355
|
+
|
|
356
|
+
await expect(command.execute({ list: true })).rejects.toThrow('Backup service failed')
|
|
357
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('[error]'))
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
it('should handle non-Error objects', async () => {
|
|
361
|
+
backupServiceMocks.listBackups.mockRejectedValue('String error')
|
|
362
|
+
|
|
363
|
+
await expect(command.execute({ list: true })).rejects.toBe('String error')
|
|
364
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith('[error]error.unknown')
|
|
365
|
+
})
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
describe('getHelpText', () => {
|
|
369
|
+
it('should return help text', () => {
|
|
370
|
+
const helpText = RollbackCommand.getHelpText()
|
|
371
|
+
|
|
372
|
+
expect(helpText).toContain('Rollback catalog updates')
|
|
373
|
+
expect(helpText).toContain('--list')
|
|
374
|
+
expect(helpText).toContain('--latest')
|
|
375
|
+
expect(helpText).toContain('--delete-all')
|
|
376
|
+
expect(helpText).toContain('Examples:')
|
|
377
|
+
})
|
|
378
|
+
})
|
|
379
|
+
|
|
380
|
+
describe('workspace path handling', () => {
|
|
381
|
+
it('should use process.cwd() when no workspace option provided', async () => {
|
|
382
|
+
backupServiceMocks.listBackups.mockResolvedValue([])
|
|
383
|
+
const originalCwd = process.cwd()
|
|
384
|
+
|
|
385
|
+
await command.execute({ list: true })
|
|
386
|
+
|
|
387
|
+
expect(backupServiceMocks.listBackups).toHaveBeenCalledWith(
|
|
388
|
+
expect.stringContaining(originalCwd)
|
|
389
|
+
)
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
it('should use provided workspace path', async () => {
|
|
393
|
+
backupServiceMocks.listBackups.mockResolvedValue([])
|
|
394
|
+
|
|
395
|
+
await command.execute({ list: true, workspace: '/my/project' })
|
|
396
|
+
|
|
397
|
+
expect(backupServiceMocks.listBackups).toHaveBeenCalledWith('/my/project/pnpm-workspace.yaml')
|
|
398
|
+
})
|
|
399
|
+
})
|
|
400
|
+
})
|