|
29 | 29 | import static org.mockito.Mockito.mock;
|
30 | 30 | import static org.mockito.Mockito.when;
|
31 | 31 |
|
| 32 | +import com.google.protobuf.ByteString; |
32 | 33 | import com.google.protobuf.util.Durations;
|
33 | 34 | import com.uber.m3.tally.NoopScope;
|
| 35 | +import io.temporal.api.enums.v1.EventType; |
| 36 | +import io.temporal.api.history.v1.History; |
| 37 | +import io.temporal.api.history.v1.HistoryEvent; |
34 | 38 | import io.temporal.api.taskqueue.v1.StickyExecutionAttributes;
|
| 39 | +import io.temporal.api.workflowservice.v1.GetSystemInfoResponse; |
| 40 | +import io.temporal.api.workflowservice.v1.GetWorkflowExecutionHistoryResponse; |
35 | 41 | import io.temporal.api.workflowservice.v1.PollWorkflowTaskQueueResponse;
|
| 42 | +import io.temporal.api.workflowservice.v1.WorkflowServiceGrpc; |
36 | 43 | import io.temporal.internal.common.InternalUtils;
|
37 | 44 | import io.temporal.internal.worker.SingleWorkerOptions;
|
38 | 45 | import io.temporal.internal.worker.WorkflowExecutorCache;
|
39 | 46 | import io.temporal.internal.worker.WorkflowRunLockManager;
|
40 | 47 | import io.temporal.internal.worker.WorkflowTaskHandler;
|
| 48 | +import io.temporal.serviceclient.WorkflowServiceStubs; |
41 | 49 | import io.temporal.testUtils.HistoryUtils;
|
42 | 50 | import io.temporal.testing.internal.SDKTestWorkflowRule;
|
43 | 51 | import java.time.Duration;
|
| 52 | +import java.util.List; |
44 | 53 | import java.util.Optional;
|
45 | 54 | import org.junit.Rule;
|
46 | 55 | import org.junit.Test;
|
@@ -77,6 +86,62 @@ public void ifStickyExecutionAttributesAreNotSetThenWorkflowsAreNotCached() thro
|
77 | 86 | assertFalse(result.getTaskCompleted().hasStickyAttributes());
|
78 | 87 | }
|
79 | 88 |
|
| 89 | + @Test |
| 90 | + public void workflowTaskFailOnIncompleteHistory() throws Throwable { |
| 91 | + assumeFalse("skipping for docker tests", SDKTestWorkflowRule.useExternalService); |
| 92 | + |
| 93 | + WorkflowExecutorCache cache = |
| 94 | + new WorkflowExecutorCache(10, new WorkflowRunLockManager(), new NoopScope()); |
| 95 | + WorkflowServiceStubs client = mock(WorkflowServiceStubs.class); |
| 96 | + when(client.getServerCapabilities()) |
| 97 | + .thenReturn(() -> GetSystemInfoResponse.Capabilities.newBuilder().build()); |
| 98 | + WorkflowServiceGrpc.WorkflowServiceBlockingStub blockingStub = |
| 99 | + mock(WorkflowServiceGrpc.WorkflowServiceBlockingStub.class); |
| 100 | + when(client.blockingStub()).thenReturn(blockingStub); |
| 101 | + when(blockingStub.withOption(any(), any())).thenReturn(blockingStub); |
| 102 | + |
| 103 | + // Simulate a stale history node sending a workflow task with an incomplete history |
| 104 | + List<HistoryEvent> history = |
| 105 | + HistoryUtils.generateWorkflowTaskWithInitialHistory().getHistory().getEventsList(); |
| 106 | + assertEquals(3, history.size()); |
| 107 | + assertEquals( |
| 108 | + EventType.EVENT_TYPE_WORKFLOW_TASK_STARTED, history.get(history.size() - 1).getEventType()); |
| 109 | + history = history.subList(0, history.size() - 1); |
| 110 | + when(blockingStub.getWorkflowExecutionHistory(any())) |
| 111 | + .thenReturn( |
| 112 | + GetWorkflowExecutionHistoryResponse.newBuilder() |
| 113 | + .setHistory(History.newBuilder().addAllEvents(history).build()) |
| 114 | + .build()); |
| 115 | + |
| 116 | + WorkflowTaskHandler taskHandler = |
| 117 | + new ReplayWorkflowTaskHandler( |
| 118 | + "namespace", |
| 119 | + setUpMockWorkflowFactory(), |
| 120 | + cache, |
| 121 | + SingleWorkerOptions.newBuilder().build(), |
| 122 | + null, |
| 123 | + Duration.ofSeconds(5), |
| 124 | + client, |
| 125 | + null); |
| 126 | + |
| 127 | + // Send a poll with a partial history and no cached execution so the SDK will request a full |
| 128 | + // history |
| 129 | + WorkflowTaskHandler.Result result = |
| 130 | + taskHandler.handleWorkflowTask( |
| 131 | + HistoryUtils.generateWorkflowTaskWithInitialHistory().toBuilder() |
| 132 | + .setHistory(History.newBuilder().build()) |
| 133 | + .setNextPageToken(ByteString.EMPTY) |
| 134 | + .build()); |
| 135 | + |
| 136 | + // Assert |
| 137 | + assertEquals(0, cache.size()); |
| 138 | + assertNotNull(result.getTaskFailed()); |
| 139 | + assertTrue(result.getTaskFailed().hasFailure()); |
| 140 | + assertEquals( |
| 141 | + "Premature end of stream, expectedLastEventID=3 but no more events after eventID=2", |
| 142 | + result.getTaskFailed().getFailure().getMessage()); |
| 143 | + } |
| 144 | + |
80 | 145 | @Test
|
81 | 146 | public void ifStickyExecutionAttributesAreSetThenWorkflowsAreCached() throws Throwable {
|
82 | 147 | assumeFalse("skipping for docker tests", SDKTestWorkflowRule.useExternalService);
|
|
0 commit comments