2525import pytest
2626
2727from airflow .models .dag import DAG
28+ from airflow .providers .common .compat .openlineage .facet import (
29+ Identifier ,
30+ SymlinksDatasetFacet ,
31+ )
2832from airflow .providers .google .cloud .transfers .local_to_gcs import LocalFilesystemToGCSOperator
2933
3034pytestmark = pytest .mark .db_test
@@ -72,7 +76,7 @@ def test_init(self):
7276 def test_execute (self , mock_hook ):
7377 mock_instance = mock_hook .return_value
7478 operator = LocalFilesystemToGCSOperator (
75- task_id = "gcs_to_file_sensor " ,
79+ task_id = "file_to_gcs_operator " ,
7680 dag = self .dag ,
7781 src = self .testfile1 ,
7882 dst = "test/test1.csv" ,
@@ -91,7 +95,7 @@ def test_execute(self, mock_hook):
9195 @pytest .mark .db_test
9296 def test_execute_with_empty_src (self ):
9397 operator = LocalFilesystemToGCSOperator (
94- task_id = "local_to_sensor " ,
98+ task_id = "file_to_gcs_operator " ,
9599 dag = self .dag ,
96100 src = "no_file.txt" ,
97101 dst = "test/no_file.txt" ,
@@ -104,7 +108,7 @@ def test_execute_with_empty_src(self):
104108 def test_execute_multiple (self , mock_hook ):
105109 mock_instance = mock_hook .return_value
106110 operator = LocalFilesystemToGCSOperator (
107- task_id = "gcs_to_file_sensor " , dag = self .dag , src = self .testfiles , dst = "test/" , ** self ._config
111+ task_id = "file_to_gcs_operator " , dag = self .dag , src = self .testfiles , dst = "test/" , ** self ._config
108112 )
109113 operator .execute (None )
110114 files_objects = zip (
@@ -127,7 +131,7 @@ def test_execute_multiple(self, mock_hook):
127131 def test_execute_wildcard (self , mock_hook ):
128132 mock_instance = mock_hook .return_value
129133 operator = LocalFilesystemToGCSOperator (
130- task_id = "gcs_to_file_sensor " , dag = self .dag , src = "/tmp/fake*.csv" , dst = "test/" , ** self ._config
134+ task_id = "file_to_gcs_operator " , dag = self .dag , src = "/tmp/fake*.csv" , dst = "test/" , ** self ._config
131135 )
132136 operator .execute (None )
133137 object_names = ["test/" + os .path .basename (fp ) for fp in glob ("/tmp/fake*.csv" )]
@@ -145,17 +149,80 @@ def test_execute_wildcard(self, mock_hook):
145149 ]
146150 mock_instance .upload .assert_has_calls (calls )
147151
152+ @pytest .mark .parametrize (
153+ ("src" , "dst" ),
154+ [
155+ ("/tmp/fake*.csv" , "test/test1.csv" ),
156+ ("/tmp/fake*.csv" , "test" ),
157+ ("/tmp/fake*.csv" , "test/dir" ),
158+ ],
159+ )
148160 @mock .patch ("airflow.providers.google.cloud.transfers.local_to_gcs.GCSHook" , autospec = True )
149- def test_execute_negative (self , mock_hook ):
161+ def test_execute_negative (self , mock_hook , src , dst ):
150162 mock_instance = mock_hook .return_value
151163 operator = LocalFilesystemToGCSOperator (
152- task_id = "gcs_to_file_sensor " ,
164+ task_id = "file_to_gcs_operator " ,
153165 dag = self .dag ,
154- src = "/tmp/fake*.csv" ,
155- dst = "test/test1.csv" ,
166+ src = src ,
167+ dst = dst ,
156168 ** self ._config ,
157169 )
158- print (glob ("/tmp/fake*.csv" ))
159170 with pytest .raises (ValueError ):
160171 operator .execute (None )
161172 mock_instance .assert_not_called ()
173+
174+ @pytest .mark .parametrize (
175+ ("src" , "dst" , "expected_input" , "expected_output" , "symlink" ),
176+ [
177+ ("/tmp/fake*.csv" , "test/" , "/tmp" , "test" , True ),
178+ ("/tmp/../tmp/fake*.csv" , "test/" , "/tmp" , "test" , True ),
179+ ("/tmp/fake1.csv" , "test/test1.csv" , "/tmp/fake1.csv" , "test/test1.csv" , False ),
180+ ("/tmp/fake1.csv" , "test/pre" , "/tmp/fake1.csv" , "test/pre" , False ),
181+ ],
182+ )
183+ def test_get_openlineage_facets_on_start_with_string_src (
184+ self , src , dst , expected_input , expected_output , symlink
185+ ):
186+ operator = LocalFilesystemToGCSOperator (
187+ task_id = "gcs_to_file_sensor" ,
188+ dag = self .dag ,
189+ src = src ,
190+ dst = dst ,
191+ ** self ._config ,
192+ )
193+ result = operator .get_openlineage_facets_on_start ()
194+ assert not result .job_facets
195+ assert not result .run_facets
196+ assert len (result .outputs ) == 1
197+ assert len (result .inputs ) == 1
198+ assert result .outputs [0 ].name == expected_output
199+ assert result .inputs [0 ].name == expected_input
200+ if symlink :
201+ assert result .inputs [0 ].facets ["symlink" ] == SymlinksDatasetFacet (
202+ identifiers = [Identifier (namespace = "file" , name = src , type = "file" )]
203+ )
204+
205+ @pytest .mark .parametrize (
206+ ("src" , "dst" , "expected_inputs" , "expected_output" ),
207+ [
208+ (["/tmp/fake1.csv" , "/tmp/fake2.csv" ], "test/" , ["/tmp/fake1.csv" , "/tmp/fake2.csv" ], "test" ),
209+ (["/tmp/fake1.csv" , "/tmp/fake2.csv" ], "" , ["/tmp/fake1.csv" , "/tmp/fake2.csv" ], "/" ),
210+ ],
211+ )
212+ def test_get_openlineage_facets_on_start_with_list_src (self , src , dst , expected_inputs , expected_output ):
213+ operator = LocalFilesystemToGCSOperator (
214+ task_id = "gcs_to_file_sensor" ,
215+ dag = self .dag ,
216+ src = src ,
217+ dst = dst ,
218+ ** self ._config ,
219+ )
220+ result = operator .get_openlineage_facets_on_start ()
221+ assert not result .job_facets
222+ assert not result .run_facets
223+ assert len (result .outputs ) == 1
224+ assert len (result .inputs ) == len (expected_inputs )
225+ assert result .outputs [0 ].name == expected_output
226+ assert result .outputs [0 ].namespace == "gs://dummy"
227+ assert all (inp .name in expected_inputs for inp in result .inputs )
228+ assert all (inp .namespace == "file" for inp in result .inputs )
0 commit comments