@@ -72,6 +72,7 @@ describe("configureTestPlanTools", () => {
7272 "testplan_update_test_case_steps" ,
7373 "testplan_list_test_cases" ,
7474 "testplan_show_test_results_from_build_id" ,
75+ "testplan_list_test_suites" ,
7576 ] )
7677 ) ;
7778 } ) ;
@@ -118,6 +119,269 @@ describe("configureTestPlanTools", () => {
118119 } ) ;
119120 } ) ;
120121
122+ describe ( "list_test_suites tool" , ( ) => {
123+ beforeEach ( ( ) => {
124+ ( mockTestPlanApi as any ) . getTestSuitesForPlan = jest . fn ( ) ;
125+ } ) ;
126+
127+ it ( "should call getTestSuitesForPlan and return properly nested hierarchy" , async ( ) => {
128+ configureTestPlanTools ( server , tokenProvider , connectionProvider ) ;
129+ const call = ( server . tool as jest . Mock ) . mock . calls . find ( ( [ toolName ] ) => toolName === "testplan_list_test_suites" ) ;
130+ if ( ! call ) throw new Error ( "testplan_list_test_suites tool not registered" ) ;
131+ const [ , , , handler ] = call ;
132+
133+ // Mock API response with flat list including nested suites
134+ ( ( mockTestPlanApi as any ) . getTestSuitesForPlan as jest . Mock ) . mockResolvedValue ( [
135+ {
136+ id : 100 ,
137+ name : "Root Suite" ,
138+ hasChildren : true ,
139+ children : [
140+ { id : 101 , name : "Child Suite 1" , parentSuite : { id : 100 } } ,
141+ { id : 102 , name : "Child Suite 2" , parentSuite : { id : 100 } } ,
142+ ] ,
143+ } ,
144+ {
145+ id : 101 ,
146+ name : "Child Suite 1" ,
147+ hasChildren : true ,
148+ parentSuite : { id : 100 } ,
149+ children : [ { id : 103 , name : "Grandchild Suite" , parentSuite : { id : 101 } } ] ,
150+ } ,
151+ {
152+ id : 102 ,
153+ name : "Child Suite 2" ,
154+ parentSuite : { id : 100 } ,
155+ } ,
156+ {
157+ id : 103 ,
158+ name : "Grandchild Suite" ,
159+ parentSuite : { id : 101 } ,
160+ } ,
161+ ] ) ;
162+
163+ const params = {
164+ project : "proj1" ,
165+ planId : 1 ,
166+ } ;
167+ const result = await handler ( params ) ;
168+
169+ expect ( ( mockTestPlanApi as any ) . getTestSuitesForPlan ) . toHaveBeenCalledWith ( "proj1" , 1 , 1 , undefined ) ;
170+
171+ // Parse and validate the nested structure
172+ const parsed = JSON . parse ( result . content [ 0 ] . text ) ;
173+ expect ( parsed ) . toHaveLength ( 1 ) ;
174+ expect ( parsed [ 0 ] ) . toMatchObject ( {
175+ id : 100 ,
176+ name : "Root Suite" ,
177+ children : [
178+ {
179+ id : 101 ,
180+ name : "Child Suite 1" ,
181+ children : [
182+ {
183+ id : 103 ,
184+ name : "Grandchild Suite" ,
185+ } ,
186+ ] ,
187+ } ,
188+ {
189+ id : 102 ,
190+ name : "Child Suite 2" ,
191+ } ,
192+ ] ,
193+ } ) ;
194+ } ) ;
195+
196+ it ( "should handle test suite with no children" , async ( ) => {
197+ configureTestPlanTools ( server , tokenProvider , connectionProvider ) ;
198+ const call = ( server . tool as jest . Mock ) . mock . calls . find ( ( [ toolName ] ) => toolName === "testplan_list_test_suites" ) ;
199+ if ( ! call ) throw new Error ( "testplan_list_test_suites tool not registered" ) ;
200+ const [ , , , handler ] = call ;
201+
202+ ( ( mockTestPlanApi as any ) . getTestSuitesForPlan as jest . Mock ) . mockResolvedValue ( [
203+ {
204+ id : 200 ,
205+ name : "Single Suite" ,
206+ hasChildren : false ,
207+ } ,
208+ ] ) ;
209+
210+ const params = {
211+ project : "proj1" ,
212+ planId : 2 ,
213+ } ;
214+ const result = await handler ( params ) ;
215+
216+ const parsed = JSON . parse ( result . content [ 0 ] . text ) ;
217+ expect ( parsed ) . toHaveLength ( 1 ) ;
218+ expect ( parsed [ 0 ] ) . toEqual ( {
219+ id : 200 ,
220+ name : "Single Suite" ,
221+ } ) ;
222+ } ) ;
223+
224+ it ( "should handle empty test suite list" , async ( ) => {
225+ configureTestPlanTools ( server , tokenProvider , connectionProvider ) ;
226+ const call = ( server . tool as jest . Mock ) . mock . calls . find ( ( [ toolName ] ) => toolName === "testplan_list_test_suites" ) ;
227+ if ( ! call ) throw new Error ( "testplan_list_test_suites tool not registered" ) ;
228+ const [ , , , handler ] = call ;
229+
230+ ( ( mockTestPlanApi as any ) . getTestSuitesForPlan as jest . Mock ) . mockResolvedValue ( [ ] ) ;
231+
232+ const params = {
233+ project : "proj1" ,
234+ planId : 3 ,
235+ } ;
236+ const result = await handler ( params ) ;
237+
238+ const parsed = JSON . parse ( result . content [ 0 ] . text ) ;
239+ expect ( parsed ) . toEqual ( [ ] ) ;
240+ } ) ;
241+
242+ it ( "should handle deeply nested suite hierarchy" , async ( ) => {
243+ configureTestPlanTools ( server , tokenProvider , connectionProvider ) ;
244+ const call = ( server . tool as jest . Mock ) . mock . calls . find ( ( [ toolName ] ) => toolName === "testplan_list_test_suites" ) ;
245+ if ( ! call ) throw new Error ( "testplan_list_test_suites tool not registered" ) ;
246+ const [ , , , handler ] = call ;
247+
248+ // Mock a deeply nested structure
249+ ( ( mockTestPlanApi as any ) . getTestSuitesForPlan as jest . Mock ) . mockResolvedValue ( [
250+ {
251+ id : 300 ,
252+ name : "Root" ,
253+ hasChildren : true ,
254+ children : [ { id : 301 , name : "Level 1" , parentSuite : { id : 300 } } ] ,
255+ } ,
256+ {
257+ id : 301 ,
258+ name : "Level 1" ,
259+ hasChildren : true ,
260+ parentSuite : { id : 300 } ,
261+ children : [ { id : 302 , name : "Level 2" , parentSuite : { id : 301 } } ] ,
262+ } ,
263+ {
264+ id : 302 ,
265+ name : "Level 2" ,
266+ hasChildren : true ,
267+ parentSuite : { id : 301 } ,
268+ children : [ { id : 303 , name : "Level 3" , parentSuite : { id : 302 } } ] ,
269+ } ,
270+ {
271+ id : 303 ,
272+ name : "Level 3" ,
273+ parentSuite : { id : 302 } ,
274+ } ,
275+ ] ) ;
276+
277+ const params = {
278+ project : "proj1" ,
279+ planId : 4 ,
280+ } ;
281+ const result = await handler ( params ) ;
282+
283+ const parsed = JSON . parse ( result . content [ 0 ] . text ) ;
284+ expect ( parsed [ 0 ] ) . toMatchObject ( {
285+ id : 300 ,
286+ name : "Root" ,
287+ children : [
288+ {
289+ id : 301 ,
290+ name : "Level 1" ,
291+ children : [
292+ {
293+ id : 302 ,
294+ name : "Level 2" ,
295+ children : [
296+ {
297+ id : 303 ,
298+ name : "Level 3" ,
299+ } ,
300+ ] ,
301+ } ,
302+ ] ,
303+ } ,
304+ ] ,
305+ } ) ;
306+ } ) ;
307+
308+ it ( "should handle API errors when listing test suites" , async ( ) => {
309+ configureTestPlanTools ( server , tokenProvider , connectionProvider ) ;
310+ const call = ( server . tool as jest . Mock ) . mock . calls . find ( ( [ toolName ] ) => toolName === "testplan_list_test_suites" ) ;
311+ if ( ! call ) throw new Error ( "testplan_list_test_suites tool not registered" ) ;
312+ const [ , , , handler ] = call ;
313+
314+ ( ( mockTestPlanApi as any ) . getTestSuitesForPlan as jest . Mock ) . mockRejectedValue ( new Error ( "API Error" ) ) ;
315+
316+ const params = {
317+ project : "proj1" ,
318+ planId : 5 ,
319+ } ;
320+ const result = await handler ( params ) ;
321+
322+ expect ( result . isError ) . toBe ( true ) ;
323+ expect ( result . content [ 0 ] . text ) . toContain ( "Error listing test suites: API Error" ) ;
324+ } ) ;
325+
326+ it ( "should pass continuation token when provided" , async ( ) => {
327+ configureTestPlanTools ( server , tokenProvider , connectionProvider ) ;
328+ const call = ( server . tool as jest . Mock ) . mock . calls . find ( ( [ toolName ] ) => toolName === "testplan_list_test_suites" ) ;
329+ if ( ! call ) throw new Error ( "testplan_list_test_suites tool not registered" ) ;
330+ const [ , , , handler ] = call ;
331+
332+ ( ( mockTestPlanApi as any ) . getTestSuitesForPlan as jest . Mock ) . mockResolvedValue ( [
333+ {
334+ id : 400 ,
335+ name : "Suite with Token" ,
336+ } ,
337+ ] ) ;
338+
339+ const params = {
340+ project : "proj1" ,
341+ planId : 6 ,
342+ continuationToken : "token123" ,
343+ } ;
344+ await handler ( params ) ;
345+
346+ expect ( ( mockTestPlanApi as any ) . getTestSuitesForPlan ) . toHaveBeenCalledWith ( "proj1" , 6 , 1 , "token123" ) ;
347+ } ) ;
348+
349+ it ( "should not include empty children arrays in output" , async ( ) => {
350+ configureTestPlanTools ( server , tokenProvider , connectionProvider ) ;
351+ const call = ( server . tool as jest . Mock ) . mock . calls . find ( ( [ toolName ] ) => toolName === "testplan_list_test_suites" ) ;
352+ if ( ! call ) throw new Error ( "testplan_list_test_suites tool not registered" ) ;
353+ const [ , , , handler ] = call ;
354+
355+ ( ( mockTestPlanApi as any ) . getTestSuitesForPlan as jest . Mock ) . mockResolvedValue ( [
356+ {
357+ id : 500 ,
358+ name : "Parent" ,
359+ hasChildren : true ,
360+ children : [ { id : 501 , name : "Child with no children" , parentSuite : { id : 500 } } ] ,
361+ } ,
362+ {
363+ id : 501 ,
364+ name : "Child with no children" ,
365+ parentSuite : { id : 500 } ,
366+ hasChildren : false ,
367+ } ,
368+ ] ) ;
369+
370+ const params = {
371+ project : "proj1" ,
372+ planId : 7 ,
373+ } ;
374+ const result = await handler ( params ) ;
375+
376+ const parsed = JSON . parse ( result . content [ 0 ] . text ) ;
377+ expect ( parsed [ 0 ] . children [ 0 ] ) . toEqual ( {
378+ id : 501 ,
379+ name : "Child with no children" ,
380+ } ) ;
381+ expect ( parsed [ 0 ] . children [ 0 ] . children ) . toBeUndefined ( ) ;
382+ } ) ;
383+ } ) ;
384+
121385 describe ( "create_test_plan tool" , ( ) => {
122386 it ( "should call createTestPlan with the correct parameters and return the expected result" , async ( ) => {
123387 configureTestPlanTools ( server , tokenProvider , connectionProvider ) ;
0 commit comments