@@ -640,6 +640,70 @@ async def test_get_operation_lineage_with_symlinks(
640
640
}
641
641
642
642
643
+ async def test_get_operation_lineage_with_symlink_without_input_output (
644
+ test_client : AsyncClient ,
645
+ async_session : AsyncSession ,
646
+ lineage_with_unconnected_symlinks : LineageResult ,
647
+ mocked_user : MockedUser ,
648
+ ):
649
+ lineage = lineage_with_unconnected_symlinks
650
+ operation = lineage .operations [0 ]
651
+
652
+ inputs = [input for input in lineage .inputs if input .operation_id == operation .id ]
653
+ assert inputs
654
+
655
+ outputs = [output for output in lineage .outputs if output .operation_id == operation .id ]
656
+ assert outputs
657
+
658
+ dataset_ids = {input .dataset_id for input in inputs } | {output .dataset_id for output in outputs }
659
+ assert dataset_ids
660
+
661
+ datasets = [dataset for dataset in lineage .datasets if dataset .id in dataset_ids ]
662
+ assert datasets
663
+
664
+ run = next (run for run in lineage .runs if run .id == operation .run_id )
665
+ job = next (job for job in lineage .jobs if job .id == run .job_id )
666
+
667
+ [job ] = await enrich_jobs ([job ], async_session )
668
+ [run ] = await enrich_runs ([run ], async_session )
669
+ datasets = await enrich_datasets (datasets , async_session )
670
+
671
+ response = await test_client .get (
672
+ "v1/operations/lineage" ,
673
+ headers = {"Authorization" : f"Bearer { mocked_user .access_token } " },
674
+ params = {
675
+ "since" : run .created_at .isoformat (),
676
+ "start_node_id" : str (operation .id ),
677
+ },
678
+ )
679
+
680
+ assert response .status_code == HTTPStatus .OK , response .json ()
681
+ assert response .json () == {
682
+ "relations" : {
683
+ "parents" : run_parents_to_json ([run ]) + operation_parents_to_json ([operation ]),
684
+ "symlinks" : [], # symlinks without inputs/outputs are excluded
685
+ "inputs" : [
686
+ * inputs_to_json (merge_io_by_jobs (inputs ), granularity = "JOB" ),
687
+ * inputs_to_json (inputs , granularity = "OPERATION" ),
688
+ * inputs_to_json (merge_io_by_runs (inputs ), granularity = "RUN" ),
689
+ ],
690
+ "outputs" : [
691
+ * outputs_to_json (merge_io_by_jobs (outputs ), granularity = "JOB" ),
692
+ * outputs_to_json (outputs , granularity = "OPERATION" ),
693
+ * outputs_to_json (merge_io_by_runs (outputs ), granularity = "RUN" ),
694
+ ],
695
+ "direct_column_lineage" : [],
696
+ "indirect_column_lineage" : [],
697
+ },
698
+ "nodes" : {
699
+ "datasets" : datasets_to_json (datasets ),
700
+ "jobs" : jobs_to_json ([job ]),
701
+ "runs" : runs_to_json ([run ]),
702
+ "operations" : operations_to_json ([operation ]),
703
+ },
704
+ }
705
+
706
+
643
707
async def test_get_operation_lineage_with_empty_io_stats_and_schema (
644
708
test_client : AsyncClient ,
645
709
async_session : AsyncSession ,
0 commit comments