Skip to content

Commit c20dcbf

Browse files
Merge pull request #80 from PublicisSapient/DTS-48069_TestExecutionTimeKPI_RP
DTS-48069 - Get Test Case Execution Details from Zephyr
2 parents 0fd2541 + 2ae14cf commit c20dcbf

File tree

4 files changed

+148
-4
lines changed

4 files changed

+148
-4
lines changed

jira-zephyr-scale/src/main/java/com/publicissapient/kpidashboard/zephyr/processor/service/impl/ZephyrCloudImpl.java

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.publicissapient.kpidashboard.common.constant.ProcessorConstants;
44
import com.publicissapient.kpidashboard.common.exceptions.ClientErrorMessageEnum;
55
import com.publicissapient.kpidashboard.common.model.processortool.ProcessorToolConnection;
6+
import com.publicissapient.kpidashboard.common.model.zephyr.TestCaseExecutionData;
67
import com.publicissapient.kpidashboard.common.model.zephyr.ZephyrTestCaseDTO;
78
import com.publicissapient.kpidashboard.common.processortool.service.ProcessorToolConnectionService;
89
import com.publicissapient.kpidashboard.zephyr.client.ZephyrClient;
@@ -29,6 +30,7 @@
2930
import org.springframework.web.util.UriComponentsBuilder;
3031

