44
55import io .temporal .client .WorkflowClient ;
66import io .temporal .client .WorkflowOptions ;
7+ import io .temporal .client .WorkflowStub ;
8+ import io .temporal .common .WorkflowExecutionHistory ;
79import io .temporal .springai .activity .ChatModelActivityImpl ;
810import io .temporal .springai .chat .TemporalChatClient ;
911import io .temporal .springai .model .ActivityChatModel ;
1012import io .temporal .springai .tool .DeterministicTool ;
1113import io .temporal .springai .tool .SideEffectTool ;
1214import io .temporal .testing .TestWorkflowEnvironment ;
15+ import io .temporal .testing .WorkflowReplayer ;
1316import io .temporal .worker .Worker ;
1417import io .temporal .workflow .WorkflowInterface ;
1518import io .temporal .workflow .WorkflowMethod ;
2629import org .springframework .ai .tool .annotation .Tool ;
2730
2831/**
29- * Verifies that workflows using ActivityChatModel with tools execute without non-determinism
30- * errors .
32+ * Verifies that workflows using ActivityChatModel with tools are deterministic by running them to
33+ * completion and then replaying from the captured history .
3134 */
3235class WorkflowDeterminismTest {
3336
@@ -48,13 +51,11 @@ void tearDown() {
4851 }
4952
5053 @ Test
51- void workflowWithChatModel_completesSuccessfully () {
54+ void workflowWithChatModel_replaysDeterministically () throws Exception {
5255 Worker worker = testEnv .newWorker (TASK_QUEUE );
5356 worker .registerWorkflowImplementationTypes (ChatWorkflowImpl .class );
54-
55- // Register a ChatModelActivityImpl backed by a mock model that returns a canned response
56- ChatModel mockModel = new StubChatModel ("Hello from the model!" );
57- worker .registerActivitiesImplementations (new ChatModelActivityImpl (mockModel ));
57+ worker .registerActivitiesImplementations (
58+ new ChatModelActivityImpl (new StubChatModel ("Hello from the model!" )));
5859
5960 testEnv .start ();
6061
@@ -64,16 +65,19 @@ void workflowWithChatModel_completesSuccessfully() {
6465
6566 String result = workflow .chat ("Hi" );
6667 assertEquals ("Hello from the model!" , result );
68+
69+ // Capture history and replay — any non-determinism throws here
70+ WorkflowExecutionHistory history =
71+ client .fetchHistory (WorkflowStub .fromTyped (workflow ).getExecution ().getWorkflowId ());
72+ WorkflowReplayer .replayWorkflowExecution (history , ChatWorkflowImpl .class );
6773 }
6874
6975 @ Test
70- void workflowWithDeterministicTool_completesSuccessfully () {
76+ void workflowWithTools_replaysDeterministically () throws Exception {
7177 Worker worker = testEnv .newWorker (TASK_QUEUE );
7278 worker .registerWorkflowImplementationTypes (ChatWithToolsWorkflowImpl .class );
73-
74- // Model returns a simple response (no tool calls)
75- ChatModel mockModel = new StubChatModel ("I used the tools!" );
76- worker .registerActivitiesImplementations (new ChatModelActivityImpl (mockModel ));
79+ worker .registerActivitiesImplementations (
80+ new ChatModelActivityImpl (new StubChatModel ("I used the tools!" )));
7781
7882 testEnv .start ();
7983
@@ -83,6 +87,11 @@ void workflowWithDeterministicTool_completesSuccessfully() {
8387
8488 String result = workflow .chat ("Use tools" );
8589 assertEquals ("I used the tools!" , result );
90+
91+ // Capture history and replay
92+ WorkflowExecutionHistory history =
93+ client .fetchHistory (WorkflowStub .fromTyped (workflow ).getExecution ().getWorkflowId ());
94+ WorkflowReplayer .replayWorkflowExecution (history , ChatWithToolsWorkflowImpl .class );
8695 }
8796
8897 // --- Workflow interfaces and implementations ---
0 commit comments