@@ -129,7 +129,7 @@ class RestateContainer(DockerContainer):
129129
130130 log_thread : typing .Optional [threading .Thread ] = None
131131
132- def __init__ (self , image ):
132+ def __init__ (self , image , always_replay ):
133133 super ().__init__ (image )
134134 self .with_exposed_ports (8080 , 9070 )
135135 self .with_env ('RESTATE_LOG_FILTER' , 'restate=info' )
@@ -138,7 +138,10 @@ def __init__(self, image):
138138 self .with_env ('RESTATE_SHUTDOWN_TIMEOUT' , '10s' )
139139 self .with_env ('RESTATE_ROCKSDB_TOTAL_MEMORY_SIZE' , '32 MB' )
140140 self .with_env ('RESTATE_WORKER__INVOKER__IN_MEMORY_QUEUE_LENGTH_LIMIT' , '64' )
141- self .with_env ('RESTATE_WORKER__INVOKER__INACTIVITY_TIMEOUT' , '10m' )
141+ if always_replay :
142+ self .with_env ('RESTATE_WORKER__INVOKER__INACTIVITY_TIMEOUT' , '0s' )
143+ else :
144+ self .with_env ('RESTATE_WORKER__INVOKER__INACTIVITY_TIMEOUT' , '10m' )
142145 self .with_env ('RESTATE_WORKER__INVOKER__ABORT_TIMEOUT' , '10m' )
143146
144147 self .with_kwargs (extra_hosts = {"host.docker.internal" : "host-gateway" })
@@ -188,6 +191,7 @@ class TestConfiguration:
188191 """A configuration for running tests"""
189192 restate_image : str = "restatedev/restate:latest"
190193 stream_logs : bool = False
194+ always_replay : bool = False
191195
192196
193197class RestateTestHarness :
@@ -207,7 +211,9 @@ def start(self):
207211 """start the restate server and the sdk"""
208212 self .bind_address = TcpSocketBindAddress ()
209213 self .server = AsgiServer (self .asgi_app , self .bind_address ).start ()
210- self .restate = RestateContainer (image = self .config .restate_image ) \
214+ self .restate = RestateContainer (
215+ image = self .config .restate_image ,
216+ always_replay = self .config .always_replay ) \
211217 .start (self .config .stream_logs )
212218 try :
213219 self ._register_sdk ()
@@ -256,10 +262,24 @@ def __exit__(self, exc_type, exc_value, traceback):
256262
257263def test_harness (app ,
258264 follow_logs : bool = False ,
259- restate_image : str = "restatedev/restate:latest" ) -> RestateTestHarness :
260- """create a test harness for running Restate SDKs"""
265+ restate_image : str = "restatedev/restate:latest" ,
266+ always_replay : bool = False ) -> RestateTestHarness :
267+ """
268+ Creates a test harness for running Restate services together with restate-server.
269+
270+ :param app: The application to be tested using the RestateTestHarness.
271+ :param follow_logs: Whether to stream logs for the test process (default is False).
272+ :param restate_image: The image name for the restate-server container
273+ (default is "restatedev/restate:latest").
274+ :param always_replay: When True, this forces restate-server to always replay
275+ on a suspension point. This is useful to hunt non deterministic bugs
276+ that might prevent your code to replay correctly (default is False).
277+ :return: An instance of RestateTestHarness initialized with the provided app and configuration.
278+ :rtype: RestateTestHarness
279+ """
261280 config = TestConfiguration (
262281 restate_image = restate_image ,
263282 stream_logs = follow_logs ,
283+ always_replay = always_replay
264284 )
265285 return RestateTestHarness (app , config )
0 commit comments