|
84 | 84 | from airflow.providers.standard.operators.bash import BashOperator |
85 | 85 | from airflow.providers.standard.operators.empty import EmptyOperator |
86 | 86 | from airflow.providers.standard.triggers.file import FileDeleteTrigger |
87 | | -from airflow.sdk import DAG, Asset, AssetAlias, AssetWatcher, IdentityMapper, task |
| 87 | +from airflow.sdk import DAG, Asset, AssetAlias, AssetWatcher, HourlyMapper, IdentityMapper, task |
88 | 88 | from airflow.sdk.definitions.callback import AsyncCallback, SyncCallback |
89 | 89 | from airflow.sdk.definitions.timetables.assets import PartitionedAssetTimetable |
90 | 90 | from airflow.serialization.definitions.dag import SerializedDAG |
@@ -8784,6 +8784,76 @@ def _produce_and_register_asset_event( |
8784 | 8784 | return apdr |
8785 | 8785 |
|
8786 | 8786 |
|
| 8787 | +@pytest.mark.need_serialized_dag |
| 8788 | +@pytest.mark.usefixtures("clear_asset_partition_rows") |
| 8789 | +def test_partitioned_dag_run_with_invalid_mapping( |
| 8790 | + dag_maker: DagMaker, |
| 8791 | + session: Session, |
| 8792 | +): |
| 8793 | + asset_1 = Asset(name="asset-1") |
| 8794 | + with dag_maker( |
| 8795 | + dag_id="asset-event-consumer", |
| 8796 | + schedule=PartitionedAssetTimetable( |
| 8797 | + assets=asset_1, |
| 8798 | + default_partition_mapper=HourlyMapper(), |
| 8799 | + ), |
| 8800 | + session=session, |
| 8801 | + ): |
| 8802 | + EmptyOperator(task_id="hi") |
| 8803 | + session.commit() |
| 8804 | + |
| 8805 | + runner = SchedulerJobRunner( |
| 8806 | + job=Job(job_type=SchedulerJobRunner.job_type), executors=[MockExecutor(do_update=False)] |
| 8807 | + ) |
| 8808 | + with dag_maker(dag_id="asset-event-producer", schedule=None, session=session) as dag: |
| 8809 | + EmptyOperator(task_id="hi", outlets=[asset_1]) |
| 8810 | + |
| 8811 | + partition_key = "an invalid key for HourlyMapper" |
| 8812 | + dr = dag_maker.create_dagrun(partition_key=partition_key, session=session) |
| 8813 | + [ti] = dr.get_task_instances(session=session) |
| 8814 | + session.commit() |
| 8815 | + |
| 8816 | + serialized_outlets = dag.get_task("hi").outlets |
| 8817 | + TaskInstance.register_asset_changes_in_db( |
| 8818 | + ti=ti, |
| 8819 | + task_outlets=[o.asprofile() for o in serialized_outlets], |
| 8820 | + outlet_events=[], |
| 8821 | + session=session, |
| 8822 | + ) |
| 8823 | + session.commit() |
| 8824 | + event = session.scalar( |
| 8825 | + select(AssetEvent).where( |
| 8826 | + AssetEvent.source_dag_id == dag.dag_id, |
| 8827 | + AssetEvent.source_run_id == dr.run_id, |
| 8828 | + ) |
| 8829 | + ) |
| 8830 | + assert event is not None |
| 8831 | + assert event.partition_key == partition_key |
| 8832 | + apdr = session.scalar( |
| 8833 | + select(AssetPartitionDagRun) |
| 8834 | + .join( |
| 8835 | + PartitionedAssetKeyLog, |
| 8836 | + PartitionedAssetKeyLog.asset_partition_dag_run_id == AssetPartitionDagRun.id, |
| 8837 | + ) |
| 8838 | + .where(PartitionedAssetKeyLog.asset_event_id == event.id) |
| 8839 | + ) |
| 8840 | + assert apdr is None |
| 8841 | + |
| 8842 | + partition_dags = runner._create_dagruns_for_partitioned_asset_dags(session=session) |
| 8843 | + assert len(partition_dags) == 0 |
| 8844 | + assert partition_dags == set() |
| 8845 | + |
| 8846 | + audit_log = session.scalar(select(Log)) |
| 8847 | + assert audit_log is not None |
| 8848 | + assert audit_log.extra == ( |
| 8849 | + "Could not map partition_key 'an invalid key for HourlyMapper' " |
| 8850 | + "for asset (name='asset-1', uri='asset-1') in target Dag 'asset-event-consumer'. " |
| 8851 | + "This likely indicates that the partition mapper in the target Dag is misconfigured or " |
| 8852 | + "does not support this partition key.\n" |
| 8853 | + "ValueError: time data 'an invalid key for HourlyMapper' does not match format '%Y-%m-%dT%H:%M:%S'" |
| 8854 | + ) |
| 8855 | + |
| 8856 | + |
8787 | 8857 | @pytest.mark.need_serialized_dag |
8788 | 8858 | @pytest.mark.usefixtures("clear_asset_partition_rows") |
8789 | 8859 | def test_partitioned_dag_run_with_customized_mapper( |
|
0 commit comments