Skip to content
Closed
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
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
SERVER_PORT=4550
BASE_IMAGE=openjdk:21-jdk-slim
PACT_BROKER_URL=https://hmcts-dts.pactflow.io
PACT_ENV=dev/pactTest
14 changes: 14 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ plugins {
id 'maven-publish'
id "com.github.ben-manes.versions" version "0.52.0"
id "org.cyclonedx.bom" version "2.3.1"
id "au.com.dius.pact" version "4.6.17"
}

group = 'uk.gov.hmcts.cp'
Expand Down Expand Up @@ -272,4 +273,17 @@ dependencies {
exclude group: 'junit', module: 'junit'
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}

testImplementation 'au.com.dius.pact.provider:junit5:4.6.17'
testImplementation 'au.com.dius.pact.provider:spring6:4.6.17'

test{
systemProperty 'pact.verifier.publishResults', System.getProperty('pact.verifier.publishResults')
systemProperty 'pact.provider.version', System.getProperty('pact.provider.version')
systemProperty("pact.provider.branch", "dev/pactTest")
systemProperty 'PACT_BROKER_TOKEN', System.getProperty('PACT_BROKER_TOKEN')
systemProperty 'PACT_BROKER_HOST', System.getProperty('PACT_BROKER_HOST')
//systemProperty 'GIT_BRANCH', System.getProperty('GIT_BRANCH')

}
}
35 changes: 35 additions & 0 deletions publish-pacts.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/bin/bash

# Load env vars from .env if available
if [ -f .env ]; then
set -a
source .env
set +a
fi

# Export Git metadata
export GIT_COMMIT=$(git rev-parse HEAD)

if [ -n "$GIT_BRANCH" ]; then
BRANCH_NAME="$GIT_BRANCH"
else
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
fi

export GIT_BRANCH="$BRANCH_NAME"

# Run provider verification with results published
./gradlew test \
-Dpact.provider.version="$GIT_COMMIT" \
-Dpact.verifier.publishResults=true \
-Dpact.provider.branch="$GIT_BRANCH" \
-DPACT_BROKER_TOKEN="$PACT_BROKER_TOKEN" \
-DPACT_BROKER_HOST="$PACT_BROKER_URL"

# Optional: tag provider in the broker
pact-broker create-version-tag \
--pacticipant "VPCourtSchedulePactProvider" \
--version "$GIT_COMMIT" \
--tag "$PACT_ENV" \
--broker-base-url "$PACT_BROKER_URL" \
--broker-token "$PACT_BROKER_TOKEN"
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import org.springframework.stereotype.Repository;
import uk.gov.hmcts.cp.openapi.model.CourtScheduleResponse;

