This repository has been archived by the owner on Dec 13, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3507 from v1r3n/testing_framework
Support for unit and integration tests of the workflows
- Loading branch information
Showing
20 changed files
with
1,724 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
136 changes: 136 additions & 0 deletions
136
client/src/test/java/com/netflix/conductor/client/testing/AbstractWorkflowTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
/* | ||
* Copyright 2023 Netflix, Inc. | ||
* <p> | ||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* <p> | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* <p> | ||
* Unless required by applicable law or agreed to in writing, software distributed under the License 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.netflix.conductor.client.testing; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.InputStreamReader; | ||
import java.util.HashMap; | ||
import java.util.LinkedList; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
import org.junit.jupiter.api.BeforeAll; | ||
import org.junit.jupiter.api.TestInstance; | ||
|
||
import com.netflix.conductor.client.http.MetadataClient; | ||
import com.netflix.conductor.client.http.WorkflowClient; | ||
import com.netflix.conductor.common.config.ObjectMapperProvider; | ||
import com.netflix.conductor.common.metadata.tasks.TaskResult; | ||
import com.netflix.conductor.common.metadata.tasks.TaskType; | ||
import com.netflix.conductor.common.metadata.workflow.WorkflowDef; | ||
import com.netflix.conductor.common.metadata.workflow.WorkflowTask; | ||
import com.netflix.conductor.common.run.Workflow; | ||
import com.netflix.conductor.common.run.WorkflowTestRequest; | ||
|
||
import com.fasterxml.jackson.core.type.TypeReference; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertNotNull; | ||
|
||
@TestInstance(TestInstance.Lifecycle.PER_CLASS) | ||
public abstract class AbstractWorkflowTests { | ||
|
||
protected static ObjectMapper objectMapper = new ObjectMapperProvider().getObjectMapper(); | ||
|
||
protected static TypeReference<Map<String, List<WorkflowTestRequest.TaskMock>>> mockType = | ||
new TypeReference<Map<String, List<WorkflowTestRequest.TaskMock>>>() {}; | ||
|
||
protected MetadataClient metadataClient; | ||
|
||
protected WorkflowClient workflowClient; | ||
|
||
@BeforeAll | ||
public void setup() { | ||
|
||
String baseURL = "http://localhost:8080/api/"; | ||
metadataClient = new MetadataClient(); | ||
metadataClient.setRootURI(baseURL); | ||
|
||
workflowClient = new WorkflowClient(); | ||
workflowClient.setRootURI(baseURL); | ||
} | ||
|
||
protected WorkflowTestRequest getWorkflowTestRequest(WorkflowDef def) throws IOException { | ||
WorkflowTestRequest testRequest = new WorkflowTestRequest(); | ||
testRequest.setInput(new HashMap<>()); | ||
testRequest.setName(def.getName()); | ||
testRequest.setVersion(def.getVersion()); | ||
testRequest.setWorkflowDef(def); | ||
|
||
Map<String, List<WorkflowTestRequest.TaskMock>> taskRefToMockOutput = new HashMap<>(); | ||
for (WorkflowTask task : def.collectTasks()) { | ||
List<WorkflowTestRequest.TaskMock> taskRuns = new LinkedList<>(); | ||
WorkflowTestRequest.TaskMock mock = new WorkflowTestRequest.TaskMock(); | ||
mock.setStatus(TaskResult.Status.COMPLETED); | ||
Map<String, Object> output = new HashMap<>(); | ||
|
||
output.put("response", Map.of()); | ||
mock.setOutput(output); | ||
taskRuns.add(mock); | ||
taskRefToMockOutput.put(task.getTaskReferenceName(), taskRuns); | ||
|
||
if (task.getType().equals(TaskType.SUB_WORKFLOW.name())) { | ||
Object inlineSubWorkflowDefObj = task.getSubWorkflowParam().getWorkflowDefinition(); | ||
if (inlineSubWorkflowDefObj != null) { | ||
// If not null, it represents WorkflowDef object | ||
WorkflowDef inlineSubWorkflowDef = (WorkflowDef) inlineSubWorkflowDefObj; | ||
WorkflowTestRequest subWorkflowTestRequest = | ||
getWorkflowTestRequest(inlineSubWorkflowDef); | ||
testRequest | ||
.getSubWorkflowTestRequest() | ||
.put(task.getTaskReferenceName(), subWorkflowTestRequest); | ||
} else { | ||
// Inline definition is null | ||
String subWorkflowName = task.getSubWorkflowParam().getName(); | ||
// Load up the sub workflow from the JSON | ||
WorkflowDef subWorkflowDef = | ||
getWorkflowDef("/workflows/" + subWorkflowName + ".json"); | ||
assertNotNull(subWorkflowDef); | ||
WorkflowTestRequest subWorkflowTestRequest = | ||
getWorkflowTestRequest(subWorkflowDef); | ||
testRequest | ||
.getSubWorkflowTestRequest() | ||
.put(task.getTaskReferenceName(), subWorkflowTestRequest); | ||
} | ||
} | ||
} | ||
testRequest.setTaskRefToMockOutput(taskRefToMockOutput); | ||
return testRequest; | ||
} | ||
|
||
protected WorkflowDef getWorkflowDef(String path) throws IOException { | ||
InputStream inputStream = AbstractWorkflowTests.class.getResourceAsStream(path); | ||
if (inputStream == null) { | ||
throw new IOException("No file found at " + path); | ||
} | ||
return objectMapper.readValue(new InputStreamReader(inputStream), WorkflowDef.class); | ||
} | ||
|
||
protected Workflow getWorkflow(String path) throws IOException { | ||
InputStream inputStream = AbstractWorkflowTests.class.getResourceAsStream(path); | ||
if (inputStream == null) { | ||
throw new IOException("No file found at " + path); | ||
} | ||
return objectMapper.readValue(new InputStreamReader(inputStream), Workflow.class); | ||
} | ||
|
||
protected Map<String, List<WorkflowTestRequest.TaskMock>> getTestInputs(String path) | ||
throws IOException { | ||
InputStream inputStream = AbstractWorkflowTests.class.getResourceAsStream(path); | ||
if (inputStream == null) { | ||
throw new IOException("No file found at " + path); | ||
} | ||
return objectMapper.readValue(new InputStreamReader(inputStream), mockType); | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowInput.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* | ||
* Copyright 2023 Netflix, Inc. | ||
* <p> | ||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* <p> | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* <p> | ||
* Unless required by applicable law or agreed to in writing, software distributed under the License 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.netflix.conductor.client.testing; | ||
|
||
import java.math.BigDecimal; | ||
|
||
public class LoanWorkflowInput { | ||
|
||
private String userEmail; | ||
|
||
private BigDecimal loanAmount; | ||
|
||
public String getUserEmail() { | ||
return userEmail; | ||
} | ||
|
||
public void setUserEmail(String userEmail) { | ||
this.userEmail = userEmail; | ||
} | ||
|
||
public BigDecimal getLoanAmount() { | ||
return loanAmount; | ||
} | ||
|
||
public void setLoanAmount(BigDecimal loanAmount) { | ||
this.loanAmount = loanAmount; | ||
} | ||
} |
108 changes: 108 additions & 0 deletions
108
client/src/test/java/com/netflix/conductor/client/testing/LoanWorkflowTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/* | ||
* Copyright 2023 Netflix, Inc. | ||
* <p> | ||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* <p> | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* <p> | ||
* Unless required by applicable law or agreed to in writing, software distributed under the License 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.netflix.conductor.client.testing; | ||
|
||
import java.io.IOException; | ||
import java.math.BigDecimal; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
import com.netflix.conductor.common.metadata.tasks.Task; | ||
import com.netflix.conductor.common.metadata.workflow.WorkflowDef; | ||
import com.netflix.conductor.common.run.Workflow; | ||
import com.netflix.conductor.common.run.WorkflowTestRequest; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertNotNull; | ||
|
||
/** Unit test a workflow with inputs read from a file. */ | ||
public class LoanWorkflowTest extends AbstractWorkflowTests { | ||
|
||
/** Uses mock inputs to verify the workflow execution and input/outputs of the tasks */ | ||
// Tests are commented out since it requires a running server | ||
// @Test | ||
public void verifyWorkflowExecutionWithMockInputs() throws IOException { | ||
WorkflowDef def = getWorkflowDef("/workflows/calculate_loan_workflow.json"); | ||
assertNotNull(def); | ||
Map<String, List<WorkflowTestRequest.TaskMock>> testInputs = | ||
getTestInputs("/test_data/loan_workflow_input.json"); | ||
assertNotNull(testInputs); | ||
|
||
WorkflowTestRequest testRequest = new WorkflowTestRequest(); | ||
testRequest.setWorkflowDef(def); | ||
|
||
LoanWorkflowInput workflowInput = new LoanWorkflowInput(); | ||
workflowInput.setUserEmail("[email protected]"); | ||
workflowInput.setLoanAmount(new BigDecimal(11_000)); | ||
testRequest.setInput(objectMapper.convertValue(workflowInput, Map.class)); | ||
|
||
testRequest.setTaskRefToMockOutput(testInputs); | ||
testRequest.setName(def.getName()); | ||
testRequest.setVersion(def.getVersion()); | ||
|
||
Workflow execution = workflowClient.testWorkflow(testRequest); | ||
assertNotNull(execution); | ||
|
||
// Assert that the workflow completed successfully | ||
assertEquals(Workflow.WorkflowStatus.COMPLETED, execution.getStatus()); | ||
|
||
// Ensure the inputs were captured correctly | ||
assertEquals( | ||
workflowInput.getLoanAmount().toString(), | ||
String.valueOf(execution.getInput().get("loanAmount"))); | ||
assertEquals(workflowInput.getUserEmail(), execution.getInput().get("userEmail")); | ||
|
||
// A total of 3 tasks were executed | ||
assertEquals(3, execution.getTasks().size()); | ||
|
||
Task fetchUserDetails = execution.getTasks().get(0); | ||
Task getCreditScore = execution.getTasks().get(1); | ||
Task calculateLoanAmount = execution.getTasks().get(2); | ||
|
||
// fetch user details received the correct input from the workflow | ||
assertEquals( | ||
workflowInput.getUserEmail(), fetchUserDetails.getInputData().get("userEmail")); | ||
|
||
// And that the task produced the right output | ||
int userAccountNo = 12345; | ||
assertEquals(userAccountNo, fetchUserDetails.getOutputData().get("userAccount")); | ||
|
||
// get credit score received the right account number from the output of the fetch user | ||
// details | ||
assertEquals(userAccountNo, getCreditScore.getInputData().get("userAccountNumber")); | ||
int expectedCreditRating = 750; | ||
|
||
// The task produced the right output | ||
assertEquals(expectedCreditRating, getCreditScore.getOutputData().get("creditRating")); | ||
|
||
// Calculate loan amount gets the right loan amount from workflow input | ||
assertEquals( | ||
workflowInput.getLoanAmount().toString(), | ||
String.valueOf(calculateLoanAmount.getInputData().get("loanAmount"))); | ||
|
||
// Calculate loan amount gets the right credit rating from the previous task | ||
assertEquals(expectedCreditRating, calculateLoanAmount.getInputData().get("creditRating")); | ||
|
||
int authorizedLoanAmount = 10_000; | ||
assertEquals( | ||
authorizedLoanAmount, | ||
calculateLoanAmount.getOutputData().get("authorizedLoanAmount")); | ||
|
||
// Finally, lets verify the workflow outputs | ||
assertEquals(userAccountNo, execution.getOutput().get("accountNumber")); | ||
assertEquals(expectedCreditRating, execution.getOutput().get("creditRating")); | ||
assertEquals(authorizedLoanAmount, execution.getOutput().get("authorizedLoanAmount")); | ||
|
||
System.out.println(execution); | ||
} | ||
} |
Oops, something went wrong.