@elisra-devops/docgen-data-provider 1.136.4 → 1.136.6

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.
@@ -951,23 +951,28 @@ describe('PipelinesDataProvider', () => {
951
951
  });
952
952
 
953
953
  describe('GetRecentReleaseArtifactInfo', () => {
954
- it('should return empty array when no releases exist', async () => {
954
+ it('should return empty array when release response has no artifacts', async () => {
955
955
  // Arrange
956
956
  const projectName = 'project1';
957
- const mockResponse = { value: [] };
958
- (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockResponse);
957
+ const releaseId = 123;
958
+ const mockReleaseResponse = { artifacts: [] };
959
+ (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockReleaseResponse);
959
960
 
960
961
  // Act
961
- const result = await pipelinesDataProvider.GetRecentReleaseArtifactInfo(projectName);
962
+ const result = await pipelinesDataProvider.GetRecentReleaseArtifactInfo(projectName, releaseId);
962
963
 
963
964
  // Assert
965
+ expect(TFSServices.getItemContent).toHaveBeenCalledWith(
966
+ expect.stringContaining(`releases/${releaseId}`),
967
+ mockToken
968
+ );
964
969
  expect(result).toEqual([]);
965
970
  });
966
971
 
967
- it('should return artifact info from most recent release', async () => {
972
+ it('should return artifact info for the specified release', async () => {
968
973
  // Arrange
969
974
  const projectName = 'project1';
970
- const mockReleasesResponse = { value: [{ id: 123 }] };
975
+ const releaseId = 123;
971
976
  const mockReleaseResponse = {
972
977
  artifacts: [
973
978
  {
@@ -978,16 +983,44 @@ describe('PipelinesDataProvider', () => {
978
983
  },
979
984
  ],
980
985
  };
981
- (TFSServices.getItemContent as jest.Mock)
982
- .mockResolvedValueOnce(mockReleasesResponse)
983
- .mockResolvedValueOnce(mockReleaseResponse);
986
+ (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockReleaseResponse);
984
987
 
985
988
  // Act
986
- const result = await pipelinesDataProvider.GetRecentReleaseArtifactInfo(projectName);
989
+ const result = await pipelinesDataProvider.GetRecentReleaseArtifactInfo(projectName, releaseId);
987
990
 
988
991
  // Assert
989
992
  expect(result).toEqual([{ artifactName: 'artifact1', artifactVersion: '1.0.0' }]);
990
993
  });
994
+
995
+ it('should return empty array without calling the API when releaseId is invalid', async () => {
996
+ // Arrange
997
+ const projectName = 'project1';
998
+
999
+ // Act
1000
+ const resultForZero = await pipelinesDataProvider.GetRecentReleaseArtifactInfo(projectName, 0);
1001
+ const resultForNaN = await pipelinesDataProvider.GetRecentReleaseArtifactInfo(projectName, NaN);
1002
+ const resultForNegative = await pipelinesDataProvider.GetRecentReleaseArtifactInfo(projectName, -5);
1003
+
1004
+ // Assert
1005
+ expect(resultForZero).toEqual([]);
1006
+ expect(resultForNaN).toEqual([]);
1007
+ expect(resultForNegative).toEqual([]);
1008
+ expect(TFSServices.getItemContent).not.toHaveBeenCalled();
1009
+ });
1010
+
1011
+ it('should return empty array when release response has no artifacts field at all', async () => {
1012
+ // Arrange
1013
+ const projectName = 'project1';
1014
+ const releaseId = 123;
1015
+ const mockReleaseResponse = { id: releaseId, name: 'Release-1' };
1016
+ (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockReleaseResponse);
1017
+
1018
+ // Act
1019
+ const result = await pipelinesDataProvider.GetRecentReleaseArtifactInfo(projectName, releaseId);
1020
+
1021
+ // Assert
1022
+ expect(result).toEqual([]);
1023
+ });
991
1024
  });
992
1025
 
993
1026
  describe('getPipelineResourcePipelinesFromObject', () => {
@@ -911,6 +911,44 @@ describe('TicketsDataProvider', () => {
911
911
  ),
912
912
  ).rejects.toThrow('Target relation is missing');
913
913
  });
914
+
915
+ it('should order sourceTargetsMap by the query sortColumns (Node Name asc, Customer ID asc)', async () => {
916
+ // Root work items arrive in relation order 1, 2, 3 — sortColumns should reorder them
917
+ // by System.NodeName then CustomerRequirementId regardless of fetch order.
918
+ const rootFieldsById: Record<number, any> = {
919
+ 1: { 'System.WorkItemType': 'Requirement', 'System.NodeName': 'B', CustomerRequirementId: 'C-20' },
920
+ 2: { 'System.WorkItemType': 'Requirement', 'System.NodeName': 'A', CustomerRequirementId: 'C-10' },
921
+ 3: { 'System.WorkItemType': 'Requirement', 'System.NodeName': 'A', CustomerRequirementId: 'C-5' },
922
+ };
923
+ jest
924
+ .spyOn(ticketsDataProvider as any, 'fetchWIForQueryResult')
925
+ .mockImplementation(async (...args: any[]) => {
926
+ const rel = args[0];
927
+ const id = rel?.target?.id ?? rel?.source?.id;
928
+ return { id, fields: rootFieldsById[id] };
929
+ });
930
+
931
+ const res = await (ticketsDataProvider as any).parseDirectLinkedQueryResultForTableFormat(
932
+ {
933
+ queryType: QueryType.OneHop,
934
+ columns: [],
935
+ sortColumns: [
936
+ { field: { referenceName: 'System.NodeName' }, descending: false },
937
+ { field: { referenceName: 'CustomerRequirementId' }, descending: false },
938
+ ],
939
+ workItemRelations: [
940
+ { source: null, target: { id: 1 } },
941
+ { source: null, target: { id: 2 } },
942
+ { source: null, target: { id: 3 } },
943
+ ],
944
+ } as any,
945
+ new Map(),
946
+ );
947
+
948
+ const orderedIds = Array.from(res.sourceTargetsMap.keys()).map((k: any) => k.id);
949
+ // Expect Node Name A-group (ids 3,2 sorted by Customer ID 5 then 10) before Node Name B (id 1)
950
+ expect(orderedIds).toEqual([3, 2, 1]);
951
+ });
914
952
  });
915
953
 
916
954
  describe('fetchWIForQueryResult', () => {
@@ -952,6 +990,45 @@ describe('TicketsDataProvider', () => {
952
990
  });
953
991
  });
954
992
 
993
+ describe('parseFlatQueryResultForTableFormat', () => {
994
+ it('should order fetchedWorkItems by the query sortColumns (Node Name asc, Customer ID asc)', async () => {
995
+ const fieldsById: Record<number, any> = {
996
+ 1: { 'System.WorkItemType': 'Requirement', 'System.NodeName': 'B', CustomerRequirementId: 'C-20' },
997
+ 2: { 'System.WorkItemType': 'Requirement', 'System.NodeName': 'A', CustomerRequirementId: 'C-10' },
998
+ 3: { 'System.WorkItemType': 'Requirement', 'System.NodeName': 'A', CustomerRequirementId: 'C-5' },
999
+ };
1000
+ jest
1001
+ .spyOn(ticketsDataProvider as any, 'fetchWIForQueryResult')
1002
+ .mockImplementation(async (...args: any[]) => {
1003
+ const wi = args[0];
1004
+ return { id: wi.id, fields: fieldsById[wi.id] };
1005
+ });
1006
+
1007
+ const res = await (ticketsDataProvider as any).parseFlatQueryResultForTableFormat({
1008
+ queryType: QueryType.Flat,
1009
+ columns: [{ referenceName: 'CustomerRequirementId', name: 'CustomerRequirementId' }],
1010
+ sortColumns: [
1011
+ { field: { referenceName: 'System.NodeName' }, descending: false },
1012
+ { field: { referenceName: 'CustomerRequirementId' }, descending: false },
1013
+ ],
1014
+ workItems: [{ id: 1 }, { id: 2 }, { id: 3 }],
1015
+ } as any);
1016
+
1017
+ const orderedIds = res.fetchedWorkItems.map((wi: any) => wi.id);
1018
+ expect(orderedIds).toEqual([3, 2, 1]);
1019
+ });
1020
+
1021
+ it('should throw when workItems is an empty array', async () => {
1022
+ await expect(
1023
+ (ticketsDataProvider as any).parseFlatQueryResultForTableFormat({
1024
+ queryType: QueryType.Flat,
1025
+ columns: [],
1026
+ workItems: [],
1027
+ } as any),
1028
+ ).rejects.toThrow('No work items were found');
1029
+ });
1030
+ });
1031
+
955
1032
  describe('isFlatQueryAllowedByTypeOrId', () => {
956
1033
  it('should accept when allowedTypes is empty and WIQL references [System.WorkItemType]', async () => {
957
1034
  const wiql = "SELECT * FROM WorkItems WHERE [System.WorkItemType] = 'Bug'";