@FunctionalInterface
//@FunctionalInterface
@Repository
public interface CourtScheduleRepository {
public interface CourtScheduleRepository {

CourtScheduleResponse getCourtScheduleByCaseUrn(String caseUrn);
void saveCourtSchedule(final String caseUrn, final CourtScheduleResponse CourtScheduleResponse);

Check failure on line 11 in src/main/java/uk/gov/hmcts/cp/repositories/CourtScheduleRepository.java

View workflow job for this annotation

GitHub Actions / pmd-analysis

The final method parameter name 'CourtScheduleResponse' doesn't match '[a-z][a-zA-Z0-9]*'

Configurable naming conventions for formal parameters of methods and lambdas. This rule reports formal parameters which do not match the regex that applies to their specific kind (e.g. lambda parameter, or final formal parameter). Each regex can be configured through properties. By default this rule uses the standard Java naming convention (Camel case). FormalParameterNamingConventions (Priority: 1, Ruleset: Code Style) https://docs.pmd-code.org/pmd-doc-7.14.0/pmd_rules_java_codestyle.html#formalparameternamingconventions

Check failure on line 11 in src/main/java/uk/gov/hmcts/cp/repositories/CourtScheduleRepository.java

View workflow job for this annotation

GitHub Actions / pmd-analysis

Final parameter in abstract method

Declaring a method parameter as final for an interface method is useless because the implementation may choose to not respect it. FinalParameterInAbstractMethod (Priority: 1, Ruleset: Code Style) https://docs.pmd-code.org/pmd-doc-7.14.0/pmd_rules_java_codestyle.html#finalparameterinabstractmethod
void clearAll();

}
Original file line number Diff line number Diff line change
@@ -1,40 +1,74 @@
package uk.gov.hmcts.cp.repositories;

import org.springframework.context.annotation.Profile;

Check warning on line 3 in src/main/java/uk/gov/hmcts/cp/repositories/InMemoryCourtScheduleRepositoryImpl.java

View workflow job for this annotation

GitHub Actions / pmd-analysis

Unused import 'org.springframework.context.annotation.Profile'

Reports import statements that can be removed. They are either unused, duplicated, or the members they import are already implicitly in scope, because they're in java.lang, or the current package. If some imports cannot be resolved, for instance because you run PMD with an incomplete auxiliary classpath, some imports may be conservatively marked as used even if they're not to avoid false positives. UnnecessaryImport (Priority: 4, Ruleset: Code Style) https://docs.pmd-code.org/pmd-doc-7.14.0/pmd_rules_java_codestyle.html#unnecessaryimport
import org.springframework.stereotype.Component;
import uk.gov.hmcts.cp.openapi.model.CourtSchedule;
import uk.gov.hmcts.cp.openapi.model.CourtScheduleResponse;
import uk.gov.hmcts.cp.openapi.model.CourtSitting;
import uk.gov.hmcts.cp.openapi.model.CourtSchedule;
import uk.gov.hmcts.cp.openapi.model.Hearing;
import uk.gov.hmcts.cp.openapi.model.CourtSitting;

import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

@Component
//@Profile("test")
public class InMemoryCourtScheduleRepositoryImpl implements CourtScheduleRepository {

private final Map<String, CourtScheduleResponse> courtScheduleResponseMap = new ConcurrentHashMap<>();

public void saveCourtSchedule(final String caseUrn, final CourtScheduleResponse CourtScheduleResponse) {

Check failure on line 24 in src/main/java/uk/gov/hmcts/cp/repositories/InMemoryCourtScheduleRepositoryImpl.java

View workflow job for this annotation

GitHub Actions / pmd-analysis

The final method parameter name 'CourtScheduleResponse' doesn't match '[a-z][a-zA-Z0-9]*'

Configurable naming conventions for formal parameters of methods and lambdas. This rule reports formal parameters which do not match the regex that applies to their specific kind (e.g. lambda parameter, or final formal parameter). Each regex can be configured through properties. By default this rule uses the standard Java naming convention (Camel case). FormalParameterNamingConventions (Priority: 1, Ruleset: Code Style) https://docs.pmd-code.org/pmd-doc-7.14.0/pmd_rules_java_codestyle.html#formalparameternamingconventions
courtScheduleResponseMap.put(caseUrn, CourtScheduleResponse);
}

public CourtScheduleResponse getCourtScheduleByCaseUrn(final String caseUrn) {
final Hearing courtScheduleHearing = Hearing.builder()
if (!courtScheduleResponseMap.containsKey(caseUrn)) {
saveCourtSchedule(caseUrn, createCourtScheduleResponse());
}
return courtScheduleResponseMap.get(caseUrn);
}

public void clearAll() {
courtScheduleResponseMap.clear();
}

private CourtScheduleResponse createCourtScheduleResponse() {

OffsetDateTime sittingStartTime = OffsetDateTime.now(ZoneOffset.UTC)

Check warning on line 41 in src/main/java/uk/gov/hmcts/cp/repositories/InMemoryCourtScheduleRepositoryImpl.java

View workflow job for this annotation

GitHub Actions / pmd-analysis

Local variable 'sittingStartTime' could be declared final

A local variable assigned only once can be declared final. LocalVariableCouldBeFinal (Priority: 3, Ruleset: Code Style) https://docs.pmd-code.org/pmd-doc-7.14.0/pmd_rules_java_codestyle.html#localvariablecouldbefinal
.truncatedTo(ChronoUnit.SECONDS);

final Hearing hearing = Hearing.builder()
.hearingId(UUID.randomUUID().toString())
.listNote("Requires interpreter")
.hearingDescription("Sentencing for theft case")
.hearingType("Trial")
.courtSittings(List.of(
CourtSitting.builder()
.courtHouse("Central Criminal Court")
.sittingStart(OffsetDateTime.now())
.sittingEnd(OffsetDateTime.now().plusMinutes(60))
.sittingStart(sittingStartTime)
.sittingEnd(sittingStartTime.plusMinutes(60))
.judiciaryId(UUID.randomUUID().toString())
.build())
).build();

return CourtScheduleResponse.builder()
.courtSchedule(List.of(
CourtSchedule.builder()
.hearings(List.of(courtScheduleHearing)
.hearings(List.of(hearing)
).build()
)
).build();
}


public static void main(String[] args) {
OffsetDateTime sittingStartTime = OffsetDateTime.now(ZoneOffset.UTC)

Check warning on line 69 in src/main/java/uk/gov/hmcts/cp/repositories/InMemoryCourtScheduleRepositoryImpl.java

View workflow job for this annotation

GitHub Actions / pmd-analysis

Local variable 'sittingStartTime' could be declared final

A local variable assigned only once can be declared final. LocalVariableCouldBeFinal (Priority: 3, Ruleset: Code Style) https://docs.pmd-code.org/pmd-doc-7.14.0/pmd_rules_java_codestyle.html#localvariablecouldbefinal
.truncatedTo(ChronoUnit.SECONDS);
System.out.println(sittingStartTime);

Check failure on line 71 in src/main/java/uk/gov/hmcts/cp/repositories/InMemoryCourtScheduleRepositoryImpl.java

View workflow job for this annotation

GitHub Actions / pmd-analysis

Usage of System.out/err

References to System.(out|err).print are usually intended for debugging purposes and can remain in the codebase even in production code. By using a logger one can enable/disable this behaviour at will (and by priority) and avoid clogging the Standard out log. SystemPrintln (Priority: 2, Ruleset: Best Practices) https://docs.pmd-code.org/pmd-doc-7.14.0/pmd_rules_java_bestpractices.html#systemprintln

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.springframework.web.server.ResponseStatusException;
import uk.gov.hmcts.cp.openapi.model.CourtSchedule;
import uk.gov.hmcts.cp.openapi.model.CourtScheduleResponse;

import uk.gov.hmcts.cp.openapi.model.CourtSitting;
import uk.gov.hmcts.cp.openapi.model.Hearing;
import uk.gov.hmcts.cp.repositories.CourtScheduleRepository;
Expand Down Expand Up @@ -52,24 +53,24 @@ void getJudgeById_ShouldReturnJudgesWithOkStatus() {
assertNotNull(responseBody.getCourtSchedule());
assertEquals(1, responseBody.getCourtSchedule().size());

CourtSchedule schedule = responseBody.getCourtSchedule().get(0);
assertNotNull(schedule.getHearings());
assertEquals(1, schedule.getHearings().size());
CourtSchedule courtSchedule = responseBody.getCourtSchedule().get(0);
assertNotNull(courtSchedule.getHearings());
assertEquals(1, courtSchedule.getHearings().size());

Hearing hearing = schedule.getHearings().get(0);
Hearing hearing = courtSchedule.getHearings().get(0);
assertNotNull(hearing.getHearingId());
assertEquals("Requires interpreter", hearing.getListNote());
assertEquals("Sentencing for theft case", hearing.getHearingDescription());
assertEquals("Trial", hearing.getHearingType());
assertNotNull(hearing.getCourtSittings());
assertEquals(1, hearing.getCourtSittings().size());

CourtSitting sitting =
CourtSitting courtSitting =
hearing.getCourtSittings().get(0);
assertEquals("Central Criminal Court", sitting.getCourtHouse());
assertNotNull(sitting.getSittingStart());
assertTrue(sitting.getSittingEnd().isAfter(sitting.getSittingStart()));
assertNotNull(sitting.getJudiciaryId());
assertEquals("Central Criminal Court", courtSitting.getCourtHouse());
assertNotNull(courtSitting.getSittingStart());
assertTrue(courtSitting.getSittingEnd().isAfter(courtSitting.getSittingStart()));
assertNotNull(courtSitting.getJudiciaryId());

}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package uk.gov.hmcts.cp.pact.provider;

import au.com.dius.pact.provider.junit5.HttpTestTarget;
import au.com.dius.pact.provider.junit5.PactVerificationContext;
import au.com.dius.pact.provider.junit5.PactVerificationInvocationContextProvider;
import au.com.dius.pact.provider.junitsupport.Provider;
import au.com.dius.pact.provider.junitsupport.State;
import au.com.dius.pact.provider.junitsupport.loader.PactBroker;
import au.com.dius.pact.provider.junitsupport.loader.PactBrokerAuth;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import uk.gov.hmcts.cp.openapi.model.CourtScheduleResponse;
import uk.gov.hmcts.cp.openapi.model.CourtSchedule;
import uk.gov.hmcts.cp.openapi.model.Hearing;
import uk.gov.hmcts.cp.openapi.model.CourtSitting;
import uk.gov.hmcts.cp.repositories.CourtScheduleRepository;

import java.time.OffsetDateTime;
import java.util.List;
import java.util.UUID;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//@ActiveProfiles("test") // important: use the in-memory repo!
@ExtendWith({SpringExtension.class, PactVerificationInvocationContextProvider.class})
@Provider("VPCourtSchedulePactProvider")
@PactBroker(
scheme = "https",
host = "hmcts-dts.pactflow.io",
providerBranch = "dev/pactTest",
authentication = @PactBrokerAuth(token = "<token>")
)
public class CourtScheduleProviderPactTest {

@Autowired
private CourtScheduleRepository courtScheduleRepository;

@LocalServerPort
private int port;

@BeforeEach
void setupTarget(PactVerificationContext context) {
System.out.println("Running test on port: " + port);
context.setTarget(new HttpTestTarget("localhost", port));
System.out.println("pact.verifier.publishResults: " + System.getProperty("pact.verifier.publishResults"));
}

/* @BeforeEach
public void setupTestTarget(PactVerificationContext context) {
context.setTarget(new HttpTestTarget("localhost", 8080));
}*/

@State("court schedule for case 456789 exists")
public void setupCourtSchedule() {
courtScheduleRepository.clearAll();
var courtSitting = CourtSitting.builder()
.courtHouse("Central Criminal Court")
.sittingStart(OffsetDateTime.now())
.sittingEnd(OffsetDateTime.now().plusMinutes(60))
.judiciaryId(UUID.randomUUID().toString())
.build();
var hearing = Hearing.builder()
.hearingId(UUID.randomUUID().toString())
.listNote("Requires interpreter")
.hearingDescription("Sentencing for theft case")
.hearingType("Trial")
.courtSittings(List.of(courtSitting))
.build();

var schedule = CourtSchedule.builder()
.hearings(List.of(hearing))
.build();

var response = CourtScheduleResponse.builder()
.courtSchedule(List.of(schedule))
.build();

courtScheduleRepository.saveCourtSchedule("456789", response);
}

@TestTemplate
void pactVerificationTestTemplate(PactVerificationContext context) {
context.verifyInteraction();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ void getCourtScheduleByCaseUrn_shouldReturnCourtScheduleResponse() {
assertNotNull(hearing.getCourtSittings());
assertEquals(1, hearing.getCourtSittings().size());

CourtSitting sitting =
CourtSitting sitting =
hearing.getCourtSittings().get(0);
assertEquals("Central Criminal Court", sitting.getCourtHouse());
assertNotNull(sitting.getSittingStart());
Expand Down
Loading