Skip to content

Commit

Permalink
[xray-exporter] Add ability to create test executions (#3846)
Browse files Browse the repository at this point in the history
  • Loading branch information
uarlouski authored Apr 18, 2023
1 parent 0621798 commit d2ed3ff
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 40 deletions.
53 changes: 49 additions & 4 deletions docs/modules/integrations/pages/xray-exporter.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ include::partial$jira-configuration.adoc[]

NOTE: The properties marked with *bold* are mandatory.

=== General

[cols="2,1,3", options="header"]
|===

Expand Down Expand Up @@ -46,10 +48,6 @@ Please make sure that the `bdd.configuration.formats` property includes JSON val
|false
|Statuses of test cases allowed to update

|`xray-exporter.test-execution-key`
|false
|The key of a `Test Execution` to which the exported test cases along with their statuses will be added

|`xray-exporter.test-set-key`
|false
|The key of a `Test Set` to which the exported test cases will be added
Expand All @@ -63,6 +61,53 @@ xray-exporter.project-key=ABBA
xray-exporter.json-results-directory=/Users/happytester/Repositories/app-tests/output/results/jbehave
----

=== Test Execution

[cols="2,1,3", options="header"]
|===

|Property
|Required
|Description

|`xray-exporter.test-execution-key`
|false
|The key of `Test Execution` which the exported test cases along with their statuses will be added to

|`xray-exporter.test-execution-summary`
|false
|The `Test Execution` summary

|===

Test execution import varies depending on values in `xray-exporter.test-execution-key` and `xray-exporter.test-execution-summary`, the following matrix shows this behavior change:

[cols="1,1", options="header"]
|===

|Configuration
|Result

|Both `xray-exporter.test-execution-key` and `xray-exporter.test-execution-summary` are set
a|Test execution summary and associated test cases will be updated, the update of test cases is performed according to following rules:

* new test cases are added to the test execution
* statuses of existing test cases are updated

|Only `xray-exporter.test-execution-summary` is set
|New test execution will be created

|Only `xray-exporter.test-execution-key` is set
a|Associated test cases will be updated according to following rules:

* new test cases are added to the test execution
* statuses of existing test cases are updated

|Neither `xray-exporter.test-execution-key` nor `xray-exporter.test-execution-summary` are set
|Text execution import is skipped

|===

== Jira Fields Mapping

The Xray is a Jira plugin that uses custom Jira fields for it's data, one of the ways to find out custom field names for particular field used by Xray on Jira UI (if access to Jira configuration is prohibited) is to request description of some issue like https://jira.example.com/rest/api/latest/issue/DUMMY-1.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 the original author or authors.
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -26,6 +26,7 @@ public class XrayExporterOptions
private Path jsonResultsDirectory;
private String testSetKey;
private String testExecutionKey;
private String testExecutionSummary;

public Path getJsonResultsDirectory()
{
Expand Down Expand Up @@ -56,4 +57,14 @@ public void setTestExecutionKey(String testExecutionKey)
{
this.testExecutionKey = testExecutionKey;
}

public String getTestExecutionSummary()
{
return testExecutionSummary;
}

public void setTestExecutionSummary(String testExecutionSummary)
{
this.testExecutionSummary = testExecutionSummary;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 the original author or authors.
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -105,30 +105,32 @@ private void addTestCasesToTestSet(List<Entry<String, Scenario>> testCases)
.map(Entry::getKey)
.collect(Collectors.toList());

updateSafely(() -> xrayFacade.updateTestSet(testSetKey, testCaseIds), "test set", testSetKey);
executeSafely(() -> xrayFacade.updateTestSet(testSetKey, testCaseIds), "test set", testSetKey);
}
}

private void addTestCasesToTestExecution(List<Entry<String, Scenario>> testCases)
{
String testExecutionKey = xrayExporterOptions.getTestExecutionKey();
if (testExecutionKey != null)

if (testExecutionKey != null || xrayExporterOptions.getTestExecutionSummary() != null)
{
TestExecution testExecution = testExecutionFactory.create(testCases);
updateSafely(() -> xrayFacade.updateTestExecution(testExecution), "test execution", testExecutionKey);
executeSafely(() -> xrayFacade.importTestExecution(testExecution), "test execution", testExecutionKey);
}
}

private void updateSafely(FailableRunnable runnable, String type, String key)
private void executeSafely(FailableRunnable runnable, String type, String key)
{
try
{
runnable.run();
}
catch (IOException | JiraConfigurationException thrown)
{
String errorMessage = String.format("Failed updating %s with the key %s: %s", type, key,
thrown.getMessage());
String errorMessage = key == null
? String.format("Failed to create %s: %s", type, thrown.getMessage())
: String.format("Failed to update %s with the key %s: %s", type, key, thrown.getMessage());
errors.add(errorMessage);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 the original author or authors.
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -26,8 +26,10 @@
import com.fasterxml.jackson.databind.module.SimpleModule;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.function.FailableFunction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vividus.jira.JiraClient;
import org.vividus.jira.JiraClientProvider;
import org.vividus.jira.JiraConfigurationException;
import org.vividus.jira.JiraFacade;
Expand Down Expand Up @@ -92,17 +94,30 @@ public <T extends AbstractTestCase> void updateTestCase(String testCaseKey, T te
.log("{} Test with key {} has been updated");
}

public void updateTestExecution(TestExecution testExecution) throws IOException, JiraConfigurationException
public void importTestExecution(TestExecution testExecution) throws IOException, JiraConfigurationException
{
String testExecutionRequest = objectMapper.writeValueAsString(testExecution);
FailableFunction<JiraClient, String, IOException> importFunction = client -> client
.executePost("/rest/raven/1.0/import/execution", testExecutionRequest);

String testExecutionKey = testExecution.getTestExecutionKey();
LOGGER.atInfo()
.addArgument(testExecutionKey)
.addArgument(testExecutionRequest)
.log("Updating Test Execution with ID {}: {}");
jiraClientProvider.getByIssueKey(testExecutionKey).executePost("/rest/raven/1.0/import/execution",
testExecutionRequest);
LOGGER.atInfo().addArgument(testExecutionKey).log("Test Execution with key {} has been updated");
if (testExecutionKey != null)
{
LOGGER.atInfo()
.addArgument(testExecutionKey)
.addArgument(testExecutionRequest)
.log("Updating Test Execution with ID {}: {}");
importFunction.apply(jiraClientProvider.getByIssueKey(testExecutionKey));
LOGGER.atInfo().addArgument(testExecutionKey).log("Test Execution with key {} has been updated");
}
else
{
LOGGER.atInfo().addArgument(testExecutionRequest).log("Creating Test Execution: {}");
String response = importFunction.apply(jiraClientProvider.getByJiraConfigurationKey(jiraInstanceKey));
LOGGER.atInfo()
.addArgument(() -> JsonPathUtils.getData(response, "$.testExecIssue.key"))
.log("Test Execution with key {} has been created");
}
}

public void updateTestSet(String testSetKey, List<String> testCaseKeys)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 the original author or authors.
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -21,6 +21,7 @@
import java.time.ZoneId;
import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

Expand All @@ -30,6 +31,7 @@
import org.vividus.model.jbehave.Scenario;
import org.vividus.xray.configuration.XrayExporterOptions;
import org.vividus.xray.model.TestExecution;
import org.vividus.xray.model.TestExecutionInfo;
import org.vividus.xray.model.TestExecutionItem;
import org.vividus.xray.model.TestExecutionItemStatus;

Expand All @@ -41,7 +43,17 @@ public class TestExecutionFactory
public TestExecution create(List<Entry<String, Scenario>> scenarios)
{
TestExecution testExecution = new TestExecution();
testExecution.setTestExecutionKey(xrayExporterOptions.getTestExecutionKey());

Optional.ofNullable(xrayExporterOptions.getTestExecutionKey())
.ifPresent(testExecution::setTestExecutionKey);

Optional.ofNullable(xrayExporterOptions.getTestExecutionSummary())
.ifPresent(summary ->
{
TestExecutionInfo info = new TestExecutionInfo();
info.setSummary(summary);
testExecution.setInfo(info);
});

List<TestExecutionItem> tests = scenarios.stream()
.map(TestExecutionFactory::createTestInfo)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 the original author or authors.
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -18,9 +18,14 @@

import java.util.List;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;

@JsonInclude(Include.NON_NULL)
public class TestExecution
{
private String testExecutionKey;
private TestExecutionInfo info;
private List<TestExecutionItem> tests;

public String getTestExecutionKey()
Expand All @@ -33,6 +38,16 @@ public void setTestExecutionKey(String testExecutionKey)
this.testExecutionKey = testExecutionKey;
}

public TestExecutionInfo getInfo()
{
return info;
}

public void setInfo(TestExecutionInfo info)
{
this.info = info;
}

public List<TestExecutionItem> getTests()
{
return tests;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2019-2023 the original author or authors.
*
* 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.vividus.xray.model;

public class TestExecutionInfo
{
private String summary;

public String getSummary()
{
return summary;
}

public void setSummary(String summary)
{
this.summary = summary;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 the original author or authors.
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -56,6 +56,8 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
Expand Down Expand Up @@ -175,7 +177,7 @@ void shouldExportTestWithLabelsAndComponentsAndUpdatableTestCaseId()
verifyManualTestCaseParameters(Set.of("dummy-label-1", "dummy-label-2"),
Set.of("dummy-component-1", "dummy-component-2"));

verify(xrayFacade).updateTestExecution(testExecution);
verify(xrayFacade).importTestExecution(testExecution);
List<Entry<String, Scenario>> scenarios = scenariosCaptor.getValue();
assertThat(scenarios, hasSize(1));
assertEquals(ISSUE_ID, scenarios.get(0).getKey());
Expand All @@ -184,9 +186,13 @@ void shouldExportTestWithLabelsAndComponentsAndUpdatableTestCaseId()
validateLogs(jsonResultsUri, getExportingScenarioEvent(), getExportSuccessfulEvent());
}

@Test
void shouldCompleteExportIfExportAttemptThrownIOException() throws URISyntaxException, IOException,
NonEditableIssueStatusException, JiraConfigurationException
@ParameterizedTest
@CsvSource({
TEST_EXECUTION_KEY + ", Failed to update test execution with the key TEST-EXEC: error message",
", Failed to create test execution: error message"
})
void shouldCompleteExportIfExportAttemptThrownIOException(String testExecutionKey, String testExecutionMessage)
throws URISyntaxException, IOException, NonEditableIssueStatusException, JiraConfigurationException
{
URI jsonResultsUri = getJsonResultsUri("continueiferror");
xrayExporterOptions.setJsonResultsDirectory(Paths.get(jsonResultsUri));
Expand All @@ -203,18 +209,18 @@ void shouldCompleteExportIfExportAttemptThrownIOException() throws URISyntaxExce
xrayExporterOptions.setTestSetKey(TEST_SET_KEY);
doThrow(exception).when(xrayFacade).updateTestSet(TEST_SET_KEY, List.of(ISSUE_ID));
errorLogMessage += "Error #2" + lineSeparator()
+ "Failed updating test set with the key TEST-SET: error message" + lineSeparator();
+ "Failed to update test set with the key TEST-SET: error message" + lineSeparator();

xrayExporterOptions.setTestExecutionKey(TEST_EXECUTION_KEY);
doThrow(exception).when(xrayFacade).updateTestExecution(any());
errorLogMessage += "Error #3" + lineSeparator()
+ "Failed updating test execution with the key TEST-EXEC: error message" + lineSeparator();
xrayExporterOptions.setTestExecutionKey(testExecutionKey);
xrayExporterOptions.setTestExecutionSummary("summary");
doThrow(exception).when(xrayFacade).importTestExecution(any());
errorLogMessage += "Error #3" + lineSeparator() + testExecutionMessage + lineSeparator();

xrayExporter.exportResults();

verify(xrayFacade).updateTestCase(ISSUE_ID, testCase);
verify(xrayFacade).updateTestSet(TEST_SET_KEY, List.of(ISSUE_ID));
verify(xrayFacade).updateTestExecution(any());
verify(xrayFacade).importTestExecution(any());
verifyManualTestCaseParameters(Set.of(), Set.of());
validateLogs(jsonResultsUri, getExportingScenarioEvent(), error(exception, ERROR_MESSAGE),
getExportingScenarioEvent(), getExportFailedErrorEvent(errorLogMessage));
Expand Down
Loading

0 comments on commit d2ed3ff

Please sign in to comment.