1- import re
21import psycopg2
32import unittest .mock
43import pytest
54import tap_postgres
5+ from tap_postgres .sync_strategies import logical_replication
66import tap_postgres .sync_strategies .full_table as full_table
77import tap_postgres .sync_strategies .common as pg_common
88import singer
@@ -49,10 +49,29 @@ def do_not_dump_catalog(catalog):
4949full_table .UPDATE_BOOKMARK_PERIOD = 1
5050
5151@pytest .mark .parametrize ('use_secondary' , [False , True ])
52+ @unittest .mock .patch ('tap_postgres.sync_logical_streams' , wraps = tap_postgres .sync_logical_streams )
5253@unittest .mock .patch ('psycopg2.connect' , wraps = psycopg2 .connect )
5354class TestLogicalInterruption :
5455 maxDiff = None
5556
57+ def setup_class (self ):
58+ conn_config = get_test_connection_config ()
59+ slot_name = logical_replication .generate_replication_slot_name (
60+ dbname = conn_config ['dbname' ], tap_id = conn_config ['tap_id' ]
61+ )
62+ with get_test_connection (superuser = True ) as conn :
63+ with conn .cursor () as cur :
64+ cur .execute (f"SELECT * FROM pg_create_logical_replication_slot('{ slot_name } ', 'wal2json')" )
65+
66+ def teardown_class (self ):
67+ conn_config = get_test_connection_config ()
68+ slot_name = logical_replication .generate_replication_slot_name (
69+ dbname = conn_config ['dbname' ], tap_id = conn_config ['tap_id' ]
70+ )
71+ with get_test_connection (superuser = True ) as conn :
72+ with conn .cursor () as cur :
73+ cur .execute (f"SELECT * FROM pg_drop_replication_slot('{ slot_name } ')" )
74+
5675 def setup_method (self ):
5776 table_spec_1 = {"columns" : [{"name" : "id" , "type" : "serial" , "primary_key" : True },
5877 {"name" : 'name' , "type" : "character varying" },
@@ -67,15 +86,24 @@ def setup_method(self):
6786 global CAUGHT_MESSAGES
6887 CAUGHT_MESSAGES .clear ()
6988
70- def test_catalog (self , mock_connect , use_secondary ):
89+ def test_catalog (self , mock_connect , mock_sync_logical_streams , use_secondary ):
7190 singer .write_message = singer_write_message_no_cow
7291 pg_common .write_schema_message = singer_write_message_ok
7392
7493 conn_config = get_test_connection_config (use_secondary = use_secondary )
7594 streams = tap_postgres .do_discovery (conn_config )
7695
7796 # Assert that we connected to the correct database
78- expected_connection = {
97+ primary_connection = {
98+ 'application_name' : unittest .mock .ANY ,
99+ 'dbname' : unittest .mock .ANY ,
100+ 'user' : unittest .mock .ANY ,
101+ 'password' : unittest .mock .ANY ,
102+ 'connect_timeout' :unittest .mock .ANY ,
103+ 'host' : conn_config ['host' ],
104+ 'port' : conn_config ['port' ],
105+ }
106+ secondary_connection = {
79107 'application_name' : unittest .mock .ANY ,
80108 'dbname' : unittest .mock .ANY ,
81109 'user' : unittest .mock .ANY ,
@@ -84,8 +112,8 @@ def test_catalog(self, mock_connect, use_secondary):
84112 'host' : conn_config ['secondary_host' ] if use_secondary else conn_config ['host' ],
85113 'port' : conn_config ['secondary_port' ] if use_secondary else conn_config ['port' ],
86114 }
87- mock_connect . assert_called_once_with ( ** expected_connection )
88- mock_connect .reset_mock ( )
115+
116+ mock_connect .assert_called_once_with ( ** secondary_connection )
89117
90118 cow_stream = [s for s in streams if s ['table_name' ] == 'COW' ][0 ]
91119 assert cow_stream is not None
@@ -114,15 +142,20 @@ def test_catalog(self, mock_connect, use_secondary):
114142 state = {}
115143 #the initial phase of cows logical replication will be a full table.
116144 #it will sync the first record and then blow up on the 2nd record
145+ mock_connect .reset_mock ()
117146 try :
118147 tap_postgres .do_sync (get_test_connection_config (use_secondary = use_secondary ), {'streams' : streams }, None , state )
119148 except Exception :
120149 blew_up_on_cow = True
121150
122151 assert blew_up_on_cow is True
123152
124- mock_connect .assert_called_with (** expected_connection )
125- mock_connect .reset_mock ()
153+ mock_sync_logical_streams .assert_not_called ()
154+
155+ mock_connect .assert_has_calls (
156+ [unittest .mock .call (** primary_connection )] * 2 + \
157+ [unittest .mock .call (** secondary_connection )] * 4
158+ )
126159
127160 assert 7 == len (CAUGHT_MESSAGES )
128161
@@ -171,12 +204,17 @@ def test_catalog(self, mock_connect, use_secondary):
171204 global COW_RECORD_COUNT
172205 COW_RECORD_COUNT = 0
173206 CAUGHT_MESSAGES .clear ()
207+ mock_connect .reset_mock ()
174208 tap_postgres .do_sync (get_test_connection_config (use_secondary = use_secondary ), {'streams' : streams }, None , old_state )
175209
176- mock_connect .assert_called_with (** expected_connection )
177- mock_connect .reset_mock ()
210+ mock_sync_logical_streams .assert_called_once ()
211+
212+ mock_connect .assert_has_calls (
213+ [unittest .mock .call (** primary_connection )] * 2 + \
214+ [unittest .mock .call (** secondary_connection )] * 4
215+ )
178216
179- assert 8 == len (CAUGHT_MESSAGES )
217+ assert 10 == len (CAUGHT_MESSAGES )
180218
181219 assert CAUGHT_MESSAGES [0 ]['type' ] == 'SCHEMA'
182220
@@ -225,6 +263,13 @@ def test_catalog(self, mock_connect, use_secondary):
225263 assert CAUGHT_MESSAGES [7 ].value ['bookmarks' ]['public-COW' ].get ('lsn' ) == end_lsn
226264 assert CAUGHT_MESSAGES [7 ].value ['bookmarks' ]['public-COW' ].get ('version' ) == new_version
227265
266+ assert CAUGHT_MESSAGES [8 ]['type' ] == 'SCHEMA'
267+
268+ assert isinstance (CAUGHT_MESSAGES [9 ], singer .messages .StateMessage )
269+ assert CAUGHT_MESSAGES [9 ].value ['bookmarks' ]['public-COW' ].get ('xmin' ) is None
270+ assert CAUGHT_MESSAGES [9 ].value ['bookmarks' ]['public-COW' ].get ('lsn' ) == end_lsn
271+ assert CAUGHT_MESSAGES [9 ].value ['bookmarks' ]['public-COW' ].get ('version' ) == new_version
272+
228273@pytest .mark .parametrize ('use_secondary' , [False , True ])
229274@unittest .mock .patch ('psycopg2.connect' , wraps = psycopg2 .connect )
230275class TestFullTableInterruption :
0 commit comments