diff --git a/Jenkinsfile_CNP b/Jenkinsfile_CNP index 23afc603..52196e17 100644 --- a/Jenkinsfile_CNP +++ b/Jenkinsfile_CNP @@ -6,6 +6,7 @@ import uk.gov.hmcts.contino.GradleBuilder import uk.gov.hmcts.contino.Kubectl import uk.gov.hmcts.contino.GithubAPI import uk.gov.hmcts.pipeline.TeamConfig +import uk.gov.hmcts.contino.AppPipelineDsl def type = "java" def product = "ccd" @@ -117,11 +118,16 @@ withPipeline(type, product, component) { env.DOCUMENT_STORE_HOST = "http://dm-store-aat.service.core-compute-aat.internal" env.ROLE_ASSIGNMENT_HOST = "http://am-role-assignment-service-aat.service.core-compute-aat.internal" env.LOG_AND_AUDIT_HOST = "http://lau-case-backend-aat.service.core-compute-aat.internal" + env.PACT_BROKER_FULL_URL = "https://pact-broker.platform.hmcts.net" + env.PACT_BROKER_URL = "pact-broker.platform.hmcts.net" + env.PACT_BROKER_PORT = "443" + env.PACT_BROKER_SCHEME = "https" def githubApi = new GithubAPI(this) if (!githubApi.getLabelsbyPattern(env.BRANCH_NAME, "keep-helm")) { enableCleanupOfHelmReleaseAlways() } + enablePactAs([AppPipelineDsl.PactRoles.CONSUMER, AppPipelineDsl.PactRoles.PROVIDER]) } onNonPR() { @@ -135,6 +141,15 @@ withPipeline(type, product, component) { env.DOCUMENT_STORE_HOST = "http://dm-store-aat.service.core-compute-aat.internal" env.ROLE_ASSIGNMENT_HOST = "http://am-role-assignment-service-aat.service.core-compute-aat.internal" env.LOG_AND_AUDIT_HOST = "http://lau-case-backend-aat.service.core-compute-aat.internal" + env.PACT_BROKER_FULL_URL = "https://pact-broker.platform.hmcts.net" + env.PACT_BROKER_URL = "pact-broker.platform.hmcts.net" + env.PACT_BROKER_PORT = "443" + env.PACT_BROKER_SCHEME = "https" + enablePactAs([PactRoles.CONSUMER, AppPipelineDsl.PactRoles.PROVIDER]) + } + + afterSuccess('pact-provider-verification') { + steps.archiveArtifacts allowEmptyArchive: true, artifacts: 'build/reports/tests/**/*' } afterAlways('akschartsinstall') { diff --git a/build.gradle b/build.gradle index f745374a..97f83503 100644 --- a/build.gradle +++ b/build.gradle @@ -1,17 +1,18 @@ plugins { - id 'application' - id 'checkstyle' - id 'pmd' - id 'jacoco' - id 'io.spring.dependency-management' version '1.1.2' - id 'org.springframework.boot' version '3.1.2' - id 'org.owasp.dependencycheck' version '8.3.1' - id 'com.github.ben-manes.versions' version '0.47.0' - id 'org.sonarqube' version '4.3.0.3225' - id 'uk.gov.hmcts.java' version '0.12.43' - id 'io.freefair.lombok' version '8.1.0' - id 'org.flywaydb.flyway' version '9.21.1' - id "info.solidsoft.pitest" version '1.9.11' + id 'application' + id 'checkstyle' + id 'pmd' + id 'jacoco' + id 'io.spring.dependency-management' version '1.1.2' + id 'org.springframework.boot' version '3.1.2' + id 'org.owasp.dependencycheck' version '8.3.1' + id 'com.github.ben-manes.versions' version '0.47.0' + id 'org.sonarqube' version '4.3.0.3225' + id 'uk.gov.hmcts.java' version '0.12.43' + id 'io.freefair.lombok' version '8.1.0' + id 'org.flywaydb.flyway' version '9.21.1' + id "info.solidsoft.pitest" version '1.9.11' + id "au.com.dius.pact" version '4.4.0-beta.2' } group = 'uk.gov.hmcts.reform' @@ -46,6 +47,15 @@ sourceSets { resources.srcDir file('src/integrationTest/resources') } + contractTest { + java { + compileClasspath += main.output + runtimeClasspath += main.output + srcDir file('src/contractTest/java') + } + resources.srcDir file('src/contractTest/resources') + } + } tasks.withType(JavaCompile) { @@ -114,6 +124,10 @@ pmd { ruleSetFiles = files("config/pmd/ruleset.xml") } +static def getCheckedOutGitCommitHash() { + 'git rev-parse --verify --short HEAD'.execute().text.trim() +} + jacocoTestReport { executionData(test) reports { @@ -236,9 +250,27 @@ def versions = [ springSecurity : '6.1.2', testcontainers : '1.18.3', wiremock : '2.35.0', + pactVersion : '4.4.0-beta.2', + serenity : '3.1.20', ] ext['jackson.version'] = '2.14.1' +project.ext.pacticipantVersion = getCheckedOutGitCommitHash() + +rootProject.tasks.named("processTestResources") { + duplicatesStrategy = 'include' +} + +rootProject.tasks.named("processFunctionalTestResources") { + duplicatesStrategy = 'include' +} + +rootProject.tasks.named("processIntegrationTestResources") { + duplicatesStrategy = 'include' +} +rootProject.tasks.named("processContractTestResources") { + duplicatesStrategy = 'include' +} dependencies { @@ -294,12 +326,59 @@ dependencies { functionalTestImplementation sourceSets.main.runtimeClasspath functionalTestImplementation sourceSets.test.runtimeClasspath - functionalTestCompileOnly group: 'org.apiguardian', name: 'apiguardian-api' , version: versions.apiGuardian - integrationTestCompileOnly group: 'org.apiguardian', name: 'apiguardian-api' , version: versions.apiGuardian + functionalTestCompileOnly group: 'org.apiguardian', name: 'apiguardian-api', version: versions.apiGuardian + integrationTestCompileOnly group: 'org.apiguardian', name: 'apiguardian-api', version: versions.apiGuardian + + //pact contract testing + contractTestImplementation group: 'au.com.dius.pact.consumer', name: 'junit5', version: versions.pactVersion + contractTestImplementation group: 'au.com.dius.pact.consumer', name: 'junit', version: versions.pactVersion + contractTestRuntimeOnly(group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: versions.junit) + contractTestImplementation(group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: versions.junit) + + contractTestImplementation sourceSets.main.runtimeClasspath + contractTestImplementation sourceSets.test.runtimeClasspath } mainClassName = 'uk.gov.hmcts.reform.ccd.ApplicationBootstrap' +task runAndPublishConsumerPactTests(type: Test){ + logger.lifecycle("Runs pact Tests") + testClassesDirs = sourceSets.contractTest.output.classesDirs + classpath = sourceSets.contractTest.runtimeClasspath + +} + +runAndPublishConsumerPactTests.finalizedBy pactPublish + +pact { + broker { + pactBrokerUrl = System.getenv("PACT_BROKER_FULL_URL") ?: 'http://localhost:80' + } + publish { + pactDirectory = 'build/pacts' + tags = [System.getenv("PACT_BRANCH_NAME") ?: 'Dev'] + version = project.pacticipantVersion + } +} + +task contract(type: Test, description: 'Runs the pact contract tests.', group: 'Verification') { + description = "Runs the consumer Pact tests" + useJUnitPlatform() + testClassesDirs = sourceSets.contractTest.output.classesDirs + classpath = sourceSets.contractTest.runtimeClasspath +} + +task runProviderPactVerification(type: Test) { + logger.lifecycle("Runs provider pact Tests") + testClassesDirs = sourceSets.contractTest.output.classesDirs + classpath = sourceSets.contractTest.runtimeClasspath + systemProperty 'pact.verifier.publishResults', System.getProperty('pact.verifier.publishResults') + systemProperty 'pact.provider.version', project.pacticipantVersion +} + +runProviderPactVerification.dependsOn contract +runProviderPactVerification.finalizedBy pactVerify + bootJar { getArchiveFileName().set(provider { 'ccd-case-disposer.jar' @@ -313,15 +392,3 @@ bootJar { wrapper { distributionType = Wrapper.DistributionType.ALL } - -rootProject.tasks.named("processTestResources") { - duplicatesStrategy = 'include' -} - -rootProject.tasks.named("processFunctionalTestResources") { - duplicatesStrategy = 'include' -} - -rootProject.tasks.named("processIntegrationTestResources") { - duplicatesStrategy = 'include' -} diff --git a/src/contractTest/java/uk/gov/hmcts/reform/ccd/SpringBootContractBaseTest.java b/src/contractTest/java/uk/gov/hmcts/reform/ccd/SpringBootContractBaseTest.java new file mode 100644 index 00000000..ab7695d2 --- /dev/null +++ b/src/contractTest/java/uk/gov/hmcts/reform/ccd/SpringBootContractBaseTest.java @@ -0,0 +1,36 @@ +package uk.gov.hmcts.reform.ccd; + +import au.com.dius.pact.consumer.junit5.PactConsumerTestExt; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.http.HttpHeaders; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.springframework.http.HttpHeaders.AUTHORIZATION; + +@Slf4j +@ExtendWith(PactConsumerTestExt.class) +@ExtendWith(SpringExtension.class) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestPropertySource(locations = {"classpath:application.properties"}) +public abstract class SpringBootContractBaseTest { + + public static final String PACT_TEST_EMAIL_VALUE = "ia-caseofficer@fake.hmcts.net"; + public static final String PACT_TEST_PASSWORD_VALUE = "London01"; + public static final String PACT_TEST_CLIENT_ID_VALUE = "pact"; + public static final String PACT_TEST_CLIENT_SECRET_VALUE = "pactsecret"; + public static final String PACT_TEST_SCOPES_VALUE = "openid profile roles"; + public static final String SERVICE_AUTHORIZATION = "ServiceAuthorization"; + public static final String AUTH_TOKEN = "Bearer someAuthorizationToken"; + public static final String SERVICE_AUTH_TOKEN = "Bearer someServiceAuthorizationToken"; + + + public HttpHeaders getHttpHeaders() { + HttpHeaders headers = new HttpHeaders(); + headers.add(SERVICE_AUTHORIZATION, SERVICE_AUTH_TOKEN); + headers.add(AUTHORIZATION, AUTH_TOKEN); + return headers; + } +} diff --git a/src/contractTest/java/uk/gov/hmcts/reform/ccd/consumer/wa/TaskManagerDeleteTaskConsumerTest.java b/src/contractTest/java/uk/gov/hmcts/reform/ccd/consumer/wa/TaskManagerDeleteTaskConsumerTest.java new file mode 100644 index 00000000..a6effdc1 --- /dev/null +++ b/src/contractTest/java/uk/gov/hmcts/reform/ccd/consumer/wa/TaskManagerDeleteTaskConsumerTest.java @@ -0,0 +1,60 @@ +package uk.gov.hmcts.reform.ccd.consumer.wa; + +import au.com.dius.pact.consumer.MockServer; +import au.com.dius.pact.consumer.dsl.PactDslWithProvider; +import au.com.dius.pact.consumer.junit5.PactTestFor; +import au.com.dius.pact.core.model.PactSpecVersion; +import au.com.dius.pact.core.model.RequestResponsePact; +import au.com.dius.pact.core.model.annotations.Pact; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import uk.gov.hmcts.reform.ccd.SpringBootContractBaseTest; + +public class TaskManagerDeleteTaskConsumerTest extends SpringBootContractBaseTest { + + private static final String WA_URL = "/task"; + private static final String WA_DELETE_TASK = WA_URL + "/delete"; + + @Pact(provider = "wa_task_management_api_delete_task_by_id", consumer = "wa_task_management_api") + public RequestResponsePact executeDeleteTaskById201(PactDslWithProvider builder) { + + return builder + .given("delete a task using case reference id") + .uponReceiving("Request to delete") + .path(WA_DELETE_TASK) + .method(HttpMethod.POST.toString()) + .body(deleteTaskWithRequest(), String.valueOf(ContentType.JSON)) + .matchHeader(SERVICE_AUTHORIZATION, SERVICE_AUTH_TOKEN) + .willRespondWith() + .status(HttpStatus.CREATED.value()) + .toPact(); + } + + @Test + @PactTestFor(pactMethod = "executeDeleteTaskById201", pactVersion = PactSpecVersion.V3) + void testDeleteTaskByTaskId201(MockServer mockServer) { + + RestAssured + .given() + .headers(getHttpHeaders()) + .contentType(ContentType.JSON) + .body(deleteTaskWithRequest()) + .post(mockServer.getUrl() + WA_DELETE_TASK) + .then() + .statusCode(201); + + } + + private String deleteTaskWithRequest() { + return """ + { + "deleteCaseTasksAction": { + "caseRef": "1234567890123456" + } + } + """; + } +} diff --git a/src/contractTest/resources/application.properties b/src/contractTest/resources/application.properties new file mode 100644 index 00000000..0e955cfb --- /dev/null +++ b/src/contractTest/resources/application.properties @@ -0,0 +1,8 @@ +core_case_data.api.url=${ccd_casedatastore_baseurl:http://localhost:3451} +ccd.jurisdictionid=SSCS +ccd.casetype=Benefit +ccd.eventid.create=createTestCase +idam.api.url=${IDAM_API_URL:http://localhost:8891} +idam.oauth2.user.email=${IDAM_LAU_SYSTEMUPDATE_USER:system.update@hmcts.net} +idam.oauth2.user.password=${IDAM_LAU_SYSTEMUPDATE_PASSWORD:Pa55word11} +idam.client.secret=${IDAM_OAUTH2_CLIENT_SECRET:AAAAAAAAAAA}