Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unit tests for WorkflowExecutionUtils #959

Merged
merged 1 commit into from
Nov 12, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,365 @@
/**
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* <p>Modifications copyright (C) 2017 Uber Technologies, Inc.
*
* <p>Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file
* except in compliance with the License. A copy of the License is located at
*
* <p>http://aws.amazon.com/apache2.0
*
* <p>or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.uber.cadence.internal.common;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

import com.uber.cadence.EventType;
import com.uber.cadence.GetWorkflowExecutionHistoryResponse;
import com.uber.cadence.History;
import com.uber.cadence.HistoryEvent;
import com.uber.cadence.StartChildWorkflowExecutionFailedEventAttributes;
import com.uber.cadence.WorkflowExecution;
import com.uber.cadence.WorkflowExecutionCloseStatus;
import com.uber.cadence.client.WorkflowTerminatedException;
import com.uber.cadence.client.WorkflowTimedOutException;
import com.uber.cadence.serviceclient.IWorkflowService;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.Optional;
import java.util.concurrent.CancellationException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.junit.Before;
import org.junit.Test;

public class WorkflowExecutionUtilsTest {

private IWorkflowService mockService;
private WorkflowExecution workflowExecution;

@Before
public void setUp() {
mockService = mock(IWorkflowService.class);
workflowExecution =
new WorkflowExecution().setWorkflowId("testWorkflowId").setRunId("testRunId");
}

// Helper method to create a response with a single event
private GetWorkflowExecutionHistoryResponse createResponseWithEvent(HistoryEvent event) {
GetWorkflowExecutionHistoryResponse response = new GetWorkflowExecutionHistoryResponse();
response.setHistory(new History().setEvents(Collections.singletonList(event)));
return response;
}

// ===========================
// Test for successful completion case
// ===========================
@Test
public void testGetWorkflowExecutionResult_SuccessfulCompletion() throws Exception {
// Mock a completed event with a result
HistoryEvent completedEvent = new HistoryEvent();
completedEvent.setEventType(EventType.WorkflowExecutionCompleted);
byte[] expectedResult = "result".getBytes();
completedEvent.setWorkflowExecutionCompletedEventAttributes(
new com.uber.cadence.WorkflowExecutionCompletedEventAttributes().setResult(expectedResult));

// Mock the response from GetWorkflowExecutionHistoryWithTimeout
GetWorkflowExecutionHistoryResponse response = createResponseWithEvent(completedEvent);
when(mockService.GetWorkflowExecutionHistoryWithTimeout(any(), anyLong())).thenReturn(response);

byte[] result =
WorkflowExecutionUtils.getWorkflowExecutionResult(
mockService, "testDomain", workflowExecution, Optional.empty(), 5, TimeUnit.SECONDS);

assertArrayEquals(expectedResult, result);
}

// ===========================
// Test for canceled workflow case
// ===========================
@Test(expected = CancellationException.class)
public void testGetWorkflowExecutionResult_CancelledWorkflow() throws Exception {
// Mock a canceled event
HistoryEvent canceledEvent = new HistoryEvent();
canceledEvent.setEventType(EventType.WorkflowExecutionCanceled);
canceledEvent.setWorkflowExecutionCanceledEventAttributes(
new com.uber.cadence.WorkflowExecutionCanceledEventAttributes());

GetWorkflowExecutionHistoryResponse response = createResponseWithEvent(canceledEvent);
when(mockService.GetWorkflowExecutionHistoryWithTimeout(any(), anyLong())).thenReturn(response);

WorkflowExecutionUtils.getWorkflowExecutionResult(
mockService, "testDomain", workflowExecution, Optional.empty(), 5, TimeUnit.SECONDS);
}

// ===========================
// Test for terminated workflow case
// ===========================
@Test(expected = WorkflowTerminatedException.class)
public void testGetWorkflowExecutionResult_TerminatedWorkflow() throws Exception {
// Mock a terminated event
HistoryEvent terminatedEvent = new HistoryEvent();
terminatedEvent.setEventType(EventType.WorkflowExecutionTerminated);
terminatedEvent.setWorkflowExecutionTerminatedEventAttributes(
new com.uber.cadence.WorkflowExecutionTerminatedEventAttributes());

GetWorkflowExecutionHistoryResponse response = createResponseWithEvent(terminatedEvent);
when(mockService.GetWorkflowExecutionHistoryWithTimeout(any(), anyLong())).thenReturn(response);

WorkflowExecutionUtils.getWorkflowExecutionResult(
mockService, "testDomain", workflowExecution, Optional.empty(), 5, TimeUnit.SECONDS);
}

// ===========================
// Test for timed-out workflow case
// ===========================
@Test(expected = WorkflowTimedOutException.class)
public void testGetWorkflowExecutionResult_TimedOutWorkflow() throws Exception {
// Mock a timed-out event
HistoryEvent timedOutEvent = new HistoryEvent();
timedOutEvent.setEventType(EventType.WorkflowExecutionTimedOut);
timedOutEvent.setWorkflowExecutionTimedOutEventAttributes(
new com.uber.cadence.WorkflowExecutionTimedOutEventAttributes());

GetWorkflowExecutionHistoryResponse response = createResponseWithEvent(timedOutEvent);
when(mockService.GetWorkflowExecutionHistoryWithTimeout(any(), anyLong())).thenReturn(response);

WorkflowExecutionUtils.getWorkflowExecutionResult(
mockService, "testDomain", workflowExecution, Optional.empty(), 5, TimeUnit.SECONDS);
}

// ===========================
// Test for an unsupported close event type
// ===========================
@Test(expected = RuntimeException.class)
public void testGetWorkflowExecutionResult_UnsupportedCloseEvent() throws Exception {
// Mock an event type that doesn't represent a closed workflow
HistoryEvent unsupportedEvent = new HistoryEvent();
unsupportedEvent.setEventType(EventType.ActivityTaskScheduled); // Not a close event

GetWorkflowExecutionHistoryResponse response = createResponseWithEvent(unsupportedEvent);
when(mockService.GetWorkflowExecutionHistoryWithTimeout(any(), anyLong())).thenReturn(response);

WorkflowExecutionUtils.getWorkflowExecutionResult(
mockService, "testDomain", workflowExecution, Optional.empty(), 5, TimeUnit.SECONDS);
}

// ===========================
// Test for timeout exceeded while waiting
// ===========================
@Test(expected = TimeoutException.class)
public void testGetWorkflowExecutionResult_TimeoutExceeded() throws Exception {
// Simulate a delay by mocking the service call to throw a TimeoutException
WorkflowExecutionUtils.getWorkflowExecutionResult(
mockService, "testDomain", workflowExecution, Optional.empty(), -1, TimeUnit.MILLISECONDS);
}

// ===========================
// Test getId with StartChildWorkflowExecutionFailedEvent
// ===========================
@Test
public void testGetId_WithStartChildWorkflowExecutionFailedEvent() {
// Create a StartChildWorkflowExecutionFailed event with a workflow ID
HistoryEvent event = new HistoryEvent();
event.setEventType(EventType.StartChildWorkflowExecutionFailed);
StartChildWorkflowExecutionFailedEventAttributes attributes =
new StartChildWorkflowExecutionFailedEventAttributes();
attributes.setWorkflowId("testWorkflowId");
event.setStartChildWorkflowExecutionFailedEventAttributes(attributes);

// Call getId and verify it returns the workflow ID
String result = WorkflowExecutionUtils.getId(event);
assertEquals("testWorkflowId", result);
}

// ===========================
// Test getId with other event types (should return null)
// ===========================
@Test
public void testGetId_WithNonChildWorkflowEvent() {
// Create a HistoryEvent of a different type (e.g., WorkflowExecutionStarted)
HistoryEvent event = new HistoryEvent();
event.setEventType(EventType.WorkflowExecutionStarted);

// Call getId and verify it returns null
String result = WorkflowExecutionUtils.getId(event);
assertNull(result);
}

@Test
public void testGetId_WithNullEvent() {
// Call getId with a null event and verify it returns null
String result = WorkflowExecutionUtils.getId(null);
assertNull(result);
}

// ===========================
// Test getCloseStatus for each close event type
// ===========================

@Test
public void testGetCloseStatus_Completed() {
HistoryEvent event = new HistoryEvent();
event.setEventType(EventType.WorkflowExecutionCompleted);

WorkflowExecutionCloseStatus result = WorkflowExecutionUtils.getCloseStatus(event);
assertEquals(WorkflowExecutionCloseStatus.COMPLETED, result);
}

@Test
public void testGetCloseStatus_Canceled() {
HistoryEvent event = new HistoryEvent();
event.setEventType(EventType.WorkflowExecutionCanceled);

WorkflowExecutionCloseStatus result = WorkflowExecutionUtils.getCloseStatus(event);
assertEquals(WorkflowExecutionCloseStatus.CANCELED, result);
}

@Test
public void testGetCloseStatus_Failed() {
HistoryEvent event = new HistoryEvent();
event.setEventType(EventType.WorkflowExecutionFailed);

WorkflowExecutionCloseStatus result = WorkflowExecutionUtils.getCloseStatus(event);
assertEquals(WorkflowExecutionCloseStatus.FAILED, result);
}

@Test
public void testGetCloseStatus_TimedOut() {
HistoryEvent event = new HistoryEvent();
event.setEventType(EventType.WorkflowExecutionTimedOut);

WorkflowExecutionCloseStatus result = WorkflowExecutionUtils.getCloseStatus(event);
assertEquals(WorkflowExecutionCloseStatus.TIMED_OUT, result);
}

@Test
public void testGetCloseStatus_ContinuedAsNew() {
HistoryEvent event = new HistoryEvent();
event.setEventType(EventType.WorkflowExecutionContinuedAsNew);

WorkflowExecutionCloseStatus result = WorkflowExecutionUtils.getCloseStatus(event);
assertEquals(WorkflowExecutionCloseStatus.CONTINUED_AS_NEW, result);
}

@Test
public void testGetCloseStatus_Terminated() {
HistoryEvent event = new HistoryEvent();
event.setEventType(EventType.WorkflowExecutionTerminated);

WorkflowExecutionCloseStatus result = WorkflowExecutionUtils.getCloseStatus(event);
assertEquals(WorkflowExecutionCloseStatus.TERMINATED, result);
}

// ===========================
// Test getCloseStatus with an unsupported event type
// ===========================
@Test
public void testGetCloseStatus_InvalidEvent() {
HistoryEvent event = new HistoryEvent();
event.setEventType(EventType.ActivityTaskScheduled); // Unsupported close event type

IllegalArgumentException exception =
assertThrows(
IllegalArgumentException.class,
() -> {
WorkflowExecutionUtils.getCloseStatus(event);
});

assertTrue(exception.getMessage().contains("Not close event"));
}

// Helper method to create a response with events and a next page token
private GetWorkflowExecutionHistoryResponse createResponseWithEvents(
HistoryEvent[] events, byte[] nextPageToken) {
GetWorkflowExecutionHistoryResponse response = new GetWorkflowExecutionHistoryResponse();
response.setHistory(new History().setEvents(Arrays.asList(events)));
response.setNextPageToken(nextPageToken);
return response;
}
// ===========================
// Test for single-page history
// ===========================
@Test
public void testGetHistory_SinglePage() throws Exception {
// Create a single-page history with two events
HistoryEvent event1 =
new HistoryEvent().setEventType(EventType.WorkflowExecutionStarted).setEventId(1);
HistoryEvent event2 =
new HistoryEvent().setEventType(EventType.WorkflowExecutionCompleted).setEventId(2);

GetWorkflowExecutionHistoryResponse response =
createResponseWithEvents(new HistoryEvent[] {event1, event2}, null);
when(mockService.GetWorkflowExecutionHistory(any())).thenReturn(response);

Iterator<HistoryEvent> iterator =
WorkflowExecutionUtils.getHistory(mockService, "testDomain", workflowExecution);

assertTrue(iterator.hasNext());
assertEquals(event1, iterator.next());
assertTrue(iterator.hasNext());
assertEquals(event2, iterator.next());
assertFalse(iterator.hasNext()); // End of history
}

// ===========================
// Test for multi-page history
// ===========================
@Test
public void testGetHistory_MultiPage() throws Exception {
// Create a multi-page history with three events over two pages
HistoryEvent event1 =
new HistoryEvent().setEventType(EventType.WorkflowExecutionStarted).setEventId(1);
HistoryEvent event2 =
new HistoryEvent().setEventType(EventType.ActivityTaskScheduled).setEventId(2);
HistoryEvent event3 =
new HistoryEvent().setEventType(EventType.WorkflowExecutionCompleted).setEventId(3);

// Page 1 with a nextPageToken
byte[] nextPageToken = new byte[] {1};
GetWorkflowExecutionHistoryResponse responsePage1 =
createResponseWithEvents(new HistoryEvent[] {event1, event2}, nextPageToken);

// Page 2 with no nextPageToken, indicating the end of history
GetWorkflowExecutionHistoryResponse responsePage2 =
createResponseWithEvents(new HistoryEvent[] {event3}, null);

when(mockService.GetWorkflowExecutionHistory(any()))
.thenReturn(responsePage1) // First call returns page 1
.thenReturn(responsePage2); // Second call returns page 2

Iterator<HistoryEvent> iterator =
WorkflowExecutionUtils.getHistory(mockService, "testDomain", workflowExecution);

// Verify the iterator iterates over both pages
assertTrue(iterator.hasNext());
assertEquals(event1, iterator.next());
assertTrue(iterator.hasNext());
assertEquals(event2, iterator.next());
assertTrue(iterator.hasNext());
assertEquals(event3, iterator.next());
assertFalse(iterator.hasNext()); // End of history
}

// ===========================
// Test for empty history
// ===========================
@Test
public void testGetHistory_EmptyHistory() throws Exception {
// Create an empty history response
GetWorkflowExecutionHistoryResponse response =
createResponseWithEvents(new HistoryEvent[] {}, null);
when(mockService.GetWorkflowExecutionHistory(any())).thenReturn(response);

Iterator<HistoryEvent> iterator =
WorkflowExecutionUtils.getHistory(mockService, "testDomain", workflowExecution);

// Verify that hasNext() is false immediately
assertFalse(iterator.hasNext());
}
}