Skip to content

Commit 11e0440

Browse files
author
Roberto Nares
committed
CTP-11042 Improve baggage and publish logging
1 parent c56aa7a commit 11e0440

3 files changed

Lines changed: 238 additions & 3 deletions

File tree

coverage-integration-core/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@
5252
<scope>test</scope>
5353
</dependency>
5454

55+
<dependency>
56+
<groupId>ch.qos.logback</groupId>
57+
<artifactId>logback-classic</artifactId>
58+
<version>1.5.38</version>
59+
<scope>test</scope>
60+
</dependency>
61+
5562
<dependency>
5663
<groupId>org.wiremock</groupId>
5764
<artifactId>wiremock</artifactId>

coverage-integration-core/src/main/java/com/parasoft/coverage/integration/core/ParasoftCoverageApiClient.java

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,16 @@ public CoverageTestContext startTest(String test, String testCase)
155155

156156
String baggageHeader = status == null ? null : status.getBaggage();
157157

158+
if (parallelIdEnabled && (baggageHeader == null || baggageHeader.isBlank())) {
159+
LOGGER.debug(
160+
"CTP startTest response did not include the baggage property: test={}, testCase={}, parallelId={}, responsePresent={}",
161+
test,
162+
testCase,
163+
parallelId,
164+
status != null);
165+
LOGGER.warn("This version of CTP does not support parallel tests within a single coverage session.");
166+
}
167+
158168
LOGGER.debug("Started Parasoft coverage test: test={}, testCase={}", test, testCase);
159169
return new CoverageTestContext(parallelId, baggageHeader);
160170
}
@@ -208,6 +218,7 @@ private String createParallelId()
208218
@Override
209219
public void publishResults(String sessionId, String testConfig, String userId, String toolName)
210220
{
221+
LOGGER.info("Publishing coverage and test results to DTP...");
211222
try {
212223
String effectiveUserId = userId != null ? userId : this.userId;
213224
CoverageUploadRequestModelV3 uploadRequest = new CoverageUploadRequestModelV3();
@@ -232,12 +243,19 @@ private void pollPublishStatus(String sessionId)
232243
coverageApi.getCoverageSessionPublishStatus(environmentId, sessionId, userId);
233244

234245
if (result != null) {
235-
LOGGER.info(result.getMessage());
236-
237246
StatusEnum status = result.getStatus();
238-
if (status == StatusEnum.PUBLISHED || status == StatusEnum.ERROR) {
247+
String message = result.getMessage();
248+
249+
if (status == StatusEnum.PUBLISHED) {
250+
LOGGER.info(message);
251+
return;
252+
}
253+
if (status == StatusEnum.ERROR) {
254+
LOGGER.error(message);
239255
return;
240256
}
257+
258+
LOGGER.debug(message);
241259
}
242260
}
243261
catch (ApiException e) {

coverage-integration-core/src/test/java/com/parasoft/coverage/integration/core/ParasoftCoverageApiClientTest.java

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
2929
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
3030
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
31+
import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
3132
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
3233
import static org.junit.jupiter.api.Assertions.assertEquals;
3334
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -38,8 +39,16 @@
3839
import java.time.Duration;
3940
import java.util.UUID;
4041

42+
import org.junit.jupiter.api.AfterEach;
43+
import org.junit.jupiter.api.BeforeEach;
4144
import org.junit.jupiter.api.Test;
4245
import org.junit.jupiter.api.extension.RegisterExtension;
46+
import org.slf4j.LoggerFactory;
47+
48+
import ch.qos.logback.classic.Level;
49+
import ch.qos.logback.classic.Logger;
50+
import ch.qos.logback.classic.spi.ILoggingEvent;
51+
import ch.qos.logback.core.read.ListAppender;
4352

4453
import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
4554
import com.github.tomakehurst.wiremock.matching.RequestPatternBuilder;
@@ -59,6 +68,15 @@ class ParasoftCoverageApiClientTest
5968
private static final String BAGGAGE_HEADER = "test-operator-id=automation-user+parallel-id";
6069
private static final String FAILURE_MESSAGE = "expected 4 but was 5";
6170

71+
private static final String MISSING_BAGGAGE_WARNING =
72+
"This version of CTP does not support parallel tests within a single coverage session.";
73+
private static final String PUBLISH_STATUS_MESSAGE =
74+
"Publishing coverage and test results to DTP...";
75+
private static final String PUBLISH_SUCCESS_MESSAGE =
76+
"Successfully published coverage and results to DTP.";
77+
private static final String PUBLISH_FAILURE_MESSAGE =
78+
"Failed to publish coverage and results to DTP.";
79+
6280
private static final String SESSION_START_PATH = "/api/v3/environments/42/agents/session/start";
6381
private static final String TEST_START_PATH = "/api/v3/environments/42/agents/test/start";
6482
private static final String TEST_STOP_PATH = "/api/v3/environments/42/agents/test/stop";
@@ -82,11 +100,45 @@ class ParasoftCoverageApiClientTest
82100
}
83101
""";
84102

103+
private static final String TEST_STATUS_WITHOUT_BAGGAGE_RESPONSE = """
104+
{
105+
"test": "com.example.CalculatorTest",
106+
"testCase": "addsNumbers",
107+
"session": "coverage-session-123"
108+
}
109+
""";
110+
85111
@RegisterExtension
86112
static final WireMockExtension WIREMOCK = WireMockExtension.newInstance()
87113
.options(wireMockConfig().dynamicPort())
88114
.build();
89115

116+
private final Logger clientLogger =
117+
(Logger) LoggerFactory.getLogger(ParasoftCoverageApiClient.class);
118+
private final ListAppender<ILoggingEvent> logAppender = new ListAppender<>();
119+
private Level originalLogLevel;
120+
private boolean originalAdditive;
121+
122+
@BeforeEach
123+
void attachLogAppender()
124+
{
125+
originalLogLevel = clientLogger.getLevel();
126+
originalAdditive = clientLogger.isAdditive();
127+
clientLogger.setLevel(Level.DEBUG);
128+
clientLogger.setAdditive(false);
129+
logAppender.start();
130+
clientLogger.addAppender(logAppender);
131+
}
132+
133+
@AfterEach
134+
void detachLogAppender()
135+
{
136+
clientLogger.detachAppender(logAppender);
137+
logAppender.stop();
138+
clientLogger.setLevel(originalLogLevel);
139+
clientLogger.setAdditive(originalAdditive);
140+
}
141+
90142
@Test
91143
void executesCoverageLifecycleWithBearerAuthenticationAndParallelId()
92144
{
@@ -207,6 +259,55 @@ void omitsParallelIdWhenParallelExecutionIsDisabled()
207259
""")));
208260
}
209261

