@elisra-devops/docgen-data-provider 1.125.0 → 1.127.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/.github/workflows/release.yml +6 -0
- package/bin/modules/GitDataProvider.d.ts +7 -0
- package/bin/modules/GitDataProvider.js +7 -0
- package/bin/modules/GitDataProvider.js.map +1 -1
- package/bin/modules/TestDataProvider.d.ts +2 -0
- package/bin/modules/TestDataProvider.js +61 -26
- package/bin/modules/TestDataProvider.js.map +1 -1
- package/bin/modules/TicketsDataProvider.js +56 -24
- package/bin/modules/TicketsDataProvider.js.map +1 -1
- package/bin/tests/modules/testDataProvider.test.js +93 -7
- package/bin/tests/modules/testDataProvider.test.js.map +1 -1
- package/package.json +1 -1
- package/src/modules/GitDataProvider.ts +43 -36
- package/src/modules/TestDataProvider.ts +75 -26
- package/src/modules/TicketsDataProvider.ts +74 -43
- package/src/tests/modules/testDataProvider.test.ts +132 -15
|
@@ -321,13 +321,17 @@ describe('TestDataProvider', () => {
|
|
|
321
321
|
],
|
|
322
322
|
count: 2,
|
|
323
323
|
};
|
|
324
|
-
(TFSServices.
|
|
324
|
+
(TFSServices.getItemContentWithHeaders as jest.Mock).mockResolvedValueOnce({ data: mockData, headers: {} });
|
|
325
325
|
|
|
326
326
|
const result = await bearerProvider.GetTestSuitesForPlan(mockProject, mockPlanId);
|
|
327
327
|
|
|
328
|
-
expect(TFSServices.
|
|
329
|
-
`${mockOrgUrl.replace(/\/+$/, '')}/${mockProject}/_apis/testplan/Plans/${mockPlanId}/suites?
|
|
330
|
-
mockBearerToken
|
|
328
|
+
expect(TFSServices.getItemContentWithHeaders).toHaveBeenCalledWith(
|
|
329
|
+
`${mockOrgUrl.replace(/\/+$/, '')}/${mockProject}/_apis/testplan/Plans/${mockPlanId}/suites?expand=children&api-version=7.0`,
|
|
330
|
+
mockBearerToken,
|
|
331
|
+
'get',
|
|
332
|
+
{},
|
|
333
|
+
{},
|
|
334
|
+
false
|
|
331
335
|
);
|
|
332
336
|
expect(result.testSuites).toEqual(
|
|
333
337
|
expect.arrayContaining([
|
|
@@ -353,22 +357,25 @@ describe('TestDataProvider', () => {
|
|
|
353
357
|
const workItemsPayload = {
|
|
354
358
|
value: [{ id: 101, fields: { 'System.Description': 'Enriched Desc A' } }],
|
|
355
359
|
};
|
|
360
|
+
(TFSServices.getItemContentWithHeaders as jest.Mock).mockResolvedValueOnce({ data: suitesPayload, headers: {} });
|
|
356
361
|
(TFSServices.getItemContent as jest.Mock).mockImplementation((url: string) => {
|
|
357
362
|
if (url.includes('/_apis/wit/workitemsbatch')) {
|
|
358
363
|
return Promise.resolve(workItemsPayload);
|
|
359
364
|
}
|
|
360
|
-
return Promise.resolve(
|
|
365
|
+
return Promise.resolve(undefined);
|
|
361
366
|
});
|
|
362
367
|
|
|
363
368
|
const result = await bearerProvider.GetTestSuitesForPlan(mockProject, mockPlanId);
|
|
364
369
|
|
|
365
|
-
expect(TFSServices.
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
370
|
+
expect(TFSServices.getItemContentWithHeaders).toHaveBeenCalledWith(
|
|
371
|
+
`${mockOrgUrl.replace(/\/+$/, '')}/${mockProject}/_apis/testplan/Plans/${mockPlanId}/suites?expand=children&api-version=7.0`,
|
|
372
|
+
mockBearerToken,
|
|
373
|
+
'get',
|
|
374
|
+
{},
|
|
375
|
+
{},
|
|
376
|
+
false
|
|
369
377
|
);
|
|
370
|
-
expect(TFSServices.getItemContent).
|
|
371
|
-
2,
|
|
378
|
+
expect(TFSServices.getItemContent).toHaveBeenCalledWith(
|
|
372
379
|
`${mockOrgUrl.replace(/\/+$/, '')}/${mockProject}/_apis/wit/workitemsbatch?api-version=7.1`,
|
|
373
380
|
mockBearerToken,
|
|
374
381
|
'post',
|
|
@@ -386,6 +393,112 @@ describe('TestDataProvider', () => {
|
|
|
386
393
|
])
|
|
387
394
|
);
|
|
388
395
|
});
|
|
396
|
+
|
|
397
|
+
describe('bearer-token branch — REST contract', () => {
|
|
398
|
+
it('should call the documented endpoint with expand=children, not includeChildren', async () => {
|
|
399
|
+
const bearerProvider = new TestDataProvider(mockOrgUrl, mockBearerToken);
|
|
400
|
+
(TFSServices.getItemContentWithHeaders as jest.Mock).mockResolvedValueOnce({
|
|
401
|
+
data: { value: [], count: 0 },
|
|
402
|
+
headers: {},
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
await bearerProvider.GetTestSuitesForPlan(mockProject, mockPlanId);
|
|
406
|
+
|
|
407
|
+
const calledUrl = (TFSServices.getItemContentWithHeaders as jest.Mock).mock.calls[0][0] as string;
|
|
408
|
+
expect(calledUrl).toContain(`/_apis/testplan/Plans/${mockPlanId}/suites`);
|
|
409
|
+
expect(calledUrl).toContain('expand=children');
|
|
410
|
+
expect(calledUrl).toContain('api-version=7.0');
|
|
411
|
+
expect(calledUrl).not.toContain('includeChildren');
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it('should follow x-ms-continuationtoken across pages and aggregate the result', async () => {
|
|
415
|
+
const bearerProvider = new TestDataProvider(mockOrgUrl, mockBearerToken);
|
|
416
|
+
const page1 = {
|
|
417
|
+
data: { value: [{ id: 1, name: 'A' }, { id: 2, name: 'B' }], count: 2 },
|
|
418
|
+
headers: { 'x-ms-continuationtoken': 'TOKEN_PAGE_2' },
|
|
419
|
+
};
|
|
420
|
+
const page2 = {
|
|
421
|
+
data: { value: [{ id: 3, name: 'C' }], count: 1 },
|
|
422
|
+
headers: { 'x-ms-continuationtoken': 'TOKEN_PAGE_3' },
|
|
423
|
+
};
|
|
424
|
+
const page3 = {
|
|
425
|
+
data: { value: [{ id: 4, name: 'D' }], count: 1 },
|
|
426
|
+
headers: {},
|
|
427
|
+
};
|
|
428
|
+
(TFSServices.getItemContentWithHeaders as jest.Mock)
|
|
429
|
+
.mockResolvedValueOnce(page1)
|
|
430
|
+
.mockResolvedValueOnce(page2)
|
|
431
|
+
.mockResolvedValueOnce(page3);
|
|
432
|
+
|
|
433
|
+
const result = await bearerProvider.GetTestSuitesForPlan(mockProject, mockPlanId);
|
|
434
|
+
|
|
435
|
+
expect((TFSServices.getItemContentWithHeaders as jest.Mock).mock.calls).toHaveLength(3);
|
|
436
|
+
expect(result.testSuites.map((s: any) => s.id)).toEqual([1, 2, 3, 4]);
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it('should send the continuation token verbatim on subsequent page requests', async () => {
|
|
440
|
+
const bearerProvider = new TestDataProvider(mockOrgUrl, mockBearerToken);
|
|
441
|
+
(TFSServices.getItemContentWithHeaders as jest.Mock)
|
|
442
|
+
.mockResolvedValueOnce({
|
|
443
|
+
data: { value: [{ id: 1 }], count: 1 },
|
|
444
|
+
headers: { 'x-ms-continuationtoken': 'CT_XYZ' },
|
|
445
|
+
})
|
|
446
|
+
.mockResolvedValueOnce({ data: { value: [], count: 0 }, headers: {} });
|
|
447
|
+
|
|
448
|
+
await bearerProvider.GetTestSuitesForPlan(mockProject, mockPlanId);
|
|
449
|
+
|
|
450
|
+
const secondCallUrl = (TFSServices.getItemContentWithHeaders as jest.Mock).mock.calls[1][0] as string;
|
|
451
|
+
expect(secondCallUrl).toContain('continuationToken=CT_XYZ');
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it('should accept the alternate "x-ms-continuation-token" header spelling', async () => {
|
|
455
|
+
const bearerProvider = new TestDataProvider(mockOrgUrl, mockBearerToken);
|
|
456
|
+
(TFSServices.getItemContentWithHeaders as jest.Mock)
|
|
457
|
+
.mockResolvedValueOnce({
|
|
458
|
+
data: { value: [{ id: 1 }], count: 1 },
|
|
459
|
+
headers: { 'x-ms-continuation-token': 'CT_ALT' },
|
|
460
|
+
})
|
|
461
|
+
.mockResolvedValueOnce({ data: { value: [{ id: 2 }], count: 1 }, headers: {} });
|
|
462
|
+
|
|
463
|
+
const result = await bearerProvider.GetTestSuitesForPlan(mockProject, mockPlanId);
|
|
464
|
+
|
|
465
|
+
expect((TFSServices.getItemContentWithHeaders as jest.Mock).mock.calls).toHaveLength(2);
|
|
466
|
+
expect(result.testSuites.map((s: any) => s.id)).toEqual([1, 2]);
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
it('should stop at the safety cap and not loop forever on misbehaving server', async () => {
|
|
470
|
+
const bearerProvider = new TestDataProvider(mockOrgUrl, mockBearerToken);
|
|
471
|
+
(TFSServices.getItemContentWithHeaders as jest.Mock).mockResolvedValue({
|
|
472
|
+
data: { value: [{ id: 0 }], count: 1 },
|
|
473
|
+
headers: { 'x-ms-continuationtoken': 'NEVER_ENDING' },
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
await bearerProvider.GetTestSuitesForPlan(mockProject, mockPlanId);
|
|
477
|
+
|
|
478
|
+
const callCount = (TFSServices.getItemContentWithHeaders as jest.Mock).mock.calls.length;
|
|
479
|
+
expect(callCount).toBeLessThanOrEqual(50);
|
|
480
|
+
expect(callCount).toBeGreaterThan(0);
|
|
481
|
+
expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('reached MAX_PAGES'));
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
describe('PAT-token branch — regression guard', () => {
|
|
486
|
+
it('should still hit the internal _testManagement endpoint with a single call (no pagination)', async () => {
|
|
487
|
+
const patProvider = new TestDataProvider(mockOrgUrl, mockToken);
|
|
488
|
+
(TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce({
|
|
489
|
+
testSuites: [{ id: 1 }, { id: 2 }],
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
const result = await patProvider.GetTestSuitesForPlan(mockProject, mockPlanId);
|
|
493
|
+
|
|
494
|
+
expect(TFSServices.getItemContent).toHaveBeenCalledWith(
|
|
495
|
+
`${mockOrgUrl.replace(/\/+$/, '')}/${mockProject}/_api/_testManagement/GetTestSuitesForPlan?__v=5&planId=${mockPlanId}`,
|
|
496
|
+
mockToken
|
|
497
|
+
);
|
|
498
|
+
expect((TFSServices.getItemContentWithHeaders as jest.Mock).mock.calls).toHaveLength(0);
|
|
499
|
+
expect(result.testSuites).toHaveLength(2);
|
|
500
|
+
});
|
|
501
|
+
});
|
|
389
502
|
});
|
|
390
503
|
|
|
391
504
|
describe('GetTestSuiteById', () => {
|
|
@@ -429,14 +542,18 @@ describe('TestDataProvider', () => {
|
|
|
429
542
|
value: [{ id: '123', name: 'Suite 1', parentSuite: { id: 0 }, description: 'Suite Desc' }],
|
|
430
543
|
};
|
|
431
544
|
const mockSuiteData = [new suiteData('Suite 1', '123', '456', 1)];
|
|
432
|
-
(TFSServices.
|
|
545
|
+
(TFSServices.getItemContentWithHeaders as jest.Mock).mockResolvedValueOnce({ data: mockTestSuites, headers: {} });
|
|
433
546
|
(Helper.findSuitesRecursive as jest.Mock).mockReturnValueOnce(mockSuiteData);
|
|
434
547
|
|
|
435
548
|
const result = await bearerProvider.GetTestSuiteById(mockProject, mockPlanId, mockSuiteId, true);
|
|
436
549
|
|
|
437
|
-
expect(TFSServices.
|
|
438
|
-
`${mockOrgUrl.replace(/\/+$/, '')}/${mockProject}/_apis/testplan/Plans/${mockPlanId}/suites?
|
|
439
|
-
mockBearerToken
|
|
550
|
+
expect(TFSServices.getItemContentWithHeaders).toHaveBeenCalledWith(
|
|
551
|
+
`${mockOrgUrl.replace(/\/+$/, '')}/${mockProject}/_apis/testplan/Plans/${mockPlanId}/suites?expand=children&api-version=7.0`,
|
|
552
|
+
mockBearerToken,
|
|
553
|
+
'get',
|
|
554
|
+
{},
|
|
555
|
+
{},
|
|
556
|
+
false
|
|
440
557
|
);
|
|
441
558
|
const suitesArg = (Helper.findSuitesRecursive as jest.Mock).mock.calls[0][3];
|
|
442
559
|
expect(suitesArg[0].title).toBe('Suite 1');
|