@elisra-devops/docgen-data-provider 1.109.1 → 1.110.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.
@@ -88,11 +88,109 @@ describe('TicketsDataProvider', () => {
88
88
  expect(result).toEqual({ tree1: null, tree2: null });
89
89
  });
90
90
  });
91
+ describe('fetchCustomerRequirementQueries', () => {
92
+ it('should return flat, tree, and oneHop query nodes without WIQL type filtering', async () => {
93
+ const root = {
94
+ id: 'root',
95
+ name: 'SYSRS',
96
+ isFolder: true,
97
+ hasChildren: true,
98
+ children: [
99
+ {
100
+ id: 'flat-query',
101
+ name: 'Flat Customer Pull',
102
+ isFolder: false,
103
+ hasChildren: false,
104
+ queryType: 'flat',
105
+ wiql: "SELECT * FROM WorkItems WHERE [System.WorkItemType] = 'Task'",
106
+ columns: [],
107
+ _links: { wiql: { href: 'flat-url' } },
108
+ },
109
+ {
110
+ id: 'tree-query',
111
+ name: 'Tree Customer Pull',
112
+ isFolder: false,
113
+ hasChildren: false,
114
+ queryType: 'tree',
115
+ wiql: "SELECT * FROM WorkItemLinks WHERE [System.Links.LinkType] = 'System.LinkTypes.Hierarchy-Forward'",
116
+ columns: [],
117
+ _links: { wiql: { href: 'tree-url' } },
118
+ },
119
+ {
120
+ id: 'onehop-query',
121
+ name: 'OneHop Customer Pull',
122
+ isFolder: false,
123
+ hasChildren: false,
124
+ queryType: 'oneHop',
125
+ wiql: "SELECT * FROM WorkItemLinks WHERE [System.Links.LinkType] = 'System.LinkTypes.Related'",
126
+ columns: [],
127
+ _links: { wiql: { href: 'onehop-url' } },
128
+ },
129
+ {
130
+ id: 'unsupported-query',
131
+ name: 'Unsupported',
132
+ isFolder: false,
133
+ hasChildren: false,
134
+ queryType: 'invalid',
135
+ _links: { wiql: { href: 'unsupported-url' } },
136
+ },
137
+ ],
138
+ };
139
+ const result = await ticketsDataProvider.fetchCustomerRequirementQueries(root, []);
140
+ expect(result.systemRequirementsQueryTree.children.map((child) => child.queryType)).toEqual([
141
+ 'flat',
142
+ 'tree',
143
+ 'oneHop',
144
+ ]);
145
+ expect(result.systemRequirementsQueryTree.children.map((child) => child.wiql.href)).toEqual([
146
+ 'flat-url',
147
+ 'tree-url',
148
+ 'onehop-url',
149
+ ]);
150
+ });
151
+ it('should include system-to-subsystem queries because customer discovery has no folder exclusions', async () => {
152
+ const root = {
153
+ id: 'root',
154
+ name: 'SYSRS',
155
+ isFolder: true,
156
+ hasChildren: true,
157
+ children: [
158
+ {
159
+ id: 'excluded-folder',
160
+ name: 'System To Subsystem',
161
+ isFolder: true,
162
+ hasChildren: true,
163
+ children: [
164
+ {
165
+ id: 'system-to-subsystem-query',
166
+ name: 'System To Subsystem Query',
167
+ isFolder: false,
168
+ hasChildren: false,
169
+ queryType: 'tree',
170
+ _links: { wiql: { href: 'system-to-subsystem-url' } },
171
+ },
172
+ ],
173
+ },
174
+ {
175
+ id: 'included-query',
176
+ name: 'Included Query',
177
+ isFolder: false,
178
+ hasChildren: false,
179
+ queryType: 'oneHop',
180
+ _links: { wiql: { href: 'included-url' } },
181
+ },
182
+ ],
183
+ };
184
+ const result = await ticketsDataProvider.fetchCustomerRequirementQueries(root);
185
+ expect(result.systemRequirementsQueryTree.children).toHaveLength(2);
186
+ expect(result.systemRequirementsQueryTree.children[0].children[0]).toEqual(expect.objectContaining({ id: 'system-to-subsystem-query', queryType: 'tree' }));
187
+ expect(result.systemRequirementsQueryTree.children[1]).toEqual(expect.objectContaining({ id: 'included-query', queryType: 'oneHop' }));
188
+ });
189
+ });
91
190
  describe('fetchSysRsQueries', () => {
92
- it('should use sysrs root, exclude trace folders from system requirements and resolve both trace directions', async () => {
191
+ it('should scan the dedicated sysrs folder for customer queries regardless of query type', async () => {
93
192
  const rootQueries = { id: 'root' };
94
193
  const sysRsRoot = { id: 'sysrs-root', name: 'SYSRS' };
95
- const subsystemToSystemFolder = { id: 'fwd-folder', name: 'System To Customer' };
96
194
  const systemToSubsystemFolder = { id: 'rev-folder', name: 'System To Subsystem' };
97
195
  const getDocTypeRootSpy = jest
98
196
  .spyOn(ticketsDataProvider, 'getDocTypeRoot')
@@ -100,42 +198,39 @@ describe('TicketsDataProvider', () => {
100
198
  const fetchSystemRequirementQueriesSpy = jest
101
199
  .spyOn(ticketsDataProvider, 'fetchSystemRequirementQueries')
102
200
  .mockResolvedValue({ systemRequirementsQueryTree: { id: 'system-tree' } });
103
- const fetchFlatUpstreamQueriesSpy = jest
104
- .spyOn(ticketsDataProvider, 'fetchFlatUpstreamQueries')
201
+ const fetchCustomerRequirementQueriesSpy = jest
202
+ .spyOn(ticketsDataProvider, 'fetchCustomerRequirementQueries')
105
203
  .mockResolvedValue({ systemRequirementsQueryTree: { id: 'customer-tree' } });
106
204
  const findChildFolderByPossibleNamesSpy = jest
107
205
  .spyOn(ticketsDataProvider, 'findChildFolderByPossibleNames')
108
- .mockResolvedValueOnce(subsystemToSystemFolder)
109
206
  .mockResolvedValueOnce(systemToSubsystemFolder);
110
207
  const fetchRequirementsTraceQueriesForFolderSpy = jest
111
208
  .spyOn(ticketsDataProvider, 'fetchRequirementsTraceQueriesForFolder')
112
- .mockResolvedValueOnce({ id: 'fwd-trace-tree' })
113
209
  .mockResolvedValueOnce({ id: 'rev-trace-tree' });
114
210
  const result = await ticketsDataProvider.fetchSysRsQueries(rootQueries);
115
211
  expect(getDocTypeRootSpy).toHaveBeenCalledWith(rootQueries, 'sysrs');
212
+ // fetchSystemRequirementQueries only excludes the sub-system trace folder;
213
+ // the legacy System-to-Customer exclusion list is gone.
116
214
  expect(fetchSystemRequirementQueriesSpy).toHaveBeenCalledWith(sysRsRoot, [
117
- 'system to customer',
118
- 'system-to-customer',
119
- 'system customer',
120
- 'subsystem to system',
121
- 'customer to system',
122
215
  'system to subsystem',
123
216
  'system-to-subsystem',
124
217
  'system subsystem',
125
218
  ]);
126
- expect(findChildFolderByPossibleNamesSpy).toHaveBeenNthCalledWith(1, sysRsRoot, expect.arrayContaining(['system to customer', 'subsystem to system', 'customer to system']));
127
- expect(findChildFolderByPossibleNamesSpy).toHaveBeenNthCalledWith(2, sysRsRoot, expect.arrayContaining(['system to subsystem', 'system-to-subsystem']));
128
- expect(fetchRequirementsTraceQueriesForFolderSpy).toHaveBeenNthCalledWith(1, subsystemToSystemFolder);
129
- expect(fetchRequirementsTraceQueriesForFolderSpy).toHaveBeenNthCalledWith(2, systemToSubsystemFolder);
130
- expect(fetchFlatUpstreamQueriesSpy).toHaveBeenCalledWith(subsystemToSystemFolder, [], ['requirement']);
219
+ // Only one folder lookup remains (sub-system trace); no System-to-Customer lookup.
220
+ expect(findChildFolderByPossibleNamesSpy).toHaveBeenCalledTimes(1);
221
+ expect(findChildFolderByPossibleNamesSpy).toHaveBeenCalledWith(sysRsRoot, expect.arrayContaining(['system to subsystem', 'system-to-subsystem']));
222
+ expect(fetchRequirementsTraceQueriesForFolderSpy).toHaveBeenCalledTimes(1);
223
+ expect(fetchRequirementsTraceQueriesForFolderSpy).toHaveBeenCalledWith(systemToSubsystemFolder);
224
+ // Customer picker scans the whole sysRsRoot with no query/folder exclusions.
225
+ expect(fetchCustomerRequirementQueriesSpy).toHaveBeenCalledWith(sysRsRoot);
131
226
  expect(result).toEqual({
132
227
  systemRequirementsQueries: { systemRequirementsQueryTree: { id: 'system-tree' } },
133
228
  customerRequirementsQueries: { systemRequirementsQueryTree: { id: 'customer-tree' } },
134
- subsystemToSystemRequirementsQueries: { id: 'fwd-trace-tree' },
229
+ subsystemToSystemRequirementsQueries: null,
135
230
  systemToSubsystemRequirementsQueries: { id: 'rev-trace-tree' },
136
231
  });
137
232
  });
138
- it('should return null customer/trace trees when trace folders are missing (no sysRsRoot fallback scan)', async () => {
233
+ it('should return null customer/trace trees when the dedicated sysrs folder is missing (no fallback scan)', async () => {
139
234
  const rootQueries = { id: 'root' };
140
235
  jest
141
236
  .spyOn(ticketsDataProvider, 'getDocTypeRoot')
@@ -143,32 +238,24 @@ describe('TicketsDataProvider', () => {
143
238
  const fetchSystemRequirementQueriesSpy = jest
144
239
  .spyOn(ticketsDataProvider, 'fetchSystemRequirementQueries')
145
240
  .mockResolvedValue({ systemRequirementsQueryTree: { id: 'system-tree' } });
146
- const fetchFlatUpstreamQueriesSpy = jest
147
- .spyOn(ticketsDataProvider, 'fetchFlatUpstreamQueries')
241
+ const fetchCustomerRequirementQueriesSpy = jest
242
+ .spyOn(ticketsDataProvider, 'fetchCustomerRequirementQueries')
148
243
  .mockResolvedValue({ systemRequirementsQueryTree: { id: 'should-not-be-used' } });
149
- jest
150
- .spyOn(ticketsDataProvider, 'findChildFolderByPossibleNames')
151
- .mockResolvedValueOnce(null)
152
- .mockResolvedValueOnce(null);
244
+ jest.spyOn(ticketsDataProvider, 'findChildFolderByPossibleNames').mockResolvedValueOnce(null);
153
245
  const fetchRequirementsTraceQueriesForFolderSpy = jest
154
246
  .spyOn(ticketsDataProvider, 'fetchRequirementsTraceQueriesForFolder')
155
- .mockResolvedValueOnce(null)
156
247
  .mockResolvedValueOnce(null);
157
248
  const result = await ticketsDataProvider.fetchSysRsQueries(rootQueries);
158
249
  expect(fetchSystemRequirementQueriesSpy).toHaveBeenCalledWith(rootQueries, [
159
- 'system to customer',
160
- 'system-to-customer',
161
- 'system customer',
162
- 'subsystem to system',
163
- 'customer to system',
164
250
  'system to subsystem',
165
251
  'system-to-subsystem',
166
252
  'system subsystem',
167
253
  ]);
168
- expect(fetchRequirementsTraceQueriesForFolderSpy).toHaveBeenNthCalledWith(1, null);
169
- expect(fetchRequirementsTraceQueriesForFolderSpy).toHaveBeenNthCalledWith(2, null);
170
- // No fallback scan of sysRsRoot when the System-to-Customer folder is absent.
171
- expect(fetchFlatUpstreamQueriesSpy).not.toHaveBeenCalled();
254
+ expect(fetchRequirementsTraceQueriesForFolderSpy).toHaveBeenCalledTimes(1);
255
+ expect(fetchRequirementsTraceQueriesForFolderSpy).toHaveBeenCalledWith(null);
256
+ // No fallback scan when the dedicated sysrs folder is absent — otherwise
257
+ // unrelated flat queries from the Shared Queries root would leak in.
258
+ expect(fetchCustomerRequirementQueriesSpy).not.toHaveBeenCalled();
172
259
  expect(result).toEqual({
173
260
  systemRequirementsQueries: { systemRequirementsQueryTree: { id: 'system-tree' } },
174
261
  customerRequirementsQueries: null,
@@ -1087,6 +1174,35 @@ describe('TicketsDataProvider', () => {
1087
1174
  // Assert
1088
1175
  expect(result).toBeDefined();
1089
1176
  });
1177
+ // Regression: WI_DEFAULT_FIELDS must include System.WorkItemType so that
1178
+ // downstream Requirement-type filters (e.g. the customer-coverage table)
1179
+ // do not drop every row because workItemType resolves to empty string.
1180
+ it('should request System.WorkItemType in default fields for flat queries and expose workItemType on parsed items', async () => {
1181
+ const mockWiqlHref = 'https://example.com/wiql';
1182
+ const mockQueryResult = {
1183
+ queryType: 'flat',
1184
+ workItems: [{ id: 42, url: 'https://example.com/wi/42' }],
1185
+ };
1186
+ const mockWiResponse = {
1187
+ fields: {
1188
+ 'System.Title': 'Customer Requirement',
1189
+ 'System.WorkItemType': 'Requirement',
1190
+ 'System.Description': 'Desc',
1191
+ },
1192
+ _links: { html: { href: 'https://example.com/wi/42' } },
1193
+ };
1194
+ tfs_1.TFSServices.getItemContent
1195
+ .mockResolvedValueOnce(mockQueryResult)
1196
+ .mockResolvedValueOnce(mockWiResponse);
1197
+ const result = await ticketsDataProvider.GetQueryResultsFromWiql(mockWiqlHref, false, new Map());
1198
+ // The per-item fetch URL must request System.WorkItemType.
1199
+ const perItemCall = tfs_1.TFSServices.getItemContent.mock.calls.find((call) => typeof call[0] === 'string' && call[0].startsWith('https://example.com/wi/42'));
1200
+ expect(perItemCall).toBeDefined();
1201
+ expect(perItemCall[0]).toContain('System.WorkItemType');
1202
+ // The parsed item must expose workItemType so downstream filters work.
1203
+ expect(Array.isArray(result)).toBe(true);
1204
+ expect(result[0]).toMatchObject({ id: 42, workItemType: 'Requirement' });
1205
+ });
1090
1206
  it('should return undefined when queryType is not supported', async () => {
1091
1207
  tfs_1.TFSServices.getItemContent.mockResolvedValueOnce({ queryType: 'Unknown' });
1092
1208
  const res = await ticketsDataProvider.GetQueryResultsFromWiql('https://example.com/wiql', false, new Map());