262+
@Test
263+
void logsDebugAndWarningWhenParallelStartTestResponseDoesNotIncludeBaggage()
264+
{
265+
WIREMOCK.stubFor(post(urlEqualTo(TEST_START_PATH))
266+
.willReturn(okJson(TEST_STATUS_WITHOUT_BAGGAGE_RESPONSE)));
267+
268+
ParasoftCoverageApiClient client = createClient(
269+
true,
270+
null,
271+
null,
272+
BEARER_TOKEN);
273+
274+
CoverageTestContext testContext = client.startTest(TEST_ID, TEST_CASE_ID);
275+
276+
assertNotNull(testContext);
277+
assertNotNull(testContext.getParallelId());
278+
assertNull(testContext.getBaggageHeader());
279+
assertEquals(1, countLogEvents(
280+
Level.DEBUG,
281+
"CTP startTest response did not include the baggage property: test="
282+
+ TEST_ID
283+
+ ", testCase="
284+
+ TEST_CASE_ID
285+
+ ", parallelId="
286+
+ testContext.getParallelId()
287+
+ ", responsePresent=true"));
288+
assertEquals(1, countLogEvents(Level.WARN, MISSING_BAGGAGE_WARNING));
289+
}
290+
291+
@Test
292+
void doesNotLogMissingBaggageWarningWhenParallelExecutionIsDisabled()
293+
{
294+
WIREMOCK.stubFor(post(urlEqualTo(TEST_START_PATH))
295+
.willReturn(okJson(TEST_STATUS_WITHOUT_BAGGAGE_RESPONSE)));
296+
297+
ParasoftCoverageApiClient client = createClient(
298+
false,
299+
null,
300+
null,
301+
BEARER_TOKEN);
302+
303+
CoverageTestContext testContext = client.startTest(TEST_ID, TEST_CASE_ID);
304+
305+
assertNotNull(testContext);
306+
assertNull(testContext.getParallelId());
307+
assertNull(testContext.getBaggageHeader());
308+
assertEquals(0, countLogEvents(Level.WARN, MISSING_BAGGAGE_WARNING));
309+
}
310+
210311
@Test
211312
void usesBasicAuthenticationWhenBearerTokenIsNotConfigured()
212313
{
@@ -692,6 +793,7 @@ void returnsTestContextWithoutBaggageWhenStartTestRequestFails()
692793
assertFalse(testContext.getParallelId().isBlank());
693794
assertDoesNotThrow(() -> UUID.fromString(testContext.getParallelId()));
694795
assertNull(testContext.getBaggageHeader());
796+
assertEquals(0, countLogEvents(Level.WARN, MISSING_BAGGAGE_WARNING));
695797

696798
WIREMOCK.verify(1, postRequestedFor(urlEqualTo(TEST_START_PATH))
697799
.withHeader("Authorization", equalTo("Bearer " + BEARER_TOKEN))
@@ -885,6 +987,114 @@ void publishesCoverageAsynchronouslyAndPollsUntilPublished()
885987
WIREMOCK.verify(1, pollRequest);
886988
}
887989

990+
@Test
991+
void logsPublishStartAndCompletionOnceWithoutRepeatingIntermediateInfoMessages()
992+
{
993+
WIREMOCK.stubFor(post(urlPathEqualTo(COVERAGE_PATH))
994+
.willReturn(okJson("""
995+
{
996+
"status": "PUBLISHING"
997+
}
998+
""")));
999+
1000+
String scenarioName = "publish status logging";
1001+
String secondPollState = "second poll";
1002+
String publishedState = "published";
1003+
1004+
WIREMOCK.stubFor(get(urlPathEqualTo(COVERAGE_PATH))
1005+
.inScenario(scenarioName)
1006+
.whenScenarioStateIs(STARTED)
1007+
.willReturn(okJson("""
1008+
{
1009+
"status": "PUBLISHING",
1010+
"message": "Publishing coverage and test results to DTP..."
1011+
}
1012+
"""))
1013+
.willSetStateTo(secondPollState));
1014+
1015+
WIREMOCK.stubFor(get(urlPathEqualTo(COVERAGE_PATH))
1016+
.inScenario(scenarioName)
1017+
.whenScenarioStateIs(secondPollState)
1018+
.willReturn(okJson("""
1019+
{
1020+
"status": "PUBLISHING",
1021+
"message": "Publishing coverage and test results to DTP..."
1022+
}
1023+
"""))
1024+
.willSetStateTo(publishedState));
1025+
1026+
WIREMOCK.stubFor(get(urlPathEqualTo(COVERAGE_PATH))
1027+
.inScenario(scenarioName)
1028+
.whenScenarioStateIs(publishedState)
1029+
.willReturn(okJson("""
1030+
{
1031+
"status": "PUBLISHED",
1032+
"message": "Successfully published coverage and results to DTP.",
1033+
"passed": 1,
1034+
"failed": 0,
1035+
"incomplete": 0
1036+
}
1037+
""")));
1038+
1039+
ParasoftCoverageApiClient client = createClient(
1040+
false,
1041+
null,
1042+
null,
1043+
BEARER_TOKEN);
1044+
1045+
client.publishResults(
1046+
SESSION_ID,
1047+
"Unit Test Configuration",
1048+
USER_ID,
1049+
"JUnit");
1050+
1051+
assertEquals(1, countLogEvents(Level.INFO, PUBLISH_STATUS_MESSAGE));
1052+
assertEquals(2, countLogEvents(Level.DEBUG, PUBLISH_STATUS_MESSAGE));
1053+
assertEquals(1, countLogEvents(Level.INFO, PUBLISH_SUCCESS_MESSAGE));
1054+
}
1055+
1056+
@Test
1057+
void logsPublishFailureOnce()
1058+
{
1059+
WIREMOCK.stubFor(post(urlPathEqualTo(COVERAGE_PATH))
1060+
.willReturn(okJson("""
1061+
{
1062+
"status": "PUBLISHING"
1063+
}
1064+
""")));
1065+
1066+
WIREMOCK.stubFor(get(urlPathEqualTo(COVERAGE_PATH))
1067+
.willReturn(okJson("""
1068+
{
1069+
"status": "ERROR",
1070+
"message": "Failed to publish coverage and results to DTP."
1071+
}
1072+
""")));
1073+
1074+
ParasoftCoverageApiClient client = createClient(
1075+
false,
1076+
null,
1077+
null,
1078+
BEARER_TOKEN);
1079+
1080+
client.publishResults(
1081+
SESSION_ID,
1082+
"Unit Test Configuration",
1083+
USER_ID,
1084+
"JUnit");
1085+
1086+
assertEquals(1, countLogEvents(Level.INFO, PUBLISH_STATUS_MESSAGE));
1087+
assertEquals(1, countLogEvents(Level.ERROR, PUBLISH_FAILURE_MESSAGE));
1088+
}
1089+
1090+
private long countLogEvents(Level level, String message)
1091+
{
1092+
return logAppender.list.stream()
1093+
.filter(event -> event.getLevel() == level)
1094+
.filter(event -> message.equals(event.getFormattedMessage()))
1095+
.count();
1096+
}
1097+
8881098
private static ParasoftCoverageApiClient createClient(
8891099
boolean parallelIdEnabled,
8901100
String username,

0 commit comments

Comments
 (0)