@squiz/dxp-cli-next 5.25.3 → 5.26.0-develop.2

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,543 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const fs_1 = __importDefault(require("fs"));
16
+ const path_1 = __importDefault(require("path"));
17
+ const child_process_1 = require("child_process");
18
+ const axios_1 = __importDefault(require("axios"));
19
+ const ApplicationConfig_1 = require("../ApplicationConfig");
20
+ const ApplicationStore_1 = require("../ApplicationStore");
21
+ const ApiService_1 = require("../ApiService");
22
+ const utils_1 = require("./utils");
23
+ // Mock all external dependencies
24
+ jest.mock('fs');
25
+ jest.mock('path');
26
+ jest.mock('child_process');
27
+ jest.mock('axios');
28
+ jest.mock('../ApplicationConfig');
29
+ jest.mock('../ApplicationStore');
30
+ jest.mock('../ApiService');
31
+ // Mock global fetch
32
+ const mockFetch = jest.fn();
33
+ global.fetch = mockFetch;
34
+ const mockFs = fs_1.default;
35
+ const mockPath = path_1.default;
36
+ const mockSpawn = child_process_1.spawn;
37
+ const mockAxios = axios_1.default;
38
+ const mockFetchApplicationConfig = ApplicationConfig_1.fetchApplicationConfig;
39
+ const mockGetApplicationFile = ApplicationStore_1.getApplicationFile;
40
+ const mockApiService = ApiService_1.ApiService;
41
+ describe('Migration Utils', () => {
42
+ beforeEach(() => {
43
+ jest.clearAllMocks();
44
+ jest.resetAllMocks();
45
+ });
46
+ describe('handleCommandError', () => {
47
+ let mockCommand;
48
+ beforeEach(() => {
49
+ mockCommand = {
50
+ error: jest.fn(),
51
+ };
52
+ });
53
+ it('handles axios errors with response data message', () => {
54
+ const axiosError = {
55
+ message: 'Network Error',
56
+ response: {
57
+ data: {
58
+ message: 'Server Error',
59
+ details: 'Additional details',
60
+ },
61
+ },
62
+ };
63
+ mockAxios.isAxiosError.mockReturnValue(true);
64
+ (0, utils_1.handleCommandError)(mockCommand, axiosError);
65
+ expect(mockCommand.error).toHaveBeenCalledWith(expect.stringContaining('Network Error: Server Error - Additional details'));
66
+ });
67
+ it('handles axios errors without response data', () => {
68
+ const axiosError = {
69
+ message: 'Network Error',
70
+ };
71
+ mockAxios.isAxiosError.mockReturnValue(true);
72
+ (0, utils_1.handleCommandError)(mockCommand, axiosError);
73
+ expect(mockCommand.error).toHaveBeenCalledWith(expect.stringContaining('Network Error'));
74
+ });
75
+ it('handles regular errors with message', () => {
76
+ const error = new Error('Regular error');
77
+ mockAxios.isAxiosError.mockReturnValue(false);
78
+ (0, utils_1.handleCommandError)(mockCommand, error);
79
+ expect(mockCommand.error).toHaveBeenCalledWith(expect.stringContaining('Regular error'));
80
+ });
81
+ it('handles errors without message', () => {
82
+ const error = new Error();
83
+ error.message = '';
84
+ mockAxios.isAxiosError.mockReturnValue(false);
85
+ (0, utils_1.handleCommandError)(mockCommand, error);
86
+ expect(mockCommand.error).toHaveBeenCalledWith(expect.stringContaining('An unknown error occurred'));
87
+ });
88
+ it('shows stack trace in debug mode', () => {
89
+ const originalDebug = process.env.DEBUG;
90
+ process.env.DEBUG = 'true';
91
+ const error = new Error('Test error');
92
+ error.stack = 'Error stack trace';
93
+ mockAxios.isAxiosError.mockReturnValue(false);
94
+ (0, utils_1.handleCommandError)(mockCommand, error);
95
+ expect(mockCommand.error).toHaveBeenCalledWith('Error stack trace');
96
+ process.env.DEBUG = originalDebug;
97
+ });
98
+ });
99
+ describe('throwErrorIfNotLoggedIn', () => {
100
+ let mockCommand;
101
+ beforeEach(() => {
102
+ mockCommand = {
103
+ error: jest.fn(),
104
+ };
105
+ });
106
+ it('throws error when not logged in', () => __awaiter(void 0, void 0, void 0, function* () {
107
+ mockGetApplicationFile.mockResolvedValue(undefined);
108
+ yield (0, utils_1.throwErrorIfNotLoggedIn)(mockCommand);
109
+ expect(mockCommand.error).toHaveBeenCalledWith(expect.stringContaining('You must login to interact with the migration service'));
110
+ }));
111
+ it('does not throw error when logged in', () => __awaiter(void 0, void 0, void 0, function* () {
112
+ mockGetApplicationFile.mockResolvedValue('session-cookie');
113
+ yield (0, utils_1.throwErrorIfNotLoggedIn)(mockCommand);
114
+ expect(mockCommand.error).not.toHaveBeenCalled();
115
+ }));
116
+ });
117
+ describe('buildMigrationUrl', () => {
118
+ it('builds url without override', () => __awaiter(void 0, void 0, void 0, function* () {
119
+ const mockConfig = {
120
+ baseUrl: 'https://example.com',
121
+ tenant: 'test-tenant',
122
+ region: 'au',
123
+ };
124
+ mockFetchApplicationConfig.mockResolvedValue(mockConfig);
125
+ const result = yield (0, utils_1.buildMigrationUrl)('tenant-id');
126
+ expect(result).toBe('https://example.com/__dxp/service/aiapps/migration');
127
+ expect(mockFetchApplicationConfig).toHaveBeenCalledWith('tenant-id');
128
+ }));
129
+ it('returns override url when provided', () => __awaiter(void 0, void 0, void 0, function* () {
130
+ const overrideUrl = 'https://override.com';
131
+ const result = yield (0, utils_1.buildMigrationUrl)('tenant-id', overrideUrl);
132
+ expect(result).toBe(overrideUrl);
133
+ expect(mockFetchApplicationConfig).not.toHaveBeenCalled();
134
+ }));
135
+ });
136
+ describe('validateAxiosStatus', () => {
137
+ it('returns true for successful status codes', () => {
138
+ expect((0, utils_1.validateAxiosStatus)(200)).toBe(true);
139
+ expect((0, utils_1.validateAxiosStatus)(201)).toBe(true);
140
+ expect((0, utils_1.validateAxiosStatus)(399)).toBe(true);
141
+ });
142
+ it('returns false for error status codes', () => {
143
+ expect((0, utils_1.validateAxiosStatus)(400)).toBe(false);
144
+ expect((0, utils_1.validateAxiosStatus)(404)).toBe(false);
145
+ expect((0, utils_1.validateAxiosStatus)(500)).toBe(false);
146
+ });
147
+ });
148
+ describe('validateExportFolder', () => {
149
+ beforeEach(() => {
150
+ mockPath.join.mockImplementation((...paths) => paths.join('/'));
151
+ });
152
+ it('validates successful export folder structure', () => {
153
+ mockFs.existsSync.mockImplementation(filePath => {
154
+ return [
155
+ '/export/path',
156
+ '/export/path/export',
157
+ '/export/path/export/export.xml',
158
+ ].includes(filePath);
159
+ });
160
+ mockFs.statSync.mockImplementation(filePath => ({
161
+ isDirectory: () => filePath !== '/export/path/export/export.xml',
162
+ isFile: () => filePath === '/export/path/export/export.xml',
163
+ }));
164
+ expect(() => (0, utils_1.validateExportFolder)('/export/path')).not.toThrow();
165
+ });
166
+ it('throws error when export folder does not exist', () => {
167
+ mockFs.existsSync.mockReturnValue(false);
168
+ expect(() => (0, utils_1.validateExportFolder)('/nonexistent')).toThrow('Export folder does not exist: /nonexistent');
169
+ });
170
+ it('throws error when export path is not a directory', () => {
171
+ mockFs.existsSync.mockReturnValue(true);
172
+ mockFs.statSync.mockReturnValue({
173
+ isDirectory: () => false,
174
+ isFile: () => true,
175
+ });
176
+ expect(() => (0, utils_1.validateExportFolder)('/export/file.txt')).toThrow('Export path is not a directory: /export/file.txt');
177
+ });
178
+ it('throws error when nested export folder does not exist', () => {
179
+ mockFs.existsSync.mockImplementation(filePath => filePath === '/export/path');
180
+ mockFs.statSync.mockReturnValue({
181
+ isDirectory: () => true,
182
+ });
183
+ expect(() => (0, utils_1.validateExportFolder)('/export/path')).toThrow('Nested export folder does not exist: /export/path/export');
184
+ });
185
+ it('throws error when export.xml does not exist', () => {
186
+ mockFs.existsSync.mockImplementation(filePath => {
187
+ return ['/export/path', '/export/path/export'].includes(filePath);
188
+ });
189
+ mockFs.statSync.mockReturnValue({
190
+ isDirectory: () => true,
191
+ });
192
+ expect(() => (0, utils_1.validateExportFolder)('/export/path')).toThrow('export.xml file does not exist in: /export/path/export');
193
+ });
194
+ it('throws error when export.xml is not a file', () => {
195
+ mockFs.existsSync.mockReturnValue(true);
196
+ mockFs.statSync.mockImplementation(filePath => ({
197
+ isDirectory: () => filePath !== '/export/path/export/export.xml',
198
+ isFile: () => false,
199
+ }));
200
+ expect(() => (0, utils_1.validateExportFolder)('/export/path')).toThrow('export.xml is not a valid file: /export/path/export/export.xml');
201
+ });
202
+ it('throws error when nested export path is not a directory', () => {
203
+ mockFs.existsSync.mockReturnValue(true);
204
+ mockFs.statSync.mockImplementation(filePath => {
205
+ if (filePath === '/export/path') {
206
+ return { isDirectory: () => true };
207
+ }
208
+ else if (filePath === '/export/path/export') {
209
+ return { isDirectory: () => false, isFile: () => true };
210
+ }
211
+ return { isDirectory: () => false, isFile: () => false };
212
+ });
213
+ expect(() => (0, utils_1.validateExportFolder)('/export/path')).toThrow('Nested export path is not a directory: /export/path/export');
214
+ });
215
+ });
216
+ describe('createTarFile', () => {
217
+ let mockChildProcess;
218
+ beforeEach(() => {
219
+ mockChildProcess = {
220
+ on: jest.fn(),
221
+ };
222
+ mockSpawn.mockReturnValue(mockChildProcess);
223
+ mockPath.join.mockImplementation((...paths) => paths.join('/'));
224
+ mockPath.dirname.mockReturnValue('/parent');
225
+ mockPath.basename.mockReturnValue('export');
226
+ // Mock Date.now() to return consistent timestamp
227
+ jest.spyOn(Date, 'now').mockReturnValue(1234567890);
228
+ });
229
+ afterEach(() => {
230
+ jest.restoreAllMocks();
231
+ });
232
+ it('creates tar file successfully', () => __awaiter(void 0, void 0, void 0, function* () {
233
+ const promise = (0, utils_1.createTarFile)('/parent/export');
234
+ // Simulate successful tar creation
235
+ const closeCallback = mockChildProcess.on.mock.calls.find((call) => call[0] === 'close')[1];
236
+ closeCallback(0);
237
+ const result = yield promise;
238
+ expect(result).toBe(`${process.cwd()}/export_1234567890.tar.gz`);
239
+ expect(mockSpawn).toHaveBeenCalledWith('tar', [
240
+ '-czf',
241
+ `${process.cwd()}/export_1234567890.tar.gz`,
242
+ '-C',
243
+ '/parent',
244
+ 'export',
245
+ ]);
246
+ }));
247
+ it('handles tar command failure', () => __awaiter(void 0, void 0, void 0, function* () {
248
+ const promise = (0, utils_1.createTarFile)('/parent/export');
249
+ // Simulate tar command failure
250
+ const closeCallback = mockChildProcess.on.mock.calls.find((call) => call[0] === 'close')[1];
251
+ closeCallback(1);
252
+ yield expect(promise).rejects.toThrow('tar command failed with exit code 1');
253
+ }));
254
+ it('handles spawn error', () => __awaiter(void 0, void 0, void 0, function* () {
255
+ const promise = (0, utils_1.createTarFile)('/parent/export');
256
+ // Simulate spawn error
257
+ const errorCallback = mockChildProcess.on.mock.calls.find((call) => call[0] === 'error')[1];
258
+ errorCallback(new Error('Spawn failed'));
259
+ yield expect(promise).rejects.toThrow('Failed to create tar file: Spawn failed');
260
+ }));
261
+ });
262
+ describe('getMigrationHeaders', () => {
263
+ it('returns headers with tenant from config', () => __awaiter(void 0, void 0, void 0, function* () {
264
+ const mockConfig = {
265
+ tenant: 'test-tenant',
266
+ baseUrl: 'https://example.com',
267
+ region: 'au',
268
+ };
269
+ mockFetchApplicationConfig.mockResolvedValue(mockConfig);
270
+ const result = yield (0, utils_1.getMigrationHeaders)('tenant-id');
271
+ expect(result).toEqual({
272
+ 'x-dxp-tenant': 'test-tenant',
273
+ });
274
+ expect(mockFetchApplicationConfig).toHaveBeenCalledWith('tenant-id');
275
+ }));
276
+ });
277
+ describe('uploadFileToS3', () => {
278
+ beforeEach(() => {
279
+ mockFs.readFileSync.mockReturnValue(Buffer.from('file content'));
280
+ mockFetchApplicationConfig.mockResolvedValue({
281
+ tenant: 'test-tenant',
282
+ baseUrl: 'https://example.com',
283
+ region: 'au',
284
+ });
285
+ });
286
+ it('uploads file successfully', () => __awaiter(void 0, void 0, void 0, function* () {
287
+ const mockResponse = {
288
+ url: 'https://s3.amazonaws.com/uploaded-file',
289
+ };
290
+ mockFetch.mockResolvedValue(mockResponse);
291
+ const result = yield (0, utils_1.uploadFileToS3)('https://upload.url', '/path/to/file.tar.gz', 'tenant-id');
292
+ expect(result).toBe('https://s3.amazonaws.com/uploaded-file');
293
+ expect(mockFetch).toHaveBeenCalledWith('https://upload.url', {
294
+ method: 'PUT',
295
+ body: Buffer.from('file content'),
296
+ headers: {
297
+ 'x-dxp-tenant': 'test-tenant',
298
+ },
299
+ });
300
+ }));
301
+ it('handles file read error', () => __awaiter(void 0, void 0, void 0, function* () {
302
+ mockFs.readFileSync.mockImplementation(() => {
303
+ throw new Error('File not found');
304
+ });
305
+ yield expect((0, utils_1.uploadFileToS3)('https://upload.url', '/nonexistent/file.tar.gz')).rejects.toThrow('File not found');
306
+ }));
307
+ });
308
+ describe('createMigration', () => {
309
+ let mockApiServiceInstance;
310
+ let mockOptions;
311
+ beforeEach(() => {
312
+ mockApiServiceInstance = {
313
+ client: {
314
+ post: jest.fn(),
315
+ },
316
+ };
317
+ mockApiService.mockImplementation(() => mockApiServiceInstance);
318
+ mockOptions = {
319
+ assetId: 'asset-123',
320
+ previewAssetId: 'preview-456',
321
+ matrixUrl: 'https://matrix.example.com',
322
+ tenant: 'test-tenant',
323
+ };
324
+ mockFetchApplicationConfig.mockResolvedValue({
325
+ tenant: 'test-tenant',
326
+ baseUrl: 'https://example.com',
327
+ region: 'au',
328
+ });
329
+ });
330
+ it('creates migration successfully', () => __awaiter(void 0, void 0, void 0, function* () {
331
+ const mockResponse = {
332
+ status: 201,
333
+ data: {
334
+ assetMigration: {
335
+ migrationId: 'migration-123',
336
+ assetId: 'asset-123',
337
+ stage: 'pending',
338
+ status: 'created',
339
+ xmlFilePath: '/path/to/xml',
340
+ matrixUrl: 'https://matrix.example.com',
341
+ previewAssetId: 'preview-456',
342
+ created: 1234567890,
343
+ updated: 1234567890,
344
+ migrationIdAssetId: 'migration-123-asset-123',
345
+ },
346
+ uploadUrl: 'https://upload.s3.amazonaws.com',
347
+ },
348
+ };
349
+ mockApiServiceInstance.client.post.mockResolvedValue(mockResponse);
350
+ const result = yield (0, utils_1.createMigration)(mockOptions);
351
+ expect(result).toEqual(mockResponse.data);
352
+ expect(mockApiServiceInstance.client.post).toHaveBeenCalledWith('https://example.com/__dxp/service/aiapps/migration/migrations', {
353
+ assetId: 'asset-123',
354
+ previewAssetId: 'preview-456',
355
+ matrixUrl: 'https://matrix.example.com',
356
+ }, {
357
+ headers: {
358
+ 'Content-Type': 'application/json',
359
+ 'x-dxp-tenant': 'test-tenant',
360
+ },
361
+ });
362
+ }));
363
+ it('handles non-success status codes', () => __awaiter(void 0, void 0, void 0, function* () {
364
+ const mockResponse = {
365
+ status: 400,
366
+ data: {},
367
+ };
368
+ mockApiServiceInstance.client.post.mockResolvedValue(mockResponse);
369
+ yield expect((0, utils_1.createMigration)(mockOptions)).rejects.toThrow('Migration creation failed with status: 400');
370
+ }));
371
+ it('handles invalid response format - missing migrationId', () => __awaiter(void 0, void 0, void 0, function* () {
372
+ const mockResponse = {
373
+ status: 200,
374
+ data: {
375
+ assetMigration: {
376
+ assetId: 'asset-123',
377
+ stage: 'pending',
378
+ status: 'created',
379
+ },
380
+ uploadUrl: 'https://upload.s3.amazonaws.com',
381
+ },
382
+ };
383
+ mockApiServiceInstance.client.post.mockResolvedValue(mockResponse);
384
+ yield expect((0, utils_1.createMigration)(mockOptions)).rejects.toThrow('Invalid response format from migration service');
385
+ }));
386
+ it('handles missing upload URL', () => __awaiter(void 0, void 0, void 0, function* () {
387
+ const mockResponse = {
388
+ status: 200,
389
+ data: {
390
+ assetMigration: {
391
+ migrationId: 'migration-123',
392
+ assetId: 'asset-123',
393
+ stage: 'pending',
394
+ status: 'created',
395
+ },
396
+ },
397
+ };
398
+ mockApiServiceInstance.client.post.mockResolvedValue(mockResponse);
399
+ yield expect((0, utils_1.createMigration)(mockOptions)).rejects.toThrow('Upload URL not found in response');
400
+ }));
401
+ it('handles API service errors', () => __awaiter(void 0, void 0, void 0, function* () {
402
+ const error = new Error('Network error');
403
+ mockApiServiceInstance.client.post.mockRejectedValue(error);
404
+ yield expect((0, utils_1.createMigration)(mockOptions)).rejects.toThrow('Network error');
405
+ }));
406
+ it('handles unknown errors', () => __awaiter(void 0, void 0, void 0, function* () {
407
+ mockApiServiceInstance.client.post.mockRejectedValue('Unknown error');
408
+ yield expect((0, utils_1.createMigration)(mockOptions)).rejects.toThrow('Failed to create migration: Unknown error');
409
+ }));
410
+ });
411
+ describe('getMigration', () => {
412
+ let mockApiServiceInstance;
413
+ let mockOptions;
414
+ beforeEach(() => {
415
+ mockApiServiceInstance = {
416
+ client: {
417
+ get: jest.fn(),
418
+ },
419
+ };
420
+ mockApiService.mockImplementation(() => mockApiServiceInstance);
421
+ mockOptions = {
422
+ migrationId: 'migration-123',
423
+ assetId: 'asset-456',
424
+ tenant: 'test-tenant',
425
+ };
426
+ mockFetchApplicationConfig.mockResolvedValue({
427
+ tenant: 'test-tenant',
428
+ baseUrl: 'https://example.com',
429
+ region: 'au',
430
+ });
431
+ });
432
+ it('gets migration successfully', () => __awaiter(void 0, void 0, void 0, function* () {
433
+ const mockResponse = {
434
+ status: 200,
435
+ data: {
436
+ migrationId: 'migration-123',
437
+ assetId: 'asset-456',
438
+ stage: 'completed',
439
+ status: 'success',
440
+ stageDetails: 'Migration completed successfully',
441
+ previewPageAssetId: 'preview-789',
442
+ componentsTarDownloadUrl: 'https://download.example.com/components.zip',
443
+ },
444
+ };
445
+ mockApiServiceInstance.client.get.mockResolvedValue(mockResponse);
446
+ const result = yield (0, utils_1.getMigration)(mockOptions);
447
+ expect(result).toEqual({
448
+ migrationId: 'migration-123',
449
+ assetId: 'asset-456',
450
+ stage: 'completed',
451
+ status: 'success',
452
+ stageDetails: 'Migration completed successfully',
453
+ previewPageAssetId: 'preview-789',
454
+ componentsTarDownloadUrl: 'https://download.example.com/components.zip',
455
+ });
456
+ expect(mockApiServiceInstance.client.get).toHaveBeenCalledWith('https://example.com/__dxp/service/aiapps/migration/migrations/migration-123/assets/asset-456', {
457
+ headers: {
458
+ 'Content-Type': 'application/json',
459
+ 'x-dxp-tenant': 'test-tenant',
460
+ },
461
+ });
462
+ }));
463
+ it('gets migration with override URL', () => __awaiter(void 0, void 0, void 0, function* () {
464
+ const optionsWithOverride = Object.assign(Object.assign({}, mockOptions), { overrideUrl: 'https://custom.migration.url' });
465
+ const mockResponse = {
466
+ status: 200,
467
+ data: {
468
+ migrationId: 'migration-123',
469
+ assetId: 'asset-456',
470
+ stage: 'pending',
471
+ status: 'processing',
472
+ },
473
+ };
474
+ mockApiServiceInstance.client.get.mockResolvedValue(mockResponse);
475
+ const result = yield (0, utils_1.getMigration)(optionsWithOverride);
476
+ expect(result.migrationId).toBe('migration-123');
477
+ expect(result.assetId).toBe('asset-456');
478
+ expect(mockApiServiceInstance.client.get).toHaveBeenCalledWith('https://custom.migration.url/migrations/migration-123/assets/asset-456', expect.any(Object));
479
+ }));
480
+ it('handles non-success status codes', () => __awaiter(void 0, void 0, void 0, function* () {
481
+ const mockResponse = {
482
+ status: 404,
483
+ data: {},
484
+ };
485
+ mockApiServiceInstance.client.get.mockResolvedValue(mockResponse);
486
+ yield expect((0, utils_1.getMigration)(mockOptions)).rejects.toThrow('Failed to get migration: 404');
487
+ }));
488
+ it('handles missing response data', () => __awaiter(void 0, void 0, void 0, function* () {
489
+ const mockResponse = {
490
+ status: 200,
491
+ data: null,
492
+ };
493
+ mockApiServiceInstance.client.get.mockResolvedValue(mockResponse);
494
+ yield expect((0, utils_1.getMigration)(mockOptions)).rejects.toThrow('No data returned from migration service');
495
+ }));
496
+ it('handles API service errors', () => __awaiter(void 0, void 0, void 0, function* () {
497
+ const error = new Error('Network error');
498
+ mockApiServiceInstance.client.get.mockRejectedValue(error);
499
+ yield expect((0, utils_1.getMigration)(mockOptions)).rejects.toThrow('Network error');
500
+ }));
501
+ it('handles unknown errors', () => __awaiter(void 0, void 0, void 0, function* () {
502
+ mockApiServiceInstance.client.get.mockRejectedValue('Unknown error');
503
+ yield expect((0, utils_1.getMigration)(mockOptions)).rejects.toThrow('Failed to get migration: Unknown error');
504
+ }));
505
+ it('works without optional fields in response', () => __awaiter(void 0, void 0, void 0, function* () {
506
+ const mockResponse = {
507
+ status: 200,
508
+ data: {
509
+ migrationId: 'migration-123',
510
+ assetId: 'asset-456',
511
+ stage: 'pending',
512
+ status: 'processing',
513
+ // No optional fields: stageDetails, previewPageAssetId, componentsTarDownloadUrl
514
+ },
515
+ };
516
+ mockApiServiceInstance.client.get.mockResolvedValue(mockResponse);
517
+ const result = yield (0, utils_1.getMigration)(mockOptions);
518
+ expect(result).toEqual({
519
+ migrationId: 'migration-123',
520
+ assetId: 'asset-456',
521
+ stage: 'pending',
522
+ status: 'processing',
523
+ stageDetails: undefined,
524
+ previewPageAssetId: undefined,
525
+ componentsTarDownloadUrl: undefined,
526
+ });
527
+ }));
528
+ it('constructs correct URL path', () => __awaiter(void 0, void 0, void 0, function* () {
529
+ const mockResponse = {
530
+ status: 200,
531
+ data: {
532
+ migrationId: 'migration-123',
533
+ assetId: 'asset-456',
534
+ stage: 'completed',
535
+ status: 'success',
536
+ },
537
+ };
538
+ mockApiServiceInstance.client.get.mockResolvedValue(mockResponse);
539
+ yield (0, utils_1.getMigration)(mockOptions);
540
+ expect(mockApiServiceInstance.client.get).toHaveBeenCalledWith('https://example.com/__dxp/service/aiapps/migration/migrations/migration-123/assets/asset-456', expect.any(Object));
541
+ }));
542
+ });
543
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squiz/dxp-cli-next",
3
- "version": "5.25.3",
3
+ "version": "5.26.0-develop.2",
4
4
  "repository": {
5
5
  "url": "https://gitlab.squiz.net/dxp/dxp-cli-next"
6
6
  },