@arela/uploader 1.0.3 → 1.0.5
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/.env.local +316 -0
- package/coverage/IdentifyCommand.js.html +1462 -0
- package/coverage/PropagateCommand.js.html +1507 -0
- package/coverage/PushCommand.js.html +1504 -0
- package/coverage/ScanCommand.js.html +1654 -0
- package/coverage/UploadCommand.js.html +1846 -0
- package/coverage/WatchCommand.js.html +4111 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +191 -0
- package/coverage/lcov-report/IdentifyCommand.js.html +1462 -0
- package/coverage/lcov-report/PropagateCommand.js.html +1507 -0
- package/coverage/lcov-report/PushCommand.js.html +1504 -0
- package/coverage/lcov-report/ScanCommand.js.html +1654 -0
- package/coverage/lcov-report/UploadCommand.js.html +1846 -0
- package/coverage/lcov-report/WatchCommand.js.html +4111 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +191 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov.info +1937 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/docs/CROSS_PLATFORM_PATH_HANDLING.md +597 -0
- package/package.json +28 -2
- package/src/commands/IdentifyCommand.js +1 -28
- package/src/commands/PropagateCommand.js +1 -1
- package/src/commands/PushCommand.js +1 -1
- package/src/commands/ScanCommand.js +27 -20
- package/src/config/config.js +27 -48
- package/src/services/ScanApiService.js +4 -5
- package/src/utils/PathNormalizer.js +272 -0
- package/tests/commands/IdentifyCommand.test.js +570 -0
- package/tests/commands/PropagateCommand.test.js +568 -0
- package/tests/commands/PushCommand.test.js +754 -0
- package/tests/commands/ScanCommand.test.js +382 -0
- package/tests/unit/PathAndTableNameGeneration.test.js +1211 -0
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for PropagateCommand
|
|
3
|
+
* Tests the arela propagate command functionality
|
|
4
|
+
*/
|
|
5
|
+
import { jest, describe, it, expect, beforeEach, afterEach } from '@jest/globals';
|
|
6
|
+
|
|
7
|
+
// Mock dependencies before importing PropagateCommand
|
|
8
|
+
const mockLogger = {
|
|
9
|
+
info: jest.fn(),
|
|
10
|
+
success: jest.fn(),
|
|
11
|
+
error: jest.fn(),
|
|
12
|
+
warn: jest.fn(),
|
|
13
|
+
debug: jest.fn(),
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const mockProgressBar = {
|
|
17
|
+
start: jest.fn(),
|
|
18
|
+
update: jest.fn(),
|
|
19
|
+
stop: jest.fn(),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const mockCliProgress = {
|
|
23
|
+
SingleBar: jest.fn(() => mockProgressBar),
|
|
24
|
+
Presets: {
|
|
25
|
+
shades_classic: {},
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const mockScanApiService = {
|
|
30
|
+
getInstanceTables: jest.fn(),
|
|
31
|
+
getPropagationStats: jest.fn(),
|
|
32
|
+
markFilesNeedingPropagation: jest.fn(),
|
|
33
|
+
fetchPedimentoSources: jest.fn(),
|
|
34
|
+
fetchFilesNeedingPropagationByDirectory: jest.fn(),
|
|
35
|
+
batchUpdatePropagation: jest.fn(),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const mockAppConfig = {
|
|
39
|
+
validateScanConfig: jest.fn(),
|
|
40
|
+
getScanConfig: jest.fn(),
|
|
41
|
+
setApiTarget: jest.fn(),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Store original console methods
|
|
45
|
+
const originalConsoleLog = console.log;
|
|
46
|
+
const originalConsoleError = console.error;
|
|
47
|
+
const originalProcessExit = process.exit;
|
|
48
|
+
|
|
49
|
+
jest.unstable_mockModule('cli-progress', () => ({
|
|
50
|
+
default: mockCliProgress,
|
|
51
|
+
SingleBar: mockCliProgress.SingleBar,
|
|
52
|
+
Presets: mockCliProgress.Presets,
|
|
53
|
+
}));
|
|
54
|
+
|
|
55
|
+
jest.unstable_mockModule('../../src/services/LoggingService.js', () => ({
|
|
56
|
+
default: mockLogger,
|
|
57
|
+
}));
|
|
58
|
+
|
|
59
|
+
jest.unstable_mockModule('../../src/services/ScanApiService.js', () => ({
|
|
60
|
+
default: jest.fn(() => mockScanApiService),
|
|
61
|
+
}));
|
|
62
|
+
|
|
63
|
+
jest.unstable_mockModule('../../src/config/config.js', () => ({
|
|
64
|
+
default: mockAppConfig,
|
|
65
|
+
}));
|
|
66
|
+
|
|
67
|
+
// Import the class after mocking
|
|
68
|
+
const { PropagateCommand } = await import('../../src/commands/PropagateCommand.js');
|
|
69
|
+
|
|
70
|
+
describe('PropagateCommand', () => {
|
|
71
|
+
let propagateCommand;
|
|
72
|
+
let mockConsoleLog;
|
|
73
|
+
let mockConsoleError;
|
|
74
|
+
let mockProcessExit;
|
|
75
|
+
|
|
76
|
+
beforeEach(() => {
|
|
77
|
+
jest.clearAllMocks();
|
|
78
|
+
|
|
79
|
+
// Mock console methods
|
|
80
|
+
mockConsoleLog = jest.fn();
|
|
81
|
+
mockConsoleError = jest.fn();
|
|
82
|
+
mockProcessExit = jest.fn();
|
|
83
|
+
console.log = mockConsoleLog;
|
|
84
|
+
console.error = mockConsoleError;
|
|
85
|
+
process.exit = mockProcessExit;
|
|
86
|
+
|
|
87
|
+
// Default mock implementations
|
|
88
|
+
mockAppConfig.getScanConfig.mockReturnValue({
|
|
89
|
+
companySlug: 'test-company',
|
|
90
|
+
serverId: 'test-server',
|
|
91
|
+
basePathFull: '/test/path',
|
|
92
|
+
});
|
|
93
|
+
mockAppConfig.validateScanConfig.mockReturnValue(undefined);
|
|
94
|
+
|
|
95
|
+
mockScanApiService.getInstanceTables.mockResolvedValue([
|
|
96
|
+
{ tableName: 'scan_test_table' },
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
mockScanApiService.getPropagationStats.mockResolvedValue({
|
|
100
|
+
totalFiles: 100,
|
|
101
|
+
withArelaPath: 50,
|
|
102
|
+
pedimentoSources: 10,
|
|
103
|
+
needsPropagation: 40,
|
|
104
|
+
pending: 40,
|
|
105
|
+
errors: 0,
|
|
106
|
+
maxAttemptsReached: 0,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
mockScanApiService.markFilesNeedingPropagation.mockResolvedValue({
|
|
110
|
+
markedCount: 40,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
mockScanApiService.fetchPedimentoSources.mockResolvedValue([]);
|
|
114
|
+
mockScanApiService.fetchFilesNeedingPropagationByDirectory.mockResolvedValue([]);
|
|
115
|
+
mockScanApiService.batchUpdatePropagation.mockResolvedValue({ updated: 0 });
|
|
116
|
+
|
|
117
|
+
propagateCommand = new PropagateCommand();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
afterEach(() => {
|
|
121
|
+
// Restore console methods
|
|
122
|
+
console.log = originalConsoleLog;
|
|
123
|
+
console.error = originalConsoleError;
|
|
124
|
+
process.exit = originalProcessExit;
|
|
125
|
+
jest.restoreAllMocks();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('constructor', () => {
|
|
129
|
+
it('should create a new PropagateCommand instance with default options', () => {
|
|
130
|
+
expect(propagateCommand).toBeInstanceOf(PropagateCommand);
|
|
131
|
+
expect(propagateCommand.options.batchSize).toBe(50);
|
|
132
|
+
expect(propagateCommand.options.showStats).toBe(false);
|
|
133
|
+
expect(propagateCommand.options.api).toBe('default');
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should accept custom options', () => {
|
|
137
|
+
const customCommand = new PropagateCommand({
|
|
138
|
+
batchSize: 100,
|
|
139
|
+
showStats: true,
|
|
140
|
+
api: 'agencia',
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
expect(customCommand.options.batchSize).toBe(100);
|
|
144
|
+
expect(customCommand.options.showStats).toBe(true);
|
|
145
|
+
expect(customCommand.options.api).toBe('agencia');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should initialize stats object', () => {
|
|
149
|
+
expect(propagateCommand.stats).toBeDefined();
|
|
150
|
+
expect(propagateCommand.stats.pedimentosProcessed).toBe(0);
|
|
151
|
+
expect(propagateCommand.stats.filesUpdated).toBe(0);
|
|
152
|
+
expect(propagateCommand.stats.filesFailed).toBe(0);
|
|
153
|
+
expect(propagateCommand.stats.directoriesProcessed).toBe(0);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe('execute', () => {
|
|
158
|
+
it('should validate configuration', async () => {
|
|
159
|
+
await propagateCommand.execute();
|
|
160
|
+
|
|
161
|
+
expect(mockAppConfig.validateScanConfig).toHaveBeenCalled();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should set API target', async () => {
|
|
165
|
+
const command = new PropagateCommand({ api: 'cliente' });
|
|
166
|
+
await command.execute();
|
|
167
|
+
|
|
168
|
+
expect(mockAppConfig.setApiTarget).toHaveBeenCalledWith('cliente');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('should fetch instance tables', async () => {
|
|
172
|
+
await propagateCommand.execute();
|
|
173
|
+
|
|
174
|
+
expect(mockScanApiService.getInstanceTables).toHaveBeenCalledWith(
|
|
175
|
+
'test-company',
|
|
176
|
+
'test-server',
|
|
177
|
+
'/test/path'
|
|
178
|
+
);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('should exit when no tables found', async () => {
|
|
182
|
+
mockScanApiService.getInstanceTables.mockResolvedValue([]);
|
|
183
|
+
|
|
184
|
+
await propagateCommand.execute();
|
|
185
|
+
|
|
186
|
+
expect(mockProcessExit).toHaveBeenCalledWith(1);
|
|
187
|
+
expect(mockConsoleError).toHaveBeenCalledWith(
|
|
188
|
+
expect.stringContaining('No tables found')
|
|
189
|
+
);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('should log found tables', async () => {
|
|
193
|
+
mockScanApiService.getInstanceTables.mockResolvedValue([
|
|
194
|
+
{ tableName: 'scan_table_1' },
|
|
195
|
+
{ tableName: 'scan_table_2' },
|
|
196
|
+
]);
|
|
197
|
+
|
|
198
|
+
await propagateCommand.execute();
|
|
199
|
+
|
|
200
|
+
expect(mockConsoleLog).toHaveBeenCalledWith(
|
|
201
|
+
expect.stringContaining('Found 2 tables')
|
|
202
|
+
);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('should show initial status', async () => {
|
|
206
|
+
await propagateCommand.execute();
|
|
207
|
+
|
|
208
|
+
expect(mockConsoleLog).toHaveBeenCalledWith('📈 Initial Status:');
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
describe('table processing', () => {
|
|
213
|
+
it('should skip table when no pedimento sources', async () => {
|
|
214
|
+
mockScanApiService.getPropagationStats.mockResolvedValue({
|
|
215
|
+
totalFiles: 100,
|
|
216
|
+
withArelaPath: 0,
|
|
217
|
+
pedimentoSources: 0,
|
|
218
|
+
needsPropagation: 0,
|
|
219
|
+
pending: 0,
|
|
220
|
+
errors: 0,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
await propagateCommand.execute();
|
|
224
|
+
|
|
225
|
+
expect(mockConsoleLog).toHaveBeenCalledWith(
|
|
226
|
+
expect.stringContaining('No pedimento sources found')
|
|
227
|
+
);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('should skip table when all files have arela_path', async () => {
|
|
231
|
+
mockScanApiService.getPropagationStats
|
|
232
|
+
.mockResolvedValueOnce({
|
|
233
|
+
totalFiles: 100,
|
|
234
|
+
withArelaPath: 100,
|
|
235
|
+
pedimentoSources: 10,
|
|
236
|
+
needsPropagation: 0,
|
|
237
|
+
pending: 0,
|
|
238
|
+
errors: 0,
|
|
239
|
+
})
|
|
240
|
+
.mockResolvedValueOnce({
|
|
241
|
+
totalFiles: 100,
|
|
242
|
+
withArelaPath: 100,
|
|
243
|
+
pedimentoSources: 10,
|
|
244
|
+
needsPropagation: 0,
|
|
245
|
+
pending: 0,
|
|
246
|
+
errors: 0,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
await propagateCommand.execute();
|
|
250
|
+
|
|
251
|
+
expect(mockConsoleLog).toHaveBeenCalledWith(
|
|
252
|
+
expect.stringContaining('All files already have arela_path')
|
|
253
|
+
);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('should mark files needing propagation', async () => {
|
|
257
|
+
mockScanApiService.getPropagationStats
|
|
258
|
+
.mockResolvedValueOnce({
|
|
259
|
+
totalFiles: 100,
|
|
260
|
+
withArelaPath: 10,
|
|
261
|
+
pedimentoSources: 10,
|
|
262
|
+
needsPropagation: 0,
|
|
263
|
+
pending: 90,
|
|
264
|
+
errors: 0,
|
|
265
|
+
})
|
|
266
|
+
.mockResolvedValueOnce({
|
|
267
|
+
totalFiles: 100,
|
|
268
|
+
withArelaPath: 10,
|
|
269
|
+
pedimentoSources: 10,
|
|
270
|
+
needsPropagation: 90,
|
|
271
|
+
pending: 90,
|
|
272
|
+
errors: 0,
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
await propagateCommand.execute();
|
|
276
|
+
|
|
277
|
+
expect(mockScanApiService.markFilesNeedingPropagation).toHaveBeenCalledWith(
|
|
278
|
+
'scan_test_table'
|
|
279
|
+
);
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
describe('propagation processing', () => {
|
|
284
|
+
beforeEach(() => {
|
|
285
|
+
// Setup for active propagation
|
|
286
|
+
mockScanApiService.getPropagationStats
|
|
287
|
+
.mockResolvedValueOnce({
|
|
288
|
+
totalFiles: 100,
|
|
289
|
+
withArelaPath: 10,
|
|
290
|
+
pedimentoSources: 5,
|
|
291
|
+
needsPropagation: 90,
|
|
292
|
+
pending: 90,
|
|
293
|
+
errors: 0,
|
|
294
|
+
})
|
|
295
|
+
.mockResolvedValueOnce({
|
|
296
|
+
totalFiles: 100,
|
|
297
|
+
withArelaPath: 10,
|
|
298
|
+
pedimentoSources: 5,
|
|
299
|
+
needsPropagation: 90,
|
|
300
|
+
pending: 90,
|
|
301
|
+
errors: 0,
|
|
302
|
+
})
|
|
303
|
+
.mockResolvedValueOnce({
|
|
304
|
+
totalFiles: 100,
|
|
305
|
+
withArelaPath: 100,
|
|
306
|
+
pedimentoSources: 5,
|
|
307
|
+
needsPropagation: 0,
|
|
308
|
+
pending: 0,
|
|
309
|
+
errors: 0,
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it('should fetch pedimento sources', async () => {
|
|
314
|
+
mockScanApiService.fetchPedimentoSources.mockResolvedValue([
|
|
315
|
+
{
|
|
316
|
+
id: 1,
|
|
317
|
+
directory_path: '/test/dir1',
|
|
318
|
+
arela_path: 'RFC/2023/3429/07/12345/',
|
|
319
|
+
rfc: 'RFC123',
|
|
320
|
+
detected_pedimento_year: 2023,
|
|
321
|
+
},
|
|
322
|
+
]);
|
|
323
|
+
|
|
324
|
+
mockScanApiService.fetchFilesNeedingPropagationByDirectory.mockResolvedValue([]);
|
|
325
|
+
|
|
326
|
+
await propagateCommand.execute();
|
|
327
|
+
|
|
328
|
+
expect(mockScanApiService.fetchPedimentoSources).toHaveBeenCalled();
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it('should process files in each directory', async () => {
|
|
332
|
+
mockScanApiService.fetchPedimentoSources.mockResolvedValue([
|
|
333
|
+
{
|
|
334
|
+
id: 1,
|
|
335
|
+
directory_path: '/test/dir1',
|
|
336
|
+
arela_path: 'RFC/2023/3429/07/12345/',
|
|
337
|
+
rfc: 'RFC123',
|
|
338
|
+
detected_pedimento_year: 2023,
|
|
339
|
+
},
|
|
340
|
+
]);
|
|
341
|
+
|
|
342
|
+
mockScanApiService.fetchFilesNeedingPropagationByDirectory.mockResolvedValue([
|
|
343
|
+
{ id: 2, file_name: 'file.xml' },
|
|
344
|
+
{ id: 3, file_name: 'file.txt' },
|
|
345
|
+
]);
|
|
346
|
+
|
|
347
|
+
mockScanApiService.batchUpdatePropagation.mockResolvedValue({ updated: 2 });
|
|
348
|
+
|
|
349
|
+
await propagateCommand.execute();
|
|
350
|
+
|
|
351
|
+
expect(mockScanApiService.fetchFilesNeedingPropagationByDirectory).toHaveBeenCalledWith(
|
|
352
|
+
'scan_test_table',
|
|
353
|
+
'/test/dir1'
|
|
354
|
+
);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it('should update progress bar during processing', async () => {
|
|
358
|
+
mockScanApiService.fetchPedimentoSources.mockResolvedValue([
|
|
359
|
+
{
|
|
360
|
+
id: 1,
|
|
361
|
+
directory_path: '/test/dir1',
|
|
362
|
+
arela_path: 'RFC/2023/3429/07/12345/',
|
|
363
|
+
rfc: 'RFC123',
|
|
364
|
+
detected_pedimento_year: 2023,
|
|
365
|
+
},
|
|
366
|
+
]);
|
|
367
|
+
|
|
368
|
+
mockScanApiService.fetchFilesNeedingPropagationByDirectory.mockResolvedValue([]);
|
|
369
|
+
|
|
370
|
+
await propagateCommand.execute();
|
|
371
|
+
|
|
372
|
+
expect(mockProgressBar.start).toHaveBeenCalled();
|
|
373
|
+
expect(mockProgressBar.update).toHaveBeenCalled();
|
|
374
|
+
expect(mockProgressBar.stop).toHaveBeenCalled();
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
it('should process multiple batches of pedimentos', async () => {
|
|
378
|
+
mockScanApiService.fetchPedimentoSources
|
|
379
|
+
.mockResolvedValueOnce([
|
|
380
|
+
{
|
|
381
|
+
id: 1,
|
|
382
|
+
directory_path: '/test/dir1',
|
|
383
|
+
arela_path: 'RFC/2023/3429/07/12345/',
|
|
384
|
+
rfc: 'RFC123',
|
|
385
|
+
detected_pedimento_year: 2023,
|
|
386
|
+
},
|
|
387
|
+
])
|
|
388
|
+
.mockResolvedValueOnce([]);
|
|
389
|
+
|
|
390
|
+
mockScanApiService.fetchFilesNeedingPropagationByDirectory.mockResolvedValue([]);
|
|
391
|
+
|
|
392
|
+
await propagateCommand.execute();
|
|
393
|
+
|
|
394
|
+
// The implementation may call once or twice depending on batch size logic
|
|
395
|
+
expect(mockScanApiService.fetchPedimentoSources).toHaveBeenCalled();
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
describe('error handling', () => {
|
|
400
|
+
it('should handle configuration validation errors', async () => {
|
|
401
|
+
mockAppConfig.validateScanConfig.mockImplementation(() => {
|
|
402
|
+
throw new Error('Missing required config');
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
await propagateCommand.execute();
|
|
406
|
+
|
|
407
|
+
expect(mockProcessExit).toHaveBeenCalledWith(1);
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it('should handle API errors gracefully', async () => {
|
|
411
|
+
mockScanApiService.getInstanceTables.mockRejectedValue(
|
|
412
|
+
new Error('API connection failed')
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
await propagateCommand.execute();
|
|
416
|
+
|
|
417
|
+
expect(mockProcessExit).toHaveBeenCalledWith(1);
|
|
418
|
+
expect(mockConsoleError).toHaveBeenCalledWith(
|
|
419
|
+
expect.stringContaining('API connection failed')
|
|
420
|
+
);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it('should handle fetch pedimento sources errors', async () => {
|
|
424
|
+
mockScanApiService.getPropagationStats
|
|
425
|
+
.mockResolvedValueOnce({
|
|
426
|
+
totalFiles: 100,
|
|
427
|
+
withArelaPath: 10,
|
|
428
|
+
pedimentoSources: 5,
|
|
429
|
+
needsPropagation: 90,
|
|
430
|
+
pending: 90,
|
|
431
|
+
errors: 0,
|
|
432
|
+
})
|
|
433
|
+
.mockResolvedValueOnce({
|
|
434
|
+
totalFiles: 100,
|
|
435
|
+
withArelaPath: 10,
|
|
436
|
+
pedimentoSources: 5,
|
|
437
|
+
needsPropagation: 90,
|
|
438
|
+
pending: 90,
|
|
439
|
+
errors: 0,
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
mockScanApiService.fetchPedimentoSources.mockRejectedValue(
|
|
443
|
+
new Error('Database error')
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
// The command should handle the error (exit or log)
|
|
447
|
+
await propagateCommand.execute();
|
|
448
|
+
|
|
449
|
+
// Should have attempted to fetch pedimento sources
|
|
450
|
+
expect(mockScanApiService.fetchPedimentoSources).toHaveBeenCalled();
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it('should handle invalid API response for files', async () => {
|
|
454
|
+
mockScanApiService.getPropagationStats
|
|
455
|
+
.mockResolvedValueOnce({
|
|
456
|
+
totalFiles: 100,
|
|
457
|
+
withArelaPath: 10,
|
|
458
|
+
pedimentoSources: 5,
|
|
459
|
+
needsPropagation: 90,
|
|
460
|
+
pending: 90,
|
|
461
|
+
errors: 0,
|
|
462
|
+
})
|
|
463
|
+
.mockResolvedValueOnce({
|
|
464
|
+
totalFiles: 100,
|
|
465
|
+
withArelaPath: 10,
|
|
466
|
+
pedimentoSources: 5,
|
|
467
|
+
needsPropagation: 90,
|
|
468
|
+
pending: 90,
|
|
469
|
+
errors: 0,
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
mockScanApiService.fetchPedimentoSources.mockResolvedValue([
|
|
473
|
+
{
|
|
474
|
+
id: 1,
|
|
475
|
+
directory_path: '/test/dir1',
|
|
476
|
+
arela_path: 'RFC/2023/3429/07/12345/',
|
|
477
|
+
},
|
|
478
|
+
]);
|
|
479
|
+
|
|
480
|
+
// Return invalid response (not an array)
|
|
481
|
+
mockScanApiService.fetchFilesNeedingPropagationByDirectory.mockResolvedValue(null);
|
|
482
|
+
|
|
483
|
+
await propagateCommand.execute();
|
|
484
|
+
|
|
485
|
+
expect(mockLogger.error).toHaveBeenCalledWith(
|
|
486
|
+
expect.stringMatching(/Invalid response/i),
|
|
487
|
+
null
|
|
488
|
+
);
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
describe('statistics display', () => {
|
|
493
|
+
it('should show final statistics', async () => {
|
|
494
|
+
mockScanApiService.getPropagationStats.mockResolvedValue({
|
|
495
|
+
totalFiles: 100,
|
|
496
|
+
withArelaPath: 100,
|
|
497
|
+
pedimentoSources: 10,
|
|
498
|
+
needsPropagation: 0,
|
|
499
|
+
pending: 0,
|
|
500
|
+
errors: 0,
|
|
501
|
+
maxAttemptsReached: 0,
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
await propagateCommand.execute();
|
|
505
|
+
|
|
506
|
+
// Final stats are shown after processing, check for any status output
|
|
507
|
+
expect(mockConsoleLog).toHaveBeenCalledWith(
|
|
508
|
+
expect.stringContaining('Initial Status')
|
|
509
|
+
);
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
it('should warn about max attempts reached', async () => {
|
|
513
|
+
// This test verifies the command handles maxAttemptsReached stats
|
|
514
|
+
mockScanApiService.getPropagationStats.mockResolvedValue({
|
|
515
|
+
totalFiles: 100,
|
|
516
|
+
withArelaPath: 90,
|
|
517
|
+
pedimentoSources: 10,
|
|
518
|
+
needsPropagation: 0,
|
|
519
|
+
pending: 0,
|
|
520
|
+
errors: 0,
|
|
521
|
+
maxAttemptsReached: 5,
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
// Should complete without error
|
|
525
|
+
await expect(propagateCommand.execute()).resolves.not.toThrow();
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
it('should show combined results for multiple tables', async () => {
|
|
529
|
+
mockScanApiService.getInstanceTables.mockResolvedValue([
|
|
530
|
+
{ tableName: 'scan_table_1' },
|
|
531
|
+
{ tableName: 'scan_table_2' },
|
|
532
|
+
]);
|
|
533
|
+
|
|
534
|
+
mockScanApiService.getPropagationStats.mockResolvedValue({
|
|
535
|
+
totalFiles: 50,
|
|
536
|
+
withArelaPath: 50,
|
|
537
|
+
pedimentoSources: 0,
|
|
538
|
+
needsPropagation: 0,
|
|
539
|
+
pending: 0,
|
|
540
|
+
errors: 0,
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
await propagateCommand.execute();
|
|
544
|
+
|
|
545
|
+
expect(mockConsoleLog).toHaveBeenCalledWith(
|
|
546
|
+
expect.stringContaining('Tables Processed: 2')
|
|
547
|
+
);
|
|
548
|
+
});
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
describe('memory usage', () => {
|
|
552
|
+
it('should report memory usage when showStats is enabled', async () => {
|
|
553
|
+
const command = new PropagateCommand({ showStats: true });
|
|
554
|
+
|
|
555
|
+
mockScanApiService.getPropagationStats.mockResolvedValue({
|
|
556
|
+
totalFiles: 100,
|
|
557
|
+
withArelaPath: 100,
|
|
558
|
+
pedimentoSources: 0,
|
|
559
|
+
needsPropagation: 0,
|
|
560
|
+
pending: 0,
|
|
561
|
+
errors: 0,
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
// The command should complete without error when showStats is enabled
|
|
565
|
+
await expect(command.execute()).resolves.not.toThrow();
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
});
|