@@ -202,3 +202,105 @@ async def test_get_job_dependencies_with_direction_downstream(
202202 },
203203 "nodes" : {"jobs" : jobs_to_json (expected_nodes )},
204204 }
205+
206+
207+ @pytest .mark .parametrize (
208+ ["depth" , "direction" , "expected_dep_indices" , "expected_job_indices" ],
209+ [
210+ (1 , "DOWNSTREAM" , [(2 , 3 )], [2 , 3 ]),
211+ (2 , "DOWNSTREAM" , [(2 , 3 ), (3 , 4 )], [2 , 3 , 4 ]),
212+ (1 , "UPSTREAM" , [(1 , 2 )], [1 , 2 ]),
213+ (2 , "UPSTREAM" , [(0 , 1 ), (1 , 2 )], [0 , 1 , 2 ]),
214+ (1 , "BOTH" , [(1 , 2 ), (2 , 3 )], [1 , 2 , 3 ]),
215+ (2 , "BOTH" , [(0 , 1 ), (1 , 2 ), (2 , 3 ), (3 , 4 )], [0 , 1 , 2 , 3 , 4 ]),
216+ (5 , "BOTH" , [(0 , 1 ), (1 , 2 ), (2 , 3 ), (3 , 4 )], [0 , 1 , 2 , 3 , 4 ]),
217+ ],
218+ ids = [
219+ "depth_1-downstream" ,
220+ "depth_2-downstream" ,
221+ "depth_1-upstream" ,
222+ "depth_2-upstream" ,
223+ "depth_1-both" ,
224+ "depth_2-both" ,
225+ "depth_5-both" ,
226+ ],
227+ )
228+ async def test_get_job_dependencies_with_depth (
229+ test_client : AsyncClient ,
230+ job_dependency_depth_chain : tuple [Job , ...],
231+ async_session : AsyncSession ,
232+ mocked_user : MockedUser ,
233+ depth : int ,
234+ direction : str ,
235+ expected_dep_indices : list [tuple [int , int ]],
236+ expected_job_indices : list [int ],
237+ ):
238+ """
239+ Fixture chain: job_0 → job_1 → job_2 → job_3 → job_4
240+ Start node is always job_2 (middle of the chain).
241+ """
242+ jobs = job_dependency_depth_chain
243+ start_job = jobs [2 ]
244+
245+ expected_jobs = await enrich_jobs ([jobs [i ] for i in expected_job_indices ], async_session )
246+
247+ response = await test_client .get (
248+ "v1/jobs/dependencies" ,
249+ headers = {"Authorization" : f"Bearer { mocked_user .access_token } " },
250+ params = {"start_node_id" : start_job .id , "depth" : depth , "direction" : direction },
251+ )
252+ assert response .status_code == HTTPStatus .OK , response .json ()
253+ assert response .json () == {
254+ "relations" : {
255+ "parents" : [],
256+ "dependencies" : [
257+ {
258+ "from" : {"kind" : "JOB" , "id" : str (jobs [i ].id )},
259+ "to" : {"kind" : "JOB" , "id" : str (jobs [j ].id )},
260+ "type" : "DIRECT_DEPENDENCY" ,
261+ }
262+ for i , j in sorted (expected_dep_indices )
263+ ],
264+ },
265+ "nodes" : {"jobs" : jobs_to_json (expected_jobs )},
266+ }
267+
268+
269+ @pytest .mark .parametrize (
270+ ["direction" , "start_node_index" ],
271+ [
272+ ("UPSTREAM" , 0 ),
273+ ("DOWNSTREAM" , 4 ),
274+ ],
275+ ids = ["upstream_boundary" , "downstream_boundary" ],
276+ )
277+ async def test_get_job_dependencies_with_depth_on_boundary (
278+ test_client : AsyncClient ,
279+ job_dependency_depth_chain : tuple [Job , ...],
280+ async_session : AsyncSession ,
281+ mocked_user : MockedUser ,
282+ direction : str ,
283+ start_node_index : int ,
284+ ):
285+ """
286+ Fixture chain: job_0 → job_1 → job_2 → job_3 → job_4
287+ Start node is job_0 or job_4.
288+ """
289+ jobs = job_dependency_depth_chain
290+ start_job = jobs [start_node_index ]
291+
292+ [expected_job ] = await enrich_jobs ([start_job ], async_session )
293+
294+ response = await test_client .get (
295+ "v1/jobs/dependencies" ,
296+ headers = {"Authorization" : f"Bearer { mocked_user .access_token } " },
297+ params = {"start_node_id" : start_job .id , "depth" : 2 , "direction" : direction },
298+ )
299+ assert response .status_code == HTTPStatus .OK , response .json ()
300+ assert response .json () == {
301+ "relations" : {
302+ "parents" : [],
303+ "dependencies" : [],
304+ },
305+ "nodes" : {"jobs" : jobs_to_json ([expected_job ])},
306+ }
0 commit comments