6
6
import json
7
7
from datetime import datetime , timedelta , timezone
8
8
from typing import Any , Dict , Iterable , List , Mapping , Optional , Tuple , Union
9
+ from unittest .mock import patch
9
10
10
11
import freezegun
11
12
import isodate
@@ -647,8 +648,7 @@ def test_group_streams():
647
648
source = ConcurrentDeclarativeSource (
648
649
source_config = _MANIFEST , config = _CONFIG , catalog = catalog , state = state
649
650
)
650
- concurrent_streams = source ._concurrent_streams
651
- synchronous_streams = source ._synchronous_streams
651
+ concurrent_streams , synchronous_streams = source ._group_streams (config = _CONFIG )
652
652
653
653
# 1 full refresh stream, 2 incremental streams, 1 substream w/o incremental, 1 list based substream w/o incremental
654
654
assert len (concurrent_streams ) == 5
@@ -705,8 +705,9 @@ def test_create_concurrent_cursor():
705
705
source = ConcurrentDeclarativeSource (
706
706
source_config = _MANIFEST , config = _CONFIG , catalog = _CATALOG , state = state
707
707
)
708
+ concurrent_streams , synchronous_streams = source ._group_streams (config = _CONFIG )
708
709
709
- party_members_stream = source . _concurrent_streams [0 ]
710
+ party_members_stream = concurrent_streams [0 ]
710
711
assert isinstance (party_members_stream , DefaultStream )
711
712
party_members_cursor = party_members_stream .cursor
712
713
@@ -722,7 +723,7 @@ def test_create_concurrent_cursor():
722
723
assert party_members_cursor ._lookback_window == timedelta (days = 5 )
723
724
assert party_members_cursor ._cursor_granularity == timedelta (days = 1 )
724
725
725
- locations_stream = source . _concurrent_streams [2 ]
726
+ locations_stream = concurrent_streams [2 ]
726
727
assert isinstance (locations_stream , DefaultStream )
727
728
locations_cursor = locations_stream .cursor
728
729
@@ -866,7 +867,21 @@ def _mock_party_members_skills_requests(http_mocker: HttpMocker) -> None:
866
867
)
867
868
868
869
870
+ def mocked_init (self , is_sequential_state : bool = True ):
871
+ """
872
+ This method is used to patch the existing __init__() function and always set is_sequential_state to
873
+ false. This is required because we want to test the concurrent state format. And because streams are
874
+ created under the hood of the read/discover/check command, we have no way of setting the field without
875
+ patching __init__()
876
+ """
877
+ self ._is_sequential_state = False
878
+
879
+
869
880
@freezegun .freeze_time (_NOW )
881
+ @patch (
882
+ "airbyte_cdk.sources.streams.concurrent.state_converters.abstract_stream_state_converter.AbstractStreamStateConverter.__init__" ,
883
+ mocked_init ,
884
+ )
870
885
def test_read_with_concurrent_and_synchronous_streams ():
871
886
"""
872
887
Verifies that a ConcurrentDeclarativeSource processes concurrent streams followed by synchronous streams
@@ -879,7 +894,6 @@ def test_read_with_concurrent_and_synchronous_streams():
879
894
source = ConcurrentDeclarativeSource (
880
895
source_config = _MANIFEST , config = _CONFIG , catalog = _CATALOG , state = None
881
896
)
882
- disable_emitting_sequential_state_messages (source = source )
883
897
884
898
with HttpMocker () as http_mocker :
885
899
_mock_party_members_requests (http_mocker , _NO_STATE_PARTY_MEMBERS_SLICES_AND_RESPONSES )
@@ -959,6 +973,10 @@ def test_read_with_concurrent_and_synchronous_streams():
959
973
960
974
961
975
@freezegun .freeze_time (_NOW )
976
+ @patch (
977
+ "airbyte_cdk.sources.streams.concurrent.state_converters.abstract_stream_state_converter.AbstractStreamStateConverter.__init__" ,
978
+ mocked_init ,
979
+ )
962
980
def test_read_with_concurrent_and_synchronous_streams_with_concurrent_state ():
963
981
"""
964
982
Verifies that a ConcurrentDeclarativeSource processes concurrent streams correctly using the incoming
@@ -1016,7 +1034,6 @@ def test_read_with_concurrent_and_synchronous_streams_with_concurrent_state():
1016
1034
source = ConcurrentDeclarativeSource (
1017
1035
source_config = _MANIFEST , config = _CONFIG , catalog = _CATALOG , state = state
1018
1036
)
1019
- disable_emitting_sequential_state_messages (source = source )
1020
1037
1021
1038
with HttpMocker () as http_mocker :
1022
1039
_mock_party_members_requests (http_mocker , party_members_slices_and_responses )
@@ -1080,6 +1097,10 @@ def test_read_with_concurrent_and_synchronous_streams_with_concurrent_state():
1080
1097
1081
1098
1082
1099
@freezegun .freeze_time (_NOW )
1100
+ @patch (
1101
+ "airbyte_cdk.sources.streams.concurrent.state_converters.abstract_stream_state_converter.AbstractStreamStateConverter.__init__" ,
1102
+ mocked_init ,
1103
+ )
1083
1104
def test_read_with_concurrent_and_synchronous_streams_with_sequential_state ():
1084
1105
"""
1085
1106
Verifies that a ConcurrentDeclarativeSource processes concurrent streams correctly using the incoming
@@ -1105,7 +1126,6 @@ def test_read_with_concurrent_and_synchronous_streams_with_sequential_state():
1105
1126
source = ConcurrentDeclarativeSource (
1106
1127
source_config = _MANIFEST , config = _CONFIG , catalog = _CATALOG , state = state
1107
1128
)
1108
- disable_emitting_sequential_state_messages (source = source )
1109
1129
1110
1130
party_members_slices_and_responses = _NO_STATE_PARTY_MEMBERS_SLICES_AND_RESPONSES + [
1111
1131
(
@@ -1204,6 +1224,10 @@ def test_read_with_concurrent_and_synchronous_streams_with_sequential_state():
1204
1224
1205
1225
1206
1226
@freezegun .freeze_time (_NOW )
1227
+ @patch (
1228
+ "airbyte_cdk.sources.streams.concurrent.state_converters.abstract_stream_state_converter.AbstractStreamStateConverter.__init__" ,
1229
+ mocked_init ,
1230
+ )
1207
1231
def test_read_concurrent_with_failing_partition_in_the_middle ():
1208
1232
"""
1209
1233
Verify that partial state is emitted when only some partitions are successful during a concurrent sync attempt
@@ -1236,7 +1260,6 @@ def test_read_concurrent_with_failing_partition_in_the_middle():
1236
1260
source = ConcurrentDeclarativeSource (
1237
1261
source_config = _MANIFEST , config = _CONFIG , catalog = catalog , state = []
1238
1262
)
1239
- disable_emitting_sequential_state_messages (source = source )
1240
1263
1241
1264
location_slices = [
1242
1265
{"start" : "2024-07-01" , "end" : "2024-07-31" },
@@ -1263,6 +1286,10 @@ def test_read_concurrent_with_failing_partition_in_the_middle():
1263
1286
1264
1287
1265
1288
@freezegun .freeze_time (_NOW )
1289
+ @patch (
1290
+ "airbyte_cdk.sources.streams.concurrent.state_converters.abstract_stream_state_converter.AbstractStreamStateConverter.__init__" ,
1291
+ mocked_init ,
1292
+ )
1266
1293
def test_read_concurrent_skip_streams_not_in_catalog ():
1267
1294
"""
1268
1295
Verifies that the ConcurrentDeclarativeSource only syncs streams that are specified in the incoming ConfiguredCatalog
@@ -1311,8 +1338,6 @@ def test_read_concurrent_skip_streams_not_in_catalog():
1311
1338
# palaces requests
1312
1339
http_mocker .get (HttpRequest ("https://persona.metaverse.com/palaces" ), _PALACES_RESPONSE )
1313
1340
1314
- disable_emitting_sequential_state_messages (source = source )
1315
-
1316
1341
messages = list (
1317
1342
source .read (logger = source .logger , config = _CONFIG , catalog = catalog , state = [])
1318
1343
)
@@ -1429,11 +1454,12 @@ def test_streams_with_stream_state_interpolation_should_be_synchronous():
1429
1454
catalog = _CATALOG ,
1430
1455
state = None ,
1431
1456
)
1457
+ concurrent_streams , synchronous_streams = source ._group_streams (config = _CONFIG )
1432
1458
1433
1459
# 1 full refresh stream, 2 with parent stream without incremental dependency
1434
- assert len (source . _concurrent_streams ) == 3
1460
+ assert len (concurrent_streams ) == 3
1435
1461
# 2 incremental stream with interpolation on state (locations and party_members), 1 incremental with parent stream (palace_enemies), 1 stream with async retriever
1436
- assert len (source . _synchronous_streams ) == 4
1462
+ assert len (synchronous_streams ) == 4
1437
1463
1438
1464
1439
1465
def test_given_partition_routing_and_incremental_sync_then_stream_is_not_concurrent ():
@@ -1569,9 +1595,10 @@ def test_given_partition_routing_and_incremental_sync_then_stream_is_not_concurr
1569
1595
source = ConcurrentDeclarativeSource (
1570
1596
source_config = manifest , config = _CONFIG , catalog = catalog , state = state
1571
1597
)
1598
+ concurrent_streams , synchronous_streams = source ._group_streams (config = _CONFIG )
1572
1599
1573
- assert len (source . _concurrent_streams ) == 0
1574
- assert len (source . _synchronous_streams ) == 1
1600
+ assert len (concurrent_streams ) == 0
1601
+ assert len (synchronous_streams ) == 1
1575
1602
1576
1603
1577
1604
def create_wrapped_stream (stream : DeclarativeStream ) -> Stream :
@@ -1725,9 +1752,3 @@ def get_states_for_stream(
1725
1752
for message in messages
1726
1753
if message .state and message .state .stream .stream_descriptor .name == stream_name
1727
1754
]
1728
-
1729
-
1730
- def disable_emitting_sequential_state_messages (source : ConcurrentDeclarativeSource ) -> None :
1731
- for concurrent_stream in source ._concurrent_streams : # type: ignore # This is the easiest way to disable behavior from the test
1732
- if isinstance (concurrent_stream .cursor , ConcurrentCursor ):
1733
- concurrent_stream .cursor ._connector_state_converter ._is_sequential_state = False # type: ignore # see above
0 commit comments