@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.
@@ -321,13 +321,17 @@ describe('TestDataProvider', () => {
321
321
  ],
322
322
  count: 2,
323
323
  };
324
- (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockData);
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.getItemContent).toHaveBeenCalledWith(
329
- `${mockOrgUrl.replace(/\/+$/, '')}/${mockProject}/_apis/testplan/Plans/${mockPlanId}/suites?includeChildren=true&api-version=7.0`,
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(suitesPayload);
365
+ return Promise.resolve(undefined);
361
366
  });
362
367
 
363
368
  const result = await bearerProvider.GetTestSuitesForPlan(mockProject, mockPlanId);
364
369
 
365
- expect(TFSServices.getItemContent).toHaveBeenNthCalledWith(
366
- 1,
367
- `${mockOrgUrl.replace(/\/+$/, '')}/${mockProject}/_apis/testplan/Plans/${mockPlanId}/suites?includeChildren=true&api-version=7.0`,
368
- mockBearerToken
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).toHaveBeenNthCalledWith(
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.getItemContent as jest.Mock).mockResolvedValueOnce(mockTestSuites);
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.getItemContent).toHaveBeenCalledWith(
438
- `${mockOrgUrl.replace(/\/+$/, '')}/${mockProject}/_apis/testplan/Plans/${mockPlanId}/suites?includeChildren=true&api-version=7.0`,
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');