3132
import java.util.ArrayList;
33+
import java.util.Collections;
3234
import java.util.HashMap;
3335
import java.util.HashSet;
3436
import java.util.Iterator;
@@ -48,6 +50,8 @@ public class ZephyrCloudImpl implements ZephyrClient {
4850

4951
private static final String FOLDERS_RESOURCE_ENDPOINT = "https://api.zephyrscale.smartbear.com/v2/folders?maxResults=1000";
5052
private static final String TEST_CASE_ENDPOINT = "testcases";
53+
private static final String TEST_CASE_EXECUTION_DATA_ENDPOINT = "testexecutions";
54+
private static final String TEST_CASE = "testCase";
5155
private static final String PROJECT_KEY = "projectKey";
5256
private static final String START_AT = "startAt";
5357
private static final String MAX_RESULTS = "maxResults";
@@ -124,7 +128,7 @@ public List<ZephyrTestCaseDTO> getTestCase(int startAt, ProjectConfFieldMapping
124128
ResponseEntity<String> response = restTemplate.exchange(testCaseUrl, HttpMethod.GET, httpEntity,
125129
String.class);
126130
if (response.getStatusCode() == HttpStatus.OK && Objects.nonNull(response.getBody())) {
127-
parseResponseAndPrepareTestCases(testCaseList, accessToken, jiraCloudCredential, response, folderMap);
131+
parseResponseAndPrepareTestCases(testCaseList, accessToken, jiraCloudCredential, response, folderMap, zephyrCloudUrl);
128132
} else {
129133
String statusCode = response.getStatusCode().toString();
130134
log.error("Error while fetching projects from {}. with status {}", testCaseUrl, statusCode);
@@ -166,7 +170,7 @@ private void isClientException(ProcessorToolConnection toolInfo, Exception excep
166170
* @param folderMap
167171
*/
168172
private void parseResponseAndPrepareTestCases(List<ZephyrTestCaseDTO> testCaseList, String accessToken,
169-
String jiraCloudCredential, ResponseEntity<String> response, Map<String, String> folderMap)
173+
String jiraCloudCredential, ResponseEntity<String> response, Map<String, String> folderMap, String zephyrCloudUrl)
170174
throws ParseException {
171175
JSONArray testCaseArr = parseData(response.getBody(), VALUES);
172176
for (Object testCaseObj : testCaseArr) {
@@ -188,6 +192,7 @@ private void parseResponseAndPrepareTestCases(List<ZephyrTestCaseDTO> testCaseLi
188192
zephyrTestCaseDTO.setFolder(folder);
189193
zephyrTestCaseDTO.setIssueLinks(issueLinks);
190194
zephyrTestCaseDTO.setCustomFields(customFields);
195+
zephyrTestCaseDTO.setTestCaseExecutionData(fetchExecutionsForTestCase(key, zephyrCloudUrl ,accessToken ));
191196
testCaseList.add(zephyrTestCaseDTO);
192197
}
193198
}
@@ -432,4 +437,72 @@ private static boolean folderFound(String currentId, ZephyrCloudFolderResponse f
432437
}
433438
return false;
434439
}
440+
441+
public List<TestCaseExecutionData> fetchExecutionsForTestCase(String testCaseKey, String zephyrCloudUrl, String accessToken) {
442+
try {
443+
String executionsUrl = UriComponentsBuilder.fromHttpUrl(zephyrCloudUrl)
444+
.path(TEST_CASE_EXECUTION_DATA_ENDPOINT)
445+
.queryParam(TEST_CASE, testCaseKey)
446+
.build(false)
447+
.toString();
448+
449+
log.info("Calling Zephyr executions API: {}", executionsUrl);
450+
451+
HttpEntity<String> httpEntity = zephyrUtil.buildAuthHeaderUsingToken(accessToken);
452+
ResponseEntity<String> response = restTemplate.exchange(executionsUrl, HttpMethod.GET, httpEntity, String.class);
453+
454+
if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
455+
return parseResponseAndPrepareTestCaseExecutionData(response.getBody());
456+
} else {
457+
log.warn("Failed to fetch executions for testCaseKey {}. Status: {}", testCaseKey, response.getStatusCode());
458+
}
459+
460+
} catch (Exception e) {
461+
log.error("Error while fetching executions for testCaseKey {}: {}", testCaseKey, e.getMessage(), e);
462+
}
463+
464+
return Collections.emptyList();
465+
}
466+
private List<TestCaseExecutionData> parseResponseAndPrepareTestCaseExecutionData(String responseBody) throws ParseException {
467+
List<TestCaseExecutionData> executions = new ArrayList<>();
468+
JSONArray executionsArray = parseData(responseBody, VALUES);
469+
470+
for (Object obj : executionsArray) {
471+
JSONObject executionJson = (JSONObject) obj;
472+
TestCaseExecutionData execution = new TestCaseExecutionData();
473+
474+
execution.setExecutionId(safeInt(executionJson.get("id")) );
475+
execution.setKey((String) executionJson.get("key"));
476+
execution.setExecutionTime(safeInt(executionJson.get("executionTime")));
477+
execution.setEstimatedTime(safeInt(executionJson.get("estimatedTime")));
478+
execution.setActualEndDate((String) executionJson.get("actualEndDate"));
479+
execution.setComment((String) executionJson.get("comment"));
480+
481+
JSONObject testCycle = (JSONObject) executionJson.get("testCycle");
482+
if (testCycle != null) {
483+
execution.setTestCycleId(safeInt(testCycle.get("id")));
484+
}
485+
486+
JSONObject testCase = (JSONObject) executionJson.get(TEST_CASE);
487+
if (testCase != null) {
488+
execution.setTestCaseId(safeInt(testCase.get("id")));
489+
}
490+
491+
execution.setExecutedById((String) executionJson.get("executedById"));
492+
execution.setAssignedToId((String) executionJson.get("assignedToId"));
493+
executions.add(execution);
494+
}
495+
496+
return executions;
497+
}
498+
499+
private int safeInt(Object value) {
500+
try {
501+
return value != null ? Integer.parseInt(value.toString()) : 0;
502+
} catch (NumberFormatException e) {
503+
log.warn("Unable to parse integer from value: {}", value);
504+
return 0;
505+
}
506+
}
507+
435508
}

jira-zephyr-scale/src/main/java/com/publicissapient/kpidashboard/zephyr/processor/service/impl/ZephyrDBServiceImpl.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ private void setTestCaseDetails(ProcessorToolConnection processorToolConnection,
148148
}
149149
testCaseDetails.setDefectRaisedBy(testCase.getOwner());
150150
testCaseDetails.setName(testCase.getName());
151+
testCaseDetails.setExecutions(testCase.getTestCaseExecutionData());
151152
}
152153

