Skip to content

Commit b754d16

Browse files
authored
Adding pact verification Test (#43)
Added code to run pact provider test and publish verification report to pact broker
1 parent 9c989e8 commit b754d16

File tree

11 files changed

+245
-22
lines changed

11 files changed

+245
-22
lines changed

.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
SERVER_PORT=4550
2-
BASE_IMAGE=openjdk:21-jdk-slim
2+
BASE_IMAGE=openjdk:21-jdk-slim
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: Run pact provider test and publish verification report to pact broker
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
workflow_dispatch:
11+
12+
jobs:
13+
publish-provider-pacts:
14+
runs-on: ubuntu-latest
15+
16+
env:
17+
PACT_BROKER_URL: https://hmcts-dts.pactflow.io
18+
PACT_BROKER_HOST: hmcts-dts.pactflow.io
19+
PACT_BROKER_TOKEN: ${{ secrets.PACT_BROKER_TOKEN }}
20+
PACT_VERIFIER_PUBLISH_RESULTS: true
21+
GIT_COMMIT: ${{ github.sha }}
22+
GIT_BRANCH: ${{ github.ref_name }}
23+
PACT_ENV: dev/pactTest
24+
25+
26+
steps:
27+
- name: Checkout code
28+
uses: actions/checkout@v4
29+
30+
- name: Set up JDK 21
31+
uses: actions/setup-java@v3
32+
with:
33+
distribution: temurin
34+
java-version: 21
35+
36+
- name: Install Pact CLI
37+
run: npm install -g @pact-foundation/pact-cli
38+
39+
- name: Make provider script executable
40+
run: chmod +x publish-pacts.sh
41+
42+
- name: Run provider publish script
43+
run: ./publish-pacts.sh

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,6 @@ applicationinsights-agent-*.jar
3030
*.log
3131

3232
.DS_Store
33-
*/.DS_Store
33+
*/.DS_Store
34+
35+
.env

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@
44

55
Further documentation see the [HMCTS Marketplace Springboot template readme](https://github.com/hmcts/service-hmcts-marketplace-springboot-template/blob/main/README.md).
66

7+
### Run pact provider test and publish verification report to pact broker locally
8+
9+
update .env file with below details
10+
PACT_BROKER_URL= <<PactFlow broker url>>
11+
PACT_BROKER_HOST=<<PactFlow broker url excluding https://>>
12+
PACT_BROKER_TOKEN= <<Your PactFlow broker token>>
13+
PACT_ENV= << This is the environment in PactFlow broker to which we tag the contracts>
14+
PACT_VERIFIER_PUBLISH_RESULTS=true
15+
run ./publish-pacts.sh
16+
717
## License
818

919
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details

build.gradle

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ plugins {
77
id 'maven-publish'
88
id "com.github.ben-manes.versions" version "0.52.0"
99
id "org.cyclonedx.bom" version "2.3.1"
10+
id "au.com.dius.pact" version "4.6.17"
1011
}
1112

1213
group = 'uk.gov.hmcts.cp'
@@ -85,7 +86,7 @@ tasks.named('test') {
8586
// Mockito must be added as an agent, see:
8687
// https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#0.3
8788
jvmArgs += [
88-
"-javaagent:${configurations.testRuntimeClasspath.find { it.name.contains('mockito-core') }}", '-Xshare:off'
89+
"-javaagent:${configurations.testRuntimeClasspath.find { it.name.contains('mockito-core') }}", '-Xshare:off'
8990
]
9091
testLogging {
9192
events "passed", "skipped", "failed"
@@ -272,4 +273,26 @@ dependencies {
272273
exclude group: 'junit', module: 'junit'
273274
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
274275
}
276+
277+
testImplementation 'au.com.dius.pact.provider:junit5:4.6.17'
278+
testImplementation 'au.com.dius.pact.provider:spring6:4.6.17'
279+
280+
test{
281+
useJUnitPlatform {
282+
excludeTags 'pact'
283+
}
284+
}
285+
286+
tasks.register('pactVerificationTest', Test) {
287+
useJUnitPlatform {
288+
includeTags 'pact'
289+
}
290+
systemProperty 'pact.broker.url', System.getenv('PACT_BROKER_URL')
291+
systemProperty 'pact.broker.token', System.getenv('PACT_BROKER_TOKEN')
292+
systemProperty 'pact.provider.version', System.getenv('GIT_COMMIT') ?: 'dev'
293+
systemProperty 'pact.provider.branch', System.getenv('GIT_BRANCH')
294+
systemProperty 'pact.verifier.publishResults', System.getenv('PACT_VERIFIER_PUBLISH_RESULTS')
295+
systemProperty 'pact.broker.host', System.getenv('PACT_BROKER_HOST')
296+
systemProperty 'pact.env', System.getenv('PACT_ENV')
297+
}
275298
}

publish-pacts.sh

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/bin/bash
2+
3+
# Load env vars from .env if available
4+
if [ -f .env ]; then
5+
set -a
6+
source .env
7+
set +a
8+
fi
9+
10+
# Export Git metadata
11+
export GIT_COMMIT=$(git rev-parse HEAD)
12+
13+
if [ -n "$GIT_BRANCH" ]; then
14+
BRANCH_NAME="$GIT_BRANCH"
15+
else
16+
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
17+
fi
18+
19+
export GIT_BRANCH="$BRANCH_NAME"
20+
21+
# Run provider verification with results published
22+
gradle pactVerificationTest \
23+
-Dpact.provider.version="$GIT_COMMIT" \
24+
-Dpact.verifier.publishResults=true \
25+
-Dpact.provider.branch="$GIT_BRANCH" \
26+
-DPACT_BROKER_TOKEN="$PACT_BROKER_TOKEN" \
27+
-DPACT_BROKER_HOST="$PACT_BROKER_URL"
28+
29+
# Tag provider in the broker
30+
pact-broker create-version-tag \
31+
--pacticipant "VPCourtScheduleProvider" \
32+
--version "$GIT_COMMIT" \
33+
--tag "$PACT_ENV" \
34+
--broker-base-url "$PACT_BROKER_URL" \
35+
--broker-token "$PACT_BROKER_TOKEN"

src/main/java/uk/gov/hmcts/cp/repositories/CourtScheduleRepository.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
import org.springframework.stereotype.Repository;
44
import uk.gov.hmcts.cp.openapi.model.CourtScheduleResponse;
55

6-
@FunctionalInterface
76
@Repository
8-
public interface CourtScheduleRepository {
7+
public interface CourtScheduleRepository {
98

109
CourtScheduleResponse getCourtScheduleByCaseUrn(String caseUrn);
10+
void saveCourtSchedule(String caseUrn, CourtScheduleResponse courtScheduleResponse);
11+
void clearAll();
1112

1213
}
Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,64 @@
11
package uk.gov.hmcts.cp.repositories;
22

33
import org.springframework.stereotype.Component;
4-
import uk.gov.hmcts.cp.openapi.model.CourtSchedule;
54
import uk.gov.hmcts.cp.openapi.model.CourtScheduleResponse;
6-
import uk.gov.hmcts.cp.openapi.model.CourtSitting;
5+
import uk.gov.hmcts.cp.openapi.model.CourtSchedule;
76
import uk.gov.hmcts.cp.openapi.model.Hearing;
7+
import uk.gov.hmcts.cp.openapi.model.CourtSitting;
88

99
import java.time.OffsetDateTime;
10+
import java.time.ZoneOffset;
11+
import java.time.temporal.ChronoUnit;
1012
import java.util.List;
13+
import java.util.Map;
1114
import java.util.UUID;
15+
import java.util.concurrent.ConcurrentHashMap;
1216

1317
@Component
1418
public class InMemoryCourtScheduleRepositoryImpl implements CourtScheduleRepository {
1519

20+
private final Map<String, CourtScheduleResponse> courtScheduleResponseMap = new ConcurrentHashMap<>();
21+
22+
public void saveCourtSchedule(final String caseUrn, final CourtScheduleResponse courtScheduleResponse) {
23+
courtScheduleResponseMap.put(caseUrn, courtScheduleResponse);
24+
}
25+
1626
public CourtScheduleResponse getCourtScheduleByCaseUrn(final String caseUrn) {
17-
final Hearing courtScheduleHearing = Hearing.builder()
27+
if (!courtScheduleResponseMap.containsKey(caseUrn)) {
28+
saveCourtSchedule(caseUrn, createCourtScheduleResponse());
29+
}
30+
return courtScheduleResponseMap.get(caseUrn);
31+
}
32+
33+
public void clearAll() {
34+
courtScheduleResponseMap.clear();
35+
}
36+
37+
private CourtScheduleResponse createCourtScheduleResponse() {
38+
39+
final OffsetDateTime sittingStartTime = OffsetDateTime.now(ZoneOffset.UTC)
40+
.truncatedTo(ChronoUnit.SECONDS);
41+
42+
final Hearing hearing = Hearing.builder()
1843
.hearingId(UUID.randomUUID().toString())
1944
.listNote("Requires interpreter")
2045
.hearingDescription("Sentencing for theft case")
2146
.hearingType("Trial")
2247
.courtSittings(List.of(
2348
CourtSitting.builder()
2449
.courtHouse("Central Criminal Court")
25-
.sittingStart(OffsetDateTime.now())
26-
.sittingEnd(OffsetDateTime.now().plusMinutes(60))
50+
.sittingStart(sittingStartTime)
51+
.sittingEnd(sittingStartTime.plusMinutes(60))
2752
.judiciaryId(UUID.randomUUID().toString())
2853
.build())
2954
).build();
3055

3156
return CourtScheduleResponse.builder()
3257
.courtSchedule(List.of(
3358
CourtSchedule.builder()
34-
.hearings(List.of(courtScheduleHearing)
59+
.hearings(List.of(hearing)
3560
).build()
3661
)
3762
).build();
3863
}
39-
4064
}

src/test/java/uk/gov/hmcts/cp/controllers/CourtScheduleControllerTest.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.springframework.web.server.ResponseStatusException;
1010
import uk.gov.hmcts.cp.openapi.model.CourtSchedule;
1111
import uk.gov.hmcts.cp.openapi.model.CourtScheduleResponse;
12+
1213
import uk.gov.hmcts.cp.openapi.model.CourtSitting;
1314
import uk.gov.hmcts.cp.openapi.model.Hearing;
1415
import uk.gov.hmcts.cp.repositories.CourtScheduleRepository;
@@ -52,24 +53,24 @@ void getJudgeById_ShouldReturnJudgesWithOkStatus() {
5253
assertNotNull(responseBody.getCourtSchedule());
5354
assertEquals(1, responseBody.getCourtSchedule().size());
5455

55-
CourtSchedule schedule = responseBody.getCourtSchedule().get(0);
56-
assertNotNull(schedule.getHearings());
57-
assertEquals(1, schedule.getHearings().size());
56+
CourtSchedule courtSchedule = responseBody.getCourtSchedule().get(0);
57+
assertNotNull(courtSchedule.getHearings());
58+
assertEquals(1, courtSchedule.getHearings().size());
5859

59-
Hearing hearing = schedule.getHearings().get(0);
60+
Hearing hearing = courtSchedule.getHearings().get(0);
6061
assertNotNull(hearing.getHearingId());
6162
assertEquals("Requires interpreter", hearing.getListNote());
6263
assertEquals("Sentencing for theft case", hearing.getHearingDescription());
6364
assertEquals("Trial", hearing.getHearingType());
6465
assertNotNull(hearing.getCourtSittings());
6566
assertEquals(1, hearing.getCourtSittings().size());
6667

67-
CourtSitting sitting =
68+
CourtSitting courtSitting =
6869
hearing.getCourtSittings().get(0);
69-
assertEquals("Central Criminal Court", sitting.getCourtHouse());
70-
assertNotNull(sitting.getSittingStart());
71-
assertTrue(sitting.getSittingEnd().isAfter(sitting.getSittingStart()));
72-
assertNotNull(sitting.getJudiciaryId());
70+
assertEquals("Central Criminal Court", courtSitting.getCourtHouse());
71+
assertNotNull(courtSitting.getSittingStart());
72+
assertTrue(courtSitting.getSittingEnd().isAfter(courtSitting.getSittingStart()));
73+
assertNotNull(courtSitting.getJudiciaryId());
7374

7475
}
7576

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package uk.gov.hmcts.cp.pact.provider;
2+
3+
import au.com.dius.pact.provider.junit5.HttpTestTarget;
4+
import au.com.dius.pact.provider.junit5.PactVerificationContext;
5+
import au.com.dius.pact.provider.junit5.PactVerificationInvocationContextProvider;
6+
import au.com.dius.pact.provider.junitsupport.Provider;
7+
import au.com.dius.pact.provider.junitsupport.State;
8+
import au.com.dius.pact.provider.junitsupport.loader.PactBroker;
9+
import au.com.dius.pact.provider.junitsupport.loader.PactBrokerAuth;
10+
import org.junit.jupiter.api.BeforeEach;
11+
import org.junit.jupiter.api.Tag;
12+
import org.junit.jupiter.api.TestTemplate;
13+
import org.junit.jupiter.api.extension.ExtendWith;
14+
import org.springframework.beans.factory.annotation.Autowired;
15+
import org.springframework.boot.test.context.SpringBootTest;
16+
import org.springframework.boot.test.web.server.LocalServerPort;
17+
import org.springframework.test.context.junit.jupiter.SpringExtension;
18+
import uk.gov.hmcts.cp.openapi.model.CourtScheduleResponse;
19+
import uk.gov.hmcts.cp.openapi.model.CourtSchedule;
20+
import uk.gov.hmcts.cp.openapi.model.Hearing;
21+
import uk.gov.hmcts.cp.openapi.model.CourtSitting;
22+
import uk.gov.hmcts.cp.repositories.CourtScheduleRepository;
23+
24+
import java.time.OffsetDateTime;
25+
import java.util.List;
26+
import java.util.UUID;
27+
28+
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
29+
@ExtendWith({SpringExtension.class, PactVerificationInvocationContextProvider.class})
30+
@Provider("VPCourtScheduleProvider")
31+
@PactBroker(
32+
scheme = "https",
33+
host = "${pact.broker.host}",
34+
authentication = @PactBrokerAuth(token = "${pact.broker.token}")
35+
)
36+
@Tag("pact")
37+
public class CourtScheduleProviderPactTest {
38+
39+
@Autowired
40+
private CourtScheduleRepository courtScheduleRepository;
41+
42+
@LocalServerPort
43+
private int port;
44+
45+
@BeforeEach
46+
void setupTarget(PactVerificationContext context) {
47+
System.out.println("Running test on port: " + port);
48+
context.setTarget(new HttpTestTarget("localhost", port));
49+
System.out.println("pact.verifier.publishResults: " + System.getProperty("pact.verifier.publishResults"));
50+
}
51+
52+
@State("court schedule for case 456789 exists")
53+
public void setupCourtSchedule() {
54+
courtScheduleRepository.clearAll();
55+
var courtSitting = CourtSitting.builder()
56+
.courtHouse("Central Criminal Court")
57+
.sittingStart(OffsetDateTime.now())
58+
.sittingEnd(OffsetDateTime.now().plusMinutes(60))
59+
.judiciaryId(UUID.randomUUID().toString())
60+
.build();
61+
var hearing = Hearing.builder()
62+
.hearingId(UUID.randomUUID().toString())
63+
.listNote("Requires interpreter")
64+
.hearingDescription("Sentencing for theft case")
65+
.hearingType("Trial")
66+
.courtSittings(List.of(courtSitting))
67+
.build();
68+
69+
var schedule = CourtSchedule.builder()
70+
.hearings(List.of(hearing))
71+
.build();
72+
73+
var response = CourtScheduleResponse.builder()
74+
.courtSchedule(List.of(schedule))
75+
.build();
76+
77+
courtScheduleRepository.saveCourtSchedule("456789", response);
78+
}
79+
80+
@TestTemplate
81+
void pactVerificationTestTemplate(PactVerificationContext context) {
82+
context.verifyInteraction();
83+
}
84+
}

0 commit comments

Comments
 (0)