@elisra-devops/docgen-data-provider 1.63.13 → 1.67.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.
Files changed (91) hide show
  1. package/.github/workflows/ci.yml +26 -9
  2. package/.github/workflows/release.yml +9 -10
  3. package/bin/helpers/tfs.d.ts +3 -0
  4. package/bin/helpers/tfs.js +44 -7
  5. package/bin/helpers/tfs.js.map +1 -1
  6. package/bin/modules/GitDataProvider.d.ts +10 -0
  7. package/bin/modules/GitDataProvider.js +10 -0
  8. package/bin/modules/GitDataProvider.js.map +1 -1
  9. package/bin/modules/TestDataProvider.js +0 -1
  10. package/bin/modules/TestDataProvider.js.map +1 -1
  11. package/bin/modules/TicketsDataProvider.d.ts +63 -24
  12. package/bin/modules/TicketsDataProvider.js +216 -114
  13. package/bin/modules/TicketsDataProvider.js.map +1 -1
  14. package/bin/tests/helpers/helper.test.js +279 -0
  15. package/bin/tests/helpers/helper.test.js.map +1 -0
  16. package/bin/{helpers/test → tests/helpers}/tfs.test.js +312 -49
  17. package/bin/tests/helpers/tfs.test.js.map +1 -0
  18. package/bin/tests/index.test.js +25 -0
  19. package/bin/tests/index.test.js.map +1 -0
  20. package/bin/tests/models/tfs-data.test.js +160 -0
  21. package/bin/tests/models/tfs-data.test.js.map +1 -0
  22. package/bin/{modules/test → tests/modules}/JfrogDataProvider.test.js +9 -9
  23. package/bin/tests/modules/JfrogDataProvider.test.js.map +1 -0
  24. package/bin/tests/modules/ResultDataProvider.test.js +1942 -0
  25. package/bin/tests/modules/ResultDataProvider.test.js.map +1 -0
  26. package/bin/tests/modules/gitDataProvider.test.js +1888 -0
  27. package/bin/tests/modules/gitDataProvider.test.js.map +1 -0
  28. package/bin/{modules/test → tests/modules}/managmentDataProvider.test.js +13 -1
  29. package/bin/tests/modules/managmentDataProvider.test.js.map +1 -0
  30. package/bin/tests/modules/pipelineDataProvider.test.d.ts +1 -0
  31. package/bin/tests/modules/pipelineDataProvider.test.js +783 -0
  32. package/bin/tests/modules/pipelineDataProvider.test.js.map +1 -0
  33. package/bin/tests/modules/testDataProvider.test.d.ts +1 -0
  34. package/bin/tests/modules/testDataProvider.test.js +717 -0
  35. package/bin/tests/modules/testDataProvider.test.js.map +1 -0
  36. package/bin/tests/modules/ticketsDataProvider.test.d.ts +1 -0
  37. package/bin/tests/modules/ticketsDataProvider.test.js +1681 -0
  38. package/bin/tests/modules/ticketsDataProvider.test.js.map +1 -0
  39. package/bin/tests/utils/DataProviderUtils.test.d.ts +1 -0
  40. package/bin/tests/utils/DataProviderUtils.test.js +61 -0
  41. package/bin/tests/utils/DataProviderUtils.test.js.map +1 -0
  42. package/bin/tests/utils/testStepParserHelper.test.d.ts +1 -0
  43. package/bin/tests/utils/testStepParserHelper.test.js +359 -0
  44. package/bin/tests/utils/testStepParserHelper.test.js.map +1 -0
  45. package/package.json +9 -1
  46. package/src/helpers/tfs.ts +51 -7
  47. package/src/modules/GitDataProvider.ts +10 -0
  48. package/src/modules/TestDataProvider.ts +0 -1
  49. package/src/modules/TicketsDataProvider.ts +298 -141
  50. package/src/tests/helpers/helper.test.ts +337 -0
  51. package/src/tests/helpers/tfs.test.ts +1092 -0
  52. package/src/tests/index.test.ts +28 -0
  53. package/src/tests/models/tfs-data.test.ts +203 -0
  54. package/src/tests/modules/JfrogDataProvider.test.ts +167 -0
  55. package/src/tests/modules/ResultDataProvider.test.ts +2571 -0
  56. package/src/tests/modules/gitDataProvider.test.ts +2628 -0
  57. package/src/{modules/test → tests/modules}/managmentDataProvider.test.ts +33 -1
  58. package/src/tests/modules/pipelineDataProvider.test.ts +1038 -0
  59. package/src/tests/modules/testDataProvider.test.ts +1046 -0
  60. package/src/tests/modules/ticketsDataProvider.test.ts +2204 -0
  61. package/src/tests/utils/DataProviderUtils.test.ts +76 -0
  62. package/src/tests/utils/testStepParserHelper.test.ts +437 -0
  63. package/tsconfig.json +1 -0
  64. package/bin/helpers/test/tfs.test.js.map +0 -1
  65. package/bin/modules/test/JfrogDataProvider.test.js.map +0 -1
  66. package/bin/modules/test/ResultDataProvider.test.js +0 -444
  67. package/bin/modules/test/ResultDataProvider.test.js.map +0 -1
  68. package/bin/modules/test/gitDataProvider.test.js +0 -428
  69. package/bin/modules/test/gitDataProvider.test.js.map +0 -1
  70. package/bin/modules/test/managmentDataProvider.test.js.map +0 -1
  71. package/bin/modules/test/pipelineDataProvider.test.js +0 -237
  72. package/bin/modules/test/pipelineDataProvider.test.js.map +0 -1
  73. package/bin/modules/test/testDataProvider.test.js +0 -234
  74. package/bin/modules/test/testDataProvider.test.js.map +0 -1
  75. package/bin/modules/test/ticketsDataProvider.test.js +0 -348
  76. package/bin/modules/test/ticketsDataProvider.test.js.map +0 -1
  77. package/src/helpers/test/tfs.test.ts +0 -748
  78. package/src/modules/test/JfrogDataProvider.test.ts +0 -171
  79. package/src/modules/test/ResultDataProvider.test.ts +0 -542
  80. package/src/modules/test/gitDataProvider.test.ts +0 -645
  81. package/src/modules/test/pipelineDataProvider.test.ts +0 -292
  82. package/src/modules/test/testDataProvider.test.ts +0 -318
  83. package/src/modules/test/ticketsDataProvider.test.ts +0 -462
  84. /package/bin/{helpers/test/tfs.test.d.ts → tests/helpers/helper.test.d.ts} +0 -0
  85. /package/bin/{modules/test/JfrogDataProvider.test.d.ts → tests/helpers/tfs.test.d.ts} +0 -0
  86. /package/bin/{modules/test/ResultDataProvider.test.d.ts → tests/index.test.d.ts} +0 -0
  87. /package/bin/{modules/test/gitDataProvider.test.d.ts → tests/models/tfs-data.test.d.ts} +0 -0
  88. /package/bin/{modules/test/managmentDataProvider.test.d.ts → tests/modules/JfrogDataProvider.test.d.ts} +0 -0
  89. /package/bin/{modules/test/pipelineDataProvider.test.d.ts → tests/modules/ResultDataProvider.test.d.ts} +0 -0
  90. /package/bin/{modules/test/testDataProvider.test.d.ts → tests/modules/gitDataProvider.test.d.ts} +0 -0
  91. /package/bin/{modules/test/ticketsDataProvider.test.d.ts → tests/modules/managmentDataProvider.test.d.ts} +0 -0
