Skip to content
Merged
Show file tree
Hide file tree
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
Expand Up @@ -3,6 +3,7 @@
import com.publicissapient.kpidashboard.common.constant.ProcessorConstants;
import com.publicissapient.kpidashboard.common.exceptions.ClientErrorMessageEnum;
import com.publicissapient.kpidashboard.common.model.processortool.ProcessorToolConnection;
import com.publicissapient.kpidashboard.common.model.zephyr.TestCaseExecutionData;
import com.publicissapient.kpidashboard.common.model.zephyr.ZephyrTestCaseDTO;
import com.publicissapient.kpidashboard.common.processortool.service.ProcessorToolConnectionService;
import com.publicissapient.kpidashboard.zephyr.client.ZephyrClient;
Expand All @@ -29,6 +30,7 @@
import org.springframework.web.util.UriComponentsBuilder;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
Expand All @@ -48,6 +50,8 @@ public class ZephyrCloudImpl implements ZephyrClient {

private static final String FOLDERS_RESOURCE_ENDPOINT = "https://api.zephyrscale.smartbear.com/v2/folders?maxResults=1000";
private static final String TEST_CASE_ENDPOINT = "testcases";
private static final String TEST_CASE_EXECUTION_DATA_ENDPOINT = "testexecutions";
private static final String TEST_CASE = "testCase";
private static final String PROJECT_KEY = "projectKey";
private static final String START_AT = "startAt";
private static final String MAX_RESULTS = "maxResults";
Expand Down Expand Up @@ -124,7 +128,7 @@ public List<ZephyrTestCaseDTO> getTestCase(int startAt, ProjectConfFieldMapping
ResponseEntity<String> response = restTemplate.exchange(testCaseUrl, HttpMethod.GET, httpEntity,
String.class);
if (response.getStatusCode() == HttpStatus.OK && Objects.nonNull(response.getBody())) {
parseResponseAndPrepareTestCases(testCaseList, accessToken, jiraCloudCredential, response, folderMap);
parseResponseAndPrepareTestCases(testCaseList, accessToken, jiraCloudCredential, response, folderMap, zephyrCloudUrl);
} else {
String statusCode = response.getStatusCode().toString();
log.error("Error while fetching projects from {}. with status {}", testCaseUrl, statusCode);
Expand Down Expand Up @@ -166,7 +170,7 @@ private void isClientException(ProcessorToolConnection toolInfo, Exception excep
* @param folderMap
*/
private void parseResponseAndPrepareTestCases(List<ZephyrTestCaseDTO> testCaseList, String accessToken,
String jiraCloudCredential, ResponseEntity<String> response, Map<String, String> folderMap)
String jiraCloudCredential, ResponseEntity<String> response, Map<String, String> folderMap, String zephyrCloudUrl)
throws ParseException {
JSONArray testCaseArr = parseData(response.getBody(), VALUES);
for (Object testCaseObj : testCaseArr) {
Expand All @@ -188,6 +192,7 @@ private void parseResponseAndPrepareTestCases(List<ZephyrTestCaseDTO> testCaseLi
zephyrTestCaseDTO.setFolder(folder);
zephyrTestCaseDTO.setIssueLinks(issueLinks);
zephyrTestCaseDTO.setCustomFields(customFields);
zephyrTestCaseDTO.setTestCaseExecutionData(fetchExecutionsForTestCase(key, zephyrCloudUrl ,accessToken ));
testCaseList.add(zephyrTestCaseDTO);
}
}
Expand Down Expand Up @@ -432,4 +437,72 @@ private static boolean folderFound(String currentId, ZephyrCloudFolderResponse f
}
return false;
}

public List<TestCaseExecutionData> fetchExecutionsForTestCase(String testCaseKey, String zephyrCloudUrl, String accessToken) {
try {
String executionsUrl = UriComponentsBuilder.fromHttpUrl(zephyrCloudUrl)
.path(TEST_CASE_EXECUTION_DATA_ENDPOINT)
.queryParam(TEST_CASE, testCaseKey)
.build(false)
.toString();

log.info("Calling Zephyr executions API: {}", executionsUrl);

HttpEntity<String> httpEntity = zephyrUtil.buildAuthHeaderUsingToken(accessToken);
ResponseEntity<String> response = restTemplate.exchange(executionsUrl, HttpMethod.GET, httpEntity, String.class);

if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
return parseResponseAndPrepareTestCaseExecutionData(response.getBody());
} else {
log.warn("Failed to fetch executions for testCaseKey {}. Status: {}", testCaseKey, response.getStatusCode());
}

} catch (Exception e) {
log.error("Error while fetching executions for testCaseKey {}: {}", testCaseKey, e.getMessage(), e);
}

return Collections.emptyList();
}
private List<TestCaseExecutionData> parseResponseAndPrepareTestCaseExecutionData(String responseBody) throws ParseException {
List<TestCaseExecutionData> executions = new ArrayList<>();
JSONArray executionsArray = parseData(responseBody, VALUES);

for (Object obj : executionsArray) {
JSONObject executionJson = (JSONObject) obj;
TestCaseExecutionData execution = new TestCaseExecutionData();

execution.setExecutionId(safeInt(executionJson.get("id")) );
execution.setKey((String) executionJson.get("key"));
execution.setExecutionTime(safeInt(executionJson.get("executionTime")));
execution.setEstimatedTime(safeInt(executionJson.get("estimatedTime")));
execution.setActualEndDate((String) executionJson.get("actualEndDate"));
execution.setComment((String) executionJson.get("comment"));

JSONObject testCycle = (JSONObject) executionJson.get("testCycle");
if (testCycle != null) {
execution.setTestCycleId(safeInt(testCycle.get("id")));
}

JSONObject testCase = (JSONObject) executionJson.get(TEST_CASE);
if (testCase != null) {
execution.setTestCaseId(safeInt(testCase.get("id")));
}

execution.setExecutedById((String) executionJson.get("executedById"));
execution.setAssignedToId((String) executionJson.get("assignedToId"));
executions.add(execution);
}

return executions;
}

private int safeInt(Object value) {
try {
return value != null ? Integer.parseInt(value.toString()) : 0;
} catch (NumberFormatException e) {
log.warn("Unable to parse integer from value: {}", value);
return 0;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ private void setTestCaseDetails(ProcessorToolConnection processorToolConnection,
}
testCaseDetails.setDefectRaisedBy(testCase.getOwner());
testCaseDetails.setName(testCase.getName());
testCaseDetails.setExecutions(testCase.getTestCaseExecutionData());
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.publicissapient.kpidashboard.zephyr.processor.service.impl;

import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
Expand All @@ -16,11 +17,13 @@
import java.util.Map;
import java.util.Set;

import com.publicissapient.kpidashboard.common.model.zephyr.TestCaseExecutionData;
import org.apache.commons.io.IOUtils;
import org.bson.types.ObjectId;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentMatchers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.http.HttpEntity;
Expand Down Expand Up @@ -71,6 +74,22 @@ public class ZephyrCloudImplTest {

private String folderPath;

private static final String TEST_CASE_KEY = "TC-123";
private static final String ZEPHYR_URL = "https://zephyr.example.com";
private static final String TOKEN = "token";

private static final String EXECUTION_JSON = """
{
"values": [
{
"id": "1",
"statusName": "Pass",
"executionTime": 120,
"executionEndDate": "2025-08-12T10:15:30Z"
}
]
}
""";
@Mock
private HttpEntity<String> mockHttpEntity;

Expand Down Expand Up @@ -271,4 +290,55 @@ public void getTestCaseFailed() throws Exception {
private String getServerResponseFromJson(String resource) throws Exception {
return IOUtils.toString(getClass().getClassLoader().getResourceAsStream(resource), StandardCharsets.UTF_8);
}

@Test
void whenApiReturnsValidResponse_expectParsedExecutionData() {

HttpEntity<String> httpEntity = new HttpEntity<>("headers");
when(zephyrUtil.buildAuthHeaderUsingToken(TOKEN)).thenReturn(httpEntity);

ResponseEntity<String> response = new ResponseEntity<>(EXECUTION_JSON, HttpStatus.OK);
when(restTemplate.exchange(
ArgumentMatchers.contains(TEST_CASE_KEY),
eq(HttpMethod.GET),
eq(httpEntity),
eq(String.class)
)).thenReturn(response);

List<TestCaseExecutionData> result = zephyrCloud.fetchExecutionsForTestCase(TEST_CASE_KEY, ZEPHYR_URL, TOKEN);

assertThat(result).isNotEmpty();
assertThat(result.get(0).getExecutionTime()).isEqualTo(120);
}

@Test
void whenApiReturnsNonOkStatus_expectEmptyList() {
HttpEntity<String> httpEntity = new HttpEntity<>("headers");
when(zephyrUtil.buildAuthHeaderUsingToken(TOKEN)).thenReturn(httpEntity);

ResponseEntity<String> response = new ResponseEntity<>("", HttpStatus.BAD_REQUEST);
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(httpEntity), eq(String.class)))
.thenReturn(response);

List<TestCaseExecutionData> result = zephyrCloud.fetchExecutionsForTestCase(TEST_CASE_KEY, ZEPHYR_URL, TOKEN);

assertThat(result).isEmpty();
}

@Test
void whenApiThrowsException_expectEmptyList() {
HttpEntity<String> httpEntity = new HttpEntity<>("headers");
when(zephyrUtil.buildAuthHeaderUsingToken(TOKEN)).thenReturn(httpEntity);

when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(httpEntity), eq(String.class)))
.thenThrow(new RuntimeException("Boom"));

List<TestCaseExecutionData> result = zephyrCloud.fetchExecutionsForTestCase(TEST_CASE_KEY, ZEPHYR_URL, TOKEN);

assertThat(result).isEmpty();
}

}



4 changes: 2 additions & 2 deletions knowhow-scm-processor/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

<groupId>com.company</groupId>
<artifactId>knowhow-scm-processor</artifactId>
<version>1.0.0-SNAPSHOT</version>
<version>14.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SCM Processor</name>
<description>Multi-platform Git metadata scanner and analyzer</description>
Expand Down Expand Up @@ -260,7 +260,7 @@
<dependency>
<groupId>com.publicissapient.kpidashboard</groupId>
<artifactId>common</artifactId>
<version>13.4.0-SNAPSHOT</version>
<version>14.0.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
Expand Down