@@ -78,6 +78,102 @@ def mock_read_records(responses, default_response=None, **kwargs):
7878 )
7979
8080
81+ def test_check_stream_names_can_be_overridden_from_config ():
82+ static_stream = MagicMock (spec = Stream )
83+ static_stream .name = "static_stream"
84+ static_stream .availability_strategy = None
85+ selected_stream = MagicMock (spec = Stream )
86+ selected_stream .name = "selected_stream"
87+ selected_stream .availability_strategy = None
88+ selected_stream .read_records .return_value = iter ([record ])
89+ selected_stream .stream_slices .return_value = iter ([{}])
90+ source = MagicMock ()
91+ source .streams .return_value = [static_stream , selected_stream ]
92+
93+ check_stream = CheckStream (["static_stream" ], parameters = {})
94+
95+ assert check_stream .check_connection (
96+ source , logger , {"__airbyte_check_stream_names" : ["selected_stream" ]}
97+ ) == (True , None )
98+ static_stream .stream_slices .assert_not_called ()
99+
100+
101+ def test_check_stream_names_override_empty_list_falls_back_to_manifest_streams ():
102+ stream = MagicMock (spec = Stream )
103+ stream .name = "static_stream"
104+ stream .availability_strategy = None
105+ stream .read_records .return_value = iter ([record ])
106+ stream .stream_slices .return_value = iter ([{}])
107+ source = MagicMock ()
108+ source .streams .return_value = [stream ]
109+
110+ check_stream = CheckStream (["static_stream" ], parameters = {})
111+
112+ assert check_stream .check_connection (source , logger , {"__airbyte_check_stream_names" : []}) == (
113+ True ,
114+ None ,
115+ )
116+ stream .stream_slices .assert_called_once ()
117+
118+
119+ @pytest .mark .parametrize ("override" , ["selected_stream" , [1 ], ["selected_stream" , 1 ], None ])
120+ def test_check_stream_names_override_requires_list_of_strings (override ):
121+ stream = MagicMock (spec = Stream )
122+ stream .name = "selected_stream"
123+ stream .availability_strategy = None
124+ source = MagicMock ()
125+ source .streams .return_value = [stream ]
126+
127+ check_stream = CheckStream (["selected_stream" ], parameters = {})
128+
129+ with pytest .raises (ValueError , match = "__airbyte_check_stream_names must be a list of strings." ):
130+ check_stream .check_connection (source , logger , {"__airbyte_check_stream_names" : override })
131+
132+
133+ def test_check_stream_names_override_rejects_unknown_stream ():
134+ stream = MagicMock (spec = Stream )
135+ stream .name = "selected_stream"
136+ stream .availability_strategy = None
137+ source = MagicMock ()
138+ source .streams .return_value = [stream ]
139+
140+ check_stream = CheckStream (["selected_stream" ], parameters = {})
141+
142+ with pytest .raises (ValueError , match = "unknown_stream is not part of the catalog." ):
143+ check_stream .check_connection (
144+ source , logger , {"__airbyte_check_stream_names" : ["unknown_stream" ]}
145+ )
146+
147+
148+ def test_check_stream_names_override_returns_unavailable_stream_message ():
149+ stream = MagicMock (spec = Stream )
150+ stream .name = "selected_stream"
151+ stream .availability_strategy = None
152+ stream .stream_slices .return_value = iter ([])
153+ source = MagicMock ()
154+ source .streams .return_value = [stream ]
155+
156+ check_stream = CheckStream (["other_stream" ], parameters = {})
157+
158+ stream_is_available , reason = check_stream .check_connection (
159+ source , logger , {"__airbyte_check_stream_names" : ["selected_stream" ]}
160+ )
161+ assert not stream_is_available
162+ assert "no stream slices were found, likely because the parent stream is empty" in reason
163+
164+
165+ def test_check_stream_names_override_validates_before_stream_discovery ():
166+ source = MagicMock ()
167+ check_stream = CheckStream (["selected_stream" ], parameters = {})
168+
169+ with pytest .raises (ValueError , match = "__airbyte_check_stream_names must be a list of strings." ):
170+ check_stream .check_connection (
171+ source , logger , {"__airbyte_check_stream_names" : "selected_stream" }
172+ )
173+
174+ source .streams .assert_not_called ()
175+
176+
81177def test_check_empty_stream ():
82178 stream = MagicMock (spec = Stream )
83179 stream .name = "s1"
@@ -700,6 +796,57 @@ def test_check_stream1(
700796 assert connection_status .status == expected_result
701797
702798
799+ def test_check_empty_static_stream_override_falls_back_to_manifest_streams_and_checks_dynamic_streams ():
800+ manifest = {
801+ ** deepcopy (_MANIFEST_WITHOUT_CHECK_COMPONENT ),
802+ ** {
803+ "check" : {
804+ "type" : "CheckStream" ,
805+ "stream_names" : ["static_stream" ],
806+ "dynamic_streams_check_configs" : [
807+ {
808+ "type" : "DynamicStreamCheckConfig" ,
809+ "dynamic_stream_name" : "http_dynamic_stream" ,
810+ },
811+ ],
812+ }
813+ },
814+ }
815+ check_config = {** _CONFIG , "__airbyte_check_stream_names" : []}
816+
817+ with HttpMocker () as http_mocker :
818+ static_stream_request = HttpRequest (url = "https://api.test.com/static" )
819+ static_stream_response = HttpResponse (body = json .dumps ([]), status_code = 500 )
820+ http_mocker .get (static_stream_request , static_stream_response )
821+
822+ items_request = HttpRequest (url = "https://api.test.com/items" )
823+ items_response = HttpResponse (
824+ body = json .dumps ([{"id" : 1 , "name" : "item_1" }, {"id" : 2 , "name" : "item_2" }])
825+ )
826+ http_mocker .get (items_request , items_response )
827+
828+ item_request_1 = HttpRequest (url = "https://api.test.com/items/1" )
829+ item_response = HttpResponse (body = json .dumps ([]), status_code = 200 )
830+ http_mocker .get (item_request_1 , item_response )
831+
832+ item_request_2 = HttpRequest (url = "https://api.test.com/items/2" )
833+ item_response = HttpResponse (body = json .dumps ([]), status_code = 200 )
834+ http_mocker .get (item_request_2 , item_response )
835+
836+ source = ConcurrentDeclarativeSource (
837+ source_config = manifest ,
838+ config = check_config ,
839+ catalog = None ,
840+ state = None ,
841+ )
842+
843+ connection_status = source .check (logger , check_config )
844+
845+ http_mocker .assert_number_of_calls (static_stream_request , 6 )
846+ http_mocker .assert_number_of_calls (item_request_2 , 0 )
847+ assert connection_status .status == Status .FAILED
848+
849+
703850def test_check_stream_missing_fields ():
704851 """Test if ValueError is raised when dynamic_streams_check_configs is missing required fields."""
705852 manifest = {
0 commit comments