@@ -1,748 +0,0 @@
1
- // First, mock axios BEFORE importing TFSServices
2
- import axios from 'axios';
3
- jest.mock('axios');
4
-
5
- // Set up the mock axios instance that axios.create will return
6
- const mockAxiosInstance = {
7
- request: jest.fn()
8
- };
9
- (axios.create as jest.Mock).mockReturnValue(mockAxiosInstance);
10
-
11
- // NOW import TFSServices (it will use our mock)
12
- import { TFSServices } from '../tfs';
13
- import logger from '../../utils/logger';
14
-
15
- // Mock logger
16
- jest.mock('../../utils/logger');
17
-
18
- describe('TFSServices', () => {
19
- // Store the original implementation of random to restore it later
20
- const originalRandom = Math.random;
21
-
22
- beforeEach(() => {
23
- jest.clearAllMocks();
24
- // Mock Math.random to return a predictable value for tests with retry
25
- Math.random = jest.fn().mockReturnValue(0.5);
26
- });
27
-
28
- afterEach(() => {
29
- // Restore the original Math.random implementation
30
- Math.random = originalRandom;
31
- });
32
-
33
- describe('downloadZipFile', () => {
34
- it('should download a zip file successfully', async () => {
35
- // Arrange
36
- const url = 'https://example.com/file.zip';
37
- const pat = 'token123';
38
- const mockResponse = { data: Buffer.from('zip-file-content') };
39
-
40
- // Configure mock response
41
- mockAxiosInstance.request.mockResolvedValueOnce(mockResponse);
42
-
43
- // Act
44
- const result = await TFSServices.downloadZipFile(url, pat);
45
-
46
- // Assert
47
- expect(result).toEqual(mockResponse);
48
- expect(mockAxiosInstance.request).toHaveBeenCalledWith({
49
- url,
50
- headers: { 'Content-Type': 'application/zip' },
51
- auth: { username: '', password: pat }
52
- });
53
- });
54
-
55
- it('should log and throw error when download fails', async () => {
56
- // Arrange
57
- const url = 'https://example.com/file.zip';
58
- const pat = 'token123';
59
- const mockError = new Error('Network error');
60
-
61
- // Configure mock to throw error
62
- mockAxiosInstance.request.mockRejectedValueOnce(mockError);
63
-
64
- // Act & Assert
65
- await expect(TFSServices.downloadZipFile(url, pat)).rejects.toThrow();
66
- expect(logger.error).toHaveBeenCalledWith(`error download zip file , url : ${url}`);
67
- });
68
- });
69
-
70
- describe('fetchAzureDevOpsImageAsBase64', () => {
71
- it('should fetch and convert image to base64', async () => {
72
- // Arrange
73
- const url = 'https://example.com/image.png';
74
- const pat = 'token123';
75
- const mockResponse = {
76
- data: Buffer.from('image-data'),
77
- headers: { 'content-type': 'image/png' }
78
- };
79
-
80
- // Configure mock response
81
- mockAxiosInstance.request.mockResolvedValueOnce(mockResponse);
82
-
83
- // Act
84
- const result = await TFSServices.fetchAzureDevOpsImageAsBase64(url, pat);
85
-
86
- // Assert
87
- expect(result).toEqual('data:image/png;base64,aW1hZ2UtZGF0YQ==');
88
- expect(mockAxiosInstance.request).toHaveBeenCalledWith(expect.objectContaining({
89
- url,
90
- method: 'get',
91
- auth: { username: '', password: pat },
92
- responseType: 'arraybuffer'
93
- }));
94
- });
95
-
96
- it('should handle errors and retry for retryable errors', async () => {
97
- // Arrange
98
- const url = 'https://example.com/image.png';
99
- const pat = 'token123';
100
-
101
- // Create a rate limit error (retry-eligible)
102
- const rateLimitError = new Error('Rate limit exceeded');
103
- (rateLimitError as any).response = { status: 429 };
104
-
105
- // Configure mock to fail once then succeed
106
- mockAxiosInstance.request
107
- .mockRejectedValueOnce(rateLimitError)
108
- .mockResolvedValueOnce({
109
- data: Buffer.from('image-data'),
110
- headers: { 'content-type': 'image/png' }
111
- });
112
-
113
- // Mock setTimeout to execute immediately
114
- jest.spyOn(global, 'setTimeout').mockImplementation((fn: any) => {
115
- fn();
116
- return {} as any;
117
- });
118
-
119
- // Act
120
- const result = await TFSServices.fetchAzureDevOpsImageAsBase64(url, pat);
121
-
122
- // Assert
123
- expect(result).toEqual('data:image/png;base64,aW1hZ2UtZGF0YQ==');
124
- expect(mockAxiosInstance.request).toHaveBeenCalledTimes(2);
125
- expect(logger.warn).toHaveBeenCalled();
126
- });
127
- });
128
-
129
- describe('getItemContent', () => {
130
- it('should get item content successfully with GET request', async () => {
131
- // Arrange
132
- const url = 'https://example.com/api/item';
133
- const pat = 'token123';
134
- const mockResponse = { data: { id: 123, name: 'Test Item' } };
135
-
136
- // Configure mock response
137
- mockAxiosInstance.request.mockResolvedValueOnce(mockResponse);
138
-
139
- // Act
140
- const result = await TFSServices.getItemContent(url, pat);
141
-
142
- // Assert
143
- expect(result).toEqual(mockResponse.data);
144
- expect(mockAxiosInstance.request).toHaveBeenCalledWith(expect.objectContaining({
145
- url: url.replace(/ /g, '%20'),
146
- method: 'get',
147
- auth: { username: '', password: pat },
148
- timeout: 10000 // Verify the actual timeout value is 10000ms
149
- }));
150
- });
151
-
152
- it('should fail when request times out', async () => {
153
- // Arrange
154
- const url = 'https://example.com/api/slow-item';
155
- const pat = 'token123';
156
-
157
- // Create a timeout error
158
- const timeoutError = new Error('timeout of 1000ms exceeded');
159
- timeoutError.name = 'TimeoutError';
160
- (timeoutError as any).code = 'ECONNABORTED';
161
-
162
- // Configure mock to simulate timeout (will retry 3 times by default)
163
- mockAxiosInstance.request
164
- .mockRejectedValueOnce(timeoutError)
165
- .mockRejectedValueOnce(timeoutError)
166
- .mockRejectedValueOnce(timeoutError);
167
-
168
- // Mock setTimeout to execute immediately for faster tests
169
- jest.spyOn(global, 'setTimeout').mockImplementation((fn: any) => {
170
- fn();
171
- return {} as any;
172
- });
173
-
174
- // Act & Assert
175
- await expect(TFSServices.getItemContent(url, pat)).rejects.toThrow('timeout');
176
- expect(mockAxiosInstance.request).toHaveBeenCalledTimes(3); // Initial + 2 retries
177
- expect(logger.warn).toHaveBeenCalledTimes(2); // Two retry warnings
178
- });
179
-
180
- it('should fail when network connection fails', async () => {
181
- // Arrange
182
- const url = 'https://example.com/api/item';
183
- const pat = 'token123';
184
-
185
- // Create different network errors
186
- const connectionResetError = new Error('socket hang up');
187
- (connectionResetError as any).code = 'ECONNRESET';
188
-
189
- const connectionRefusedError = new Error('connect ECONNREFUSED');
190
- (connectionRefusedError as any).code = 'ECONNREFUSED';
191
-
192
- // Configure mock to simulate different network failures on each retry
193
- mockAxiosInstance.request
194
- .mockRejectedValueOnce(connectionResetError)
195
- .mockRejectedValueOnce(connectionRefusedError)
196
- .mockRejectedValueOnce(connectionResetError);
197
-
198
- // Mock setTimeout to execute immediately for faster tests
199
- jest.spyOn(global, 'setTimeout').mockImplementation((fn: any) => {
200
- fn();
201
- return {} as any;
202
- });
203
-
204
- // Act & Assert
205
- await expect(TFSServices.getItemContent(url, pat)).rejects.toThrow();
206
- expect(mockAxiosInstance.request).toHaveBeenCalledTimes(2); // Initial + 2 retries
207
- expect(logger.error).toHaveBeenCalled(); // Should log detailed error
208
- });
209
-
210
- it('should handle DNS resolution failures', async () => {
211
- // Arrange
212
- const url = 'https://nonexistent-domain.example.com/api/item';
213
- const pat = 'token123';
214
-
215
- // Create DNS resolution error
216
- const dnsError = new Error('getaddrinfo ENOTFOUND nonexistent-domain.example.com');
217
- (dnsError as any).code = 'ENOTFOUND';
218
-
219
- // Configure mock to simulate DNS failure
220
- mockAxiosInstance.request.mockRejectedValue(dnsError);
221
-
222
- // Act & Assert
223
- await expect(TFSServices.getItemContent(url, pat)).rejects.toThrow('ENOTFOUND');
224
- expect(mockAxiosInstance.request).toHaveBeenCalledTimes(2); // Should retry DNS failures too
225
- });
226
-
227
- it('should handle spaces in URL by replacing them with %20', async () => {
228
- // Arrange
229
- const url = 'https://example.com/api/item with spaces';
230
- const pat = 'token123';
231
- const mockResponse = { data: { id: 123, name: 'Test Item' } };
232
-
233
- // Configure mock response
234
- mockAxiosInstance.request.mockResolvedValueOnce(mockResponse);
235
-
236
- // Act
237
- await TFSServices.getItemContent(url, pat);
238
-
239
- // Assert
240
- expect(mockAxiosInstance.request).toHaveBeenCalledWith(
241
- expect.objectContaining({ url: 'https://example.com/api/item%20with%20spaces' })
242
- );
243
- });
244
-
245
-
246
- });
247
-
248
- describe('getJfrogRequest', () => {
249
- it('should make a successful GET request to JFrog', async () => {
250
- // Arrange
251
- const url = 'https://jfrog.example.com/api/artifacts';
252
- const headers = { Authorization: 'Bearer token123' };
253
- const mockResponse = { data: { artifacts: [{ name: 'artifact1' }] } };
254
-
255
- // Configure mock response
256
- mockAxiosInstance.request.mockResolvedValueOnce(mockResponse);
257
-
258
- // Act
259
- const result = await TFSServices.getJfrogRequest(url, headers);
260
-
261
- // Assert
262
- expect(result).toEqual(mockResponse.data);
263
- expect(mockAxiosInstance.request).toHaveBeenCalledWith({
264
- url,
265
- method: 'GET',
266
- headers
267
- });
268
- });
269
-
270
- it('should handle errors from JFrog API', async () => {
271
- // Arrange
272
- const url = 'https://jfrog.example.com/api/artifacts';
273
- const headers = { Authorization: 'Bearer token123' };
274
- const mockError = new Error('JFrog API error');
275
-
276
- // Configure mock to throw error
277
- mockAxiosInstance.request.mockRejectedValueOnce(mockError);
278
-
279
- // Act & Assert
280
- await expect(TFSServices.getJfrogRequest(url, headers)).rejects.toThrow();
281
- expect(logger.error).toHaveBeenCalled();
282
- });
283
- });
284
-
285
- describe('postRequest', () => {
286
- it('should make a successful POST request', async () => {
287
- // Arrange
288
- const url = 'https://example.com/api/resource';
289
- const pat = 'token123';
290
- const data = { name: 'New Resource' };
291
- const mockResponse = { data: { id: 123, name: 'New Resource' } };
292
-
293
- // Configure mock response
294
- mockAxiosInstance.request.mockResolvedValueOnce(mockResponse);
295
-
296
- // Act
297
- const result = await TFSServices.postRequest(url, pat, 'post', data);
298
-
299
- // Assert
300
- expect(result).toEqual(mockResponse);
301
- expect(mockAxiosInstance.request).toHaveBeenCalledWith({
302
- url,
303
- method: 'post',
304
- auth: { username: '', password: pat },
305
- data,
306
- headers: { headers: { 'Content-Type': 'application/json' } }
307
- });
308
- });
309
-
310
- it('should work with custom headers and methods', async () => {
311
- // Arrange
312
- const url = 'https://example.com/api/resource';
313
- const pat = 'token123';
314
- const data = { name: 'Update Resource' };
315
- const customHeaders = { 'Content-Type': 'application/xml' };
316
- const mockResponse = { data: { id: 123, name: 'Updated Resource' } };
317
-
318
- // Configure mock response
319
- mockAxiosInstance.request.mockResolvedValueOnce(mockResponse);
320
-
321
- // Act
322
- const result = await TFSServices.postRequest(url, pat, 'put', data, customHeaders);
323
-
324
- // Assert
325
- expect(result).toEqual(mockResponse);
326
- expect(mockAxiosInstance.request).toHaveBeenCalledWith({
327
- url,
328
- method: 'put',
329
- auth: { username: '', password: pat },
330
- data,
331
- headers: customHeaders
332
- });
333
- });
334
-
335
- it('should handle errors in POST requests', async () => {
336
- // Arrange
337
- const url = 'https://example.com/api/resource';
338
- const pat = 'token123';
339
- const data = { name: 'New Resource' };
340
- const mockError = new Error('Validation error');
341
- (mockError as any).response = { status: 400, data: { message: 'Invalid data' } };
342
-
343
- // Configure mock to throw error
344
- mockAxiosInstance.request.mockRejectedValueOnce(mockError);
345
-
346
- // Act & Assert
347
- await expect(TFSServices.postRequest(url, pat, 'post', data)).rejects.toThrow();
348
- expect(logger.error).toHaveBeenCalled();
349
- });
350
- });
351
-
352
- describe('executeWithRetry', () => {
353
- it('should retry on network timeouts', async () => {
354
- // Arrange
355
- const url = 'https://example.com/api/slow-resource';
356
- const pat = 'token123';
357
-
358
- // Create a timeout error
359
- const timeoutError = new Error('timeout of 10000ms exceeded');
360
-
361
- // Configure mock to fail with timeout then succeed
362
- mockAxiosInstance.request
363
- .mockRejectedValueOnce(timeoutError)
364
- .mockResolvedValueOnce({ data: { success: true } });
365
-
366
- // Mock setTimeout to execute immediately
367
- jest.spyOn(global, 'setTimeout').mockImplementation((fn: any) => {
368
- fn();
369
- return {} as any;
370
- });
371
-
372
- // Act
373
- const result = await TFSServices.getItemContent(url, pat);
374
-
375
- // Assert
376
- expect(result).toEqual({ success: true });
377
- expect(mockAxiosInstance.request).toHaveBeenCalledTimes(2);
378
- expect(logger.warn).toHaveBeenCalled();
379
- });
380
-
381
- it('should retry on server errors (5xx)', async () => {
382
- // Arrange
383
- const url = 'https://example.com/api/unstable-resource';
384
- const pat = 'token123';
385
-
386
- // Create a 503 error
387
- const serverError = new Error('Service unavailable');
388
- (serverError as any).response = { status: 503 };
389
-
390
- // Configure mock to fail with server error then succeed
391
- mockAxiosInstance.request
392
- .mockRejectedValueOnce(serverError)
393
- .mockResolvedValueOnce({ data: { success: true } });
394
-
395
- // Mock setTimeout to execute immediately
396
- jest.spyOn(global, 'setTimeout').mockImplementation((fn: any) => {
397
- fn();
398
- return {} as any;
399
- });
400
-
401
- // Act
402
- const result = await TFSServices.getItemContent(url, pat);
403
-
404
- // Assert
405
- expect(result).toEqual({ success: true });
406
- expect(mockAxiosInstance.request).toHaveBeenCalledTimes(2);
407
- });
408
-
409
- it('should give up after max retry attempts', async () => {
410
- // Arrange
411
- const url = 'https://example.com/api/failing-resource';
412
- const pat = 'token123';
413
-
414
- // Create a persistent server error
415
- const serverError = new Error('Service unavailable');
416
- (serverError as any).response = { status: 503 };
417
-
418
- // Configure mock to always fail with server error
419
- mockAxiosInstance.request.mockRejectedValue(serverError);
420
-
421
- // Mock setTimeout to execute immediately
422
- jest.spyOn(global, 'setTimeout').mockImplementation((fn: any) => {
423
- fn();
424
- return {} as any;
425
- });
426
-
427
- // Act & Assert
428
- await expect(TFSServices.getItemContent(url, pat)).rejects.toThrow();
429
-
430
- // Should try original request + retries up to maxAttempts (3)
431
- expect(mockAxiosInstance.request).toHaveBeenCalledTimes(3);
432
- });
433
- });
434
- // Add these test cases to the existing test file
435
-
436
- describe('Stress testing', () => {
437
- describe('getItemContent - Sequential large data requests', () => {
438
- it('should handle multiple sequential requests with large datasets', async () => {
439
- // Arrange
440
- const url = 'https://example.com/api/large-dataset';
441
- const pat = 'token123';
442
-
443
- // Create 5 large responses of different sizes
444
- const responses = Array(5).fill(0).map((_, i) => {
445
- // Create increasingly large responses (500KB, 1MB, 1.5MB, 2MB, 2.5MB)
446
- const size = 25000 * (i + 1);
447
- return {
448
- data: {
449
- items: Array(size).fill(0).map((_, j) => ({
450
- id: j,
451
- name: `Item ${j}`,
452
- description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
453
- data: `value-${j}-${Math.random().toString(36).substring(2, 15)}`
454
- }))
455
- }
456
- };
457
- });
458
-
459
- // Configure the mock to return the different responses in sequence
460
- for (const response of responses) {
461
- mockAxiosInstance.request.mockResolvedValueOnce(response);
462
- }
463
-
464
- // Act - Make multiple sequential requests
465
- const results = [];
466
- for (let i = 0; i < responses.length; i++) {
467
- const result = await TFSServices.getItemContent(`${url}/${i}`, pat);
468
- results.push(result);
469
- }
470
-
471
- // Assert
472
- expect(results.length).toBe(responses.length);
473
- for (let i = 0; i < results.length; i++) {
474
- expect(results[i].items.length).toBe(responses[i].data.items.length);
475
- }
476
- expect(mockAxiosInstance.request).toHaveBeenCalledTimes(responses.length);
477
- });
478
-
479
- it('should handle sequential requests with mixed success/failure patterns', async () => {
480
- // Arrange
481
- const url = 'https://example.com/api/sequential';
482
- const pat = 'token123';
483
-
484
- // Set up a sequence of responses/errors
485
- // 1. Success
486
- // 2. Rate limit error (429) - should retry and succeed
487
- // 3. Success
488
- // 4. Server error (503) - should retry and succeed
489
- // 5. Success
490
-
491
- const rateLimitError = new Error('Rate limit exceeded');
492
- (rateLimitError as any).response = { status: 429 };
493
-
494
- const serverError = new Error('Server error');
495
- (serverError as any).response = { status: 503 };
496
-
497
- // Response 1
498
- mockAxiosInstance.request.mockResolvedValueOnce({
499
- data: { id: 1, success: true }
500
- });
501
-
502
- // Response 2 - fails with rate limit first, then succeeds
503
- mockAxiosInstance.request
504
- .mockRejectedValueOnce(rateLimitError)
505
- .mockResolvedValueOnce({
506
- data: { id: 2, success: true }
507
- });
508
-
509
- // Response 3
510
- mockAxiosInstance.request.mockResolvedValueOnce({
511
- data: { id: 3, success: true }
512
- });
513
-
514
- // Response 4 - fails with server error first, then succeeds
515
- mockAxiosInstance.request
516
- .mockRejectedValueOnce(serverError)
517
- .mockResolvedValueOnce({
518
- data: { id: 4, success: true }
519
- });
520
-
521
- // Response 5
522
- mockAxiosInstance.request.mockResolvedValueOnce({
523
- data: { id: 5, success: true }
524
- });
525
-
526
- // Mock setTimeout to execute immediately for retry delay
527
- jest.spyOn(global, 'setTimeout').mockImplementation((fn: any) => {
528
- fn();
529
- return {} as any;
530
- });
531
-
532
- // Act - Make sequential requests
533
- const results = [];
534
- for (let i = 1; i <= 5; i++) {
535
- const result = await TFSServices.getItemContent(`${url}/${i}`, pat);
536
- results.push(result);
537
- }
538
-
539
- // Assert
540
- expect(results.length).toBe(5);
541
- expect(results.map(r => r.id)).toEqual([1, 2, 3, 4, 5]);
542
-
543
- // Check total number of requests (5 successful responses + 2 retries)
544
- expect(mockAxiosInstance.request).toHaveBeenCalledTimes(7);
545
- });
546
- });
547
-
548
- describe('postRequest - Sequential large data submissions', () => {
549
- it('should handle multiple sequential POST requests with large payloads', async () => {
550
- // Arrange
551
- const url = 'https://example.com/api/resource';
552
- const pat = 'token123';
553
-
554
- // Create 5 increasingly large payloads
555
- const payloads = Array(5).fill(0).map((_, i) => {
556
- // Create payloads of increasing size (100KB, 200KB, 300KB, 400KB, 500KB)
557
- const size = 5000 * (i + 1);
558
- return {
559
- name: `Resource ${i + 1}`,
560
- description: `Large resource ${i + 1}`,
561
- items: Array(size).fill(0).map((_, j) => ({
562
- id: j,
563
- value: `item-${j}-${Math.random().toString(36).substring(2, 15)}`,
564
- timestamp: new Date().toISOString()
565
- }))
566
- };
567
- });
568
-
569
- // Set up mock responses
570
- for (let i = 0; i < payloads.length; i++) {
571
- mockAxiosInstance.request.mockResolvedValueOnce({
572
- data: { id: i + 1, status: 'created', itemCount: payloads[i].items.length }
573
- });
574
- }
575
-
576
- // Act - Make multiple sequential POST requests
577
- const results = [];
578
- for (let i = 0; i < payloads.length; i++) {
579
- const result = await TFSServices.postRequest(url, pat, 'post', payloads[i]);
580
- results.push(result);
581
- }
582
-
583
- // Assert
584
- expect(results.length).toBe(payloads.length);
585
- for (let i = 0; i < results.length; i++) {
586
- expect(results[i].data.id).toBe(i + 1);
587
- expect(results[i].data.itemCount).toBe(payloads[i].items.length);
588
- }
589
- expect(mockAxiosInstance.request).toHaveBeenCalledTimes(payloads.length);
590
- });
591
-
592
- it('should handle a mix of success and failure during sequential POST operations', async () => {
593
- // Arrange
594
- const url = 'https://example.com/api/resource';
595
- const pat = 'token123';
596
-
597
- // Create test data
598
- const payloads = Array(5).fill(0).map((_, i) => ({ name: `Resource ${i + 1}` }));
599
-
600
- // Validation error
601
- const validationError = new Error('Validation error');
602
- (validationError as any).response = {
603
- status: 400,
604
- data: { message: 'Invalid data', details: 'Field X is required' }
605
- };
606
-
607
- // Server error
608
- const serverError = new Error('Server error');
609
- (serverError as any).response = { status: 500 };
610
-
611
- // Configure mock responses/errors for each request
612
- // 1. Success
613
- mockAxiosInstance.request.mockResolvedValueOnce({
614
- data: { id: 1, status: 'created' }
615
- });
616
-
617
- // 2. Validation error
618
- mockAxiosInstance.request.mockRejectedValueOnce(validationError);
619
-
620
- // 3. Success
621
- mockAxiosInstance.request.mockResolvedValueOnce({
622
- data: { id: 3, status: 'created' }
623
- });
624
-
625
- // 4. Server error
626
- mockAxiosInstance.request.mockRejectedValueOnce(serverError);
627
-
628
- // 5. Success
629
- mockAxiosInstance.request.mockResolvedValueOnce({
630
- data: { id: 5, status: 'created' }
631
- });
632
-
633
- // Act & Assert
634
- // Request 1 - should succeed
635
- const result1 = await TFSServices.postRequest(url, pat, 'post', payloads[0]);
636
- expect(result1.data.id).toBe(1);
637
-
638
- // Request 2 - should fail with validation error
639
- await expect(TFSServices.postRequest(url, pat, 'post', payloads[1]))
640
- .rejects.toThrow('Validation error');
641
-
642
- // Request 3 - should succeed despite previous failure
643
- const result3 = await TFSServices.postRequest(url, pat, 'post', payloads[2]);
644
- expect(result3.data.id).toBe(3);
645
-
646
- // Request 4 - should fail with server error
647
- await expect(TFSServices.postRequest(url, pat, 'post', payloads[3]))
648
- .rejects.toThrow('Server error');
649
-
650
- // Request 5 - should succeed despite previous failure
651
- const result5 = await TFSServices.postRequest(url, pat, 'post', payloads[4]);
652
- expect(result5.data.id).toBe(5);
653
-
654
- // Verify all requests were made
655
- expect(mockAxiosInstance.request).toHaveBeenCalledTimes(5);
656
- });
657
-
658
- it('should handle large payload with many nested objects', async () => {
659
- // Arrange
660
- const url = 'https://example.com/api/complex-resource';
661
- const pat = 'token123';
662
-
663
- // Create a deeply nested object structure
664
- const createNestedObject = (depth: number, breadth: number, current = 0): any => {
665
- if (current >= depth) {
666
- return { value: `leaf-${Math.random()}` };
667
- }
668
-
669
- const children: Record<string, any> = {};
670
- for (let i = 0; i < breadth; i++) {
671
- children[`child-${current}-${i}`] = createNestedObject(depth, breadth, current + 1);
672
- }
673
-
674
- return {
675
- id: `node-${current}-${Math.random()}`,
676
- level: current,
677
- children
678
- };
679
- };
680
-
681
- // Create a complex payload with depth 5 and breadth 5 (5^5 = 3,125 nodes)
682
- const complexPayload = {
683
- name: 'Complex Resource',
684
- type: 'hierarchical',
685
- rootNode: createNestedObject(5, 5)
686
- };
687
-
688
- // Configure mock response
689
- mockAxiosInstance.request.mockResolvedValueOnce({
690
- data: { id: 123, status: 'created', complexity: 'high' }
691
- });
692
-
693
- // Act
694
- const result = await TFSServices.postRequest(url, pat, 'post', complexPayload);
695
-
696
- // Assert
697
- expect(result.data.id).toBe(123);
698
- expect(result.data.status).toBe('created');
699
-
700
- // Verify the request was made with the complex payload
701
- expect(mockAxiosInstance.request).toHaveBeenCalledWith(expect.objectContaining({
702
- url,
703
- method: 'post',
704
- data: complexPayload
705
- }));
706
- });
707
- });
708
-
709
- describe('High volume sequential operations', () => {
710
- it('should handle a high volume of sequential API calls', async () => {
711
- // Arrange
712
- const url = 'https://example.com/api/items';
713
- const pat = 'token123';
714
- const requestCount = 50; // Make 50 sequential requests
715
-
716
- // Configure mock responses for all requests
717
- for (let i = 0; i < requestCount; i++) {
718
- mockAxiosInstance.request.mockResolvedValueOnce({
719
- data: { id: i, success: true, timestamp: new Date().toISOString() }
720
- });
721
- }
722
-
723
- // Act
724
- const results = [];
725
- for (let i = 0; i < requestCount; i++) {
726
- // Alternate between GET and POST requests
727
- if (i % 2 === 0) {
728
- const result = await TFSServices.getItemContent(`${url}/${i}`, pat);
729
- results.push(result);
730
- } else {
731
- const result = await TFSServices.postRequest(url, pat, 'post', { itemId: i });
732
- results.push(result.data);
733
- }
734
- }
735
-
736
- // Assert
737
- expect(results.length).toBe(requestCount);
738
- for (let i = 0; i < requestCount; i++) {
739
- expect(results[i].id).toBe(i);
740
- expect(results[i].success).toBe(true);
741
- }
742
- expect(mockAxiosInstance.request).toHaveBeenCalledTimes(requestCount);
743
- });
744
- });
745
- });
746
-
747
-
748
- });