@@ -878,6 +878,23 @@ def __call__(
878878 def serialized_dag (self ) -> SerializedDAG : ...
879879
880880
881+ def _delete_bundle_if_unreferenced (session , bundle_name ):
882+ """Delete a DagBundleModel row, but only once no DagModel still references it.
883+
884+ ``DagModel.bundle_name`` is a foreign key with no ``ON DELETE`` action, and the bundle is
885+ shared across tests, so it can only be dropped after the last referencing Dag is gone.
886+ """
887+ from sqlalchemy import delete , func , select
888+
889+ from airflow .models .dag import DagModel
890+ from airflow .models .dagbundle import DagBundleModel
891+
892+ if not session .scalar (
893+ select (func .count ()).select_from (DagModel ).where (DagModel .bundle_name == bundle_name )
894+ ):
895+ session .execute (delete (DagBundleModel ).where (DagBundleModel .name == bundle_name ))
896+
897+
881898@pytest .fixture
882899def dag_maker (request ) -> Generator [DagMaker , None , None ]:
883900 """
@@ -946,6 +963,7 @@ def __init__(self):
946963 self .dagbag = DagBag (os .devnull )
947964 else :
948965 self .dagbag = DagBag (os .devnull , include_examples = False ) # type: ignore[call-arg]
966+ self .created_bundle_names : set [str ] = set ()
949967
950968 def __enter__ (self ):
951969 self .serialized_model = None
@@ -1380,6 +1398,7 @@ def __call__(
13801398 ):
13811399 self .session .add (DagBundleModel (name = self .bundle_name ))
13821400 self .session .commit ()
1401+ self .created_bundle_names .add (self .bundle_name )
13831402
13841403 return self
13851404
@@ -1424,6 +1443,9 @@ def cleanup(self):
14241443 self .session .execute (delete (DagModel ).where (DagModel .dag_id .in_ (dag_ids )))
14251444 self .session .execute (delete (TaskMap ).where (TaskMap .dag_id .in_ (dag_ids )))
14261445 self .session .execute (delete (AssetEvent ).where (AssetEvent .source_dag_id .in_ (dag_ids )))
1446+ if AIRFLOW_V_3_0_PLUS :
1447+ for bundle_name in self .created_bundle_names :
1448+ _delete_bundle_if_unreferenced (self .session , bundle_name )
14271449 self .session .commit ()
14281450 if self ._own_session :
14291451 self .session .expunge_all ()
@@ -1743,6 +1765,8 @@ def session():
17431765
17441766@pytest .fixture
17451767def get_test_dag ():
1768+ created = {"bundle" : False , "import_error_files" : set ()}
1769+
17461770 def _get (dag_id : str ):
17471771 from airflow import settings
17481772 from airflow .models .serialized_dag import SerializedDagModel
@@ -1784,6 +1808,7 @@ def _get(dag_id: str):
17841808 stacktrace = stacktrace ,
17851809 )
17861810 )
1811+ created ["import_error_files" ].add (str (dag_file ))
17871812
17881813 return
17891814
@@ -1798,6 +1823,7 @@ def _get(dag_id: str):
17981823 session = settings .Session ()
17991824 if not session .scalar (select (func .count ()).where (DagBundleModel .name == "testing" )):
18001825 session .add (DagBundleModel (name = "testing" ))
1826+ created ["bundle" ] = True
18011827 session .flush ()
18021828 SerializedDAG .bulk_write_to_db ("testing" , None , [dag ], session = session )
18031829 session .commit ()
@@ -1808,7 +1834,25 @@ def _get(dag_id: str):
18081834
18091835 return dag
18101836
1811- return _get
1837+ yield _get
1838+
1839+ from tests_common .test_utils .version_compat import AIRFLOW_V_3_0_PLUS
1840+
1841+ if not AIRFLOW_V_3_0_PLUS :
1842+ return
1843+
1844+ from sqlalchemy import delete
1845+
1846+ from airflow .models .errors import ParseImportError
1847+ from airflow .utils .session import create_session
1848+
1849+ with create_session () as session :
1850+ if created ["import_error_files" ]:
1851+ session .execute (
1852+ delete (ParseImportError ).where (ParseImportError .filename .in_ (created ["import_error_files" ]))
1853+ )
1854+ if created ["bundle" ]:
1855+ _delete_bundle_if_unreferenced (session , "testing" )
18121856
18131857
18141858@pytest .fixture
@@ -2918,40 +2962,60 @@ def mock_xcom_backend():
29182962def testing_dag_bundle ():
29192963 from tests_common .test_utils .version_compat import AIRFLOW_V_3_0_PLUS
29202964
2921- if AIRFLOW_V_3_0_PLUS :
2922- from sqlalchemy import func , select
2965+ if not AIRFLOW_V_3_0_PLUS :
2966+ yield
2967+ return
2968+
2969+ from sqlalchemy import func , select
29232970
2924- from airflow .models .dagbundle import DagBundleModel
2925- from airflow .utils .session import create_session
2971+ from airflow .models .dagbundle import DagBundleModel
2972+ from airflow .utils .session import create_session
29262973
2974+ created = False
2975+ with create_session () as session :
2976+ if (
2977+ session .scalar (
2978+ select (func .count ()).select_from (DagBundleModel ).where (DagBundleModel .name == "testing" )
2979+ )
2980+ == 0
2981+ ):
2982+ session .add (DagBundleModel (name = "testing" ))
2983+ created = True
2984+
2985+ yield
2986+
2987+ if created :
29272988 with create_session () as session :
2928- if (
2929- session .scalar (
2930- select (func .count ()).select_from (DagBundleModel ).where (DagBundleModel .name == "testing" )
2931- )
2932- == 0
2933- ):
2934- testing = DagBundleModel (name = "testing" )
2935- session .add (testing )
2989+ _delete_bundle_if_unreferenced (session , "testing" )
29362990
29372991
29382992@pytest .fixture
29392993def testing_team ():
29402994 from tests_common .test_utils .version_compat import AIRFLOW_V_3_0_PLUS
29412995
2942- if AIRFLOW_V_3_0_PLUS :
2943- from sqlalchemy import select
2996+ if not AIRFLOW_V_3_0_PLUS :
2997+ yield None
2998+ return
29442999
2945- from airflow .models .team import Team
2946- from airflow .utils .session import create_session
3000+ from sqlalchemy import delete , select
29473001
3002+ from airflow .models .team import Team
3003+ from airflow .utils .session import create_session
3004+
3005+ created = False
3006+ with create_session () as session :
3007+ team = session .scalar (select (Team ).where (Team .name == "testing" ))
3008+ if not team :
3009+ team = Team (name = "testing" )
3010+ session .add (team )
3011+ session .commit ()
3012+ created = True
3013+ yield team
3014+
3015+ if created :
3016+ # FKs to team.name are CASCADE / SET NULL, so deleting the row is safe.
29483017 with create_session () as session :
2949- team = session .scalar (select (Team ).where (Team .name == "testing" ))
2950- if not team :
2951- team = Team (name = "testing" )
2952- session .add (team )
2953- session .flush ()
2954- yield team
3018+ session .execute (delete (Team ).where (Team .name == "testing" ))
29553019
29563020
29573021@pytest .fixture
0 commit comments