153154
/**

jira-zephyr-scale/src/test/java/com/publicissapient/kpidashboard/zephyr/processor/service/impl/ZephyrCloudImplTest.java

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.publicissapient.kpidashboard.zephyr.processor.service.impl;
22

3+
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
34
import static org.junit.Assert.assertEquals;
45
import static org.junit.Assert.assertThrows;
56
import static org.mockito.ArgumentMatchers.any;
@@ -16,11 +17,13 @@
1617
import java.util.Map;
1718
import java.util.Set;
1819

20+
import com.publicissapient.kpidashboard.common.model.zephyr.TestCaseExecutionData;
1921
import org.apache.commons.io.IOUtils;
2022
import org.bson.types.ObjectId;
2123
import org.junit.jupiter.api.BeforeEach;
2224
import org.junit.jupiter.api.Test;
2325
import org.junit.jupiter.api.extension.ExtendWith;
26+
import org.mockito.ArgumentMatchers;
2427
import org.mockito.InjectMocks;
2528
import org.mockito.Mock;
2629
import org.springframework.http.HttpEntity;
@@ -71,6 +74,22 @@ public class ZephyrCloudImplTest {
7174

7275
private String folderPath;
7376

77+
private static final String TEST_CASE_KEY = "TC-123";
78+
private static final String ZEPHYR_URL = "https://zephyr.example.com";
79+
private static final String TOKEN = "token";
80+
81+
private static final String EXECUTION_JSON = """
82+
{
83+
"values": [
84+
{
85+
"id": "1",
86+
"statusName": "Pass",
87+
"executionTime": 120,
88+
"executionEndDate": "2025-08-12T10:15:30Z"
89+
}
90+
]
91+
}
92+
""";
7493
@Mock
7594
private HttpEntity<String> mockHttpEntity;
7695

@@ -271,4 +290,55 @@ public void getTestCaseFailed() throws Exception {
271290
private String getServerResponseFromJson(String resource) throws Exception {
272291
return IOUtils.toString(getClass().getClassLoader().getResourceAsStream(resource), StandardCharsets.UTF_8);
273292
}
293+
294+
@Test
295+
void whenApiReturnsValidResponse_expectParsedExecutionData() {
296+
297+
HttpEntity<String> httpEntity = new HttpEntity<>("headers");
298+
when(zephyrUtil.buildAuthHeaderUsingToken(TOKEN)).thenReturn(httpEntity);
299+
300+
ResponseEntity<String> response = new ResponseEntity<>(EXECUTION_JSON, HttpStatus.OK);
301+
when(restTemplate.exchange(
302+
ArgumentMatchers.contains(TEST_CASE_KEY),
303+
eq(HttpMethod.GET),
304+
eq(httpEntity),
305+
eq(String.class)
306+
)).thenReturn(response);
307+
308+
List<TestCaseExecutionData> result = zephyrCloud.fetchExecutionsForTestCase(TEST_CASE_KEY, ZEPHYR_URL, TOKEN);
309+
310+
assertThat(result).isNotEmpty();
311+
assertThat(result.get(0).getExecutionTime()).isEqualTo(120);
312+
}
313+
314+
@Test
315+
void whenApiReturnsNonOkStatus_expectEmptyList() {
316+
HttpEntity<String> httpEntity = new HttpEntity<>("headers");
317+
when(zephyrUtil.buildAuthHeaderUsingToken(TOKEN)).thenReturn(httpEntity);
318+
319+
ResponseEntity<String> response = new ResponseEntity<>("", HttpStatus.BAD_REQUEST);
320+
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(httpEntity), eq(String.class)))
321+
.thenReturn(response);
322+
323+
List<TestCaseExecutionData> result = zephyrCloud.fetchExecutionsForTestCase(TEST_CASE_KEY, ZEPHYR_URL, TOKEN);
324+
325+
assertThat(result).isEmpty();
326+
}
327+
328+
@Test
329+
void whenApiThrowsException_expectEmptyList() {
330+
HttpEntity<String> httpEntity = new HttpEntity<>("headers");
331+
when(zephyrUtil.buildAuthHeaderUsingToken(TOKEN)).thenReturn(httpEntity);
332+
333+
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(httpEntity), eq(String.class)))
334+
.thenThrow(new RuntimeException("Boom"));
335+
336+
List<TestCaseExecutionData> result = zephyrCloud.fetchExecutionsForTestCase(TEST_CASE_KEY, ZEPHYR_URL, TOKEN);
337+
338+
assertThat(result).isEmpty();
339+
}
340+
274341
}
342+
343+
344+

knowhow-scm-processor/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
<groupId>com.company</groupId>
1616
<artifactId>knowhow-scm-processor</artifactId>
17-
<version>1.0.0-SNAPSHOT</version>
17+
<version>14.0.0-SNAPSHOT</version>
1818
<packaging>jar</packaging>
1919
<name>SCM Processor</name>
2020
<description>Multi-platform Git metadata scanner and analyzer</description>
@@ -260,7 +260,7 @@
260260
<dependency>
261261
<groupId>com.publicissapient.kpidashboard</groupId>
262262
<artifactId>common</artifactId>
263-
<version>13.4.0-SNAPSHOT</version>
263+
<version>14.0.0-SNAPSHOT</version>
264264
<exclusions>
265265
<exclusion>
266266
<groupId>ch.qos.logback</groupId>

0 commit comments

Comments
 (0)