From 07d9937e54502037684d08be7877c1d43f139cf8 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Thu, 27 Mar 2025 13:25:48 +0000 Subject: [PATCH 01/51] Move away from jcenter, apply plugins in the plugin block and fix unit test runner --- build.gradle | 66 +++++++++++++++------------------------------------- 1 file changed, 19 insertions(+), 47 deletions(-) diff --git a/build.gradle b/build.gradle index dc54e11d..27f2c4ae 100644 --- a/build.gradle +++ b/build.gradle @@ -1,34 +1,24 @@ import org.springframework.boot.gradle.tasks.run.BootRun -buildscript { - ext { - springBootVersion = '2.7.18' - } - repositories { - mavenLocal() - jcenter() - } - dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") - classpath("net.serenity-bdd:serenity-gradle-plugin:2.4.34") - } -} - plugins { - id 'application' - id 'checkstyle' - id 'com.github.ben-manes.versions' version '0.51.0' - id 'io.spring.dependency-management' version '1.0.11.RELEASE' - id 'jacoco' - id 'org.owasp.dependencycheck' version '9.0.4' - id 'org.sonarqube' version '5.0.0.4638' - id 'org.springframework.boot' version '2.7.0' - id 'info.solidsoft.pitest' version '1.7.4' - id 'au.com.dius.pact' version '4.1.7' + id 'application' + id 'checkstyle' + id 'com.github.ben-manes.versions' version '0.51.0' + id 'io.spring.dependency-management' version '1.0.11.RELEASE' + id 'jacoco' + id 'org.owasp.dependencycheck' version '9.0.4' + id 'org.sonarqube' version '5.0.0.4638' + id 'org.springframework.boot' version '2.7.0' + id 'info.solidsoft.pitest' version '1.7.4' + id 'au.com.dius.pact' version '4.1.7' + id 'net.serenity-bdd.serenity-gradle-plugin' version '2.4.34' } -apply plugin: 'net.serenity-bdd.aggregator' -apply plugin: 'com.github.ben-manes.versions' +repositories { + mavenLocal() + mavenCentral() + maven { url = 'https://jitpack.io' } +} group = 'uk.gov.hmcts.reform.prl' version = '3.0.0' @@ -99,24 +89,6 @@ compileTestJava { options.compilerArgs << '-Xlint:deprecation' } -repositories { - mavenLocal() - jcenter() - - maven { - url "http://repo.maven.apache.org/maven2" - allowInsecureProtocol = true - } - maven { - url 'https://repo.spring.io/libs-milestone' - allowInsecureProtocol = true - } - maven { - url 'https://jitpack.io' - allowInsecureProtocol = true - } -} - distributions { main { contents { @@ -452,9 +424,11 @@ dependencies { functionalTestAnnotationProcessor group: 'org.projectlombok', name: 'lombok', version: versions.lombok } -task smoke(type: Test, description: 'Runs the smoke tests.', group: 'Verification') { +tasks.withType(Test) { useJUnitPlatform() +} +task smoke(type: Test, description: 'Runs the smoke tests.', group: 'Verification') { setTestClassesDirs(sourceSets.smokeTest.output.classesDirs) setClasspath(sourceSets.smokeTest.runtimeClasspath) } @@ -469,7 +443,6 @@ task bootRunAat(type: BootRun, description: 'Runs the app using AAT config', dep } task functional(type: Test, description: 'Runs the functional tests.', group: 'Verification') { - useJUnitPlatform() setTestClassesDirs(sourceSets.functionalTest.output.classesDirs) setClasspath(sourceSets.functionalTest.runtimeClasspath) @@ -486,7 +459,6 @@ task integration(type: Test, description: 'Runs the integration tests.', group: task contract(type: Test) { group = 'Delivery pipeline' description = 'Runs the consumer Pact tests' - useJUnitPlatform() testClassesDirs = sourceSets.contractTest.output.classesDirs classpath = sourceSets.contractTest.runtimeClasspath systemProperty 'pact.rootDir', "pacts" From babaece67a7553754e4e99af9058bee48b117ee3 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Thu, 27 Mar 2025 13:27:38 +0000 Subject: [PATCH 02/51] Update deprecated property --- build.gradle | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 27f2c4ae..70e4ff1b 100644 --- a/build.gradle +++ b/build.gradle @@ -72,11 +72,11 @@ configurations { } bootJar { - manifest { - attributes 'Implementation-Title': project.name, - 'Implementation-Version': project.version - } - archiveName 'prl-dgs-api.jar' + manifest { + attributes 'Implementation-Title': project.name, + 'Implementation-Version': project.version + } + archiveFileName = 'prl-dgs-api.jar' } mainClassName = 'uk.gov.hmcts.reform.prl.documentgenerator.DocumentGeneratorApplication' From 443c651511cc02090f519555c0c1e378f159ea37 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Thu, 27 Mar 2025 13:33:40 +0000 Subject: [PATCH 03/51] Move project metadata properties into the application block --- build.gradle | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index 70e4ff1b..70340a2b 100644 --- a/build.gradle +++ b/build.gradle @@ -14,21 +14,24 @@ plugins { id 'net.serenity-bdd.serenity-gradle-plugin' version '2.4.34' } -repositories { - mavenLocal() - mavenCentral() - maven { url = 'https://jitpack.io' } +application { + group = 'uk.gov.hmcts.reform.prl' + mainClass = 'uk.gov.hmcts.reform.prl.documentgenerator.DocumentGeneratorApplication' + version = '0.0.1' } -group = 'uk.gov.hmcts.reform.prl' -version = '3.0.0' - -java { +java{ toolchain { languageVersion = JavaLanguageVersion.of(17) } } +repositories { + mavenLocal() + mavenCentral() + maven { url = 'https://jitpack.io' } +} + sourceSets { integrationTest { java { @@ -79,8 +82,6 @@ bootJar { archiveFileName = 'prl-dgs-api.jar' } -mainClassName = 'uk.gov.hmcts.reform.prl.documentgenerator.DocumentGeneratorApplication' - compileJava { options.compilerArgs << '-parameters' << '-Xlint:deprecation' } From 6fe138a7916de84277a8392f6093be9c3fd6d1a7 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Thu, 27 Mar 2025 13:34:51 +0000 Subject: [PATCH 04/51] Update report generation syntax --- build.gradle | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index 70340a2b..069c74c6 100644 --- a/build.gradle +++ b/build.gradle @@ -488,13 +488,13 @@ task developAddRelaseSuffix() { } jacocoTestReport { - executionData(test) + executionData(test) - reports { - xml.enabled = true - html.enabled = true - xml.destination file("${buildDir}/reports/jacoco/test/jacocoTestReport.xml") - } + reports { + xml.getRequired().set(true) + html.getRequired().set(true) + xml.destination file("${buildDir}/reports/jacoco/test/jacocoTestReport.xml") + } } jacocoTestCoverageVerification { From 1bbeb751e8186f56390f81aa337c4cd2e255335c Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Thu, 27 Mar 2025 13:36:49 +0000 Subject: [PATCH 05/51] Update main to mainClass for bootRunAat task --- build.gradle | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 069c74c6..13a53fc4 100644 --- a/build.gradle +++ b/build.gradle @@ -435,12 +435,12 @@ task smoke(type: Test, description: 'Runs the smoke tests.', group: 'Verificatio } task bootRunAat(type: BootRun, description: 'Runs the app using AAT config', dependsOn: 'classes', group: 'Application') { - main = 'uk.gov.hmcts.reform.prl.documentgenerator.DocumentGeneratorApplication' - doFirst() { - classpath = sourceSets.main.runtimeClasspath - systemProperties = System.properties - systemProperty 'spring.profiles.active', 'aat' - } + mainClass = 'uk.gov.hmcts.reform.prl.documentgenerator.DocumentGeneratorApplication' + doFirst() { + classpath = sourceSets.main.runtimeClasspath + systemProperties = System.properties + systemProperty 'spring.profiles.active', 'aat' + } } task functional(type: Test, description: 'Runs the functional tests.', group: 'Verification') { @@ -484,7 +484,7 @@ pitest { } task developAddRelaseSuffix() { - version = "${version}-SNAPSHOT" + version = "${version}-SNAPSHOT" } jacocoTestReport { From ab22b030c890c8a3c1ee2637ede9f6bc20c6128b Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Thu, 27 Mar 2025 13:37:40 +0000 Subject: [PATCH 06/51] Remove force property --- build.gradle | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 13a53fc4..22ca2cd5 100644 --- a/build.gradle +++ b/build.gradle @@ -220,13 +220,10 @@ dependencies { testImplementation group: 'org.springframework.cloud', name: 'spring-cloud-contract-wiremock', version: '3.1.1' - implementation (group: 'commons-beanutils', name: 'commons-beanutils', version: versions.commonsBeanUtils) { - force = true - } - implementation (group: 'org.springframework.security', name: 'spring-security-rsa', version: versions.spring_security_rsa) { - force = true - exclude group: 'org.springframework.security', module: 'spring-security-crypto' - } + implementation (group: 'commons-beanutils', name: 'commons-beanutils', version: versions.commonsBeanUtils) + implementation (group: 'org.springframework.security', name: 'spring-security-rsa', version: versions.spring_security_rsa) { + exclude group: 'org.springframework.security', module: 'spring-security-crypto' + } implementation group: 'org.springframework.security', name: 'spring-security-crypto', version: versions.springSecurityCrypto From 95e82e9f0dac46a642192c94a4e798700920a96c Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Thu, 27 Mar 2025 13:42:24 +0000 Subject: [PATCH 07/51] Fix indentation --- build.gradle | 197 +++++++++++++++++++++++++-------------------------- 1 file changed, 97 insertions(+), 100 deletions(-) diff --git a/build.gradle b/build.gradle index 22ca2cd5..ccc12dab 100644 --- a/build.gradle +++ b/build.gradle @@ -185,27 +185,27 @@ ext['spring-framework.version'] = '5.3.27' dependencies { - //integration test - integrationTestImplementation group: 'com.mashape.unirest', name: 'unirest-java', version: versions.unirest - integrationTestImplementation group: 'com.nimbusds', name: 'nimbus-jose-jwt', version: versions.nimbus - integrationTestImplementation group: 'net.serenity-bdd', name: 'serenity-core', version: versions.serenity - integrationTestImplementation group: 'net.serenity-bdd', name: 'serenity-cucumber', version: versions.serenityCucumber - integrationTestImplementation group: 'net.serenity-bdd', name: 'serenity-junit', version: versions.serenity - integrationTestImplementation group: 'net.serenity-bdd', name: 'serenity-rest-assured', version: versions.serenity - integrationTestImplementation group: 'net.serenity-bdd', name: 'serenity-spring', version: versions.serenity - integrationTestImplementation group: 'org.apache.commons', name: 'commons-lang3', version: versions.commonsLang3 - integrationTestImplementation group: 'org.apache.pdfbox', name: 'pdfbox', version: versions.pdfbox - integrationTestImplementation group: 'org.projectlombok', name: 'lombok', version: versions.lombok - integrationTestImplementation group: 'org.skyscreamer', name:'jsonassert', version: versions.jsonAssert - integrationTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test' - integrationTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-web' - - integrationTestImplementation (group: 'com.github.hmcts', name: 'service-auth-provider-java-client', version: versions.serviceTokenGenerator){ - exclude group: 'io.reactivex', module: 'io.reactivex' - exclude group: 'io.reactivex', module: 'rxnetty' - exclude group: 'io.reactivex', module: 'rxnetty-contexts' - exclude group: 'io.reactivex', module: 'rxnetty-servo' - } + // integration test + integrationTestImplementation group: 'com.mashape.unirest', name: 'unirest-java', version: versions.unirest + integrationTestImplementation group: 'com.nimbusds', name: 'nimbus-jose-jwt', version: versions.nimbus + integrationTestImplementation group: 'net.serenity-bdd', name: 'serenity-core', version: versions.serenity + integrationTestImplementation group: 'net.serenity-bdd', name: 'serenity-cucumber', version: versions.serenityCucumber + integrationTestImplementation group: 'net.serenity-bdd', name: 'serenity-junit', version: versions.serenity + integrationTestImplementation group: 'net.serenity-bdd', name: 'serenity-rest-assured', version: versions.serenity + integrationTestImplementation group: 'net.serenity-bdd', name: 'serenity-spring', version: versions.serenity + integrationTestImplementation group: 'org.apache.commons', name: 'commons-lang3', version: versions.commonsLang3 + integrationTestImplementation group: 'org.apache.pdfbox', name: 'pdfbox', version: versions.pdfbox + integrationTestImplementation group: 'org.projectlombok', name: 'lombok', version: versions.lombok + integrationTestImplementation group: 'org.skyscreamer', name:'jsonassert', version: versions.jsonAssert + integrationTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test' + integrationTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-web' + + integrationTestImplementation (group: 'com.github.hmcts', name: 'service-auth-provider-java-client', version: versions.serviceTokenGenerator){ + exclude group: 'io.reactivex', module: 'io.reactivex' + exclude group: 'io.reactivex', module: 'rxnetty' + exclude group: 'io.reactivex', module: 'rxnetty-contexts' + exclude group: 'io.reactivex', module: 'rxnetty-servo' + } functionalTestImplementation (group: 'com.github.hmcts', name: 'service-auth-provider-java-client', version: versions.serviceTokenGenerator){ exclude group: 'io.reactivex', module: 'io.reactivex' @@ -216,8 +216,6 @@ dependencies { functionalTestImplementation group: 'com.github.hmcts', name:'ccd-case-document-am-client', version: versions.ccdCaseDocumentAmClient - - testImplementation group: 'org.springframework.cloud', name: 'spring-cloud-contract-wiremock', version: '3.1.1' implementation (group: 'commons-beanutils', name: 'commons-beanutils', version: versions.commonsBeanUtils) @@ -225,96 +223,95 @@ dependencies { exclude group: 'org.springframework.security', module: 'spring-security-crypto' } - implementation group: 'org.springframework.security', name: 'spring-security-crypto', version: versions.springSecurityCrypto + implementation group: 'org.springframework.security', name: 'spring-security-crypto', version: versions.springSecurityCrypto - implementation group: 'org.springframework.boot', name: 'spring-boot-starter-actuator' - implementation group: 'org.bouncycastle', name: 'bcprov-jdk18on', version: versions.bouncycastle + implementation group: 'org.springframework.boot', name: 'spring-boot-starter-actuator' + implementation group: 'org.bouncycastle', name: 'bcprov-jdk18on', version: versions.bouncycastle - implementation group: 'com.github.hmcts', name:'ccd-case-document-am-client', version: versions.ccdCaseDocumentAmClient + implementation group: 'com.github.hmcts', name:'ccd-case-document-am-client', version: versions.ccdCaseDocumentAmClient - implementation(group: 'org.apache.logging.log4j', name: 'log4j-to-slf4j', version: versions.apacheLogging) - implementation(group: 'org.apache.logging.log4j', name: 'log4j-api', version: versions.apacheLogging) + implementation(group: 'org.apache.logging.log4j', name: 'log4j-to-slf4j', version: versions.apacheLogging) + implementation(group: 'org.apache.logging.log4j', name: 'log4j-api', version: versions.apacheLogging) - implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: versions.springBoot - implementation group: 'com.puppycrawl.tools', name: 'checkstyle', version: versions.puppyCrawl - implementation group: 'commons-io', name: 'commons-io', version: versions.commonsIo - implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.9' + implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: versions.springBoot + implementation group: 'com.puppycrawl.tools', name: 'checkstyle', version: versions.puppyCrawl + implementation group: 'commons-io', name: 'commons-io', version: versions.commonsIo + implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.9' implementation group: 'commons-fileupload', name: 'commons-fileupload', version: '1.5' implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-netflix-ribbon', version: '2.2.10.RELEASE' - implementation (group: 'org.springframework.cloud', name: 'spring-cloud-starter-openfeign', version: versions.springCloud) { - exclude group: 'io.reactivex', module: 'io.reactivex' - exclude group: 'io.reactivex', module: 'rxnetty' - exclude group: 'io.reactivex', module: 'rxnetty-contexts' - exclude group: 'io.reactivex', module: 'rxnetty-servo' - exclude group: 'org.bouncycastle' - } + implementation (group: 'org.springframework.cloud', name: 'spring-cloud-starter-openfeign', version: versions.springCloud) { + exclude group: 'io.reactivex', module: 'io.reactivex' + exclude group: 'io.reactivex', module: 'rxnetty' + exclude group: 'io.reactivex', module: 'rxnetty-contexts' + exclude group: 'io.reactivex', module: 'rxnetty-servo' + exclude group: 'org.bouncycastle' + } - implementation group: 'io.github.openfeign', name: 'feign-httpclient', version: versions.feignHttpClient - implementation group: 'org.springframework.hateoas', name: 'spring-hateoas', version: versions.springHateoas - implementation group: 'org.apache.commons', name: 'commons-lang3', version : versions.commonsLang3 - - implementation 'com.github.hmcts.java-logging:logging:6.1.6' - implementation 'com.github.hmcts.java-logging:logging-appinsights:6.1.6' - implementation group: 'org.springframework', name: 'spring-context-support', version: versions.springFramework - implementation ( group: 'com.github.hmcts', name: 'service-auth-provider-java-client', version: versions.serviceTokenGenerator) { - exclude group: 'io.reactivex', module: 'io.reactivex' - exclude group: 'io.reactivex', module: 'rxnetty' - exclude group: 'io.reactivex', module: 'rxnetty-contexts' - exclude group: 'io.reactivex', module: 'rxnetty-servo' - exclude group: 'org.bouncycastle' - } + implementation group: 'io.github.openfeign', name: 'feign-httpclient', version: versions.feignHttpClient + implementation group: 'org.springframework.hateoas', name: 'spring-hateoas', version: versions.springHateoas + implementation group: 'org.apache.commons', name: 'commons-lang3', version : versions.commonsLang3 - implementation group: 'junit', name: 'junit', version: versions.junit - implementation group: 'org.pitest', name: 'pitest', version: versions.pitest - implementation group:'info.solidsoft.gradle.pitest', name: 'gradle-pitest-plugin', version: versions.gradlePitest - implementation group:'org.codehaus.sonar-plugins', name:'sonar-pitest-plugin', version: versions.sonarPitest - implementation group: 'org.apache.httpcomponents', name: 'httpclient', version: versions.httpComponents - implementation (group: 'com.launchdarkly', name: 'launchdarkly-java-server-sdk', version: '7.4.0') - implementation group: 'net.logstash.logback', name: 'logstash-logback-encoder', version:'7.4' + implementation 'com.github.hmcts.java-logging:logging:6.1.6' + implementation 'com.github.hmcts.java-logging:logging-appinsights:6.1.6' + implementation group: 'org.springframework', name: 'spring-context-support', version: versions.springFramework + implementation ( group: 'com.github.hmcts', name: 'service-auth-provider-java-client', version: versions.serviceTokenGenerator) { + exclude group: 'io.reactivex', module: 'io.reactivex' + exclude group: 'io.reactivex', module: 'rxnetty' + exclude group: 'io.reactivex', module: 'rxnetty-contexts' + exclude group: 'io.reactivex', module: 'rxnetty-servo' + exclude group: 'org.bouncycastle' + } + implementation group: 'junit', name: 'junit', version: versions.junit + implementation group: 'org.pitest', name: 'pitest', version: versions.pitest + implementation group:'info.solidsoft.gradle.pitest', name: 'gradle-pitest-plugin', version: versions.gradlePitest + implementation group:'org.codehaus.sonar-plugins', name:'sonar-pitest-plugin', version: versions.sonarPitest + implementation group: 'org.apache.httpcomponents', name: 'httpclient', version: versions.httpComponents + implementation (group: 'com.launchdarkly', name: 'launchdarkly-java-server-sdk', version: '7.4.0') + implementation group: 'net.logstash.logback', name: 'logstash-logback-encoder', version:'7.4' annotationProcessor("org.projectlombok:lombok:${versions.lombok}") - annotationProcessor "org.springframework.boot:spring-boot-configuration-processor" - implementation group: 'org.projectlombok', name: 'lombok', version: versions.lombok - implementation("org.springframework.boot:spring-boot-starter-validation") - testImplementation group: 'com.github.tomakehurst', name:'wiremock', version: versions.wiremockVersion - testImplementation group: 'io.rest-assured', name: 'rest-assured', version: versions.restAssured - testImplementation group: 'io.rest-assured', name: 'json-path', version: versions.restAssured - testImplementation group: 'io.rest-assured', name: 'xml-path', version: versions.restAssured - testImplementation(group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: versions.springBoot) { - exclude(module: 'commons-logging') - } - testImplementation('org.springframework.security:spring-security-test') - runtimeOnly('org.springframework.boot:spring-boot-devtools') - - runtimeOnly("org.springframework.boot:spring-boot-properties-migrator") - - contractTestImplementation group: 'org.apache.httpcomponents', name: 'httpclient', version: versions.httpComponents - contractTestImplementation group: 'au.com.dius.pact.consumer', name: 'junit5', version: versions.pact_version - contractTestImplementation group: 'au.com.dius.pact.consumer', name: 'java8', version: versions.pact_version - contractTestRuntime group: 'au.com.dius.pact.consumer', name: 'junit5', version: versions.pact_version - contractTestRuntime group: 'au.com.dius.pact.consumer', name: 'java8', version: versions.pact_version - - contractTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test' - contractTestImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2") - contractTestRuntime("org.junit.jupiter:junit-jupiter-engine:5.9.2") - contractTestImplementation('org.junit.jupiter:junit-jupiter-api:5.10.2') - - contractTestImplementation sourceSets.main.runtimeClasspath - contractTestImplementation sourceSets.test.runtimeClasspath - - functionalTestImplementation sourceSets.main.runtimeClasspath - functionalTestImplementation sourceSets.test.runtimeClasspath - smokeTestImplementation sourceSets.main.runtimeClasspath - smokeTestImplementation sourceSets.test.runtimeClasspath - - testImplementation 'com.github.hmcts:fortify-client:1.2.2:all' - testImplementation group: 'io.rest-assured', name: 'rest-assured', version: versions.restAssured - testImplementation group: 'io.rest-assured', name: 'json-path', version: versions.restAssured - testImplementation group: 'io.rest-assured', name: 'xml-path', version: versions.restAssured - testImplementation group: 'org.mockito', name: 'mockito-inline', version: '5.2.0' + annotationProcessor "org.springframework.boot:spring-boot-configuration-processor" + implementation group: 'org.projectlombok', name: 'lombok', version: versions.lombok + implementation("org.springframework.boot:spring-boot-starter-validation") + testImplementation group: 'com.github.tomakehurst', name:'wiremock', version: versions.wiremockVersion + testImplementation group: 'io.rest-assured', name: 'rest-assured', version: versions.restAssured + testImplementation group: 'io.rest-assured', name: 'json-path', version: versions.restAssured + testImplementation group: 'io.rest-assured', name: 'xml-path', version: versions.restAssured + testImplementation(group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: versions.springBoot) { + exclude(module: 'commons-logging') + } + testImplementation('org.springframework.security:spring-security-test') + runtimeOnly('org.springframework.boot:spring-boot-devtools') + + runtimeOnly("org.springframework.boot:spring-boot-properties-migrator") + + contractTestImplementation group: 'org.apache.httpcomponents', name: 'httpclient', version: versions.httpComponents + contractTestImplementation group: 'au.com.dius.pact.consumer', name: 'junit5', version: versions.pact_version + contractTestImplementation group: 'au.com.dius.pact.consumer', name: 'java8', version: versions.pact_version + contractTestRuntime group: 'au.com.dius.pact.consumer', name: 'junit5', version: versions.pact_version + contractTestRuntime group: 'au.com.dius.pact.consumer', name: 'java8', version: versions.pact_version + + contractTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test' + contractTestImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2") + contractTestRuntime("org.junit.jupiter:junit-jupiter-engine:5.9.2") + contractTestImplementation('org.junit.jupiter:junit-jupiter-api:5.10.2') + + contractTestImplementation sourceSets.main.runtimeClasspath + contractTestImplementation sourceSets.test.runtimeClasspath + + functionalTestImplementation sourceSets.main.runtimeClasspath + functionalTestImplementation sourceSets.test.runtimeClasspath + smokeTestImplementation sourceSets.main.runtimeClasspath + smokeTestImplementation sourceSets.test.runtimeClasspath + + testImplementation 'com.github.hmcts:fortify-client:1.2.2:all' + testImplementation group: 'io.rest-assured', name: 'rest-assured', version: versions.restAssured + testImplementation group: 'io.rest-assured', name: 'json-path', version: versions.restAssured + testImplementation group: 'io.rest-assured', name: 'xml-path', version: versions.restAssured + testImplementation group: 'org.mockito', name: 'mockito-inline', version: '5.2.0' } task fortifyScan(type: JavaExec) { From 073ae3aff53e3f4a70c5f153afe309011fbcfa00 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Thu, 27 Mar 2025 13:44:21 +0000 Subject: [PATCH 08/51] Update main to mainClass --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ccc12dab..5aeec6bc 100644 --- a/build.gradle +++ b/build.gradle @@ -315,7 +315,7 @@ dependencies { } task fortifyScan(type: JavaExec) { - main = "uk.gov.hmcts.fortifyclient.FortifyClientMainApp" + mainClass = "uk.gov.hmcts.fortifyclient.FortifyClientMainApp" classpath += sourceSets.test.runtimeClasspath jvmArgs = ['--add-opens=java.base/java.lang.reflect=ALL-UNNAMED'] } From 2e3361306d8b6e6270660ef00008819cc5e12532 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Thu, 27 Mar 2025 13:52:04 +0000 Subject: [PATCH 09/51] Update gradle wrapper to 8.13 --- gradle/wrapper/gradle-wrapper.jar | Bin 55616 -> 43705 bytes gradle/wrapper/gradle-wrapper.properties | 5 +- gradlew | 291 ++++++++++++++--------- gradlew.bat | 64 +++-- 4 files changed, 209 insertions(+), 151 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf016b3885f6930543d57b744ea8c220a1a..9bbc975c742b298b441bfb90dbc124400a3751b9 100644 GIT binary patch literal 43705 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 DHx0+F literal 55616 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 \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -97,92 +132,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" fi +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 8bc24402..9d21a218 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -5,7 +5,7 @@ @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem -@rem http://www.apache.org/licenses/LICENSE-2.0 +@rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,25 +27,29 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -51,48 +57,36 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal From dad658b32bc5f956027f5a3c5a952f893e369917 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Thu, 27 Mar 2025 13:52:30 +0000 Subject: [PATCH 10/51] Update report location for checkstyle --- build.gradle | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 5aeec6bc..14cb5b9a 100644 --- a/build.gradle +++ b/build.gradle @@ -112,14 +112,14 @@ checkstyle { // make build fail on Checkstyle issues (https://github.com/gradle/gradle/issues/881) tasks.withType(Checkstyle).each { checkstyleTask -> - checkstyleTask.doLast { - reports.all { report -> - def outputFile = report.destination - if (outputFile.exists() && outputFile.text.contains(" + def outputFile = report.outputLocation.get().asFile + if (outputFile.exists() && outputFile.text.contains(" Date: Thu, 27 Mar 2025 13:57:34 +0000 Subject: [PATCH 11/51] Remove unused pmd config --- config/pmd/ruleset.xml | 72 ------------------------------------------ 1 file changed, 72 deletions(-) delete mode 100644 config/pmd/ruleset.xml diff --git a/config/pmd/ruleset.xml b/config/pmd/ruleset.xml deleted file mode 100644 index 03b66c19..00000000 --- a/config/pmd/ruleset.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - HMCTS PMD rule set - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From a5b03bcac16d94efd4582b4c7b757580352545b0 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Thu, 27 Mar 2025 14:06:22 +0000 Subject: [PATCH 12/51] Externalise checkstyle and owasp dependency check configuration to the rse-gradle-plugin --- build.gradle | 33 +-------------------------------- 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/build.gradle b/build.gradle index 14cb5b9a..eb4c2b73 100644 --- a/build.gradle +++ b/build.gradle @@ -2,11 +2,10 @@ import org.springframework.boot.gradle.tasks.run.BootRun plugins { id 'application' - id 'checkstyle' + id 'uk.gov.hmcts.java' version '0.12.65' id 'com.github.ben-manes.versions' version '0.51.0' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'jacoco' - id 'org.owasp.dependencycheck' version '9.0.4' id 'org.sonarqube' version '5.0.0.4638' id 'org.springframework.boot' version '2.7.0' id 'info.solidsoft.pitest' version '1.7.4' @@ -104,36 +103,6 @@ tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } -checkstyle { - toolVersion = '9.2.1' - configFile = project(':').file('checkstyle.xml') - configProperties = ["suppressionFile": project(':').file('suppressions.xml')] -} - -// make build fail on Checkstyle issues (https://github.com/gradle/gradle/issues/881) -tasks.withType(Checkstyle).each { checkstyleTask -> - checkstyleTask.doLast { - reports.all { report -> - def outputFile = report.outputLocation.get().asFile - if (outputFile.exists() && outputFile.text.contains(" Date: Thu, 27 Mar 2025 14:22:26 +0000 Subject: [PATCH 13/51] Fix white space --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index eb4c2b73..09c51cd8 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ application { version = '0.0.1' } -java{ +java { toolchain { languageVersion = JavaLanguageVersion.of(17) } From 741b3f8cb6648b0b25766dfee72fd015aa596050 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Thu, 27 Mar 2025 14:27:33 +0000 Subject: [PATCH 14/51] Update github workflow to use java 17 --- .github/workflows/ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d34ddcc..19e8735d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Template CI +name: Java CI on: pull_request: @@ -14,10 +14,10 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - name: Set up JDK 11 - uses: actions/setup-java@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 with: - java-version: 11 + distribution: 'temurin' + java-version: '17' - name: Build run: ./gradlew check From cdb6a74c1539f29fef9304ff0bc5747a3543f1fe Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Thu, 27 Mar 2025 14:47:39 +0000 Subject: [PATCH 15/51] Remove checkstyle config --- config/checkstyle/checkstyle.xml | 251 ------------------------------- 1 file changed, 251 deletions(-) delete mode 100644 config/checkstyle/checkstyle.xml diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml deleted file mode 100644 index 25743d96..00000000 --- a/config/checkstyle/checkstyle.xml +++ /dev/null @@ -1,251 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 56e435f59e33f13ffc037dbdd050e8aa3b611353 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Thu, 27 Mar 2025 14:48:06 +0000 Subject: [PATCH 16/51] Remove checkstyle dependency --- build.gradle | 2 -- 1 file changed, 2 deletions(-) diff --git a/build.gradle b/build.gradle index 09c51cd8..2ceef99a 100644 --- a/build.gradle +++ b/build.gradle @@ -123,7 +123,6 @@ def versions = [ pdfbox: '2.0.24', pitest: '1.7.3', powerMock: '2.0.0', - puppyCrawl: '8.29', reformsJavaLogging: '5.1.1', restAssured: '4.5.1', serenity: '2.2.13', @@ -203,7 +202,6 @@ dependencies { implementation(group: 'org.apache.logging.log4j', name: 'log4j-api', version: versions.apacheLogging) implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: versions.springBoot - implementation group: 'com.puppycrawl.tools', name: 'checkstyle', version: versions.puppyCrawl implementation group: 'commons-io', name: 'commons-io', version: versions.commonsIo implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.9' From f988bfb1bbfec428927e5e5b0e57c8ff3e493552 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Thu, 27 Mar 2025 14:49:53 +0000 Subject: [PATCH 17/51] Optimise imports --- .../prl/functionaltest/DocumentGenerateAndStoreE2ETest.java | 3 --- .../DocumentGenerateControllerFunctionalTest.java | 1 - .../prl/documentgenerator/DocumentGeneratorApplication.java | 1 - .../controller/DocumentGeneratorController.java | 1 - .../service/impl/DocmosisPDFGenerationServiceImpl.java | 1 - .../java/uk/gov/hmcts/reform/prl/DgsApiSmokeTests.java | 3 --- .../config/launchdarkly/LDClientFactoryTest.java | 1 - .../config/launchdarkly/LaunchDarklyClientTest.java | 3 --- .../prl/documentgenerator/controller/RootControllerTest.java | 1 - .../functionaltest/DocumentGenerateAndStoreE2ETest.java | 1 - .../management/monitoring/health/HealthCheckITest.java | 1 - .../monitoring/health/WebServiceHealthCheckUTest.java | 3 --- .../service/impl/DocumentManagementServiceImplUTest.java | 4 ---- .../prl/documentgenerator/util/ResourceLoaderUTest.java | 1 - 14 files changed, 25 deletions(-) diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/DocumentGenerateAndStoreE2ETest.java b/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/DocumentGenerateAndStoreE2ETest.java index 3129aa76..9df9572a 100644 --- a/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/DocumentGenerateAndStoreE2ETest.java +++ b/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/DocumentGenerateAndStoreE2ETest.java @@ -1,9 +1,7 @@ package uk.gov.hmcts.reform.prl.functionaltest; import com.github.tomakehurst.wiremock.client.WireMock; -import com.github.tomakehurst.wiremock.junit.WireMockClassRule; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; -import org.junit.ClassRule; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; @@ -16,7 +14,6 @@ import org.springframework.context.annotation.PropertySource; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/DocumentGenerateControllerFunctionalTest.java b/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/DocumentGenerateControllerFunctionalTest.java index 2abe42f0..86a526c2 100644 --- a/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/DocumentGenerateControllerFunctionalTest.java +++ b/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/DocumentGenerateControllerFunctionalTest.java @@ -1,6 +1,5 @@ package uk.gov.hmcts.reform.prl.functionaltest; -import groovy.util.logging.Slf4j; import io.restassured.RestAssured; import io.restassured.specification.RequestSpecification; import org.apache.commons.lang3.StringUtils; diff --git a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/DocumentGeneratorApplication.java b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/DocumentGeneratorApplication.java index 4085ae0c..2185ed1d 100644 --- a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/DocumentGeneratorApplication.java +++ b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/DocumentGeneratorApplication.java @@ -2,7 +2,6 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.openfeign.EnableFeignClients; import uk.gov.hmcts.reform.authorisation.ServiceAuthAutoConfiguration; @SpringBootApplication( diff --git a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/DocumentGeneratorController.java b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/DocumentGeneratorController.java index a06d6a18..39857bbb 100644 --- a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/DocumentGeneratorController.java +++ b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/DocumentGeneratorController.java @@ -9,7 +9,6 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; diff --git a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocmosisPDFGenerationServiceImpl.java b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocmosisPDFGenerationServiceImpl.java index 7227f2eb..a52822b5 100644 --- a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocmosisPDFGenerationServiceImpl.java +++ b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocmosisPDFGenerationServiceImpl.java @@ -5,7 +5,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FilenameUtils; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.FileSystemResource; import org.springframework.http.HttpEntity; diff --git a/src/smokeTest/java/uk/gov/hmcts/reform/prl/DgsApiSmokeTests.java b/src/smokeTest/java/uk/gov/hmcts/reform/prl/DgsApiSmokeTests.java index c2b5d24b..bbb46251 100644 --- a/src/smokeTest/java/uk/gov/hmcts/reform/prl/DgsApiSmokeTests.java +++ b/src/smokeTest/java/uk/gov/hmcts/reform/prl/DgsApiSmokeTests.java @@ -1,9 +1,7 @@ package uk.gov.hmcts.reform.prl; import com.github.tomakehurst.wiremock.client.WireMock; -import com.github.tomakehurst.wiremock.junit.WireMockClassRule; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; -import org.junit.ClassRule; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.runner.RunWith; @@ -13,7 +11,6 @@ import org.springframework.context.annotation.PropertySource; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/config/launchdarkly/LDClientFactoryTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/config/launchdarkly/LDClientFactoryTest.java index 5ecac58b..7e07bb1b 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/config/launchdarkly/LDClientFactoryTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/config/launchdarkly/LDClientFactoryTest.java @@ -3,7 +3,6 @@ import com.launchdarkly.sdk.server.interfaces.LDClientInterface; import org.junit.Before; import org.junit.Test; -import uk.gov.hmcts.reform.prl.documentgenerator.config.launchdarkly.LDClientFactory; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/config/launchdarkly/LaunchDarklyClientTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/config/launchdarkly/LaunchDarklyClientTest.java index e787b104..e4d234f5 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/config/launchdarkly/LaunchDarklyClientTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/config/launchdarkly/LaunchDarklyClientTest.java @@ -1,15 +1,12 @@ package uk.gov.hmcts.reform.prl.documentgenerator.config.launchdarkly; import com.launchdarkly.sdk.LDContext; -import com.launchdarkly.sdk.LDUser; import com.launchdarkly.sdk.server.interfaces.LDClientInterface; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; -import uk.gov.hmcts.reform.prl.documentgenerator.config.launchdarkly.LDClientFactory; -import uk.gov.hmcts.reform.prl.documentgenerator.config.launchdarkly.LaunchDarklyClient; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/RootControllerTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/RootControllerTest.java index c8e35d61..a18d690b 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/RootControllerTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/RootControllerTest.java @@ -6,7 +6,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultMatcher; diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/functionaltest/DocumentGenerateAndStoreE2ETest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/functionaltest/DocumentGenerateAndStoreE2ETest.java index ac94c667..8437b93a 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/functionaltest/DocumentGenerateAndStoreE2ETest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/functionaltest/DocumentGenerateAndStoreE2ETest.java @@ -3,7 +3,6 @@ import com.github.tomakehurst.wiremock.client.WireMock; import com.github.tomakehurst.wiremock.junit.WireMockClassRule; import org.junit.ClassRule; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/management/monitoring/health/HealthCheckITest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/management/monitoring/health/HealthCheckITest.java index 5a2f2fe8..448f1b6c 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/management/monitoring/health/HealthCheckITest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/management/monitoring/health/HealthCheckITest.java @@ -18,7 +18,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.boot.web.server.LocalServerPort; import org.springframework.cloud.contract.wiremock.WireMockSpring; import org.springframework.cloud.netflix.ribbon.StaticServerList; import org.springframework.context.annotation.Bean; diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/management/monitoring/health/WebServiceHealthCheckUTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/management/monitoring/health/WebServiceHealthCheckUTest.java index a0a7526e..43139026 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/management/monitoring/health/WebServiceHealthCheckUTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/management/monitoring/health/WebServiceHealthCheckUTest.java @@ -2,8 +2,6 @@ import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentMatchers; -import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.boot.actuate.health.Health; import org.springframework.http.HttpEntity; @@ -18,7 +16,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImplUTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImplUTest.java index 412b80c4..0e69bd04 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImplUTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImplUTest.java @@ -1,7 +1,6 @@ package uk.gov.hmcts.reform.prl.documentgenerator.service.impl; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; @@ -26,14 +25,11 @@ import static org.mockito.Mockito.when; import static uk.gov.hmcts.reform.prl.documentgenerator.functionaltest.DocumentGenerateAndStoreE2ETest.mockCaseDocsDocuments; import static uk.gov.hmcts.reform.prl.documentgenerator.util.TestData.BINARY_URL; -import static uk.gov.hmcts.reform.prl.documentgenerator.util.TestData.CASE_TYPE; import static uk.gov.hmcts.reform.prl.documentgenerator.util.TestData.FILE_URL; -import static uk.gov.hmcts.reform.prl.documentgenerator.util.TestData.JURISDICTION; import static uk.gov.hmcts.reform.prl.documentgenerator.util.TestData.MIME_TYPE; import static uk.gov.hmcts.reform.prl.documentgenerator.util.TestData.TEST_AUTH_TOKEN; import static uk.gov.hmcts.reform.prl.documentgenerator.util.TestData.TEST_GENERATED_DOCUMENT; import static uk.gov.hmcts.reform.prl.documentgenerator.util.TestData.TEST_HASH_TOKEN; -import static uk.gov.hmcts.reform.prl.documentgenerator.util.TestData.TEST_S2S_TOKEN; import static uk.gov.hmcts.reform.prl.documentgenerator.util.TestData.TEST_TEMPLATE; import static uk.gov.hmcts.reform.prl.documentgenerator.util.TestData.TEST_TEMPLATE_FILE_NAME; diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/util/ResourceLoaderUTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/util/ResourceLoaderUTest.java index 01de1cd1..039998da 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/util/ResourceLoaderUTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/util/ResourceLoaderUTest.java @@ -15,7 +15,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; @RunWith(MockitoJUnitRunner.class) From 747c4ff6b3ef8f7853c3a932c0a49bf6aed3876a Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Thu, 27 Mar 2025 14:52:54 +0000 Subject: [PATCH 18/51] Remove whitespace --- .../hmcts/reform/prl/documentgenerator/CdamApiConsumerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/contractTest/java/uk/gov/hmcts/reform/prl/documentgenerator/CdamApiConsumerTest.java b/src/contractTest/java/uk/gov/hmcts/reform/prl/documentgenerator/CdamApiConsumerTest.java index 79e66102..c8bac08e 100644 --- a/src/contractTest/java/uk/gov/hmcts/reform/prl/documentgenerator/CdamApiConsumerTest.java +++ b/src/contractTest/java/uk/gov/hmcts/reform/prl/documentgenerator/CdamApiConsumerTest.java @@ -140,7 +140,7 @@ RequestResponsePact uploadDocument(PactDslWithProvider builder) throws JSONExcep @PactTestFor(pactMethod = "uploadDocument") public void verifyUploadDocument(MockServer mockServer) throws IOException { - HttpResponse downloadDocumentResponse = Request.Post(mockServer.getUrl() + "/cases/documents" ) + HttpResponse downloadDocumentResponse = Request.Post(mockServer.getUrl() + "/cases/documents") .addHeader(SERVICE_AUTHORIZATION_HEADER, someServiceAuthToken) .addHeader(AUTHORIZATION_HEADER, someAuthToken) .execute().returnResponse(); From f309aeb78afb3a4f185e91349661b94d2369e947 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Thu, 27 Mar 2025 15:34:56 +0000 Subject: [PATCH 19/51] Refactor code to fix checkstyle violations --- .../DocumentGenerateAndStoreE2ETest.java | 21 +++++++--- ...tManagementServiceImplIntegrationTest.java | 4 +- .../impl/DocumentManagementServiceImpl.java | 5 ++- .../hmcts/reform/prl/DgsApiSmokeTests.java | 34 +++++++++++++---- .../helper/GlobalExceptionHandlerUTest.java | 8 ++-- .../DocumentGenerateAndStoreE2ETest.java | 10 +++-- .../health/WebServiceHealthCheckUTest.java | 4 +- .../DocumentManagementServiceImplTest.java | 38 ++++++++++++++----- .../util/ResourceLoaderUTest.java | 2 +- 9 files changed, 90 insertions(+), 36 deletions(-) diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/DocumentGenerateAndStoreE2ETest.java b/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/DocumentGenerateAndStoreE2ETest.java index 9df9572a..ae569cce 100644 --- a/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/DocumentGenerateAndStoreE2ETest.java +++ b/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/DocumentGenerateAndStoreE2ETest.java @@ -43,7 +43,10 @@ @RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = DocumentGeneratorApplication.class) +@SpringBootTest( + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + classes = DocumentGeneratorApplication.class +) @AutoConfigureMockMvc @PropertySource(value = "classpath:application.yml") public class DocumentGenerateAndStoreE2ETest { @@ -88,7 +91,8 @@ private void perform(String template) throws Exception { @ParameterizedTest @NullSource @ValueSource(strings = {" ", "nonExistingTemplate"}) - public void givenTemplateNameIsBlankOrNullOrTemplateNotFound_whenGenerateAndStoreDocument_thenReturnHttp400(String template) throws Exception { + public void givenTemplateNameIsBlankOrNullOrTemplateNotFound_whenGenerateAndStoreDocument_thenReturnHttp400( + String template) throws Exception { perform(template); } @@ -113,7 +117,8 @@ public void givenCouldNotConnectToAuthService_whenGenerateAndStoreDocument_thenR } @Test - public void givenAuthServiceReturnAuthenticationError_whenGenerateAndStoreDocument_thenReturnHttp401() throws Exception { + public void givenAuthServiceReturnAuthenticationError_whenGenerateAndStoreDocument_thenReturnHttp401() + throws Exception { Map caseData = new HashMap<>(); Map requestData = Collections.singletonMap( CASE_DETAILS, Collections.singletonMap(CASE_DATA, caseData) @@ -137,7 +142,8 @@ public void givenAllGoesWellForTestExample_whenGenerateAndStoreDocument_thenRetu assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments(TEST_EXAMPLE); } - private void assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments(String templateId) throws Exception { + private void assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments( + String templateId) throws Exception { //Given final Map caseData = Collections.emptyMap(); final Map values = new HashMap<>(); @@ -161,7 +167,10 @@ private void assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments(String //Then final GeneratedDocumentInfo generatedDocumentInfo = getGeneratedDocumentInfo(); - assertEquals(ObjectMapperTestUtil.convertObjectToJsonString(generatedDocumentInfo), result.getResponse().getContentAsString()); + assertEquals( + ObjectMapperTestUtil.convertObjectToJsonString(generatedDocumentInfo), + result.getResponse().getContentAsString() + ); } private GeneratedDocumentInfo getGeneratedDocumentInfo() { @@ -207,7 +216,7 @@ public static Document mockCaseDocsDocuments() { Document.Link link = new Document.Link(); Document.Link linkBinary = new Document.Link(); link.href = FILE_URL; - linkBinary.href = BINARY_URL ; + linkBinary.href = BINARY_URL; Document.Links links = new Document.Links(); links.self = link; diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/prl/DocumentManagementServiceImplIntegrationTest.java b/src/integrationTest/java/uk/gov/hmcts/reform/prl/DocumentManagementServiceImplIntegrationTest.java index ab89fbf2..2d9869af 100644 --- a/src/integrationTest/java/uk/gov/hmcts/reform/prl/DocumentManagementServiceImplIntegrationTest.java +++ b/src/integrationTest/java/uk/gov/hmcts/reform/prl/DocumentManagementServiceImplIntegrationTest.java @@ -137,7 +137,7 @@ public void givenValidAccessToken_whenAccessingDocumentPath_thenAccessProvided() HttpGet request = new HttpGet(url); request.setHeader(HttpHeaders.AUTHORIZATION, bearerToken); - HttpResponse httpResponse = HttpClientBuilder.create().build().execute( request ); + HttpResponse httpResponse = HttpClientBuilder.create().build().execute(request); assertEquals( httpResponse.getStatusLine().getStatusCode(), @@ -159,7 +159,7 @@ public void givenInvalidAccessToken_whenAccessingDocumentPath_thenNoAccessProvid HttpGet request = new HttpGet(url); request.setHeader(HttpHeaders.AUTHORIZATION, "Bearer ***INVALID***"); - HttpResponse httpResponse = HttpClientBuilder.create().build().execute( request ); + HttpResponse httpResponse = HttpClientBuilder.create().build().execute(request); JSONObject responseJson = new JSONObject(EntityUtils.toString(httpResponse.getEntity())); String responseMessage = responseJson.get("message").toString(); diff --git a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImpl.java b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImpl.java index b984c4c1..0fe4e16a 100644 --- a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImpl.java +++ b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImpl.java @@ -141,7 +141,10 @@ private String getCaseId(Map placeholders) { } @Override - public GeneratedDocumentInfo converToPdf(Map placeholders, String authorizationToken, String fileName) { + public GeneratedDocumentInfo converToPdf( + Map placeholders, + String authorizationToken, + String fileName) { log.debug( "Generate document requested with templateName [{}], placeholders of size[{}]", placeholders.size() diff --git a/src/smokeTest/java/uk/gov/hmcts/reform/prl/DgsApiSmokeTests.java b/src/smokeTest/java/uk/gov/hmcts/reform/prl/DgsApiSmokeTests.java index bbb46251..17aeedbe 100644 --- a/src/smokeTest/java/uk/gov/hmcts/reform/prl/DgsApiSmokeTests.java +++ b/src/smokeTest/java/uk/gov/hmcts/reform/prl/DgsApiSmokeTests.java @@ -40,7 +40,10 @@ @RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = DocumentGeneratorApplication.class) +@SpringBootTest( + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + classes = DocumentGeneratorApplication.class +) @AutoConfigureMockMvc @PropertySource(value = "classpath:application.yml") public class DgsApiSmokeTests { @@ -132,7 +135,8 @@ public void givenCouldNotConnectToAuthService_whenGenerateAndStoreDocument_thenR } @Test - public void givenAuthServiceReturnAuthenticationError_whenGenerateAndStoreDocument_thenReturnHttp401() throws Exception { + public void givenAuthServiceReturnAuthenticationError_whenGenerateAndStoreDocument_thenReturnHttp401() + throws Exception { Map caseData = new HashMap<>(); Map requestData = Collections.singletonMap( CASE_DETAILS, Collections.singletonMap(CASE_DATA, caseData) @@ -156,7 +160,8 @@ public void givenAllGoesWellForTestExample_whenGenerateAndStoreDocument_thenRetu assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments(TEST_EXAMPLE); } - private void assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments(String templateId) throws Exception { + private void assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments(String templateId) + throws Exception { //Given final Map caseData = Collections.emptyMap(); final Map values = new HashMap<>(); @@ -172,7 +177,9 @@ private void assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments(String //When final GenerateDocumentRequest generateDocumentRequest = new GenerateDocumentRequest(templateId, values); MvcResult result = webClient.perform(post(API_URL) - .content(ObjectMapperTestUtil.convertObjectToJsonString(generateDocumentRequest)) + .content(ObjectMapperTestUtil.convertObjectToJsonString( + generateDocumentRequest) + ) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) @@ -180,7 +187,10 @@ private void assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments(String //Then final GeneratedDocumentInfo generatedDocumentInfo = getGeneratedDocumentInfo(); - assertEquals(ObjectMapperTestUtil.convertObjectToJsonString(generatedDocumentInfo), result.getResponse().getContentAsString()); + assertEquals( + ObjectMapperTestUtil.convertObjectToJsonString(generatedDocumentInfo), + result.getResponse().getContentAsString() + ); } private GeneratedDocumentInfo getGeneratedDocumentInfo() { @@ -200,7 +210,11 @@ private void mockDocmosisPdfService(HttpStatus expectedResponse, byte[] body) { .willReturn(aResponse() .withStatus(expectedResponse.value()) .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) - .withBody(ObjectMapperTestUtil.convertObjectToJsonString(body)) + .withBody( + ObjectMapperTestUtil.convertObjectToJsonString( + body + ) + ) )); } @@ -218,7 +232,11 @@ private void mockCaseDocsClientApi(HttpStatus expectedResponse, UploadResponse u .willReturn(aResponse() .withStatus(expectedResponse.value()) .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) - .withBody(ObjectMapperTestUtil.convertObjectToJsonString(uploadResponse)) + .withBody( + ObjectMapperTestUtil.convertObjectToJsonString( + uploadResponse + ) + ) )); } @@ -226,7 +244,7 @@ public static Document mockCaseDocsDocuments() { Document.Link link = new Document.Link(); Document.Link linkBinary = new Document.Link(); link.href = FILE_URL; - linkBinary.href = BINARY_URL ; + linkBinary.href = BINARY_URL; Document.Links links = new Document.Links(); links.self = link; diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/helper/GlobalExceptionHandlerUTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/helper/GlobalExceptionHandlerUTest.java index 8cff87c5..28d68593 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/helper/GlobalExceptionHandlerUTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/helper/GlobalExceptionHandlerUTest.java @@ -39,7 +39,7 @@ public void whenHandleTemplateLoadingException_thenReturnBadRequest() { } @Test - public void givenHttpClientErrorExceptionWrappedIn_whenHandleDocumentStorageAndPDFGenerationException_thenReturnStatusCodeOfHttpClientErrorException() { + public void whenHandleDocStorageAndPdfGenException_thenReturnHttpClientErrorStatus() { final HttpStatus httpStatus = HttpStatus.MOVED_PERMANENTLY; final HttpClientErrorException httpClientErrorException = new HttpClientErrorException(httpStatus); @@ -56,7 +56,7 @@ public void givenHttpClientErrorExceptionWrappedIn_whenHandleDocumentStorageAndP } @Test - public void givenHttpClientErrorResponseCode200_whenHandleDocumentStorageAndPDFGenerationException_thenReturnStatus503() { + public void givenHttpClientError200_whenHandleDocStorageAndPdfGenException_thenReturn503() { final HttpStatus httpStatus = HttpStatus.BAD_REQUEST; final HttpClientErrorException httpClientErrorException = new HttpClientErrorException(httpStatus); @@ -73,7 +73,7 @@ public void givenHttpClientErrorResponseCode200_whenHandleDocumentStorageAndPDFG } @Test - public void givenWrappedInExceptionIsNull_whenHandleDocumentStorageAndPDFGenerationException_thenReturnInternalServerError() { + public void givenNullWrappedInException_whenHandleDocStorageAndPdfGen_thenReturn500() { final String message = "some message"; PDFGenerationException pdfGenerationException = new PDFGenerationException(message, null); @@ -86,7 +86,7 @@ public void givenWrappedInExceptionIsNull_whenHandleDocumentStorageAndPDFGenerat } @Test - public void givenWrappedInIsNotHttpClientErrorException_whenHandleDocumentStorageAndPDFGenerationException_thenReturnInternalServerError() { + public void givenNonHttpClientErrorWrappedIn_whenHandleDocStorageAndPdfGenException_thenReturn500() { final String message = "some message"; final Exception exception = new Exception(); diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/functionaltest/DocumentGenerateAndStoreE2ETest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/functionaltest/DocumentGenerateAndStoreE2ETest.java index 8437b93a..8cecca13 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/functionaltest/DocumentGenerateAndStoreE2ETest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/functionaltest/DocumentGenerateAndStoreE2ETest.java @@ -130,7 +130,8 @@ public void givenCouldNotConnectToAuthService_whenGenerateAndStoreDocument_thenR } @Test - public void givenAuthServiceReturnAuthenticationError_whenGenerateAndStoreDocument_thenReturnHttp401() throws Exception { + public void givenAuthServiceReturnAuthenticationError_whenGenerateAndStoreDocument_thenReturnHttp401() + throws Exception { Map caseData = new HashMap<>(); Map requestData = Collections.singletonMap( CASE_DETAILS, Collections.singletonMap(CASE_DATA, caseData) @@ -178,7 +179,10 @@ private void assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments(String //Then final GeneratedDocumentInfo generatedDocumentInfo = getGeneratedDocumentInfo(); - assertEquals(ObjectMapperTestUtil.convertObjectToJsonString(generatedDocumentInfo), result.getResponse().getContentAsString()); + assertEquals( + ObjectMapperTestUtil.convertObjectToJsonString(generatedDocumentInfo), + result.getResponse().getContentAsString() + ); } private GeneratedDocumentInfo getGeneratedDocumentInfo() { @@ -224,7 +228,7 @@ public static Document mockCaseDocsDocuments() { Document.Link link = new Document.Link(); Document.Link linkBinary = new Document.Link(); link.href = FILE_URL; - linkBinary.href = BINARY_URL ; + linkBinary.href = BINARY_URL; Document.Links links = new Document.Links(); links.self = link; diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/management/monitoring/health/WebServiceHealthCheckUTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/management/monitoring/health/WebServiceHealthCheckUTest.java index 43139026..89cf0158 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/management/monitoring/health/WebServiceHealthCheckUTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/management/monitoring/health/WebServiceHealthCheckUTest.java @@ -55,7 +55,9 @@ public void givenServiceReturnsServiceUnavailable_whenHealth_thenReturnDown() { when(httpEntityFactory.createRequestEntityForHealthCheck()).thenReturn(httpEntity); - HttpServerErrorException exception = new HttpServerErrorException(HttpStatus.SERVICE_UNAVAILABLE,"unknown error"); + HttpServerErrorException exception = new HttpServerErrorException( + HttpStatus.SERVICE_UNAVAILABLE,"unknown error" + ); doThrow(exception).when(restTemplate) .exchange(URI, HttpMethod.GET, httpEntity, Object.class, new HashMap<>()); diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImplTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImplTest.java index 3b24f7bf..1955b8f7 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImplTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImplTest.java @@ -13,6 +13,7 @@ import uk.gov.hmcts.reform.ccd.document.am.feign.CaseDocumentClient; import uk.gov.hmcts.reform.ccd.document.am.model.UploadResponse; import uk.gov.hmcts.reform.prl.documentgenerator.config.TemplatesConfiguration; +import uk.gov.hmcts.reform.prl.documentgenerator.functionaltest.DocumentGenerateAndStoreE2ETest; import uk.gov.hmcts.reform.prl.documentgenerator.service.PDFGenerationService; import java.util.HashMap; @@ -25,7 +26,6 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.verify; -import static uk.gov.hmcts.reform.prl.documentgenerator.functionaltest.DocumentGenerateAndStoreE2ETest.mockCaseDocsDocuments; @RunWith(MockitoJUnitRunner.class) public class DocumentManagementServiceImplTest { @@ -59,7 +59,9 @@ public class DocumentManagementServiceImplTest { static final String authToken = "userAuthToken"; static final String s2sToken = "s2sAuthToken"; static final byte[] data = {126}; - static final UploadResponse uploadResponse = new UploadResponse(List.of(mockCaseDocsDocuments())); + static final UploadResponse uploadResponse = new UploadResponse( + List.of(DocumentGenerateAndStoreE2ETest.mockCaseDocsDocuments()) + ); @Test public void testGenerateAndStoreDraftDocumentMock() { @@ -83,8 +85,12 @@ public void testGenerateAndStoreDraftDocumentMockWithDynamicName() { placeholderMap.put("dynamic_fileName","test-file.pdf"); Mockito.when(authTokenGenerator.generate()).thenReturn(s2sToken); Mockito.when(pdfGenerationService.generate(D8_PETITION_WELSH_TEMPLATE, placeholderMap)).thenReturn(data); - Mockito.when(caseDocumentClient.uploadDocuments(eq(authToken), - eq(s2sToken), eq("PRLAPPS"), eq("PRIVATELAW"), any())) + Mockito.when(caseDocumentClient.uploadDocuments( + eq(authToken), + eq(s2sToken), + eq("PRLAPPS"), + eq("PRIVATELAW"), + any())) .thenReturn(uploadResponse); classUnderTest.generateAndStoreDraftDocument(D8_PETITION_WELSH_TEMPLATE, placeholderMap, authToken); @@ -118,8 +124,12 @@ public void testConvertToPdf() { Mockito.when(authTokenGenerator.generate()).thenReturn(s2sToken); Mockito.when(pdfGenerationService.converToPdf(placeholders,"fileName")).thenReturn(test); - Mockito.when(caseDocumentClient.uploadDocuments(eq(authToken), - eq(s2sToken), eq("PRLAPPS"), eq("PRIVATELAW"), any())) + Mockito.when(caseDocumentClient.uploadDocuments( + eq(authToken), + eq(s2sToken), + eq("PRLAPPS"), + eq("PRIVATELAW"), + any())) .thenReturn(uploadResponse); classUnderTest.converToPdf(placeholders, authToken,"fileName"); @@ -133,8 +143,12 @@ public void testGenerateAndStoreFinalDocument_WithDynamicFileName() { placeholderMap.put("dynamic_fileName","test-file.pdf"); Mockito.when(authTokenGenerator.generate()).thenReturn(s2sToken); Mockito.when(pdfGenerationService.generate(FINAL_MINI_PETITION_TEMPLATE_ID, placeholderMap)).thenReturn(data); - Mockito.when(caseDocumentClient.uploadDocuments(eq(authToken), - eq(s2sToken), eq("PRLAPPS"), eq("PRIVATELAW"), any())) + Mockito.when(caseDocumentClient.uploadDocuments( + eq(authToken), + eq(s2sToken), + eq("PRLAPPS"), + eq("PRIVATELAW"), + any())) .thenReturn(uploadResponse); classUnderTest.generateAndStoreDocument(FINAL_MINI_PETITION_TEMPLATE_ID, placeholderMap, authToken); @@ -151,8 +165,12 @@ public void testGenerateAndStoreFinalDocument_WithOutDynamicFileName() { Mockito.when(pdfGenerationService.generate(FINAL_MINI_PETITION_TEMPLATE_ID, placeholderMap)).thenReturn(data); Mockito.when(templatesConfiguration.getFileNameByTemplateName(FINAL_MINI_PETITION_TEMPLATE_ID)) .thenReturn(DRAFT_MINI_PETITION_NAME_FOR_PDF_FILE); - Mockito.when(caseDocumentClient.uploadDocuments(eq(authToken), - eq(s2sToken), eq("PRLAPPS"), eq("PRIVATELAW"), any())) + Mockito.when(caseDocumentClient.uploadDocuments( + eq(authToken), + eq(s2sToken), + eq("PRLAPPS"), + eq("PRIVATELAW"), + any())) .thenReturn(uploadResponse); classUnderTest.generateAndStoreDocument(FINAL_MINI_PETITION_TEMPLATE_ID, placeholderMap, authToken); diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/util/ResourceLoaderUTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/util/ResourceLoaderUTest.java index 039998da..f7808ebc 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/util/ResourceLoaderUTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/util/ResourceLoaderUTest.java @@ -23,7 +23,7 @@ public class ResourceLoaderUTest { private static final String EXISTING_PATH = "ResourceLoadTest.txt"; private static final String DATA_IN_FILE = "Resource Load Test"; - MockedStatic nullOrEmptyValidator ; + MockedStatic nullOrEmptyValidator; @Before public void beforeTest() { From 8d1b463e848fdd6415452c77dc8a4dc9b65f2efb Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Thu, 27 Mar 2025 15:51:18 +0000 Subject: [PATCH 20/51] Update suppressions --- config/owasp/suppressions.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/config/owasp/suppressions.xml b/config/owasp/suppressions.xml index fbf9371b..00d9a5be 100644 --- a/config/owasp/suppressions.xml +++ b/config/owasp/suppressions.xml @@ -1,3 +1,14 @@ + + + ^pkg:maven/net\.minidev/json-smart@.*$ + CVE-2023-1370 + + + Vulnerability in spring boot 2.x.x> + CVE-2023-20873 + CVE-2023-20883 + CVE-2023-34055 + From d0c46c4d179355ccaa8697043ebd68984b790790 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Thu, 27 Mar 2025 15:56:34 +0000 Subject: [PATCH 21/51] Add path to suppressions --- build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle b/build.gradle index 2ceef99a..69b8f3c3 100644 --- a/build.gradle +++ b/build.gradle @@ -103,6 +103,10 @@ tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } +dependencyCheck { + suppressionFile = 'config/owasp/suppressions.xml' +} + jacoco { toolVersion = "0.8.8" } From ccc88f09c9e4d08c12d8760c58c0e6580e16b626 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Fri, 28 Mar 2025 09:28:28 +0000 Subject: [PATCH 22/51] Update launch darkley config to remove deprecated methods --- .../config/launchdarkly/LaunchDarklyClient.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/config/launchdarkly/LaunchDarklyClient.java b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/config/launchdarkly/LaunchDarklyClient.java index 49ac055a..9c51b20c 100644 --- a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/config/launchdarkly/LaunchDarklyClient.java +++ b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/config/launchdarkly/LaunchDarklyClient.java @@ -1,17 +1,18 @@ package uk.gov.hmcts.reform.prl.documentgenerator.config.launchdarkly; import com.launchdarkly.sdk.LDContext; -import com.launchdarkly.sdk.LDUser; import com.launchdarkly.sdk.server.interfaces.LDClientInterface; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.io.IOException; +@Slf4j @Service public class LaunchDarklyClient { - public static final LDUser PRL_DGS_USER = new LDUser.Builder("prl-dgs-api") + public static final LDContext PRL_DGS_USER = LDContext.builder("prl-dgs-api") .anonymous(true) .build(); @@ -28,7 +29,7 @@ public LaunchDarklyClient( } public boolean isFeatureEnabled(String feature) { - return internalClient.boolVariation(feature, LDContext.fromUser(LaunchDarklyClient.PRL_DGS_USER), false); + return internalClient.boolVariation(feature, LaunchDarklyClient.PRL_DGS_USER, false); } public boolean isFeatureEnabled(String feature, LDContext user) { @@ -39,8 +40,7 @@ private void close() { try { internalClient.close(); } catch (IOException e) { - // can't do anything clever here because things are being destroyed - e.printStackTrace(System.err); + log.warn("Failed to close LaunchDarkly client", e); } } } From eddbe52b708ae4484d6c598a03070c271893c70f Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Mon, 31 Mar 2025 13:42:20 +0100 Subject: [PATCH 23/51] Remove dependabot --- .github/dependabot.yml | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 1ac4a488..00000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,7 +0,0 @@ -version: 2 -updates: -- package-ecosystem: gradle - directory: "/" - schedule: - interval: weekly - open-pull-requests-limit: 10 From a7bd3ffd991571b8e5891e046907d5caecd0a2f2 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Mon, 31 Mar 2025 13:42:30 +0100 Subject: [PATCH 24/51] Remove unused configs --- checkstyle.xml | 205 ------------------------------ dependency-check-suppressions.xml | 0 2 files changed, 205 deletions(-) delete mode 100644 checkstyle.xml delete mode 100644 dependency-check-suppressions.xml diff --git a/checkstyle.xml b/checkstyle.xml deleted file mode 100644 index 8c12645c..00000000 --- a/checkstyle.xml +++ /dev/null @@ -1,205 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dependency-check-suppressions.xml b/dependency-check-suppressions.xml deleted file mode 100644 index e69de29b..00000000 From 3e2fe9972e0e9b7253f5273d9eb5600fc7bc3692 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Tue, 1 Apr 2025 09:49:11 +0100 Subject: [PATCH 25/51] Remove unneeded cast --- .../controller/helper/GlobalExceptionHandler.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/helper/GlobalExceptionHandler.java b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/helper/GlobalExceptionHandler.java index 0a9358db..4fe33b54 100644 --- a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/helper/GlobalExceptionHandler.java +++ b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/helper/GlobalExceptionHandler.java @@ -31,13 +31,13 @@ public ResponseEntity handleTemplateLoadingException(ErrorLoadingTemplat public ResponseEntity handleDocumentStorageAndPDFGenerationException(Exception exception) { log.error(exception.getMessage(), exception); - if (exception.getCause() instanceof HttpClientErrorException) { - HttpStatus httpClientErrorException = ((HttpClientErrorException) exception.getCause()).getStatusCode(); + if (exception.getCause() instanceof HttpClientErrorException httpClientErrorException) { + HttpStatus statusCode = httpClientErrorException.getStatusCode(); - if (httpClientErrorException == HttpStatus.BAD_REQUEST) { + if (statusCode == HttpStatus.BAD_REQUEST) { return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(exception.getMessage()); } else { - return ResponseEntity.status(httpClientErrorException).body(exception.getMessage()); + return ResponseEntity.status(statusCode).body(exception.getMessage()); } } From 8c7fff5e49cf6dba9abc0e14aff1cac23fdc0ca4 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Tue, 1 Apr 2025 09:51:01 +0100 Subject: [PATCH 26/51] Fix sonar issues, improve logging and fix typo --- .../DocumentGeneratorController.java | 83 +++++++++---------- 1 file changed, 39 insertions(+), 44 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/DocumentGeneratorController.java b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/DocumentGeneratorController.java index 39857bbb..063c71f1 100644 --- a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/DocumentGeneratorController.java +++ b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/DocumentGeneratorController.java @@ -5,7 +5,6 @@ import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -31,64 +30,61 @@ public class DocumentGeneratorController { private final DocumentManagementService documentManagementService; @Operation(description = "Generate PDF document based on the supplied template name and placeholder " - + "texts and saves it in the evidence management.", tags = {"Document Generation"}) - @ApiResponses({ - @ApiResponse(responseCode = "200", description = "PDF was generated successfully and stored in the " - + "evidence management. Returns the url to the stored document.", content = - @Content(mediaType = "application/json", schema = @Schema(implementation = String.class))), - @ApiResponse(responseCode = "400", description = "Returned when input parameters are invalid " - + "or template not found", content = - @Content(mediaType = "application/json", schema = @Schema(implementation = String.class))), - @ApiResponse(responseCode = "503", description = "Returned when the PDF Service or Evidence " - + "Management Client Api cannot be reached", content = - @Content(mediaType = "application/json", schema = @Schema(implementation = String.class))), - @ApiResponse(responseCode = "500", description = "Returned when there is an unknown server error", - content = @Content(mediaType = "application/json", schema = @Schema(implementation = String.class))) - }) + + "texts and saves it in the evidence management.", tags = {"Document Generation"}) + @ApiResponse(responseCode = "200", description = "PDF was generated successfully and stored in the " + + "evidence management. Returns the URL to the stored document.", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = String.class))) + @ApiResponse(responseCode = "400", description = "Returned when input parameters are invalid " + + "or template not found", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = String.class))) + @ApiResponse(responseCode = "503", description = "Returned when the PDF Service or Evidence " + + "Management Client API cannot be reached", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = String.class))) + @ApiResponse(responseCode = "500", description = "Returned when there is an unknown server error", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = String.class))) @PostMapping("/generatePDF") public GeneratedDocumentInfo generateAndUploadPdf( @RequestHeader(value = "Authorization", required = false) - String authorizationToken, + String authorizationToken, @Parameter(name = "GenerateDocumentRequest",description = "JSON object containing " - + "the templateName and the placeholder text map", required = true) + + "the templateName and the placeholder text map", required = true) @RequestBody @Valid - GenerateDocumentRequest templateData) { + GenerateDocumentRequest templateData) { //This service is internal to PRL system. No need to do service authentication here - log.info("Document generation requested with templateName [{}], placeholders map of size[{}]", - templateData.getTemplate(), templateData.getValues().size()); - return documentManagementService.generateAndStoreDocument(templateData.getTemplate(), templateData.getValues(), - authorizationToken); + + log.info("Generating PDF document with templateName [{}] and [{}] placeholders", + templateData.getTemplate(), templateData.getValues().size()); + return documentManagementService.generateAndStoreDocument( + templateData.getTemplate(), templateData.getValues(), authorizationToken + ); } @Operation(description = "Generate draft PDF document based on the supplied template name and placeholder texts " - + "and saves it in the evidence management.", tags = {"Document Generation"}) - @ApiResponses({ - @ApiResponse(responseCode = "200", description = "PDF was generated successfully and stored in the " - + "evidence management. Returns the url to the stored document.",content = - @Content(mediaType = "application/json", schema = @Schema(implementation = String.class))), - @ApiResponse(responseCode = "400", description = "Returned when input parameters are invalid or " - + "template not found", content = - @Content(mediaType = "application/json", schema = @Schema(implementation = String.class))), - @ApiResponse(responseCode = "503", description = "Returned when the PDF Service or Evidence Management " - + "Client Api cannot be reached", content = - @Content(mediaType = "application/json", schema = @Schema(implementation = String.class))), - @ApiResponse(responseCode = "500", description = "Returned when there is an unknown server error", - content = @Content(mediaType = "application/json", schema = @Schema(implementation = String.class))) - }) + + "and saves it in the evidence management.", tags = {"Document Generation"}) + @ApiResponse(responseCode = "200", description = "PDF was generated successfully and stored in the " + + "evidence management. Returns the URL to the stored document.", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = String.class))) + @ApiResponse(responseCode = "400", description = "Returned when input parameters are invalid or " + + "template not found", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = String.class))) + @ApiResponse(responseCode = "503", description = "Returned when the PDF Service or Evidence Management " + + "Client API cannot be reached", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = String.class))) + @ApiResponse(responseCode = "500", description = "Returned when there is an unknown server error", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = String.class))) @PostMapping("/generateDraftPDF") public GeneratedDocumentInfo generateAndUploadDraftPdf( @RequestHeader(value = "Authorization", required = false) - String authorizationToken, + String authorizationToken, @Parameter(name = "GenerateDocumentRequest",description = "JSON object containing the " - + "templateName and the placeholder text map", required = true) + + "templateName and the placeholder text map", required = true) @RequestBody @Valid - GenerateDocumentRequest templateData) { + GenerateDocumentRequest templateData) { //This service is internal to Divorce system. No need to do service authentication here - log.info("Document generation requested with templateName [{}], placeholders map of size[{}]", - templateData.getTemplate(), templateData.getValues().size() - ); + log.info("Generating draft PDF with templateName [{}] and [{}] placeholders", + templateData.getTemplate(), templateData.getValues().size()); return documentManagementService.generateAndStoreDraftDocument(templateData.getTemplate(), templateData.getValues(), authorizationToken ); @@ -106,8 +102,7 @@ public GeneratedDocumentInfo convertDocumentToPdf( GenerateDocumentRequest templateData) { //This service is internal to Divorce system. No need to do service authentication here log.info("convertDocumentToPdf is getting called"); - return documentManagementService.converToPdf( - templateData.getValues(), authorizationToken, fileName); + return documentManagementService.convertToPdf(templateData.getValues(), authorizationToken, fileName); } } From 16c7148c8ec352389e53a0b9dd4d7cd39f6f8d91 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Tue, 1 Apr 2025 09:51:52 +0100 Subject: [PATCH 27/51] Fix typo in method name --- .../service/impl/DocmosisPDFGenerationServiceImpl.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocmosisPDFGenerationServiceImpl.java b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocmosisPDFGenerationServiceImpl.java index a52822b5..99fcb429 100644 --- a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocmosisPDFGenerationServiceImpl.java +++ b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocmosisPDFGenerationServiceImpl.java @@ -87,7 +87,7 @@ private PdfDocumentRequest request(String templateName, Map plac } @Override - public byte[] converToPdf(Map placeholders, String fileName) { + public byte[] convertToPdf(Map placeholders, String fileName) { try { String filename = FilenameUtils.getBaseName(fileName) + ".pdf"; @@ -106,7 +106,6 @@ public byte[] converToPdf(Map placeholders, String fileName) { } catch (IOException e) { throw new PDFGenerationException("Failed to convertToPdf: " + e.getMessage(), e); } - } From 636055f15d0186f77af39d360d37a0497e3eddf1 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Tue, 1 Apr 2025 09:52:17 +0100 Subject: [PATCH 28/51] fix typo in method name --- .../documentgenerator/service/DocumentManagementService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/DocumentManagementService.java b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/DocumentManagementService.java index 745edc15..bc87b984 100644 --- a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/DocumentManagementService.java +++ b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/DocumentManagementService.java @@ -15,5 +15,5 @@ GeneratedDocumentInfo generateAndStoreDraftDocument(String templateName, Map placeholders); - GeneratedDocumentInfo converToPdf(Map placeholders, String authorizationToken, String filename); + GeneratedDocumentInfo convertToPdf(Map placeholders, String authorizationToken, String filename); } From 51360546cb0e78f93f2add3b4701c761ff206a7b Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Tue, 1 Apr 2025 09:53:23 +0100 Subject: [PATCH 29/51] Fix sonar issues --- .../impl/DocumentManagementServiceImpl.java | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImpl.java b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImpl.java index 0fe4e16a..6a81dd6c 100644 --- a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImpl.java +++ b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImpl.java @@ -17,8 +17,8 @@ import java.text.SimpleDateFormat; import java.time.Clock; -import java.util.Arrays; import java.util.Date; +import java.util.List; import java.util.Locale; import java.util.Map; @@ -32,7 +32,6 @@ public class DocumentManagementServiceImpl implements DocumentManagementService private static final String CURRENT_DATE_KEY = "current_date"; private static final String DATE_FORMAT = "yyyy-MM-dd'T'hh:mm:ss.SSS"; - private static final String DRAFT_PREFIX = "Draft"; private static final String IS_DRAFT = "isDraft"; public static final String DYNAMIC_FILE_NAME = "dynamic_fileName"; @@ -53,7 +52,7 @@ public class DocumentManagementServiceImpl implements DocumentManagementService @Override public GeneratedDocumentInfo generateAndStoreDocument(String templateName, Map placeholders, String authorizationToken) { - String fileName = ""; + String fileName; if (placeholders.containsKey(DYNAMIC_FILE_NAME)) { fileName = String.valueOf(placeholders.get(DYNAMIC_FILE_NAME)); } else { @@ -66,7 +65,7 @@ public GeneratedDocumentInfo generateAndStoreDocument(String templateName, Map placeholders, String authorizationToken) { - String fileName = ""; + String fileName; if (placeholders.containsKey(DYNAMIC_FILE_NAME)) { fileName = String.valueOf(placeholders.get(DYNAMIC_FILE_NAME)); } else { @@ -84,7 +83,7 @@ private GeneratedDocumentInfo getGeneratedDocumentInfo(String templateName, Map< String authorizationToken, String fileName) { String caseId = getCaseId(placeholders); if (caseId == null) { - log.warn("caseId is null for template \"" + templateName + "\""); + log.warn("caseId is null for template \"{}\"", templateName); } log.info("Generating document for case Id {}", caseId); @@ -111,7 +110,7 @@ public GeneratedDocumentInfo storeDocument(byte[] document, String authorization serviceAuthToken, "PRLAPPS", "PRIVATELAW", - Arrays.asList(new InMemoryMultipartFile("files", fileName, APPLICATION_PDF_VALUE, document + List.of(new InMemoryMultipartFile("files", fileName, APPLICATION_PDF_VALUE, document )) ); @@ -129,28 +128,31 @@ public GeneratedDocumentInfo storeDocument(byte[] document, String authorization @Override public byte[] generateDocument(String templateName, Map placeholders) { log.debug("Generate document requested with templateName [{}], placeholders of size[{}]", - templateName, placeholders.size() + templateName, + placeholders.size() ); return generatorService.generate(templateName, placeholders); } private String getCaseId(Map placeholders) { + @SuppressWarnings("unchecked") Map caseDetails = (Map) placeholders.getOrDefault("caseDetails", emptyMap()); return (String) caseDetails.get("id"); } @Override - public GeneratedDocumentInfo converToPdf( + public GeneratedDocumentInfo convertToPdf( Map placeholders, String authorizationToken, String fileName) { log.debug( - "Generate document requested with templateName [{}], placeholders of size[{}]", + "Generate pdf document requested with templateName [{}], placeholders of size[{}]", + fileName, placeholders.size() ); - byte[] generatedDocument = generatorService.converToPdf(placeholders, fileName); + byte[] generatedDocument = generatorService.convertToPdf(placeholders, fileName); return storeDocument(generatedDocument, authorizationToken, FilenameUtils.getBaseName(fileName) + ".pdf"); } } From be5a852bbac54c75a7fddeb2072554bb785de3af Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Tue, 1 Apr 2025 09:53:59 +0100 Subject: [PATCH 30/51] Fix typo in method name --- .../prl/documentgenerator/service/PDFGenerationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/PDFGenerationService.java b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/PDFGenerationService.java index a10d2795..c37c70cd 100644 --- a/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/PDFGenerationService.java +++ b/src/main/java/uk/gov/hmcts/reform/prl/documentgenerator/service/PDFGenerationService.java @@ -6,6 +6,6 @@ public interface PDFGenerationService { byte[] generate(String templateName, Map placeholders); - byte[] converToPdf(Map placeholders, String fileName); + byte[] convertToPdf(Map placeholders, String fileName); } From eaf3c23304bb1de3f6daac53b8180edc4402a848 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Tue, 1 Apr 2025 09:55:08 +0100 Subject: [PATCH 31/51] Update readme to use heading tags not emphasis for headings --- README.md | 72 ++++++++++++++++++++++--------------------------------- 1 file changed, 28 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index cc67643e..84aba026 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ -# Document Generator +# Private Law Document Generator [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Build Status](https://travis-ci.org/hmcts/prl-dgs-api.svg?branch=master)](https://travis-ci.org/hmcts/prl-dgs-api) -[![codecov](https://codecov.io/gh/hmcts/prl-dgs-api/branch/master/graph/badge.svg)](https://app.codecov.io/gh/hmcts/prl-dgs-api) [![Documentation](https://img.shields.io/static/v1?label=Documentation&message=DGS&color=informational&logo=confluence)](https://tools.hmcts.net/confluence/display/PL/PDF+document+generator) This is a document generation and template management service. This allows to generate documents based on a @@ -12,23 +10,20 @@ Document Management Store (via Case Document AM). The service provides a single RESTful endpoint that will generate the document, store it in Evidence Management Store and return the link to the stored data. -Updated with master code base until 2.1 +## Dynamic file name -### Dynamic file name -You must pass **dynamic_fileName** key in the **placeholders** map to get a dynamic file name +You must pass `dynamic_fileName` key in the `placeholders` map to get a dynamic file name -### Setup +## Setup -**Prerequisites** +### Prerequisites - [JDK 17](https://openjdk.java.net/) - [Docker](https://www.docker.com) +### Building -***Building*** - -The project uses [Gradle](https://gradle.org) as a build tool but you don't have to install it locally since there is a -`./gradlew` wrapper script. +The project uses [Gradle](https://gradle.org) as a build tool. To build project please execute the following command: @@ -36,7 +31,7 @@ To build project please execute the following command: ./gradlew build ``` -**Running** +### Running First you need to create distribution by executing following command: @@ -56,7 +51,7 @@ After it has finished downloaded run: az login ``` -This should open a browser window for you to login, use your HMCTS account +This should open a browser window for you to log in, use your HMCTS account After logging in run the following command: @@ -74,32 +69,34 @@ docker-compose up ``` As a result the following container(s) will get created and started: - - long living container for API application exposing port `4007` + +- long living container for API application exposing port `4007` #### Troubleshooting ### Managing Preview environment PODs -Make sure you have added the label 'enable_keep_helm' while creating the PR. Otherwise, add the label and re-trigger the build. + +Make sure you have added the label `enable_keep_helm` while creating the PR. Otherwise, add the label and re-trigger the build. ## Testing -**Integration tests** +### Integration tests To run all integration tests locally: -* Make a copy of `src/main/resources/example-application-aat.yml` as `src/main/resources/application-aat.yml` -* Make a copy of `src/integrationTest/resources/example-application-local.properties` as `src/integrationTest/resources/application-local.properties` -* Replace the `replace_me` secrets in the _newly created_ files. You can get the values from SCM and Azure secrets key vault (the new files are in .gitignore and should ***not*** be committed to git) -* Assuming you use IntelliJ, run your application -* And then run your gradle functional tests task -* Or if using command line: - * Start the app with AAT config using `./gradlew clean bootRunAat` - * Start the test with AAT config using `./gradlew clean functional` +- Make a copy of `src/main/resources/example-application-aat.yml` as `src/main/resources/application-aat.yml` +- Make a copy of `src/integrationTest/resources/example-application-local.properties` as `src/integrationTest/resources/application-local.properties` +- Replace the `replace_me` secrets in the _newly created_ files. You can get the values from SCM and Azure secrets key vault (the new files are in .gitignore and should _**not**_ be committed to git) +- Assuming you use IntelliJ, run your application +- And then run your gradle functional tests task +- Or if using command line: + - Start the app with AAT config using `./gradlew clean bootRunAat` + - Start the test with AAT config using `./gradlew clean functional` If you update content in [templates](https://github.com/hmcts/rdo-docmosis-templates), you can re-generate the PDFs by running the ignored test `PDFGenerationTest.ignoreMe_updateGeneratedPdfs`. The test will output generated PDFs to the folder `src/integrationTest/resources/documentgenerator/documents/regenerated` -**Unit tests** +### Unit tests To run all unit tests please execute following command: @@ -107,7 +104,7 @@ To run all unit tests please execute following command: ./gradlew test ``` -**Coding style tests** +### Coding style tests To run all checks (including unit tests) please execute following command: @@ -115,35 +112,22 @@ To run all checks (including unit tests) please execute following command: ./gradlew check ``` -**Mutation tests** - -To run all mutation tests execute the following command: - -```bash -./gradlew pitest -``` - ## Developing -**Flow Diagram** +### Flow Diagram ![diagram](docs/DataFlow.png) -**API documentation** +### API documentation API documentation is provided with Swagger: - - `http://localhost:4007/swagger-ui.html` - UI to interact with the API resources -**Versioning** +- `http://localhost:4007/swagger-ui.html` - UI to interact with the API resources -We use [SemVer](http://semver.org/) for versioning. -For the versions available, see the tags on this repository. - -**Standard API** +### Standard API We follow [RESTful API standards](https://hmcts.github.io/restful-api-standards/). ## License This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details. - From 8ff20e8e4c2275b2df34340277c19858628b13e5 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Tue, 1 Apr 2025 09:56:18 +0100 Subject: [PATCH 32/51] Update method to use javas built in utf8 encoding --- .../gov/hmcts/reform/prl/functionaltest/ResourceLoader.java | 3 +-- .../java/uk/gov/hmcts/reform/prl/ResourceLoader.java | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/ResourceLoader.java b/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/ResourceLoader.java index 37005008..fd38779b 100644 --- a/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/ResourceLoader.java +++ b/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/ResourceLoader.java @@ -3,7 +3,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.net.URL; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; @@ -11,7 +10,7 @@ public class ResourceLoader { public static String loadJson(final String filePath) throws Exception { - return new String(loadResource(filePath), Charset.forName(StandardCharsets.UTF_8.name())); + return new String(loadResource(filePath), StandardCharsets.UTF_8); } public static byte[] loadResource(final String filePath) throws Exception { diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/prl/ResourceLoader.java b/src/integrationTest/java/uk/gov/hmcts/reform/prl/ResourceLoader.java index e7276126..574c1809 100644 --- a/src/integrationTest/java/uk/gov/hmcts/reform/prl/ResourceLoader.java +++ b/src/integrationTest/java/uk/gov/hmcts/reform/prl/ResourceLoader.java @@ -3,14 +3,14 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.net.URL; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; public class ResourceLoader { public static String loadJson(final String filePath) throws Exception { - return new String(loadResource(filePath), Charset.forName("utf-8")); + return new String(loadResource(filePath), StandardCharsets.UTF_8); } public static byte[] loadResource(final String filePath) throws Exception { From 2745e802d20dd4ea97e44a83334cd01924b48428 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Tue, 1 Apr 2025 09:57:13 +0100 Subject: [PATCH 33/51] Update tests to use junit 5 --- pacts/prl-dgs-api-CCD_CASE_DOCS_AM_API.json | 4 +- .../CdamApiConsumerTest.java | 78 +++---- .../PdfGenerationServiceConsumerTest.java | 67 ++---- .../DocumentGenerateAndStoreE2ETest.java | 60 +++--- ...umentGenerateControllerFunctionalTest.java | 25 +-- .../RootControllerFunctionalTest.java | 13 +- ...entGeneratorControllerIntegrationTest.java | 24 +-- .../reform/prl/DocumentGeneratorUtil.java | 2 +- ...tManagementServiceImplIntegrationTest.java | 64 +++--- .../uk/gov/hmcts/reform/prl/IdamUtils.java | 27 +-- .../gov/hmcts/reform/prl/IntegrationTest.java | 27 +-- .../hmcts/reform/prl/DgsApiSmokeTests.java | 50 +++-- .../config/TemplatesConfigurationTest.java | 43 ++-- .../launchdarkly/LDClientFactoryTest.java | 12 +- .../launchdarkly/LaunchDarklyClientTest.java | 20 +- .../DocumentGeneratorControllerUTest.java | 23 +-- .../controller/RootControllerTest.java | 18 +- .../helper/GlobalExceptionHandlerUTest.java | 21 +- .../DocumentGenerateAndStoreE2ETest.java | 194 ++++++++++-------- .../monitoring/health/HealthCheckITest.java | 137 +++++++------ .../health/WebServiceHealthCheckUTest.java | 22 +- .../mapper/TemplateDataMapperTest.java | 57 +++-- ...DocmosisPdfGenerationServiceImplUTest.java | 69 +++---- .../DocumentManagementServiceImplTest.java | 85 ++++---- .../DocumentManagementServiceImplUTest.java | 36 ++-- .../util/NullOrEmptyValidatorUTest.java | 62 +++--- .../util/ResourceLoaderUTest.java | 42 ++-- 27 files changed, 592 insertions(+), 690 deletions(-) diff --git a/pacts/prl-dgs-api-CCD_CASE_DOCS_AM_API.json b/pacts/prl-dgs-api-CCD_CASE_DOCS_AM_API.json index abd6f195..02b8279f 100644 --- a/pacts/prl-dgs-api-CCD_CASE_DOCS_AM_API.json +++ b/pacts/prl-dgs-api-CCD_CASE_DOCS_AM_API.json @@ -19,7 +19,7 @@ "response": { "status": 200, "headers": { - "Content-Type": "application/vnd\u63AFuk\u98E0gov\u8E83hmcts\uED68dm\u347Adocument\u7C16v1haljson;charset=UTF-8" + "Content-Type": "application/vnd\u46AFuk\u1870gov\u6994hmcts\u26F2dm\uE7E0document\u30E7v1haljson;charset=UTF-8" }, "matchingRules": { "header": { @@ -53,7 +53,7 @@ "response": { "status": 500, "headers": { - "Content-Type": "application/vnd\uF5A0uk\uE1D7gov\u0205hmcts\u0E15dm\uCDCDdocument\u0B01v111haljson;charset=UTF-8" + "Content-Type": "application/vnd\u5C61uk\uE209gov\uA235hmcts\uC39Bdm\u1943document\u8E03v1haljson;charset=UTF-8" }, "matchingRules": { "header": { diff --git a/src/contractTest/java/uk/gov/hmcts/reform/prl/documentgenerator/CdamApiConsumerTest.java b/src/contractTest/java/uk/gov/hmcts/reform/prl/documentgenerator/CdamApiConsumerTest.java index c8bac08e..e55b0456 100644 --- a/src/contractTest/java/uk/gov/hmcts/reform/prl/documentgenerator/CdamApiConsumerTest.java +++ b/src/contractTest/java/uk/gov/hmcts/reform/prl/documentgenerator/CdamApiConsumerTest.java @@ -1,7 +1,6 @@ package uk.gov.hmcts.reform.prl.documentgenerator; import au.com.dius.pact.consumer.MockServer; -import au.com.dius.pact.consumer.dsl.PactDslJsonBody; import au.com.dius.pact.consumer.dsl.PactDslWithProvider; import au.com.dius.pact.consumer.junit5.PactConsumerTestExt; import au.com.dius.pact.consumer.junit5.PactTestFor; @@ -13,7 +12,7 @@ import org.apache.http.client.fluent.Executor; import org.apache.http.client.fluent.Request; import org.json.JSONException; -import org.junit.After; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; @@ -25,47 +24,41 @@ import static org.junit.jupiter.api.Assertions.assertEquals; - @ExtendWith(PactConsumerTestExt.class) @ExtendWith(SpringExtension.class) @TestInstance(TestInstance.Lifecycle.PER_CLASS) @PactTestFor(providerName = "CCD_CASE_DOCS_AM_API", port = "5170") @PactFolder("pacts") -@SpringBootTest({ - "CCD_CASE_DOCS_AM_API:http://localhost:5170" -}) +@SpringBootTest("CCD_CASE_DOCS_AM_API = http://localhost:5170") public class CdamApiConsumerTest { - private static final String SERVICE_AUTHORIZATION_HEADER = "ServiceAuthorization"; - private static final String someServiceAuthToken = "someServiceAuthToken"; - private static final String invalidServiceAuthToken = "invalidServiceAuthToken"; + private static final String SERVICE_AUTH_TOKEN = "someServiceAuthToken"; + private static final String INVALID_SERVICE_AUTH_TOKEN = "invalidServiceAuthToken"; private static final String AUTHORIZATION_HEADER = "Authorization"; - private static final String someAuthToken = "someAuthToken"; - private static final String someDocumentId = "456c0976-3178-46dd-b9ce-5ab5d47c625a"; - + private static final String SOME_AUTH_TOKEN = "someAuthToken"; + private static final String SOME_DOCUMENT_ID = "456c0976-3178-46dd-b9ce-5ab5d47c625a"; @BeforeEach - public void setUpEachTest() throws InterruptedException, IOException { + public void setUpEachTest() throws InterruptedException { Thread.sleep(2000); } - @After + @AfterEach void teardown() { Executor.closeIdleConnections(); } @Pact(provider = "CCD_CASE_DOCS_AM_API", consumer = "prl-dgs-api") - RequestResponsePact downloadDocument(PactDslWithProvider builder) throws JSONException, IOException { - // @formatter:off + RequestResponsePact downloadDocument(PactDslWithProvider builder) throws JSONException { return builder .given("A request to download a document") .uponReceiving("a request to download a valid document") .method("GET") - .headers(SERVICE_AUTHORIZATION_HEADER, someServiceAuthToken) - .headers(AUTHORIZATION_HEADER, someAuthToken) - .path("/cases/documents/" + someDocumentId) + .headers(SERVICE_AUTHORIZATION_HEADER, SERVICE_AUTH_TOKEN) + .headers(AUTHORIZATION_HEADER, SOME_AUTH_TOKEN) + .path("/cases/documents/" + SOME_DOCUMENT_ID) .willRespondWith() .matchHeader(org.springframework.http.HttpHeaders.CONTENT_TYPE, "application/vnd.uk.gov.hmcts.dm.document.v1+hal+json;charset=UTF-8") @@ -77,25 +70,25 @@ RequestResponsePact downloadDocument(PactDslWithProvider builder) throws JSONExc @PactTestFor(pactMethod = "downloadDocument") public void verifyDownloadDocument(MockServer mockServer) throws IOException { - HttpResponse downloadDocumentResponse = Request.Get(mockServer.getUrl() + "/cases/documents/" + someDocumentId) - .addHeader(SERVICE_AUTHORIZATION_HEADER, someServiceAuthToken) - .addHeader(AUTHORIZATION_HEADER, someAuthToken) + HttpResponse downloadDocumentResponse = Request.Get( + mockServer.getUrl() + "/cases/documents/" + SOME_DOCUMENT_ID + ) + .addHeader(SERVICE_AUTHORIZATION_HEADER, SERVICE_AUTH_TOKEN) + .addHeader(AUTHORIZATION_HEADER, SOME_AUTH_TOKEN) .execute().returnResponse(); - assertEquals(200, downloadDocumentResponse.getStatusLine().getStatusCode()); + assertEquals(HttpStatus.SC_OK, downloadDocumentResponse.getStatusLine().getStatusCode()); } - @Pact(provider = "CCD_CASE_DOCS_AM_API", consumer = "prl-dgs-api") - RequestResponsePact noAuthDownloadDocument(PactDslWithProvider builder) throws JSONException, IOException { - // @formatter:off + RequestResponsePact noAuthDownloadDocument(PactDslWithProvider builder) throws JSONException { return builder .given("A request to download a document") .uponReceiving("a request to download a valid document with invalid authorisation") .method("GET") - .headers(SERVICE_AUTHORIZATION_HEADER, invalidServiceAuthToken) - .path("/cases/documents/" + someDocumentId) + .headers(SERVICE_AUTHORIZATION_HEADER, INVALID_SERVICE_AUTH_TOKEN) + .path("/cases/documents/" + SOME_DOCUMENT_ID) .willRespondWith() .matchHeader(org.springframework.http.HttpHeaders.CONTENT_TYPE, "application/vnd.uk.gov.hmcts.dm.document.v1+hal+json;charset=UTF-8") @@ -103,33 +96,26 @@ RequestResponsePact noAuthDownloadDocument(PactDslWithProvider builder) throws J .toPact(); } - @Test @PactTestFor(pactMethod = "noAuthDownloadDocument") public void verifyNoAuthDownloadDocument(MockServer mockServer) throws IOException { - HttpResponse downloadDocumentResponse = Request.Get(mockServer.getUrl() + "/cases/documents/" + someDocumentId) - .addHeader(SERVICE_AUTHORIZATION_HEADER, invalidServiceAuthToken).execute().returnResponse(); - - assertEquals(500, downloadDocumentResponse.getStatusLine().getStatusCode()); + HttpResponse downloadDocumentResponse = Request.Get( + mockServer.getUrl() + "/cases/documents/" + SOME_DOCUMENT_ID + ) + .addHeader(SERVICE_AUTHORIZATION_HEADER, INVALID_SERVICE_AUTH_TOKEN).execute().returnResponse(); + assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, downloadDocumentResponse.getStatusLine().getStatusCode()); } @Pact(provider = "CCD_CASE_DOCS_AM_API", consumer = "prl-dgs-api") - RequestResponsePact uploadDocument(PactDslWithProvider builder) throws JSONException, IOException { - // @formatter:off - - PactDslJsonBody body = new PactDslJsonBody() - .stringMatcher("caseTypeId", "PRLAPPS") - .stringMatcher("jurisdictionId", "PRIVATELAW") - .asBody(); - + RequestResponsePact uploadDocument(PactDslWithProvider builder) throws JSONException { return builder .given("A request to upload a document") .uponReceiving("a request to upload a document with valid authorization") .method("POST") - .headers(SERVICE_AUTHORIZATION_HEADER, someServiceAuthToken) - .headers(AUTHORIZATION_HEADER, someAuthToken) + .headers(SERVICE_AUTHORIZATION_HEADER, SERVICE_AUTH_TOKEN) + .headers(AUTHORIZATION_HEADER, SOME_AUTH_TOKEN) .path("/cases/documents") .willRespondWith() .status(HttpStatus.SC_OK) @@ -141,11 +127,11 @@ RequestResponsePact uploadDocument(PactDslWithProvider builder) throws JSONExcep public void verifyUploadDocument(MockServer mockServer) throws IOException { HttpResponse downloadDocumentResponse = Request.Post(mockServer.getUrl() + "/cases/documents") - .addHeader(SERVICE_AUTHORIZATION_HEADER, someServiceAuthToken) - .addHeader(AUTHORIZATION_HEADER, someAuthToken) + .addHeader(SERVICE_AUTHORIZATION_HEADER, SERVICE_AUTH_TOKEN) + .addHeader(AUTHORIZATION_HEADER, SOME_AUTH_TOKEN) .execute().returnResponse(); - assertEquals(200, downloadDocumentResponse.getStatusLine().getStatusCode()); + assertEquals(HttpStatus.SC_OK, downloadDocumentResponse.getStatusLine().getStatusCode()); } } diff --git a/src/contractTest/java/uk/gov/hmcts/reform/prl/documentgenerator/PdfGenerationServiceConsumerTest.java b/src/contractTest/java/uk/gov/hmcts/reform/prl/documentgenerator/PdfGenerationServiceConsumerTest.java index 68c1746e..53392cd4 100644 --- a/src/contractTest/java/uk/gov/hmcts/reform/prl/documentgenerator/PdfGenerationServiceConsumerTest.java +++ b/src/contractTest/java/uk/gov/hmcts/reform/prl/documentgenerator/PdfGenerationServiceConsumerTest.java @@ -17,25 +17,17 @@ import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClientBuilder; import org.json.JSONException; -import org.junit.After; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; 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.mock.mockito.MockBean; import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.util.ResourceUtils; -import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; import uk.gov.hmcts.reform.prl.documentgenerator.domain.request.GenerateDocumentRequest; -import uk.gov.hmcts.reform.prl.documentgenerator.service.TemplateManagementService; -import uk.gov.hmcts.reform.prl.documentgenerator.service.impl.DocmosisPDFGenerationServiceImpl; -import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; -import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -45,48 +37,35 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) @PactTestFor(providerName = "rpePdfService_PDFGenerationEndpointV2", port = "8891") @PactFolder("pacts") -@SpringBootTest({ - "service.pdf-service.uri : http://localhost:8891/pdfs" -}) +@SpringBootTest("service.pdf-service.uri = http://localhost:8891/pdfs") public class PdfGenerationServiceConsumerTest { - private static final String SERVICE_AUTHORIZATION_HEADER = "ServiceAuthorization"; - - - @Autowired - DocmosisPDFGenerationServiceImpl docmosisPDFGenerationService; - @Autowired ObjectMapper objectMapper; - @MockBean - private AuthTokenGenerator serviceTokenGenerator; - @MockBean - private TemplateManagementService templateManagementService; - private final String someServiceAuthToken = "someServiceAuthToken"; - private final String template = "
Case number: {{ caseNo }}
"; - private Map placeholders = Map.of("caseNo", "12345"); + private static final String SERVICE_AUTHORIZATION_HEADER = "ServiceAuthorization"; + private static final String SERVICE_AUTH_TOKEN = "someServiceAuthToken"; + private static final String TEMPLATE = "
Case number: {{ caseNo }}
"; + private final Map placeholders = Map.of("caseNo", "12345"); @BeforeEach - public void setUpEachTest() throws InterruptedException, IOException { + public void setUpEachTest() throws InterruptedException { Thread.sleep(2000); } - @After + @AfterEach void teardown() { Executor.closeIdleConnections(); } @Pact(provider = "rpePdfService_PDFGenerationEndpointV2", consumer = "prl_documentGeneratorClient") RequestResponsePact generatePdfFromTemplate(PactDslWithProvider builder) throws JSONException, IOException { - // @formatter:off - return builder .given("A request to generate a pdf document") .uponReceiving("a request to generate a pdf document with a template") .method("POST") - .headers(SERVICE_AUTHORIZATION_HEADER, someServiceAuthToken) - .body(createJsonObject(new GenerateDocumentRequest(template, placeholders)), + .headers(SERVICE_AUTHORIZATION_HEADER, SERVICE_AUTH_TOKEN) + .body(createJsonObject(new GenerateDocumentRequest(TEMPLATE, placeholders)), "application/vnd.uk.gov.hmcts.pdf-service.v2+json;charset=UTF-8") .path("/pdfs") .willRespondWith() @@ -99,21 +78,11 @@ RequestResponsePact generatePdfFromTemplate(PactDslWithProvider builder) throws @Test @PactTestFor(pactMethod = "generatePdfFromTemplate") public void verifyGeneratePdfFromTemplatePact(MockServer mockServer) throws IOException, JSONException { - //Map placeholders = new HashMap<>(); - //placeholders.put(CASE_DETAILS, new HashMap<>(ImmutableMap.of( - // "case_data", new HashMap<>() - //))); - // - //when(templateManagementService.getTemplateByName("someTemplateName")).thenReturn(template.getBytes()); - //when(serviceTokenGenerator.generate()).thenReturn(someServiceAuthToken); - // - //byte[] response = docmosisPDFGenerationService.generate("someTemplateName", placeholders); - HttpClient httpClient = HttpClientBuilder.create().build(); HttpPost request = new HttpPost(mockServer.getUrl() + "/pdfs"); - StringEntity json = new StringEntity(createJsonObject(new GenerateDocumentRequest(template, placeholders))); - request.addHeader(SERVICE_AUTHORIZATION_HEADER, someServiceAuthToken); + StringEntity json = new StringEntity(createJsonObject(new GenerateDocumentRequest(TEMPLATE, placeholders))); + request.addHeader(SERVICE_AUTHORIZATION_HEADER, SERVICE_AUTH_TOKEN); request.addHeader("content-type", "application/vnd.uk.gov.hmcts.pdf-service.v2+json;charset=UTF-8"); request.setEntity(json); @@ -121,23 +90,13 @@ public void verifyGeneratePdfFromTemplatePact(MockServer mockServer) throws IOEx String responseContentType = generateDocumentResponse.getEntity().getContentType().toString(); - assertEquals(200, generateDocumentResponse.getStatusLine().getStatusCode()); + assertEquals(HttpStatus.SC_OK, generateDocumentResponse.getStatusLine().getStatusCode()); assertEquals("Content-Type: application/pdf", responseContentType); } - private File getFile(String fileName) throws FileNotFoundException { - return ResourceUtils.getFile(this.getClass().getResource("/json/" + fileName)); - } - protected String createJsonObject(Object obj) throws JSONException, IOException { return objectMapper.writeValueAsString(obj); } - private GenerateDocumentRequest buildGenerateDocumentRequest() { - Map placeholders = new HashMap<>(); - placeholders.put("caseNo", "12345"); - - return new GenerateDocumentRequest(template, placeholders); - } } diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/DocumentGenerateAndStoreE2ETest.java b/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/DocumentGenerateAndStoreE2ETest.java index ae569cce..47201d74 100644 --- a/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/DocumentGenerateAndStoreE2ETest.java +++ b/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/DocumentGenerateAndStoreE2ETest.java @@ -3,18 +3,18 @@ import com.github.tomakehurst.wiremock.client.WireMock; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.NullSource; import org.junit.jupiter.params.provider.ValueSource; -import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.PropertySource; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import uk.gov.hmcts.reform.ccd.document.am.model.Document; @@ -30,7 +30,7 @@ import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.springframework.http.HttpHeaders.CONTENT_TYPE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -42,7 +42,7 @@ import static uk.gov.hmcts.reform.prl.documentgenerator.util.TestData.TEST_HASH_TOKEN; -@RunWith(SpringRunner.class) +@ExtendWith(SpringExtension.class) @SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = DocumentGeneratorApplication.class @@ -54,28 +54,25 @@ public class DocumentGenerateAndStoreE2ETest { private static final String CASE_DOCS_API_URL = "/cases/documents"; private static final String DOCMOSIS_API_URL = "/rs/render"; private static final String S2S_API_URL = "/lease"; - private static final String CASE_DETAILS = "caseDetails"; private static final String CASE_DATA = "case_data"; - private static final String TEST_EXAMPLE = "FL-DIV-GOR-ENG-00062.docx"; @Autowired private MockMvc webClient; @RegisterExtension - public static WireMockExtension caseDocsClientApiServiceServer = WireMockExtension.newInstance().options( + public static final WireMockExtension caseDocsClientApiServiceServer = WireMockExtension.newInstance().options( wireMockConfig().port(5170)).build(); @RegisterExtension - public static WireMockExtension docmosisClientServiceServer = WireMockExtension.newInstance().options( + public static final WireMockExtension docmosisClientServiceServer = WireMockExtension.newInstance().options( wireMockConfig().port(5501)).build(); @RegisterExtension - public static WireMockExtension serviceAuthServer = WireMockExtension.newInstance().options( + public static final WireMockExtension serviceAuthServer = WireMockExtension.newInstance().options( wireMockConfig().port(4502)).build(); - private void perform(String template) throws Exception { final Map values = Collections.emptyMap(); @@ -91,22 +88,21 @@ private void perform(String template) throws Exception { @ParameterizedTest @NullSource @ValueSource(strings = {" ", "nonExistingTemplate"}) - public void givenTemplateNameIsBlankOrNullOrTemplateNotFound_whenGenerateAndStoreDocument_thenReturnHttp400( + public void givenTemplateNameIsBlankOrNullOrTemplateNotFoundWhenGenerateAndStoreDocumentThenReturnHttp400( String template) throws Exception { perform(template); } @Test - public void givenCouldNotConnectToAuthService_whenGenerateAndStoreDocument_thenReturnHttp503() throws Exception { - Map caseData = new HashMap<>(); + public void givenCouldNotConnectToAuthServiceWhenGenerateAndStoreDocumentThenReturnHttp503() throws Exception { Map requestData = Collections.singletonMap( - CASE_DETAILS, Collections.singletonMap(CASE_DATA, caseData) + CASE_DETAILS, Collections.singletonMap(CASE_DATA, new HashMap<>()) ); final GenerateDocumentRequest generateDocumentRequest = new GenerateDocumentRequest(TEST_EXAMPLE, requestData); - mockDocmosisPdfService(HttpStatus.OK, new byte[] {1}); + mockDocmosisPdfService(new byte[] {1}); mockServiceAuthServer(HttpStatus.SERVICE_UNAVAILABLE, ""); webClient.perform(post(API_URL) @@ -117,16 +113,15 @@ public void givenCouldNotConnectToAuthService_whenGenerateAndStoreDocument_thenR } @Test - public void givenAuthServiceReturnAuthenticationError_whenGenerateAndStoreDocument_thenReturnHttp401() + public void givenAuthServiceReturnAuthenticationErrorWhenGenerateAndStoreDocumentThenReturnHttp401() throws Exception { - Map caseData = new HashMap<>(); Map requestData = Collections.singletonMap( - CASE_DETAILS, Collections.singletonMap(CASE_DATA, caseData) + CASE_DETAILS, Collections.singletonMap(CASE_DATA, new HashMap<>()) ); final GenerateDocumentRequest generateDocumentRequest = new GenerateDocumentRequest(TEST_EXAMPLE, requestData); - mockDocmosisPdfService(HttpStatus.OK, new byte[] {1}); + mockDocmosisPdfService(new byte[] {1}); mockServiceAuthServer(HttpStatus.UNAUTHORIZED, ""); webClient.perform(post(API_URL) @@ -137,13 +132,12 @@ public void givenAuthServiceReturnAuthenticationError_whenGenerateAndStoreDocume } @Test - public void givenAllGoesWellForTestExample_whenGenerateAndStoreDocument_thenReturn() + public void givenAllGoesWellForTestExampleWhenGenerateAndStoreDocumentThenReturn() throws Exception { - assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments(TEST_EXAMPLE); + assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments(); } - private void assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments( - String templateId) throws Exception { + private void assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments() throws Exception { //Given final Map caseData = Collections.emptyMap(); final Map values = new HashMap<>(); @@ -152,12 +146,13 @@ private void assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments( + "3Nzk2Mzh9.gzSraSYS8EXBxLN_oWnFSRgCzcmJmMjLiuyu5CSpyHI"; final UploadResponse uploadResponse = new UploadResponse(List.of(mockCaseDocsDocuments())); - mockDocmosisPdfService(HttpStatus.OK, new byte[]{1}); - mockCaseDocsClientApi(HttpStatus.OK, uploadResponse); + mockDocmosisPdfService(new byte[]{1}); + mockCaseDocsClientApi(uploadResponse); mockServiceAuthServer(HttpStatus.OK, s2sAuthToken); //When - final GenerateDocumentRequest generateDocumentRequest = new GenerateDocumentRequest(templateId, values); + final GenerateDocumentRequest generateDocumentRequest = new GenerateDocumentRequest( + DocumentGenerateAndStoreE2ETest.TEST_EXAMPLE, values); MvcResult result = webClient.perform(post(API_URL) .content(ObjectMapperTestUtil.convertObjectToJsonString(generateDocumentRequest)) .contentType(MediaType.APPLICATION_JSON) @@ -174,21 +169,19 @@ private void assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments( } private GeneratedDocumentInfo getGeneratedDocumentInfo() { - GeneratedDocumentInfo generatedDocumentInfo = GeneratedDocumentInfo.builder() + return GeneratedDocumentInfo.builder() .url(FILE_URL) .hashToken(TEST_HASH_TOKEN) .mimeType(MIME_TYPE) .binaryUrl(BINARY_URL) .docName("TestTemplate.pdf") .build(); - - return generatedDocumentInfo; } - private void mockDocmosisPdfService(HttpStatus expectedResponse, byte[] body) { + private void mockDocmosisPdfService(byte[] body) { docmosisClientServiceServer.stubFor(WireMock.post(DOCMOSIS_API_URL) .willReturn(aResponse() - .withStatus(expectedResponse.value()) + .withStatus(HttpStatus.OK.value()) .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) .withBody(ObjectMapperTestUtil.convertObjectToJsonString(body)) )); @@ -203,10 +196,10 @@ private void mockServiceAuthServer(HttpStatus expectedResponse, String body) { )); } - private void mockCaseDocsClientApi(HttpStatus expectedResponse, UploadResponse uploadResponse) { + private void mockCaseDocsClientApi(UploadResponse uploadResponse) { caseDocsClientApiServiceServer.stubFor(WireMock.post(CASE_DOCS_API_URL) .willReturn(aResponse() - .withStatus(expectedResponse.value()) + .withStatus(HttpStatus.OK.value()) .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) .withBody(ObjectMapperTestUtil.convertObjectToJsonString(uploadResponse)) )); @@ -229,4 +222,5 @@ public static Document mockCaseDocsDocuments() { .originalDocumentName(TEST_DEFAULT_NAME_FOR_PDF_FILE) .build(); } + } diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/DocumentGenerateControllerFunctionalTest.java b/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/DocumentGenerateControllerFunctionalTest.java index 86a526c2..12bda425 100644 --- a/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/DocumentGenerateControllerFunctionalTest.java +++ b/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/DocumentGenerateControllerFunctionalTest.java @@ -1,23 +1,23 @@ package uk.gov.hmcts.reform.prl.functionaltest; -import io.restassured.RestAssured; import io.restassured.specification.RequestSpecification; import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.PropertySource; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringExtension; import uk.gov.hmcts.reform.prl.documentgenerator.DocumentGeneratorApplication; -@RunWith(SpringRunner.class) +import static io.restassured.RestAssured.given; + +@ExtendWith(SpringExtension.class) @ContextConfiguration(classes = DocumentGeneratorApplication.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @PropertySource(value = "classpath:application.yml") public class DocumentGenerateControllerFunctionalTest { - private final String userToken = "Bearer testToken"; - + private static final String USER_TOKEN = "Bearer testToken"; private static final String VALID_REQUEST_BODY = "documentgenerator/da.json"; private final String targetInstance = @@ -26,14 +26,12 @@ public class DocumentGenerateControllerFunctionalTest { "http://localhost:4007" ); - private final RequestSpecification request = RestAssured.given().relaxedHTTPSValidation().baseUri(targetInstance); - + private final RequestSpecification request = given().relaxedHTTPSValidation().baseUri(targetInstance); @Test - public void givenNoRequestBodyReturn400FromGenerateAndUploadPdf() throws Exception { - String requestBody = ResourceLoader.loadJson(VALID_REQUEST_BODY); + public void givenNoRequestBodyReturn400FromGenerateAndUploadPdf() { request - .header("Authorization", userToken) + .header("Authorization", USER_TOKEN) .when() .contentType("application/json") .post("/version/1/generatePDF") @@ -42,10 +40,9 @@ public void givenNoRequestBodyReturn400FromGenerateAndUploadPdf() throws Excepti } @Test - public void givenNoRequestBodyReturn400FromGenerateAndDraftPdf() throws Exception { - String requestBody = ResourceLoader.loadJson(VALID_REQUEST_BODY); + public void givenNoRequestBodyReturn400FromGenerateAndDraftPdf() { request - .header("Authorization", userToken) + .header("Authorization", USER_TOKEN) .when() .contentType("application/json") .post("/version/1/generateDraftPDF") diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/RootControllerFunctionalTest.java b/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/RootControllerFunctionalTest.java index e273e8ef..0903ce7d 100644 --- a/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/RootControllerFunctionalTest.java +++ b/src/functionalTest/java/uk/gov/hmcts/reform/prl/functionaltest/RootControllerFunctionalTest.java @@ -1,14 +1,14 @@ package uk.gov.hmcts.reform.prl.functionaltest; -import io.restassured.RestAssured; import io.restassured.specification.RequestSpecification; import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Test; -public class RootControllerFunctionalTest { +import static io.restassured.RestAssured.given; - private final String userToken = "Bearer testToken"; +public class RootControllerFunctionalTest { + private static final String USER_TOKEN = "Bearer testToken"; private final String targetInstance = StringUtils.defaultIfBlank( @@ -16,14 +16,13 @@ public class RootControllerFunctionalTest { "http://localhost:4007" ); - private final RequestSpecification request = RestAssured.given().relaxedHTTPSValidation().baseUri(targetInstance); - + private final RequestSpecification request = given().relaxedHTTPSValidation().baseUri(targetInstance); @Test - public void checkDgsRootFor200Response() throws Exception { + public void checkDgsRootFor200Response() { request - .header("Authorization", userToken) + .header("Authorization", USER_TOKEN) .when() .contentType("application/json") .get("/") diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/prl/DocumentGeneratorControllerIntegrationTest.java b/src/integrationTest/java/uk/gov/hmcts/reform/prl/DocumentGeneratorControllerIntegrationTest.java index f95e8f42..a17cd873 100644 --- a/src/integrationTest/java/uk/gov/hmcts/reform/prl/DocumentGeneratorControllerIntegrationTest.java +++ b/src/integrationTest/java/uk/gov/hmcts/reform/prl/DocumentGeneratorControllerIntegrationTest.java @@ -1,29 +1,25 @@ package uk.gov.hmcts.reform.prl; import io.restassured.response.Response; -import net.serenitybdd.rest.SerenityRest; import org.apache.http.HttpStatus; -import org.junit.Assert; -import org.junit.Test; -import org.springframework.beans.factory.annotation.Value; +import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import static io.restassured.RestAssured.given; +import static org.junit.jupiter.api.Assertions.assertEquals; + @SpringBootTest(classes = DocumentGeneratorControllerIntegrationTest.class) @AutoConfigureMockMvc public class DocumentGeneratorControllerIntegrationTest extends IntegrationTest { private static final String VALID_INPUT_JSON = "documentgenerator/documents/jsoninput/DA-granted-letter.json"; - @Value("${prl.document.generator.uri}") - private String prlDocumentGeneratorURI; - @Test - public void givenTemplateShouldGeneratePdf_VerifyResponse() throws Exception { - + public void givenTemplateShouldGeneratePdfVerifyResponse() throws Exception { String requestBody = ResourceLoader.loadJson(VALID_INPUT_JSON); - Response response = SerenityRest.given() + Response response = given() .contentType("application/json") .header("Authorization", getAuthorizationToken()) .body(requestBody) @@ -31,14 +27,14 @@ public void givenTemplateShouldGeneratePdf_VerifyResponse() throws Exception { .post(prlDocumentGeneratorURI) .andReturn(); - Assert.assertEquals(response.getStatusCode(), HttpStatus.SC_OK); + assertEquals(HttpStatus.SC_OK, response.getStatusCode()); } @Test - public void generatePdfWithWrongURI_ShouldThrowNotFound404() throws Exception { + public void generatePdfWithWrongURIShouldThrowNotFound404() throws Exception { String requestBody = ResourceLoader.loadJson(VALID_INPUT_JSON); - Response response = SerenityRest.given() + Response response = given() .contentType("application/json") .header("Authorization", getAuthorizationToken()) .body(requestBody) @@ -46,6 +42,6 @@ public void generatePdfWithWrongURI_ShouldThrowNotFound404() throws Exception { .post(prlDocumentGeneratorURI + "/test") .andReturn(); - Assert.assertEquals(response.getStatusCode(), HttpStatus.SC_NOT_FOUND); + assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); } } diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/prl/DocumentGeneratorUtil.java b/src/integrationTest/java/uk/gov/hmcts/reform/prl/DocumentGeneratorUtil.java index d8cd36be..6c856ac8 100644 --- a/src/integrationTest/java/uk/gov/hmcts/reform/prl/DocumentGeneratorUtil.java +++ b/src/integrationTest/java/uk/gov/hmcts/reform/prl/DocumentGeneratorUtil.java @@ -2,7 +2,7 @@ import io.restassured.response.Response; -import static net.serenitybdd.rest.SerenityRest.given; +import static io.restassured.RestAssured.given; public class DocumentGeneratorUtil { static Response generatePDF(final String requestBody, final String generateDocURI, final String userToken) { diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/prl/DocumentManagementServiceImplIntegrationTest.java b/src/integrationTest/java/uk/gov/hmcts/reform/prl/DocumentManagementServiceImplIntegrationTest.java index 2d9869af..4a8ef503 100644 --- a/src/integrationTest/java/uk/gov/hmcts/reform/prl/DocumentManagementServiceImplIntegrationTest.java +++ b/src/integrationTest/java/uk/gov/hmcts/reform/prl/DocumentManagementServiceImplIntegrationTest.java @@ -1,7 +1,6 @@ package uk.gov.hmcts.reform.prl; import io.restassured.response.Response; -import net.serenitybdd.rest.SerenityRest; import org.apache.http.HttpHeaders; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; @@ -9,19 +8,20 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; import org.json.JSONObject; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringExtension; import uk.gov.hmcts.reform.prl.documentgenerator.domain.response.GeneratedDocumentInfo; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static io.restassured.RestAssured.given; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; -@RunWith(SpringRunner.class) +@ExtendWith(SpringExtension.class) @SpringBootTest @AutoConfigureMockMvc public class DocumentManagementServiceImplIntegrationTest extends IntegrationTest { @@ -29,7 +29,6 @@ public class DocumentManagementServiceImplIntegrationTest extends IntegrationTes private static final String INVALID_TEMPLATE_DATA_JSON = "requests/invalid-template-data.json" + "-data.json"; private static final String VALID_INPUT_JSON = "requests/C100-case-data.json"; - private static final String IDAMAPI = "IDAM"; private static final String DOCMOSISAPI = "DOCMOSIS"; private static final String CCDDOCUMENTAPI = "CCDDOCUMENT"; @@ -41,60 +40,57 @@ public class DocumentManagementServiceImplIntegrationTest extends IntegrationTes private String ccdGatewayUrl; @Test - public void givenTemplateAndJsonInput_ReturnStatus200() throws Exception { - + public void givenTemplateAndJsonInputReturnStatus200() throws Exception { String requestBody = ResourceLoader.loadJson(VALID_INPUT_JSON); Response response = callPrlDocumentGenerator(requestBody); - assertEquals(200, response.getStatusCode()); + assertEquals(HttpStatus.SC_OK, response.getStatusCode()); } @Test - public void givenRequestBodyAndInvalidAuthToken_ReturnStatus401() throws Exception { - + public void givenRequestBodyAndInvalidAuthTokenReturnStatus401() throws Exception { String requestBody = ResourceLoader.loadJson(VALID_INPUT_JSON); Response response = callInvalidPrlDocumentGenerator(requestBody); - assertEquals(401, response.getStatusCode()); + assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusCode()); } @Test - public void checkStatusOfIdamApiIsUp_thenReturn200Status() throws Exception { - - Response response = SerenityRest.given() + public void checkStatusOfIdamApiIsUpThenReturn200Status() { + Response response = given() .when() .get(constructHealthUrl(IDAMAPI)) .andReturn(); - assertEquals(response.getStatusCode(), HttpStatus.SC_OK); + assertEquals(HttpStatus.SC_OK, response.getStatusCode()); } @Test - public void checkStatusOfDocmosisApiIsUp_thenReturn200Status() throws Exception { + public void checkStatusOfDocmosisApiIsUpThenReturn200Status() { - Response response = SerenityRest.given() + Response response = given() .when() .get(constructHealthUrl(DOCMOSISAPI)) .andReturn(); - assertEquals(response.getStatusCode(), HttpStatus.SC_OK); + assertEquals(HttpStatus.SC_OK, response.getStatusCode()); } @Test - public void checkStatusOfCCDCaseDocumentApiIsUp_thenReturn200Status() throws Exception { + public void checkStatusOfCCDCaseDocumentApiIsUpThenReturn200Status() { - Response response = SerenityRest.given() + Response response = given() .when() .get(constructHealthUrl(CCDDOCUMENTAPI)) .andReturn(); - assertEquals(response.getStatusCode(), HttpStatus.SC_OK); + assertEquals(HttpStatus.SC_OK, response.getStatusCode()); } @Test - public void givenInvalidTemplate_whenRequestMade_thenReturn400Response() throws Exception { + public void givenInvalidTemplateWhenRequestMadeThenReturn400Response() throws Exception { String requestBody = ResourceLoader.loadJson(INVALID_TEMPLATE_DATA_JSON); @@ -107,7 +103,7 @@ public void givenInvalidTemplate_whenRequestMade_thenReturn400Response() throws } @Test - public void givenTemplateAndJson_whenDocumentGenerated_thenCorrectFormat() throws Exception { + public void givenTemplateAndJsonWhenDocumentGeneratedThenCorrectFormat() throws Exception { String requestBody = ResourceLoader.loadJson(VALID_INPUT_JSON); Response response = callPrlDocumentGenerator(requestBody); @@ -116,11 +112,11 @@ public void givenTemplateAndJson_whenDocumentGenerated_thenCorrectFormat() throw String mimeType = generatedDocumentInfo.getMimeType(); String expectedMimeType = "application/pdf"; - assertEquals(mimeType, expectedMimeType); + assertEquals(expectedMimeType, mimeType); } @Test - public void givenValidAccessToken_whenAccessingDocumentPath_thenAccessProvided() throws Exception { + public void givenValidAccessTokenWhenAccessingDocumentPathThenAccessProvided() throws Exception { String requestBody = ResourceLoader.loadJson(VALID_INPUT_JSON); Response response = callPrlDocumentGenerator(requestBody); @@ -139,13 +135,11 @@ public void givenValidAccessToken_whenAccessingDocumentPath_thenAccessProvided() request.setHeader(HttpHeaders.AUTHORIZATION, bearerToken); HttpResponse httpResponse = HttpClientBuilder.create().build().execute(request); - assertEquals( - httpResponse.getStatusLine().getStatusCode(), - HttpStatus.SC_OK); + assertEquals(HttpStatus.SC_OK, httpResponse.getStatusLine().getStatusCode()); } @Test - public void givenInvalidAccessToken_whenAccessingDocumentPath_thenNoAccessProvided() throws Exception { + public void givenInvalidAccessTokenWhenAccessingDocumentPathThenNoAccessProvided() throws Exception { String requestBody = ResourceLoader.loadJson(VALID_INPUT_JSON); Response response = callPrlDocumentGenerator(requestBody); @@ -170,7 +164,7 @@ public void givenInvalidAccessToken_whenAccessingDocumentPath_thenNoAccessProvid } @Test - public void givenValidTemplateAndJson_whenDocumentGenerated_thenResponseContainsHashToken() throws Exception { + public void givenValidTemplateAndJsonWhenDocumentGeneratedThenResponseContainsHashToken() throws Exception { String requestBody = ResourceLoader.loadJson(VALID_INPUT_JSON); Response response = callPrlDocumentGenerator(requestBody); @@ -181,6 +175,4 @@ public void givenValidTemplateAndJson_whenDocumentGenerated_thenResponseContains assertNotNull(hashToken); } - - } diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/prl/IdamUtils.java b/src/integrationTest/java/uk/gov/hmcts/reform/prl/IdamUtils.java index 8f71711e..1f75fa8e 100644 --- a/src/integrationTest/java/uk/gov/hmcts/reform/prl/IdamUtils.java +++ b/src/integrationTest/java/uk/gov/hmcts/reform/prl/IdamUtils.java @@ -2,7 +2,6 @@ import io.restassured.response.Response; import lombok.extern.slf4j.Slf4j; -import net.serenitybdd.rest.SerenityRest; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import uk.gov.hmcts.reform.prl.model.CreateUserRequest; @@ -10,6 +9,8 @@ import java.util.Base64; +import static io.restassured.RestAssured.given; + @Slf4j public class IdamUtils { @@ -57,7 +58,7 @@ private void createUserInIdam(String username, String password) { .roles(new UserCode[]{UserCode.builder().code("citizen").build()}) .build(); - SerenityRest.given() + given() .header("Content-Type", "application/json") .body(ResourceLoader.objectToJson(userRequest)) .post(idamCreateUrl()); @@ -72,7 +73,7 @@ public void createCaseworkerUserInIdam(String username, String password) { .roles(new UserCode[]{UserCode.builder().code("caseworker-privatelaw-solicitor").build()}) .build(); - SerenityRest.given() + given() .header("Content-Type", "application/json") .body(ResourceLoader.objectToJson(userRequest)) .post(idamCreateUrl()); @@ -81,9 +82,8 @@ public void createCaseworkerUserInIdam(String username, String password) { public String generateUserTokenWithNoRoles(String username, String password) { String userLoginDetails = String.join(":", username, password); final String authHeader = "Basic " + new String(Base64.getEncoder().encode(userLoginDetails.getBytes())); - Response response = null; - response = SerenityRest.given() + Response response = given() .header("Authorization", authHeader) .header("Content-Type", MediaType.APPLICATION_FORM_URLENCODED_VALUE) .relaxedHTTPSValidation() @@ -96,7 +96,7 @@ public String generateUserTokenWithNoRoles(String username, String password) { + " body: " + response.getBody().prettyPrint()); } - response = SerenityRest.given() + response = given() .header("Content-Type", MediaType.APPLICATION_FORM_URLENCODED_VALUE) .relaxedHTTPSValidation() .post(idamTokenUrl(response.getBody().path("code"))); @@ -125,12 +125,6 @@ private String idamCreateUrl() { } private String idamCodeUrl() { - - System.out.println(idamUserBaseUrl + idamAuthorizeContextPath - + "?response_type=code" - + "&client_id=" + idamAuthClientID - + "&redirect_uri=" + idamRedirectUri); - return idamUserBaseUrl + idamAuthorizeContextPath + "?response_type=code" + "&client_id=" + idamAuthClientID @@ -138,21 +132,12 @@ private String idamCodeUrl() { } private String idamTokenUrl(String code) { - - System.out.println(idamUserBaseUrl + idamTokenContextPath - + "?code=" + code - + "&client_id=" + idamAuthClientID - + "&client_secret=" + idamSecret - + "&redirect_uri=" + idamRedirectUri - + "&grant_type=authorization_code"); - return idamUserBaseUrl + idamTokenContextPath + "?code=" + code + "&client_id=" + idamAuthClientID + "&client_secret=" + idamSecret + "&redirect_uri=" + idamRedirectUri + "&grant_type=authorization_code"; - } } diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/prl/IntegrationTest.java b/src/integrationTest/java/uk/gov/hmcts/reform/prl/IntegrationTest.java index 78427300..c2b7cb11 100644 --- a/src/integrationTest/java/uk/gov/hmcts/reform/prl/IntegrationTest.java +++ b/src/integrationTest/java/uk/gov/hmcts/reform/prl/IntegrationTest.java @@ -2,14 +2,12 @@ import io.restassured.response.Response; import lombok.extern.slf4j.Slf4j; -import net.serenitybdd.junit.spring.integration.SpringIntegrationMethodRule; import org.assertj.core.util.Strings; -import org.junit.Rule; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringExtension; import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; import java.io.IOException; @@ -20,7 +18,7 @@ import javax.annotation.PostConstruct; @Slf4j -@RunWith(SpringRunner.class) +@ExtendWith(SpringExtension.class) @ContextConfiguration(classes = {ServiceContextConfiguration.class}) public abstract class IntegrationTest { @@ -45,16 +43,9 @@ public abstract class IntegrationTest { @Autowired private IdamUtils idamTestSupportUtil; - @Rule - public SpringIntegrationMethodRule springMethodIntegration; - private static String userToken = null; private String username; - public IntegrationTest() { - this.springMethodIntegration = new SpringIntegrationMethodRule(); - } - @PostConstruct public void init() { if (!Strings.isNullOrEmpty(httpProxy)) { @@ -75,11 +66,8 @@ public Response callInvalidPrlDocumentGenerator(String requestBody) { } private String getToken() { - String authHeaderToken = null; String userLoginDetails = String.join(":", username, aatPassword); - authHeaderToken = "Bearer " + new String(Base64.getEncoder().encode((userLoginDetails).getBytes())); - - return authHeaderToken; + return "Bearer " + new String(Base64.getEncoder().encode((userLoginDetails).getBytes())); } private synchronized String getUserToken() { @@ -95,8 +83,7 @@ private synchronized String getUserToken() { } public String getAuthorizationToken() { - String authToken = getUserToken(); - return authToken; + return getUserToken(); } public String getStoredUserToken() { @@ -104,9 +91,7 @@ public String getStoredUserToken() { } public String constructHealthUrl(String apiName) { - String url = null; - url = idamTestSupportUtil.generateUrlForValidMicroservice(apiName); - return url; + return idamTestSupportUtil.generateUrlForValidMicroservice(apiName); } private void configProxyHost() { diff --git a/src/smokeTest/java/uk/gov/hmcts/reform/prl/DgsApiSmokeTests.java b/src/smokeTest/java/uk/gov/hmcts/reform/prl/DgsApiSmokeTests.java index 17aeedbe..315d5d13 100644 --- a/src/smokeTest/java/uk/gov/hmcts/reform/prl/DgsApiSmokeTests.java +++ b/src/smokeTest/java/uk/gov/hmcts/reform/prl/DgsApiSmokeTests.java @@ -3,15 +3,15 @@ import com.github.tomakehurst.wiremock.client.WireMock; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.RegisterExtension; -import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.PropertySource; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import uk.gov.hmcts.reform.ccd.document.am.model.Document; @@ -27,7 +27,7 @@ import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.springframework.http.HttpHeaders.CONTENT_TYPE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -38,8 +38,7 @@ import static uk.gov.hmcts.reform.prl.documentgenerator.util.TestData.TEST_DEFAULT_NAME_FOR_PDF_FILE; import static uk.gov.hmcts.reform.prl.documentgenerator.util.TestData.TEST_HASH_TOKEN; - -@RunWith(SpringRunner.class) +@ExtendWith(SpringExtension.class) @SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = DocumentGeneratorApplication.class @@ -73,7 +72,7 @@ public class DgsApiSmokeTests { wireMockConfig().port(4502)).build(); @Test - public void givenTemplateNameIsNull_whenGenerateAndStoreDocument_thenReturnHttp400() throws Exception { + public void givenTemplateNameIsNullWhenGenerateAndStoreDocumentThenReturnHttp400() throws Exception { final String template = null; final Map values = Collections.emptyMap(); @@ -87,7 +86,7 @@ public void givenTemplateNameIsNull_whenGenerateAndStoreDocument_thenReturnHttp4 } @Test - public void givenTemplateNameIsBlank_whenGenerateAndStoreDocument_thenReturnHttp400() throws Exception { + public void givenTemplateNameIsBlankWhenGenerateAndStoreDocumentThenReturnHttp400() throws Exception { final String template = " "; final Map values = Collections.emptyMap(); @@ -101,7 +100,7 @@ public void givenTemplateNameIsBlank_whenGenerateAndStoreDocument_thenReturnHttp } @Test - public void givenTemplateNotFound_whenGenerateAndStoreDocument_thenReturnHttp400() throws Exception { + public void givenTemplateNotFoundWhenGenerateAndStoreDocumentThenReturnHttp400() throws Exception { final String template = "nonExistingTemplate"; final Map values = Collections.emptyMap(); @@ -115,11 +114,10 @@ public void givenTemplateNotFound_whenGenerateAndStoreDocument_thenReturnHttp400 } @Test - public void givenCouldNotConnectToAuthService_whenGenerateAndStoreDocument_thenReturnHttp503() throws Exception { - Map caseData = new HashMap<>(); + public void givenCouldNotConnectToAuthServiceWhenGenerateAndStoreDocumentThenReturnHttp503() throws Exception { Map requestData = Collections.singletonMap( - CASE_DETAILS, Collections.singletonMap(CASE_DATA, caseData) + CASE_DETAILS, Collections.singletonMap(CASE_DATA, new HashMap<>()) ); final GenerateDocumentRequest generateDocumentRequest = new GenerateDocumentRequest(TEST_EXAMPLE, requestData); @@ -135,11 +133,10 @@ public void givenCouldNotConnectToAuthService_whenGenerateAndStoreDocument_thenR } @Test - public void givenAuthServiceReturnAuthenticationError_whenGenerateAndStoreDocument_thenReturnHttp401() + public void givenAuthServiceReturnAuthenticationErrorWhenGenerateAndStoreDocumentThenReturnHttp401() throws Exception { - Map caseData = new HashMap<>(); Map requestData = Collections.singletonMap( - CASE_DETAILS, Collections.singletonMap(CASE_DATA, caseData) + CASE_DETAILS, Collections.singletonMap(CASE_DATA, new HashMap<>()) ); final GenerateDocumentRequest generateDocumentRequest = new GenerateDocumentRequest(TEST_EXAMPLE, requestData); @@ -155,7 +152,7 @@ public void givenAuthServiceReturnAuthenticationError_whenGenerateAndStoreDocume } @Test - public void givenAllGoesWellForTestExample_whenGenerateAndStoreDocument_thenReturn() + public void givenAllGoesWellForTestExampleWhenGenerateAndStoreDocumentThenReturn() throws Exception { assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments(TEST_EXAMPLE); } @@ -199,23 +196,24 @@ private GeneratedDocumentInfo getGeneratedDocumentInfo() { .hashToken(TEST_HASH_TOKEN) .mimeType(MIME_TYPE) .binaryUrl(BINARY_URL) - .docName("TestTemplate.pdf") //added for filename + .docName("TestTemplate.pdf") .build(); return generatedDocumentInfo; } private void mockDocmosisPdfService(HttpStatus expectedResponse, byte[] body) { - docmosisClientServiceServer.stubFor(WireMock.post(DOCMOSIS_API_URL) - .willReturn(aResponse() - .withStatus(expectedResponse.value()) - .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) - .withBody( - ObjectMapperTestUtil.convertObjectToJsonString( - body - ) - ) - )); + docmosisClientServiceServer.stubFor( + WireMock.post(DOCMOSIS_API_URL) + .willReturn( + aResponse() + .withStatus(expectedResponse.value()) + .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) + .withBody( + ObjectMapperTestUtil.convertObjectToJsonString(body) + ) + ) + ); } private void mockServiceAuthServer(HttpStatus expectedResponse, String body) { diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/config/TemplatesConfigurationTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/config/TemplatesConfigurationTest.java index 7d007171..95a11e7b 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/config/TemplatesConfigurationTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/config/TemplatesConfigurationTest.java @@ -4,37 +4,38 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.tuple.ImmutableTriple; import org.apache.commons.lang3.tuple.Triple; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import java.util.Arrays; import java.util.Set; import static java.lang.String.format; -import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertThrows; import static uk.gov.hmcts.reform.prl.documentgenerator.domain.TemplateConstants.DOCMOSIS_TYPE; import static uk.gov.hmcts.reform.prl.documentgenerator.domain.TemplateConstants.TEST_TEMPLATE; import static uk.gov.hmcts.reform.prl.documentgenerator.domain.TemplateConstants.TEST_TEMPLATE_NAME_FOR_PDF; -@RunWith(SpringRunner.class) +@ExtendWith(SpringExtension.class) @SpringBootTest @Slf4j -public class TemplatesConfigurationTest { +class TemplatesConfigurationTest { @Autowired private TemplatesConfiguration classUnderTest; private static Set> expectedTemplateConfigs; - @BeforeClass - public static void setUp() { + @SuppressWarnings("unchecked") + @BeforeAll + static void setUp() { expectedTemplateConfigs = Sets.newHashSet( new ImmutableTriple<>(TEST_TEMPLATE, TEST_TEMPLATE_NAME_FOR_PDF, DOCMOSIS_TYPE) @@ -42,7 +43,7 @@ public static void setUp() { } @Test - public void shouldRetrieveFileName_AndPdfGeneratorName_ByTemplateName() { + void shouldRetrieveFileName_AndPdfGeneratorName_ByTemplateName() { expectedTemplateConfigs.forEach( expectedTemplateConfig -> { String templateName = expectedTemplateConfig.getLeft(); @@ -61,28 +62,30 @@ public void shouldRetrieveFileName_AndPdfGeneratorName_ByTemplateName() { } @Test - public void shouldThrowExceptionWhenUnknownTemplateIsRequested() { + void shouldThrowExceptionWhenUnknownTemplateIsRequested() { String templateName = "unknown-template"; - IllegalArgumentException illegalArgumentException = assertThrows(IllegalArgumentException.class, () -> { - classUnderTest.getFileNameByTemplateName(templateName); - }); + IllegalArgumentException illegalArgumentException = assertThrows( + IllegalArgumentException.class, + () -> classUnderTest.getFileNameByTemplateName(templateName) + ); + assertThat(illegalArgumentException.getMessage(), containsString("Unknown template: " + templateName)); } @Test - public void shouldReturnDefaultGeneratorForNonExistentTemplate() { + void shouldReturnDefaultGeneratorForNonExistentTemplate() { assertThat(classUnderTest.getGeneratorServiceNameByTemplateName("non-existent-template"), is(DOCMOSIS_TYPE)); } - @Test(expected = Exception.class) - public void shouldThrowAnExceptionWhenTemplateNameIsDuplicated() { + @Test + void shouldThrowAnExceptionWhenTemplateNameIsDuplicated() { TemplatesConfiguration templatesConfiguration = new TemplatesConfiguration(); - templatesConfiguration.setConfigurationList(asList( + templatesConfiguration.setConfigurationList(Arrays.asList( new TemplateConfiguration("thisName", null, null), new TemplateConfiguration("thisName", null, null) )); - templatesConfiguration.init(); + assertThrows(Exception.class, templatesConfiguration::init); } } diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/config/launchdarkly/LDClientFactoryTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/config/launchdarkly/LDClientFactoryTest.java index 7e07bb1b..ed54eed0 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/config/launchdarkly/LDClientFactoryTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/config/launchdarkly/LDClientFactoryTest.java @@ -1,21 +1,21 @@ package uk.gov.hmcts.reform.prl.documentgenerator.config.launchdarkly; import com.launchdarkly.sdk.server.interfaces.LDClientInterface; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; -public class LDClientFactoryTest { +class LDClientFactoryTest { private LDClientFactory factory; - @Before - public void setUp() { + @BeforeEach + void setUp() { factory = new LDClientFactory(); } @Test - public void testCreate() { + void testCreate() { LDClientInterface client = factory.create("test key", true); assertNotNull(client); } diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/config/launchdarkly/LaunchDarklyClientTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/config/launchdarkly/LaunchDarklyClientTest.java index e4d234f5..33d9a96d 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/config/launchdarkly/LaunchDarklyClientTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/config/launchdarkly/LaunchDarklyClientTest.java @@ -2,11 +2,11 @@ import com.launchdarkly.sdk.LDContext; import com.launchdarkly.sdk.server.interfaces.LDClientInterface; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -15,8 +15,8 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) -public class LaunchDarklyClientTest { +@ExtendWith(MockitoExtension.class) +class LaunchDarklyClientTest { private static final String SDK_KEY = "fake key"; private static final String FAKE_FEATURE = "fake feature"; @@ -31,20 +31,20 @@ public class LaunchDarklyClientTest { private LaunchDarklyClient launchDarklyClient; - @Before - public void setUp() { + @BeforeEach + void setUp() { when(ldClientFactory.create(eq(SDK_KEY), anyBoolean())).thenReturn(ldClient); launchDarklyClient = new LaunchDarklyClient(ldClientFactory, SDK_KEY, true); } @Test - public void testFeatureEnabled() { + void testFeatureEnabled() { when(ldClient.boolVariation(eq(FAKE_FEATURE), any(LDContext.class), anyBoolean())).thenReturn(true); assertTrue(launchDarklyClient.isFeatureEnabled(FAKE_FEATURE, ldUser)); } @Test - public void testFeatureDisabled() { + void testFeatureDisabled() { when(ldClient.boolVariation(eq(FAKE_FEATURE), any(LDContext.class), anyBoolean())).thenReturn(false); assertFalse(launchDarklyClient.isFeatureEnabled(FAKE_FEATURE, ldUser)); } diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/DocumentGeneratorControllerUTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/DocumentGeneratorControllerUTest.java index 99716ed7..ac566ba2 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/DocumentGeneratorControllerUTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/DocumentGeneratorControllerUTest.java @@ -1,10 +1,10 @@ package uk.gov.hmcts.reform.prl.documentgenerator.controller; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; import uk.gov.hmcts.reform.prl.documentgenerator.domain.request.GenerateDocumentRequest; import uk.gov.hmcts.reform.prl.documentgenerator.domain.response.GeneratedDocumentInfo; import uk.gov.hmcts.reform.prl.documentgenerator.service.DocumentManagementService; @@ -13,14 +13,13 @@ import java.util.HashMap; import java.util.Map; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) -public class DocumentGeneratorControllerUTest { - +@ExtendWith(MockitoExtension.class) +class DocumentGeneratorControllerUTest { @Mock private DocumentManagementService documentManagementService; @@ -28,7 +27,7 @@ public class DocumentGeneratorControllerUTest { private DocumentGeneratorController classUnderTest; @Test - public void whenGeneratePDF_thenReturnGeneratedPDFDocumentInfo() { + void whenGeneratePDFThenReturnGeneratedPDFDocumentInfo() { final String templateName = "templateName"; final Map placeholder = Collections.emptyMap(); @@ -47,7 +46,7 @@ public void whenGeneratePDF_thenReturnGeneratedPDFDocumentInfo() { } @Test - public void whenGeneratePDF_thenReturnGeneratedDraftPDFDocumentInfo() { + void whenGeneratePDFThenReturnGeneratedDraftPDFDocumentInfo() { final String templateName = "templateName"; final Map placeholder = Collections.emptyMap(); @@ -66,13 +65,13 @@ public void whenGeneratePDF_thenReturnGeneratedDraftPDFDocumentInfo() { } @Test - public void whenConvertPDF_thenReturnConvertedPDFDocumentInfo() { + void whenConvertPDFThenReturnConvertedPDFDocumentInfo() { final String templateName = "templateName"; Map placeholder = new HashMap<>(); final GeneratedDocumentInfo expected = GeneratedDocumentInfo.builder().build(); - when(documentManagementService.converToPdf(placeholder, "authToken","fileName")) + when(documentManagementService.convertToPdf(placeholder, "authToken","fileName")) .thenReturn(expected); GeneratedDocumentInfo actual = classUnderTest @@ -81,7 +80,7 @@ public void whenConvertPDF_thenReturnConvertedPDFDocumentInfo() { assertEquals(expected, actual); verify(documentManagementService, times(1)) - .converToPdf(placeholder, "authToken","fileName"); + .convertToPdf(placeholder, "authToken","fileName"); } } diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/RootControllerTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/RootControllerTest.java index a18d690b..7db01fb8 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/RootControllerTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/RootControllerTest.java @@ -1,12 +1,12 @@ package uk.gov.hmcts.reform.prl.documentgenerator.controller; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultMatcher; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; @@ -16,24 +16,24 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; -@RunWith(SpringRunner.class) +@ExtendWith(SpringExtension.class) @SpringBootTest @AutoConfigureMockMvc -public class RootControllerTest { +class RootControllerTest { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; - @Before - public void setup() { + @BeforeEach + void setup() { DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(this.wac); this.mockMvc = builder.build(); } @Test - public void getShouldReturn200() throws Exception { + void getShouldReturn200() throws Exception { ResultMatcher ok = MockMvcResultMatchers.status() .isOk(); diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/helper/GlobalExceptionHandlerUTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/helper/GlobalExceptionHandlerUTest.java index 28d68593..6e9b7df7 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/helper/GlobalExceptionHandlerUTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/controller/helper/GlobalExceptionHandlerUTest.java @@ -1,23 +1,20 @@ package uk.gov.hmcts.reform.prl.documentgenerator.controller.helper; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.client.HttpClientErrorException; import uk.gov.hmcts.reform.prl.documentgenerator.exception.ErrorLoadingTemplateException; import uk.gov.hmcts.reform.prl.documentgenerator.exception.PDFGenerationException; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -@RunWith(BlockJUnit4ClassRunner.class) -public class GlobalExceptionHandlerUTest { +class GlobalExceptionHandlerUTest { private final GlobalExceptionHandler classUnderTest = new GlobalExceptionHandler(); @Test - public void whenHandleBadRequestException_thenReturnBadRequest() { + void whenHandleBadRequestExceptionThenReturnBadRequest() { final Exception exception = new Exception(); ResponseEntity response = classUnderTest.handleBadRequestException(exception); @@ -26,7 +23,7 @@ public void whenHandleBadRequestException_thenReturnBadRequest() { } @Test - public void whenHandleTemplateLoadingException_thenReturnBadRequest() { + void whenHandleTemplateLoadingExceptionThenReturnBadRequest() { final String message = "some message"; final Exception exception = new Exception(); final ErrorLoadingTemplateException errorLoadingTemplateException = @@ -39,7 +36,7 @@ public void whenHandleTemplateLoadingException_thenReturnBadRequest() { } @Test - public void whenHandleDocStorageAndPdfGenException_thenReturnHttpClientErrorStatus() { + void whenHandleDocStorageAndPdfGenExceptionThenReturnHttpClientErrorStatus() { final HttpStatus httpStatus = HttpStatus.MOVED_PERMANENTLY; final HttpClientErrorException httpClientErrorException = new HttpClientErrorException(httpStatus); @@ -56,7 +53,7 @@ public void whenHandleDocStorageAndPdfGenException_thenReturnHttpClientErrorStat } @Test - public void givenHttpClientError200_whenHandleDocStorageAndPdfGenException_thenReturn503() { + void givenHttpClientError200WhenHandleDocStorageAndPdfGenExceptionThenReturn503() { final HttpStatus httpStatus = HttpStatus.BAD_REQUEST; final HttpClientErrorException httpClientErrorException = new HttpClientErrorException(httpStatus); @@ -73,7 +70,7 @@ public void givenHttpClientError200_whenHandleDocStorageAndPdfGenException_thenR } @Test - public void givenNullWrappedInException_whenHandleDocStorageAndPdfGen_thenReturn500() { + void givenNullWrappedInExceptionWhenHandleDocStorageAndPdfGenThenReturn500() { final String message = "some message"; PDFGenerationException pdfGenerationException = new PDFGenerationException(message, null); @@ -86,7 +83,7 @@ public void givenNullWrappedInException_whenHandleDocStorageAndPdfGen_thenReturn } @Test - public void givenNonHttpClientErrorWrappedIn_whenHandleDocStorageAndPdfGenException_thenReturn500() { + void givenNonHttpClientErrorWrappedInWhenHandleDocStorageAndPdfGenExceptionThenReturn500() { final String message = "some message"; final Exception exception = new Exception(); diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/functionaltest/DocumentGenerateAndStoreE2ETest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/functionaltest/DocumentGenerateAndStoreE2ETest.java index 8cecca13..0e6a8976 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/functionaltest/DocumentGenerateAndStoreE2ETest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/functionaltest/DocumentGenerateAndStoreE2ETest.java @@ -1,18 +1,21 @@ package uk.gov.hmcts.reform.prl.documentgenerator.functionaltest; +import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.client.WireMock; -import com.github.tomakehurst.wiremock.junit.WireMockClassRule; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.runner.RunWith; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.contract.wiremock.WireMockSpring; import org.springframework.context.annotation.PropertySource; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import uk.gov.hmcts.reform.ccd.document.am.model.Document; @@ -27,7 +30,7 @@ import java.util.Map; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.springframework.http.HttpHeaders.CONTENT_TYPE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -38,146 +41,154 @@ import static uk.gov.hmcts.reform.prl.documentgenerator.util.TestData.TEST_DEFAULT_NAME_FOR_PDF_FILE; import static uk.gov.hmcts.reform.prl.documentgenerator.util.TestData.TEST_HASH_TOKEN; -@RunWith(SpringRunner.class) +@ExtendWith(SpringExtension.class) @ContextConfiguration(classes = DocumentGeneratorApplication.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @PropertySource(value = "classpath:application.yml") @AutoConfigureMockMvc - public class DocumentGenerateAndStoreE2ETest { private static final String API_URL = "/version/1/generatePDF"; private static final String CASE_DOCS_API_URL = "/cases/documents"; private static final String DOCMOSIS_API_URL = "/rs/render"; private static final String S2S_API_URL = "/lease"; - private static final String CASE_DETAILS = "caseDetails"; private static final String CASE_DATA = "case_data"; - private static final String TEST_EXAMPLE = "FL-DIV-GOR-ENG-00062.docx"; @Autowired private MockMvc webClient; - @ClassRule - public static WireMockClassRule caseDocsClientApiServiceServer = new WireMockClassRule(5170); + private static WireMockServer caseDocsClientApiServiceServer; + private static WireMockServer docmosisClientServiceServer; + private static WireMockServer serviceAuthServer; + + @BeforeAll + static void setUpClass() { + caseDocsClientApiServiceServer = new WireMockServer(buildWireMockConfig(5170)); + docmosisClientServiceServer = new WireMockServer(buildWireMockConfig(5501)); + serviceAuthServer = new WireMockServer(buildWireMockConfig(4502)); + + caseDocsClientApiServiceServer.start(); + docmosisClientServiceServer.start(); + serviceAuthServer.start(); + } - @ClassRule - public static WireMockClassRule docmosisClientServiceServer = new WireMockClassRule(5501); + @AfterAll + static void tearDownClass() { + caseDocsClientApiServiceServer.stop(); + docmosisClientServiceServer.stop(); + serviceAuthServer.stop(); + } - @ClassRule - public static WireMockClassRule serviceAuthServer = new WireMockClassRule(4502); + private static WireMockConfiguration buildWireMockConfig(int port) { + return WireMockSpring + .options() + .port(port) + .jettyStopTimeout(20L) + .extensions(new ConnectionCloseExtension()); + } @Test - public void givenTemplateNameIsNull_whenGenerateAndStoreDocument_thenReturnHttp400() throws Exception { - final String template = null; + void givenTemplateNameIsNullWhenGenerateAndStoreDocumentThenReturnHttp400() throws Exception { final Map values = Collections.emptyMap(); - - final GenerateDocumentRequest generateDocumentRequest = new GenerateDocumentRequest(template, values); + final GenerateDocumentRequest generateDocumentRequest = new GenerateDocumentRequest(null, values); webClient.perform(post(API_URL) - .content(ObjectMapperTestUtil.convertObjectToJsonString(generateDocumentRequest)) - .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON)) + .content(ObjectMapperTestUtil.convertObjectToJsonString(generateDocumentRequest)) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isBadRequest()); } @Test - public void givenTemplateNameIsBlank_whenGenerateAndStoreDocument_thenReturnHttp400() throws Exception { + void givenTemplateNameIsBlankWhenGenerateAndStoreDocumentThenReturnHttp400() throws Exception { final String template = " "; final Map values = Collections.emptyMap(); - final GenerateDocumentRequest generateDocumentRequest = new GenerateDocumentRequest(template, values); webClient.perform(post(API_URL) - .content(ObjectMapperTestUtil.convertObjectToJsonString(generateDocumentRequest)) - .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON)) + .content(ObjectMapperTestUtil.convertObjectToJsonString(generateDocumentRequest)) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isBadRequest()); } @Test - public void givenTemplateNotFound_whenGenerateAndStoreDocument_thenReturnHttp400() throws Exception { + void givenTemplateNotFoundWhenGenerateAndStoreDocumentThenReturnHttp400() throws Exception { final String template = "nonExistingTemplate"; final Map values = Collections.emptyMap(); - final GenerateDocumentRequest generateDocumentRequest = new GenerateDocumentRequest(template, values); webClient.perform(post(API_URL) - .content(ObjectMapperTestUtil.convertObjectToJsonString(generateDocumentRequest)) - .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON)) + .content(ObjectMapperTestUtil.convertObjectToJsonString(generateDocumentRequest)) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isBadRequest()); } @Test - public void givenCouldNotConnectToAuthService_whenGenerateAndStoreDocument_thenReturnHttp503() throws Exception { - Map caseData = new HashMap<>(); - + void givenCouldNotConnectToAuthServiceWhenGenerateAndStoreDocumentThenReturnHttp503() throws Exception { Map requestData = Collections.singletonMap( - CASE_DETAILS, Collections.singletonMap(CASE_DATA, caseData) - ); - + CASE_DETAILS, Collections.singletonMap(CASE_DATA, new HashMap<>())); final GenerateDocumentRequest generateDocumentRequest = new GenerateDocumentRequest(TEST_EXAMPLE, requestData); - mockDocmosisPdfService(HttpStatus.OK, new byte[] {1}); + mockDocmosisPdfService(new byte[] {1}); mockServiceAuthServer(HttpStatus.SERVICE_UNAVAILABLE, ""); webClient.perform(post(API_URL) - .content(ObjectMapperTestUtil.convertObjectToJsonString(generateDocumentRequest)) - .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON)) + .content(ObjectMapperTestUtil.convertObjectToJsonString(generateDocumentRequest)) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isServiceUnavailable()); } @Test - public void givenAuthServiceReturnAuthenticationError_whenGenerateAndStoreDocument_thenReturnHttp401() - throws Exception { - Map caseData = new HashMap<>(); + void givenAuthServiceReturnAuthenticationErrorWhenGenerateAndStoreDocumentThenReturnHttp401() throws Exception { Map requestData = Collections.singletonMap( - CASE_DETAILS, Collections.singletonMap(CASE_DATA, caseData) - ); - + CASE_DETAILS, Collections.singletonMap(CASE_DATA, new HashMap<>())); final GenerateDocumentRequest generateDocumentRequest = new GenerateDocumentRequest(TEST_EXAMPLE, requestData); - mockDocmosisPdfService(HttpStatus.OK, new byte[] {1}); + mockDocmosisPdfService(new byte[] {1}); mockServiceAuthServer(HttpStatus.UNAUTHORIZED, ""); webClient.perform(post(API_URL) - .content(ObjectMapperTestUtil.convertObjectToJsonString(generateDocumentRequest)) - .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON)) + .content(ObjectMapperTestUtil.convertObjectToJsonString(generateDocumentRequest)) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isUnauthorized()); } @Test - public void givenAllGoesWellForTestExample_whenGenerateAndStoreDocument_thenReturn() - throws Exception { - assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments(TEST_EXAMPLE); + void givenAllGoesWellForTestExampleWhenGenerateAndStoreDocumentThenReturn() throws Exception { + assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments(); } - private void assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments(String templateId) throws Exception { - //Given + private void assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments() throws Exception { + // Given final Map caseData = Collections.emptyMap(); final Map values = new HashMap<>(); values.put(CASE_DETAILS, Collections.singletonMap(CASE_DATA, caseData)); - final String s2sAuthToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI" - + "3Nzk2Mzh9.gzSraSYS8EXBxLN_oWnFSRgCzcmJmMjLiuyu5CSpyHI"; + final String s2sAuthToken = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiY" + + "WRtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9.gzSraSYS8EXBxLN_oWnFSRgCzcmJmMjLiuyu5CSpyHI"; final UploadResponse uploadResponse = new UploadResponse(List.of(mockCaseDocsDocuments())); - mockDocmosisPdfService(HttpStatus.OK, new byte[]{1}); - mockCaseDocsClientApi(HttpStatus.OK, uploadResponse); + mockDocmosisPdfService(new byte[]{1}); + mockCaseDocsClientApi(uploadResponse); mockServiceAuthServer(HttpStatus.OK, s2sAuthToken); - //When - final GenerateDocumentRequest generateDocumentRequest = new GenerateDocumentRequest(templateId, values); - MvcResult result = webClient.perform(post(API_URL) - .content(ObjectMapperTestUtil.convertObjectToJsonString(generateDocumentRequest)) - .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON)) + // When + final GenerateDocumentRequest generateDocumentRequest = new GenerateDocumentRequest( + DocumentGenerateAndStoreE2ETest.TEST_EXAMPLE, values); + MvcResult result = webClient.perform( + post(API_URL) + .content(ObjectMapperTestUtil.convertObjectToJsonString(generateDocumentRequest)) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn(); - //Then + // Then final GeneratedDocumentInfo generatedDocumentInfo = getGeneratedDocumentInfo(); assertEquals( ObjectMapperTestUtil.convertObjectToJsonString(generatedDocumentInfo), @@ -186,42 +197,42 @@ private void assertReturnWhenAllGoesWellForGeneratingAndStoringDocuments(String } private GeneratedDocumentInfo getGeneratedDocumentInfo() { - GeneratedDocumentInfo generatedDocumentInfo = GeneratedDocumentInfo.builder() + return GeneratedDocumentInfo.builder() .url(FILE_URL) .hashToken(TEST_HASH_TOKEN) .mimeType(MIME_TYPE) .binaryUrl(BINARY_URL) .docName("TestTemplate.pdf") .build(); - - return generatedDocumentInfo; } - private void mockDocmosisPdfService(HttpStatus expectedResponse, byte[] body) { - docmosisClientServiceServer.stubFor(WireMock.post(DOCMOSIS_API_URL) - .willReturn(aResponse() - .withStatus(expectedResponse.value()) - .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) - .withBody(ObjectMapperTestUtil.convertObjectToJsonString(body)) - )); + private void mockDocmosisPdfService(byte[] body) { + docmosisClientServiceServer.stubFor( + WireMock.post(DOCMOSIS_API_URL) + .willReturn(aResponse() + .withStatus(HttpStatus.OK.value()) + .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) + .withBody(ObjectMapperTestUtil.convertObjectToJsonString(body)) + )); } private void mockServiceAuthServer(HttpStatus expectedResponse, String body) { serviceAuthServer.stubFor(WireMock.post(S2S_API_URL) - .willReturn(aResponse() - .withStatus(expectedResponse.value()) - .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) - .withBody(ObjectMapperTestUtil.convertObjectToJsonString(body)) - )); + .willReturn(aResponse() + .withStatus(expectedResponse.value()) + .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) + .withBody(ObjectMapperTestUtil.convertObjectToJsonString(body)) + )); } - private void mockCaseDocsClientApi(HttpStatus expectedResponse, UploadResponse uploadResponse) { - caseDocsClientApiServiceServer.stubFor(WireMock.post(CASE_DOCS_API_URL) - .willReturn(aResponse() - .withStatus(expectedResponse.value()) - .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) - .withBody(ObjectMapperTestUtil.convertObjectToJsonString(uploadResponse)) - )); + private void mockCaseDocsClientApi(UploadResponse uploadResponse) { + caseDocsClientApiServiceServer.stubFor( + WireMock.post(CASE_DOCS_API_URL) + .willReturn(aResponse() + .withStatus(HttpStatus.OK.value()) + .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) + .withBody(ObjectMapperTestUtil.convertObjectToJsonString(uploadResponse)) + )); } public static Document mockCaseDocsDocuments() { @@ -241,4 +252,5 @@ public static Document mockCaseDocsDocuments() { .originalDocumentName(TEST_DEFAULT_NAME_FOR_PDF_FILE) .build(); } + } diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/management/monitoring/health/HealthCheckITest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/management/monitoring/health/HealthCheckITest.java index 448f1b6c..67434f2f 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/management/monitoring/health/HealthCheckITest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/management/monitoring/health/HealthCheckITest.java @@ -1,7 +1,7 @@ package uk.gov.hmcts.reform.prl.documentgenerator.management.monitoring.health; +import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.core.WireMockConfiguration; -import com.github.tomakehurst.wiremock.junit.WireMockClassRule; import com.jayway.jsonpath.JsonPath; import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.ServerList; @@ -10,11 +10,11 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; -import org.junit.After; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.TestConfiguration; @@ -26,7 +26,7 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringExtension; import uk.gov.hmcts.reform.prl.documentgenerator.DocumentGeneratorApplication; import uk.gov.hmcts.reform.prl.documentgenerator.functionaltest.ConnectionCloseExtension; @@ -38,10 +38,10 @@ import static org.springframework.http.HttpHeaders.CONTENT_TYPE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; -@RunWith(SpringRunner.class) +@ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ContextConfiguration( - classes = {DocumentGeneratorApplication.class, HealthCheckITest.LocalRibbonClientConfiguration.class}) + classes = {DocumentGeneratorApplication.class, HealthCheckITest.LocalRibbonClientConfiguration.class}) @PropertySource(value = "classpath:application.properties") @TestPropertySource(properties = { "management.endpoint.health.cache.time-to-live=0", @@ -49,7 +49,8 @@ "eureka.client.enabled=false", }) @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) -public class HealthCheckITest { +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class HealthCheckITest { private static final String HEALTH_UP_RESPONSE = "{ \"status\": \"UP\"}"; private static final String HEALTH_DOWN_RESPONSE = "{ \"status\": \"DOWN\"}"; @@ -57,14 +58,9 @@ public class HealthCheckITest { @Value("${local.server.port}") private int port; - @ClassRule - public static WireMockClassRule serviceAuthServer = new WireMockClassRule(buildWireMockConfig(4502)); - - @ClassRule - public static WireMockClassRule docmosisService = new WireMockClassRule(buildWireMockConfig(5501)); - - @ClassRule - public static WireMockClassRule caseDocService = new WireMockClassRule(buildWireMockConfig(5170)); + private WireMockServer serviceAuthServer; + private WireMockServer docmosisService; + private WireMockServer caseDocService; private String healthUrl; private final HttpClient httpClient = HttpClients.createMinimal(); @@ -76,14 +72,24 @@ private HttpResponse getHealth() throws Exception { return httpClient.execute(request); } - @Before - public void setUp() { - healthUrl = "http://localhost:" + String.valueOf(port) + "/health"; + @BeforeAll + void setUp() { + serviceAuthServer = new WireMockServer(buildWireMockConfig(4502)); + docmosisService = new WireMockServer(buildWireMockConfig(5501)); + caseDocService = new WireMockServer(buildWireMockConfig(5170)); + + serviceAuthServer.start(); + docmosisService.start(); + caseDocService.start(); + + healthUrl = "http://localhost:" + port + "/health"; } - @After - public void tearDown() { - resetAllMockServices(); + @AfterAll + void tearDown() { + serviceAuthServer.stop(); + docmosisService.stop(); + caseDocService.stop(); } private static WireMockConfiguration buildWireMockConfig(int port) { @@ -95,7 +101,7 @@ private static WireMockConfiguration buildWireMockConfig(int port) { } @Test - public void givenAllDependenciesAreUp_whenCheckHealth_thenReturnStatusUp() throws Exception { + void givenAllDependenciesAreUpWhenCheckHealthThenReturnStatusUp() throws Exception { stubEndpointAndResponse(serviceAuthServer, true); stubEndpointAndResponse(docmosisService, true); stubEndpointAndResponse(caseDocService, true); @@ -105,19 +111,19 @@ public void givenAllDependenciesAreUp_whenCheckHealth_thenReturnStatusUp() throw assertThat(response.getStatusLine().getStatusCode(), equalTo(HttpStatus.OK.value())); assertThat(JsonPath.read(body, "$.status").toString(), - equalTo("UP")); + equalTo("UP")); assertThat(JsonPath.read(body, "$.components.serviceAuthProviderHealthCheck.status").toString(), - equalTo("UP")); + equalTo("UP")); assertThat(JsonPath.read(body, "$.components.docmosisHealthCheck.status").toString(), - equalTo("UP")); + equalTo("UP")); assertThat(JsonPath.read(body, "$.components.caseDocumentHealthCheck.status").toString(), - equalTo("UP")); + equalTo("UP")); assertThat(JsonPath.read(body, "$.components.diskSpace.status").toString(), - equalTo("UP")); + equalTo("UP")); } @Test - public void givenAllDependenciesAreDown_whenCheckHealth_thenReturnStatusDown() throws Exception { + void givenAllDependenciesAreDownWhenCheckHealthThenReturnStatusDown() throws Exception { stubEndpointAndResponse(serviceAuthServer, false); stubEndpointAndResponse(docmosisService, false); stubEndpointAndResponse(caseDocService, false); @@ -127,19 +133,19 @@ public void givenAllDependenciesAreDown_whenCheckHealth_thenReturnStatusDown() t assertThat(response.getStatusLine().getStatusCode(), equalTo(HttpStatus.SERVICE_UNAVAILABLE.value())); assertThat(JsonPath.read(body, "$.status").toString(), - equalTo("DOWN")); + equalTo("DOWN")); assertThat(JsonPath.read(body, "$.components.serviceAuthProviderHealthCheck.status").toString(), - equalTo("DOWN")); + equalTo("DOWN")); assertThat(JsonPath.read(body, "$.components.docmosisHealthCheck.status").toString(), - equalTo("DOWN")); + equalTo("DOWN")); assertThat(JsonPath.read(body, "$.components.caseDocumentHealthCheck.status").toString(), - equalTo("DOWN")); + equalTo("DOWN")); assertThat(JsonPath.read(body, "$.components.diskSpace.status").toString(), - equalTo("UP")); + equalTo("UP")); } @Test - public void givenDocmosisServiceIsDown_whenCheckHealth_thenReturnStatusDown() throws Exception { + void givenDocmosisServiceIsDownWhenCheckHealthThenReturnStatusDown() throws Exception { stubEndpointAndResponse(serviceAuthServer, true); stubEndpointAndResponse(docmosisService, false); stubEndpointAndResponse(caseDocService, true); @@ -149,19 +155,19 @@ public void givenDocmosisServiceIsDown_whenCheckHealth_thenReturnStatusDown() th assertThat(response.getStatusLine().getStatusCode(), equalTo(HttpStatus.SERVICE_UNAVAILABLE.value())); assertThat(JsonPath.read(body, "$.status").toString(), - equalTo("DOWN")); + equalTo("DOWN")); assertThat(JsonPath.read(body, "$.components.serviceAuthProviderHealthCheck.status").toString(), - equalTo("UP")); + equalTo("UP")); assertThat(JsonPath.read(body, "$.components.docmosisHealthCheck.status").toString(), - equalTo("DOWN")); + equalTo("DOWN")); assertThat(JsonPath.read(body, "$.components.caseDocumentHealthCheck.status").toString(), - equalTo("UP")); + equalTo("UP")); assertThat(JsonPath.read(body, "$.components.diskSpace.status").toString(), - equalTo("UP")); + equalTo("UP")); } @Test - public void givenCaseDocumentsClientIsDown_whenCheckHealth_thenReturnStatusDown() throws Exception { + void givenCaseDocumentsClientIsDownWhenCheckHealthThenReturnStatusDown() throws Exception { stubEndpointAndResponse(serviceAuthServer, true); stubEndpointAndResponse(docmosisService, true); stubEndpointAndResponse(caseDocService, false); @@ -171,19 +177,19 @@ public void givenCaseDocumentsClientIsDown_whenCheckHealth_thenReturnStatusDown( assertThat(response.getStatusLine().getStatusCode(), equalTo(HttpStatus.SERVICE_UNAVAILABLE.value())); assertThat(JsonPath.read(body, "$.status").toString(), - equalTo("DOWN")); + equalTo("DOWN")); assertThat(JsonPath.read(body, "$.components.serviceAuthProviderHealthCheck.status").toString(), - equalTo("UP")); + equalTo("UP")); assertThat(JsonPath.read(body, "$.components.docmosisHealthCheck.status").toString(), - equalTo("UP")); + equalTo("UP")); assertThat(JsonPath.read(body, "$.components.caseDocumentHealthCheck.status").toString(), - equalTo("DOWN")); + equalTo("DOWN")); assertThat(JsonPath.read(body, "$.components.diskSpace.status").toString(), - equalTo("UP")); + equalTo("UP")); } @Test - public void givenAuthServiceIsDown_whenCheckHealth_thenReturnStatusDown() throws Exception { + void givenAuthServiceIsDownWhenCheckHealthThenReturnStatusDown() throws Exception { stubEndpointAndResponse(serviceAuthServer, false); stubEndpointAndResponse(docmosisService, true); stubEndpointAndResponse(caseDocService, true); @@ -193,35 +199,32 @@ public void givenAuthServiceIsDown_whenCheckHealth_thenReturnStatusDown() throws assertThat(response.getStatusLine().getStatusCode(), equalTo(HttpStatus.SERVICE_UNAVAILABLE.value())); assertThat(JsonPath.read(body, "$.status").toString(), - equalTo("DOWN")); + equalTo("DOWN")); assertThat(JsonPath.read(body, "$.components.serviceAuthProviderHealthCheck.status").toString(), - equalTo("DOWN")); + equalTo("DOWN")); assertThat(JsonPath.read(body, "$.components.docmosisHealthCheck.status").toString(), - equalTo("UP")); + equalTo("UP")); assertThat(JsonPath.read(body, "$.components.caseDocumentHealthCheck.status").toString(), - equalTo("UP")); + equalTo("UP")); assertThat(JsonPath.read(body, "$.components.diskSpace.status").toString(), - equalTo("UP")); + equalTo("UP")); } - private void stubEndpointAndResponse(WireMockClassRule mockServer, boolean serviceUp) { - mockServer.stubFor(get(urlEqualTo("/health")) - .willReturn(aResponse() - .withStatus(serviceUp ? HttpStatus.OK.value() : HttpStatus.SERVICE_UNAVAILABLE.value()) - .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) - .withBody(serviceUp ? HEALTH_UP_RESPONSE : HEALTH_DOWN_RESPONSE))); - } - - protected final void resetAllMockServices() { - serviceAuthServer.resetAll(); - docmosisService.resetAll(); + private void stubEndpointAndResponse(WireMockServer mockServer, boolean serviceUp) { + mockServer.stubFor( + get(urlEqualTo("/health")) + .willReturn(aResponse() + .withStatus(serviceUp ? HttpStatus.OK.value() : HttpStatus.SERVICE_UNAVAILABLE.value()) + .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) + .withBody(serviceUp ? HEALTH_UP_RESPONSE : HEALTH_DOWN_RESPONSE))); } @TestConfiguration - public static class LocalRibbonClientConfiguration { + static class LocalRibbonClientConfiguration { @Bean - public ServerList ribbonServerList(@Value("${auth.provider.service.client.port}") int serverPort) { + ServerList ribbonServerList(@Value("${auth.provider.service.client.port}") int serverPort) { return new StaticServerList<>(new Server("localhost", serverPort)); } } + } diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/management/monitoring/health/WebServiceHealthCheckUTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/management/monitoring/health/WebServiceHealthCheckUTest.java index 89cf0158..c7df9bdd 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/management/monitoring/health/WebServiceHealthCheckUTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/management/monitoring/health/WebServiceHealthCheckUTest.java @@ -1,8 +1,8 @@ package uk.gov.hmcts.reform.prl.documentgenerator.management.monitoring.health; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.boot.actuate.health.Health; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; @@ -21,9 +21,9 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) -public class WebServiceHealthCheckUTest { - private static final String URI = "http://example.com"; +@ExtendWith(MockitoExtension.class) +class WebServiceHealthCheckUTest { + private static final String URI = "https://example.com"; private final RestTemplate restTemplate = mock(RestTemplate.class); private final HttpEntityFactory httpEntityFactory = mock(HttpEntityFactory.class); @@ -31,7 +31,7 @@ public class WebServiceHealthCheckUTest { URI); @Test - public void givenServiceReturnsOk_whenHealth_thenReturnUp() { + void givenServiceReturnsOkWhenHealthThenReturnUp() { final HttpEntity httpEntity = new HttpEntity<>(null); final ResponseEntity responseEntity = new ResponseEntity<>(HttpStatus.OK); @@ -50,7 +50,7 @@ public void givenServiceReturnsOk_whenHealth_thenReturnUp() { } @Test - public void givenServiceReturnsServiceUnavailable_whenHealth_thenReturnDown() { + void givenServiceReturnsServiceUnavailableWhenHealthThenReturnDown() { final HttpEntity httpEntity = new HttpEntity<>(null); when(httpEntityFactory.createRequestEntityForHealthCheck()).thenReturn(httpEntity); @@ -72,7 +72,7 @@ public void givenServiceReturnsServiceUnavailable_whenHealth_thenReturnDown() { } @Test - public void whenResourceAccessExceptionIsThrown_whenHealth_thenReturnDown() { + void whenResourceAccessExceptionIsThrownWhenHealthThenReturnDown() { final HttpEntity httpEntity = new HttpEntity<>(null); when(httpEntityFactory.createRequestEntityForHealthCheck()).thenReturn(httpEntity); @@ -92,7 +92,7 @@ public void whenResourceAccessExceptionIsThrown_whenHealth_thenReturnDown() { } @Test - public void givenExceptionIsThrown_whenHealth_thenReturnUnknown() { + void givenExceptionIsThrownWhenHealthThenReturnUnknown() { final HttpEntity httpEntity = new HttpEntity<>(null); when(httpEntityFactory.createRequestEntityForHealthCheck()).thenReturn(httpEntity); @@ -112,7 +112,7 @@ public void givenExceptionIsThrown_whenHealth_thenReturnUnknown() { } @Test - public void givenUpstreamStatusIsNot200or503_whenHealth_thenReturnUnknownStatus() { + void givenUpstreamStatusIsNot200or503WhenHealthThenReturnUnknownStatus() { final HttpEntity httpEntity = new HttpEntity<>(null); ResponseEntity responseEntity = new ResponseEntity<>(HttpStatus.MOVED_PERMANENTLY); diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/mapper/TemplateDataMapperTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/mapper/TemplateDataMapperTest.java index 5bae62b1..53a1e0c1 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/mapper/TemplateDataMapperTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/mapper/TemplateDataMapperTest.java @@ -1,13 +1,13 @@ package uk.gov.hmcts.reform.prl.documentgenerator.mapper; import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; -import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; import uk.gov.hmcts.reform.prl.documentgenerator.config.DocmosisBasePdfConfig; import uk.gov.hmcts.reform.prl.documentgenerator.exception.PDFGenerationException; @@ -15,20 +15,16 @@ import java.util.HashMap; import java.util.Map; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.when; import static uk.gov.hmcts.reform.prl.documentgenerator.domain.TemplateConstants.CASE_DATA; import static uk.gov.hmcts.reform.prl.documentgenerator.domain.TemplateConstants.CASE_DETAILS; import static uk.gov.hmcts.reform.prl.documentgenerator.domain.TemplateConstants.TEMP_PARTY_NAMES_KEY; -@RunWith(MockitoJUnitRunner.class) -public class TemplateDataMapperTest { +@ExtendWith(MockitoExtension.class) +class TemplateDataMapperTest { - // Test Values - private static final String TEST_COURT_ADDRESS = "Placeholder Court"; - - // Docmosis Base Config Constants private static final String TEMPLATE_KEY = "templateKey"; private static final String TEMPLATE_VAL = "templateVal"; private static final String FAMILY_IMG_KEY = "familyImgKey"; @@ -48,8 +44,8 @@ public class TemplateDataMapperTest { private Map expectedData; - @Before - public void setup() { + @BeforeEach + void setup() { // Mock docmosisBasePdfConfig mockDocmosisPdfBaseConfig(); @@ -61,11 +57,9 @@ public void setup() { } @Test - public void givenEmptyRequest_whenTemplateDataMapperIsCalled_returnBaseData() { - Map caseData = new HashMap<>(); - + void givenEmptyRequestWhenTemplateDataMapperIsCalled_returnBaseData() { Map requestData = Collections.singletonMap( - CASE_DETAILS, Collections.singletonMap(CASE_DATA, caseData) + CASE_DETAILS, Collections.singletonMap(CASE_DATA, new HashMap<>()) ); Map actual = templateDataMapper.map(requestData); @@ -74,7 +68,7 @@ public void givenEmptyRequest_whenTemplateDataMapperIsCalled_returnBaseData() { } @Test - public void putAllDataInmMap() { + void putAllDataInmMap() { Map caseData = new HashMap<>(); Map actual = templateDataMapper.map(caseData); @@ -83,11 +77,10 @@ public void putAllDataInmMap() { } @Test - public void putAllPartyNamesInMap() { - Map caseData = new HashMap<>(); + void putAllPartyNamesInMap() { Map partyNamesMap = new HashMap<>(); Map requestData = new HashMap<>(); - requestData.put(CASE_DETAILS, Collections.singletonMap(CASE_DATA, caseData)); + requestData.put(CASE_DETAILS, Collections.singletonMap(CASE_DATA, new HashMap<>())); requestData.put(TEMP_PARTY_NAMES_KEY, partyNamesMap); Map actual = templateDataMapper.map(requestData); @@ -96,29 +89,29 @@ public void putAllPartyNamesInMap() { } @Test - public void formatDateFromCCD_exception() { + void formatDateFromCCD_exception() { String ccdDate = "15-03-2022"; - assertThrows(PDFGenerationException.class, () -> { - templateDataMapper.formatDateFromCCD(ccdDate); - }); + assertThrows(PDFGenerationException.class, + () -> templateDataMapper.formatDateFromCCD(ccdDate) + ); } @Test - public void formatDateFromCCD_success() { + void formatDateFromCCD_success() { String ccdDate = "2022-12-03"; assertEquals("03 December 2022", templateDataMapper.formatDateFromCCD(ccdDate)); } @Test - public void formatDateTimeFromCCD_exception() { + void formatDateTimeFromCCD_exception() { String ccdDate = "15-03-2022"; - assertThrows(PDFGenerationException.class, () -> { - templateDataMapper.formatDateTimeFromCCD(ccdDate); - }); + assertThrows(PDFGenerationException.class, + () -> templateDataMapper.formatDateTimeFromCCD(ccdDate) + ); } @Test - public void formatDateTimeFromCCD_success() { + void formatDateTimeFromCCD_success() { String ccdDate = "2017-11-22T10:10:15.455"; assertEquals("22 November 2017", templateDataMapper.formatDateTimeFromCCD(ccdDate)); } diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocmosisPdfGenerationServiceImplUTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocmosisPdfGenerationServiceImplUTest.java index e4bbebf7..2908fa4b 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocmosisPdfGenerationServiceImplUTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocmosisPdfGenerationServiceImplUTest.java @@ -1,21 +1,17 @@ package uk.gov.hmcts.reform.prl.documentgenerator.service.impl; -import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.reflect.FieldUtils; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.Spy; -import org.mockito.junit.MockitoJUnitRunner; -import org.springframework.http.HttpHeaders; +import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; @@ -23,15 +19,18 @@ import uk.gov.hmcts.reform.prl.documentgenerator.mapper.TemplateDataMapper; import uk.gov.hmcts.reform.prl.documentgenerator.util.NullOrEmptyValidator; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; -@RunWith(MockitoJUnitRunner.class) -public class DocmosisPdfGenerationServiceImplUTest { +@ExtendWith(MockitoExtension.class) +class DocmosisPdfGenerationServiceImplUTest { @Mock private RestTemplate restTemplate; @@ -39,31 +38,23 @@ public class DocmosisPdfGenerationServiceImplUTest { @Mock private TemplateDataMapper templateDataMapper; - @Mock - ObjectMapper objectMapper; - - @InjectMocks @Spy private DocmosisPDFGenerationServiceImpl classUnderTest; - @Before - public void before() throws IllegalAccessException { + @BeforeEach + void before() throws IllegalAccessException { FieldUtils.writeField(classUnderTest, "docmosisPdfServiceEndpoint", "test", true); FieldUtils.writeField(classUnderTest, "docmosisPdfConvertEndpoint", "test", true); FieldUtils.writeField(classUnderTest, "docmosisPdfServiceAccessKey", "test", true); - } @Test - public void givenHttpClientErrorExceptionThrown_whenGenerateCalled_thenThrowPdfGenerationException() - throws Exception { + void givenHttpClientErrorExceptionThrownWhenGenerateCalledThenThrowPdfGenerationException() { final String template = "1"; final Map placeholders = Collections.emptyMap(); final HttpClientErrorException httpClientErrorException = Mockito.mock(HttpClientErrorException.class); - HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); Mockito.doThrow(httpClientErrorException).when(restTemplate).exchange(Mockito.anyString(), ArgumentMatchers.any(HttpMethod.class), ArgumentMatchers.any(), @@ -78,19 +69,16 @@ public void givenHttpClientErrorExceptionThrown_whenGenerateCalled_thenThrowPdfG NullOrEmptyValidator.requireNonBlank(template); } - @Test - public void givenHttpRequestGoesThrough_whenGenerateFromHtml_thenReturnProperResponse() throws Exception { + void givenHttpRequestGoesThroughWhenGenerateFromHtmlThenReturnProperResponse() { final String template = "1"; final Map placeholders = Collections.emptyMap(); byte[] test = "Any String you want".getBytes(); - ResponseEntity myEntity = new ResponseEntity(test,HttpStatus.ACCEPTED); + ResponseEntity myEntity = new ResponseEntity<>(test, HttpStatus.ACCEPTED); - HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); Mockito.when(restTemplate.exchange(ArgumentMatchers.any(String.class), ArgumentMatchers.any(HttpMethod.class), ArgumentMatchers.any(), @@ -98,25 +86,30 @@ public void givenHttpRequestGoesThrough_whenGenerateFromHtml_thenReturnProperRes byte[] expected = classUnderTest.generate(template, placeholders); - Assert.assertNotNull(expected); + assertNotNull(expected); } @Test - public void givenFileNAme_whenConvertPDf_thenReturnProperResponse() throws Exception { + void givenFileNameWhenConvertPdfThenReturnProperResponse() throws Exception { final Map placeholders = new HashMap<>(); byte[] test = "Any String you want".getBytes(); - placeholders.put("fileName",test); + placeholders.put("fileName", test); - HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); - Mockito.when(restTemplate.postForObject(ArgumentMatchers.any(String.class), - ArgumentMatchers.any(), - ArgumentMatchers.>any())).thenReturn(test); + Mockito.when(restTemplate.postForObject( + ArgumentMatchers.any(String.class), + ArgumentMatchers.any(), + ArgumentMatchers.>any())) + .thenReturn(test); - byte[] expected = classUnderTest.converToPdf(placeholders,"testFile"); + String outputFile = "testFile.pdf"; - Assert.assertNotNull(expected); + try { + byte[] expected = classUnderTest.convertToPdf(placeholders, outputFile); + assertNotNull(expected); + } finally { + Files.deleteIfExists(Path.of(outputFile)); + } } } diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImplTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImplTest.java index 1955b8f7..321cdf58 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImplTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImplTest.java @@ -1,14 +1,13 @@ package uk.gov.hmcts.reform.prl.documentgenerator.service.impl; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; import uk.gov.hmcts.reform.ccd.document.am.feign.CaseDocumentClient; import uk.gov.hmcts.reform.ccd.document.am.model.UploadResponse; @@ -22,18 +21,18 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.verify; -@RunWith(MockitoJUnitRunner.class) -public class DocumentManagementServiceImplTest { +@ExtendWith(MockitoExtension.class) +class DocumentManagementServiceImplTest { private static final String IS_DRAFT = "isDraft"; private static final String MINI_PETITION_NAME_FOR_WELSH_PDF_FILE = "DivorcePetitionWelsh.pdf"; private static final String DRAFT_MINI_PETITION_NAME_FOR_PDF_FILE = "DraftDivorcePetition.pdf"; private static final String D8_PETITION_WELSH_TEMPLATE = "FL-DIV-GNO-WEL-00256.docx"; - private static final String DRAFT_MINI_PETITION_TEMPLATE_ID = "divorcedraftminipetition"; private static final String FINAL_MINI_PETITION_TEMPLATE_ID = "divorcedraftminipetition"; @@ -56,24 +55,24 @@ public class DocumentManagementServiceImplTest { ArgumentCaptor> placeHolderCaptor; static final Map placeholderMap = new HashMap<>(); - static final String authToken = "userAuthToken"; - static final String s2sToken = "s2sAuthToken"; + static final String AUTH_TOKEN = "userAuthToken"; + static final String S2S_TOKEN = "s2sAuthToken"; static final byte[] data = {126}; static final UploadResponse uploadResponse = new UploadResponse( List.of(DocumentGenerateAndStoreE2ETest.mockCaseDocsDocuments()) ); @Test - public void testGenerateAndStoreDraftDocumentMock() { - Mockito.when(authTokenGenerator.generate()).thenReturn(s2sToken); + void testGenerateAndStoreDraftDocumentMock() { + Mockito.when(authTokenGenerator.generate()).thenReturn(S2S_TOKEN); Mockito.when(pdfGenerationService.generate(D8_PETITION_WELSH_TEMPLATE, placeholderMap)).thenReturn(data); Mockito.when(templatesConfiguration.getFileNameByTemplateName(D8_PETITION_WELSH_TEMPLATE)) .thenReturn(MINI_PETITION_NAME_FOR_WELSH_PDF_FILE); - Mockito.when(caseDocumentClient.uploadDocuments(eq(authToken), - eq(s2sToken), eq("PRLAPPS"), eq("PRIVATELAW"), any())) + Mockito.when(caseDocumentClient.uploadDocuments(eq(AUTH_TOKEN), + eq(S2S_TOKEN), eq("PRLAPPS"), eq("PRIVATELAW"), any())) .thenReturn(uploadResponse); - classUnderTest.generateAndStoreDraftDocument(D8_PETITION_WELSH_TEMPLATE, placeholderMap, authToken); + classUnderTest.generateAndStoreDraftDocument(D8_PETITION_WELSH_TEMPLATE, placeholderMap, AUTH_TOKEN); verify(pdfGenerationService).generate(same(D8_PETITION_WELSH_TEMPLATE), placeHolderCaptor.capture()); Map value = placeHolderCaptor.getValue(); @@ -81,19 +80,19 @@ public void testGenerateAndStoreDraftDocumentMock() { } @Test - public void testGenerateAndStoreDraftDocumentMockWithDynamicName() { + void testGenerateAndStoreDraftDocumentMockWithDynamicName() { placeholderMap.put("dynamic_fileName","test-file.pdf"); - Mockito.when(authTokenGenerator.generate()).thenReturn(s2sToken); + Mockito.when(authTokenGenerator.generate()).thenReturn(S2S_TOKEN); Mockito.when(pdfGenerationService.generate(D8_PETITION_WELSH_TEMPLATE, placeholderMap)).thenReturn(data); Mockito.when(caseDocumentClient.uploadDocuments( - eq(authToken), - eq(s2sToken), + eq(AUTH_TOKEN), + eq(S2S_TOKEN), eq("PRLAPPS"), eq("PRIVATELAW"), any())) .thenReturn(uploadResponse); - classUnderTest.generateAndStoreDraftDocument(D8_PETITION_WELSH_TEMPLATE, placeholderMap, authToken); + classUnderTest.generateAndStoreDraftDocument(D8_PETITION_WELSH_TEMPLATE, placeholderMap, AUTH_TOKEN); verify(pdfGenerationService).generate(same(D8_PETITION_WELSH_TEMPLATE), placeHolderCaptor.capture()); Map value = placeHolderCaptor.getValue(); @@ -101,14 +100,14 @@ public void testGenerateAndStoreDraftDocumentMockWithDynamicName() { } @Test - public void testGenerateAndStoreDraftDocument_WithDraftPrefixMock() { - Mockito.when(authTokenGenerator.generate()).thenReturn(s2sToken); + void testGenerateAndStoreDraftDocument_WithDraftPrefixMock() { + Mockito.when(authTokenGenerator.generate()).thenReturn(S2S_TOKEN); Mockito.when(pdfGenerationService.generate(DRAFT_MINI_PETITION_TEMPLATE_ID, placeholderMap)).thenReturn(data); - Mockito.when(caseDocumentClient.uploadDocuments(eq(authToken), - eq(s2sToken), eq("PRLAPPS"), eq("PRIVATELAW"), any())) + Mockito.when(caseDocumentClient.uploadDocuments(eq(AUTH_TOKEN), + eq(S2S_TOKEN), eq("PRLAPPS"), eq("PRIVATELAW"), any())) .thenReturn(uploadResponse); - classUnderTest.generateAndStoreDraftDocument(DRAFT_MINI_PETITION_TEMPLATE_ID, placeholderMap, authToken); + classUnderTest.generateAndStoreDraftDocument(DRAFT_MINI_PETITION_TEMPLATE_ID, placeholderMap, AUTH_TOKEN); verify(pdfGenerationService).generate(same(DRAFT_MINI_PETITION_TEMPLATE_ID), placeHolderCaptor.capture()); Map value = placeHolderCaptor.getValue(); @@ -116,67 +115,67 @@ public void testGenerateAndStoreDraftDocument_WithDraftPrefixMock() { } @Test - public void testConvertToPdf() { + void testConvertToPdf() { final Map placeholders = new HashMap<>(); byte[] test = "Any String you want".getBytes(); placeholders.put("fileName",test); - Mockito.when(authTokenGenerator.generate()).thenReturn(s2sToken); - Mockito.when(pdfGenerationService.converToPdf(placeholders,"fileName")).thenReturn(test); + Mockito.when(authTokenGenerator.generate()).thenReturn(S2S_TOKEN); + Mockito.when(pdfGenerationService.convertToPdf(placeholders,"fileName")).thenReturn(test); Mockito.when(caseDocumentClient.uploadDocuments( - eq(authToken), - eq(s2sToken), + eq(AUTH_TOKEN), + eq(S2S_TOKEN), eq("PRLAPPS"), eq("PRIVATELAW"), any())) .thenReturn(uploadResponse); - classUnderTest.converToPdf(placeholders, authToken,"fileName"); + classUnderTest.convertToPdf(placeholders, AUTH_TOKEN, "fileName"); - verify(pdfGenerationService).converToPdf(placeholders,"fileName"); + verify(pdfGenerationService).convertToPdf(placeholders,"fileName"); } @Test - public void testGenerateAndStoreFinalDocument_WithDynamicFileName() { + void testGenerateAndStoreFinalDocument_WithDynamicFileName() { placeholderMap.put("dynamic_fileName","test-file.pdf"); - Mockito.when(authTokenGenerator.generate()).thenReturn(s2sToken); + Mockito.when(authTokenGenerator.generate()).thenReturn(S2S_TOKEN); Mockito.when(pdfGenerationService.generate(FINAL_MINI_PETITION_TEMPLATE_ID, placeholderMap)).thenReturn(data); Mockito.when(caseDocumentClient.uploadDocuments( - eq(authToken), - eq(s2sToken), + eq(AUTH_TOKEN), + eq(S2S_TOKEN), eq("PRLAPPS"), eq("PRIVATELAW"), any())) .thenReturn(uploadResponse); - classUnderTest.generateAndStoreDocument(FINAL_MINI_PETITION_TEMPLATE_ID, placeholderMap, authToken); + classUnderTest.generateAndStoreDocument(FINAL_MINI_PETITION_TEMPLATE_ID, placeholderMap, AUTH_TOKEN); verify(pdfGenerationService).generate(same(FINAL_MINI_PETITION_TEMPLATE_ID), placeHolderCaptor.capture()); Map value = placeHolderCaptor.getValue(); - Assert.assertNotNull(value); + assertNotNull(value); } @Test - public void testGenerateAndStoreFinalDocument_WithOutDynamicFileName() { + void testGenerateAndStoreFinalDocument_WithOutDynamicFileName() { placeholderMap.remove("dynamic_fileName"); - Mockito.when(authTokenGenerator.generate()).thenReturn(s2sToken); + Mockito.when(authTokenGenerator.generate()).thenReturn(S2S_TOKEN); Mockito.when(pdfGenerationService.generate(FINAL_MINI_PETITION_TEMPLATE_ID, placeholderMap)).thenReturn(data); Mockito.when(templatesConfiguration.getFileNameByTemplateName(FINAL_MINI_PETITION_TEMPLATE_ID)) .thenReturn(DRAFT_MINI_PETITION_NAME_FOR_PDF_FILE); Mockito.when(caseDocumentClient.uploadDocuments( - eq(authToken), - eq(s2sToken), + eq(AUTH_TOKEN), + eq(S2S_TOKEN), eq("PRLAPPS"), eq("PRIVATELAW"), any())) .thenReturn(uploadResponse); - classUnderTest.generateAndStoreDocument(FINAL_MINI_PETITION_TEMPLATE_ID, placeholderMap, authToken); + classUnderTest.generateAndStoreDocument(FINAL_MINI_PETITION_TEMPLATE_ID, placeholderMap, AUTH_TOKEN); verify(pdfGenerationService).generate(same(FINAL_MINI_PETITION_TEMPLATE_ID), placeHolderCaptor.capture()); Map value = placeHolderCaptor.getValue(); - Assert.assertNotNull(value); + assertNotNull(value); } } diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImplUTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImplUTest.java index 0e69bd04..f6d5da79 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImplUTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/service/impl/DocumentManagementServiceImplUTest.java @@ -1,12 +1,12 @@ package uk.gov.hmcts.reform.prl.documentgenerator.service.impl; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator; import uk.gov.hmcts.reform.ccd.document.am.feign.CaseDocumentClient; import uk.gov.hmcts.reform.ccd.document.am.model.UploadResponse; @@ -15,11 +15,11 @@ import uk.gov.hmcts.reform.prl.documentgenerator.service.PDFGenerationService; import java.util.HashMap; +import java.util.List; -import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @@ -33,8 +33,8 @@ import static uk.gov.hmcts.reform.prl.documentgenerator.util.TestData.TEST_TEMPLATE; import static uk.gov.hmcts.reform.prl.documentgenerator.util.TestData.TEST_TEMPLATE_FILE_NAME; -@RunWith(MockitoJUnitRunner.class) -public class DocumentManagementServiceImplUTest { +@ExtendWith(MockitoExtension.class) +class DocumentManagementServiceImplUTest { @Mock private PDFGenerationService pdfGenerationService; @@ -53,14 +53,13 @@ public class DocumentManagementServiceImplUTest { private UploadResponse expectedUploadResponse; - @Before - public void setUp() { - expectedUploadResponse = new UploadResponse(asList(mockCaseDocsDocuments())); + @BeforeEach + void setUp() { + expectedUploadResponse = new UploadResponse(List.of(mockCaseDocsDocuments())); } - @Test - public void givenTemplateNameIsAosInvitation_whenGenerateAndStoreDocument_thenProceedAsExpected() { + void givenTemplateNameIsAosInvitationWhenGenerateAndStoreDocumentThenProceedAsExpected() { when(pdfGenerationService.generate(eq(TEST_TEMPLATE), any())).thenReturn(TEST_GENERATED_DOCUMENT); when(templatesConfiguration.getFileNameByTemplateName(TEST_TEMPLATE)).thenReturn(TEST_TEMPLATE_FILE_NAME); when(caseDocumentClient.uploadDocuments( @@ -74,7 +73,7 @@ public void givenTemplateNameIsAosInvitation_whenGenerateAndStoreDocument_thenPr } @Test - public void givenTemplateNameIsAosInvitation_whenGenerateAndStoreDraftDocument_thenProceedAsExpected() { + void givenTemplateNameIsAosInvitationWhenGenerateAndStoreDraftDocumentThenProceedAsExpected() { when(pdfGenerationService.generate(eq(TEST_TEMPLATE), any())).thenReturn(TEST_GENERATED_DOCUMENT); when(templatesConfiguration.getFileNameByTemplateName(TEST_TEMPLATE)).thenReturn(TEST_TEMPLATE_FILE_NAME); when(caseDocumentClient.uploadDocuments( @@ -88,15 +87,16 @@ public void givenTemplateNameIsAosInvitation_whenGenerateAndStoreDraftDocument_t } @Test - public void givenTemplateNameIsInvalid_whenGenerateAndStoreDocument_thenThrowException() { + void givenTemplateNameIsInvalidWhenGenerateAndStoreDocumentThenThrowException() { String unknownTemplateName = "unknown-template"; HashMap placeholders = new HashMap<>(); when(templatesConfiguration.getFileNameByTemplateName(unknownTemplateName)) .thenThrow(new IllegalArgumentException("Unknown template: " + unknownTemplateName)); - IllegalArgumentException illegalArgumentException = assertThrows(IllegalArgumentException.class, () -> { - classUnderTest.generateAndStoreDocument(unknownTemplateName, placeholders, "some-auth-token"); - }); + IllegalArgumentException illegalArgumentException = assertThrows( + IllegalArgumentException.class, + () -> classUnderTest.generateAndStoreDocument(unknownTemplateName, placeholders, "some-auth-token") + ); assertThat(illegalArgumentException.getMessage(), equalTo("Unknown template: " + unknownTemplateName)); } diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/util/NullOrEmptyValidatorUTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/util/NullOrEmptyValidatorUTest.java index 6ecc2c23..2ea16fd5 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/util/NullOrEmptyValidatorUTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/util/NullOrEmptyValidatorUTest.java @@ -1,43 +1,48 @@ package uk.gov.hmcts.reform.prl.documentgenerator.util; import org.apache.commons.lang3.ArrayUtils; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; -@RunWith(MockitoJUnitRunner.class) -public class NullOrEmptyValidatorUTest { +@ExtendWith(MockitoExtension.class) +class NullOrEmptyValidatorUTest { private static final String BLANK_STRING = " "; private static final String EMPTY_STRING = ""; private static final String SOME_STRING = "Some String"; @Test - public void testConstructorPrivate() throws Exception { + void testConstructorPrivate() throws Exception { Constructor constructor = NullOrEmptyValidator.class.getDeclaredConstructor(); assertTrue(Modifier.isPrivate(constructor.getModifiers())); constructor.setAccessible(true); constructor.newInstance(); } - @Test(expected = IllegalArgumentException.class) - public void givenArrayIsNull_whenRequireNonEmpty_thenThrowsIllegalArgumentException() { - NullOrEmptyValidator.requireNonEmpty(null); + @Test + void givenArrayIsNullWhenRequireNonEmptyThenThrowsIllegalArgumentException() { + assertThrows(IllegalArgumentException.class, () -> + NullOrEmptyValidator.requireNonEmpty(null) + ); } - @Test(expected = IllegalArgumentException.class) - public void givenArrayIsEmpty_whenRequireNonEmpty_thenThrowsIllegalArgumentException() { - NullOrEmptyValidator.requireNonEmpty(ArrayUtils.EMPTY_BYTE_ARRAY); + @Test + void givenArrayIsEmptyWhenRequireNonEmptyThenThrowsIllegalArgumentException() { + assertThrows(IllegalArgumentException.class, () -> + NullOrEmptyValidator.requireNonEmpty(ArrayUtils.EMPTY_BYTE_ARRAY) + ); } @Test - public void givenArrayIsNotEmptyOrNull_whenRequireNonEmpty_thenDoesNotThrowException() { + void givenArrayIsNotEmptyOrNullWhenRequireNonEmptyThenDoesNotThrowException() { try { NullOrEmptyValidator.requireNonEmpty(new byte[]{1}); } catch (Exception e) { @@ -45,23 +50,30 @@ public void givenArrayIsNotEmptyOrNull_whenRequireNonEmpty_thenDoesNotThrowExcep } } - @Test(expected = IllegalArgumentException.class) - public void givenTextIsNull_whenRequireNonBlank_thenThrowsIllegalArgumentException() { - NullOrEmptyValidator.requireNonBlank(null); + @Test + void givenTextIsNullWhenRequireNonBlankThenThrowsIllegalArgumentException() { + assertThrows(IllegalArgumentException.class, () -> + NullOrEmptyValidator.requireNonBlank(null) + ); } - @Test(expected = IllegalArgumentException.class) - public void givenTextIsEmpty_whenRequireNonBlank_thenThrowsIllegalArgumentException() { - NullOrEmptyValidator.requireNonBlank(EMPTY_STRING); + @Test + void givenTextIsEmptyWhenRequireNonBlankThenThrowsIllegalArgumentException() { + assertThrows(IllegalArgumentException.class, () -> + NullOrEmptyValidator.requireNonBlank(EMPTY_STRING) + ); } - @Test(expected = IllegalArgumentException.class) - public void givenTextIsBlank_whenRequireNonBlank_thenThrowsIllegalArgumentException() { - NullOrEmptyValidator.requireNonBlank(BLANK_STRING); + @Test + void givenTextIsBlankWhenRequireNonBlankThenThrowsIllegalArgumentException() { + assertThrows( + IllegalArgumentException.class, () -> + NullOrEmptyValidator.requireNonBlank(BLANK_STRING) + ); } @Test - public void givenTextIsNotBlank_whenRequireNonBlank_thenDoesNotThrowException() { + void givenTextIsNotBlankWhenRequireNonBlankThenDoesNotThrowException() { try { NullOrEmptyValidator.requireNonBlank(SOME_STRING); } catch (Exception e) { diff --git a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/util/ResourceLoaderUTest.java b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/util/ResourceLoaderUTest.java index f7808ebc..abda5c22 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/util/ResourceLoaderUTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/documentgenerator/util/ResourceLoaderUTest.java @@ -1,59 +1,59 @@ package uk.gov.hmcts.reform.prl.documentgenerator.util; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; import uk.gov.hmcts.reform.prl.documentgenerator.exception.ErrorLoadingTemplateException; import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import java.nio.charset.StandardCharsets; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyString; -@RunWith(MockitoJUnitRunner.class) -public class ResourceLoaderUTest { +@ExtendWith(MockitoExtension.class) +class ResourceLoaderUTest { private static final String NON_EXISTENT_PATH = "somePath"; private static final String EXISTING_PATH = "ResourceLoadTest.txt"; private static final String DATA_IN_FILE = "Resource Load Test"; MockedStatic nullOrEmptyValidator; - @Before - public void beforeTest() { + @BeforeEach + void beforeTest() { nullOrEmptyValidator = Mockito.mockStatic(NullOrEmptyValidator.class); } - @After - public void afterTest() { + @AfterEach + void afterTest() { nullOrEmptyValidator.close(); } @Test - public void testConstructorPrivate() throws Exception { + void testConstructorPrivate() throws Exception { Constructor constructor = ResourceLoader.class.getDeclaredConstructor(); assertTrue(Modifier.isPrivate(constructor.getModifiers())); constructor.setAccessible(true); constructor.newInstance(); } - @Test(expected = ErrorLoadingTemplateException.class) - public void givenFileIsDoNotExists_whenLoadResource_thenThrowsErrorLoadingTemplateException() { - - nullOrEmptyValidator.verifyNoInteractions(); - ResourceLoader.loadResource(NON_EXISTENT_PATH); - NullOrEmptyValidator.requireNonBlank(NON_EXISTENT_PATH); + @Test + void givenFileDoesNotExistWhenLoadResourceThenThrowsErrorLoadingTemplateException() { + assertThrows(ErrorLoadingTemplateException.class, + () -> ResourceLoader.loadResource(NON_EXISTENT_PATH) + ); } @Test - public void givenFileExists_whenLoadResource_thenLoadFile() { + void givenFileExistsWhenLoadResourceThenLoadFile() { byte[] data = ResourceLoader.loadResource(EXISTING_PATH); nullOrEmptyValidator.verify( () -> NullOrEmptyValidator.requireNonBlank(anyString()) From 5239a23ce0dc9322b939d7cb419bd713408d287a Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Tue, 1 Apr 2025 10:12:43 +0100 Subject: [PATCH 34/51] Remove serenity and pitest, and bump dependencies --- build.gradle | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index 69b8f3c3..07b32ec7 100644 --- a/build.gradle +++ b/build.gradle @@ -1,16 +1,14 @@ import org.springframework.boot.gradle.tasks.run.BootRun plugins { - id 'application' - id 'uk.gov.hmcts.java' version '0.12.65' - id 'com.github.ben-manes.versions' version '0.51.0' - id 'io.spring.dependency-management' version '1.0.11.RELEASE' - id 'jacoco' - id 'org.sonarqube' version '5.0.0.4638' - id 'org.springframework.boot' version '2.7.0' - id 'info.solidsoft.pitest' version '1.7.4' - id 'au.com.dius.pact' version '4.1.7' - id 'net.serenity-bdd.serenity-gradle-plugin' version '2.4.34' + id 'application' + id 'uk.gov.hmcts.java' version '0.12.65' + id 'com.github.ben-manes.versions' version '0.52.0' + id 'io.spring.dependency-management' version '1.1.7' + id 'jacoco' + id 'org.sonarqube' version '6.1.0.5360' + id 'org.springframework.boot' version '2.7.18' + id 'au.com.dius.pact' version '4.6.17' } application { From c73432c0fa2b1bfb6159e2616dbb779b5ae5f239 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Tue, 1 Apr 2025 10:13:18 +0100 Subject: [PATCH 35/51] Fix indentation --- build.gradle | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/build.gradle b/build.gradle index 07b32ec7..2bea865d 100644 --- a/build.gradle +++ b/build.gradle @@ -12,21 +12,21 @@ plugins { } application { - group = 'uk.gov.hmcts.reform.prl' - mainClass = 'uk.gov.hmcts.reform.prl.documentgenerator.DocumentGeneratorApplication' - version = '0.0.1' + group = 'uk.gov.hmcts.reform.prl' + mainClass = 'uk.gov.hmcts.reform.prl.documentgenerator.DocumentGeneratorApplication' + version = '0.0.1' } java { - toolchain { - languageVersion = JavaLanguageVersion.of(17) - } + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } } repositories { - mavenLocal() - mavenCentral() - maven { url = 'https://jitpack.io' } + mavenLocal() + mavenCentral() + maven { url = 'https://jitpack.io' } } sourceSets { From 0bdeb76269d28e281dd866a135a4b6511953a509 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Tue, 1 Apr 2025 10:14:32 +0100 Subject: [PATCH 36/51] Set sourcesets programatically --- build.gradle | 50 ++++++++++++++++---------------------------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/build.gradle b/build.gradle index 2bea865d..a1767ce4 100644 --- a/build.gradle +++ b/build.gradle @@ -29,54 +29,36 @@ repositories { maven { url = 'https://jitpack.io' } } -sourceSets { - integrationTest { - java { - compileClasspath += main.output + test.output - runtimeClasspath += main.output + test.output - srcDirs = ['src/integrationTest/java'] - } - resources { - srcDirs = ['src/integrationTest/resources'] - } - } - functionalTest { - java { - compileClasspath += main.output + test.output - runtimeClasspath += main.output + test.output - srcDirs = ['src/functionalTest/java'] - } - resources { - srcDirs = ['src/functionalTest/resources'] - } - } - contractTest { - java { - compileClasspath += main.output - runtimeClasspath += main.output - srcDir file('src/contractTest/java') +def configureSourceSet(String name) { + sourceSets.create(name) { sourceSet -> + sourceSet.java { + compileClasspath += sourceSets.main.output + sourceSets.test.output + runtimeClasspath += sourceSets.main.output + sourceSets.test.output + srcDir "src/${name}/java" } - resources.srcDir file('src/contractTest/resources') + sourceSet.resources.srcDir "src/${name}/resources" } } +['smokeTest', 'integrationTest', 'functionalTest', 'contractTest'].each { configureSourceSet(it) } + configurations { + smokeTestImplementation.extendsFrom testImplementation + smokeTestRuntime.extendsFrom testRuntime integrationTestImplementation.extendsFrom testImplementation integrationTestRuntime.extendsFrom testRuntime functionalTestImplementation.extendsFrom testImplementation functionalTestRuntime.extendsFrom testRuntime contractTestImplementation.extendsFrom testImplementation contractTestRuntime.extendsFrom testRuntime - smokeTestImplementation.extendsFrom testImplementation - smokeTestRuntime.extendsFrom testRuntime } bootJar { - manifest { - attributes 'Implementation-Title': project.name, - 'Implementation-Version': project.version - } - archiveFileName = 'prl-dgs-api.jar' + manifest { + attributes 'Implementation-Title': project.name, + 'Implementation-Version': project.version + } + archiveFileName = 'prl-dgs-api.jar' } compileJava { From e00b3504e5c63b61585083bf6b83b1a7658c22b6 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Tue, 1 Apr 2025 10:14:57 +0100 Subject: [PATCH 37/51] Fix warning --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a1767ce4..58743c66 100644 --- a/build.gradle +++ b/build.gradle @@ -79,7 +79,7 @@ distributions { } } -tasks.withType(JavaCompile) { +tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' } From ddb80ed78e783c58819450fed3bf53e125f38a42 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Tue, 1 Apr 2025 10:16:26 +0100 Subject: [PATCH 38/51] Bump dependencies --- build.gradle | 439 +++++++++++++++------------------------------------ 1 file changed, 127 insertions(+), 312 deletions(-) diff --git a/build.gradle b/build.gradle index 58743c66..e7272940 100644 --- a/build.gradle +++ b/build.gradle @@ -84,362 +84,177 @@ tasks.withType(JavaCompile).configureEach { } dependencyCheck { - suppressionFile = 'config/owasp/suppressions.xml' -} - -jacoco { - toolVersion = "0.8.8" + suppressionFile = 'config/owasp/suppressions.xml' } def versions = [ - commonsIo: '2.7', - commonsLang3: '3.9', - commonsBeanUtils: '1.9.4', - feignHttpClient: '10.2.0', - gradlePitest: '1.4.5', - guava: '32.1.2-jre', - jacksonDatabind: '2.16.0', apacheLogging: '2.20.0', - jsonAssert: '1.2.3', - junit: '4.13.1', - lombok: '1.18.26', - nimbus: '5.1', - pdfbox: '2.0.24', - pitest: '1.7.3', - powerMock: '2.0.0', - reformsJavaLogging: '5.1.1', - restAssured: '4.5.1', - serenity: '2.2.13', - serenityCucumber: '1.9.51', + ccdCaseDocumentAmClient: '1.7.2', + httpComponents: '4.5.13', + lombok: '1.18.36', + pact_version: '4.1.11', + restAssured: '5.5.1', serviceTokenGenerator: '3.0.0', - sonarPitest: '0.5', - spring_security_rsa: '1.0.12.RELEASE', springBoot: '2.7.18', - springCloud: '3.1.6', - springHateoas: '1.5.5', - unirest: '1.4.9', - wiremockVersion: '2.27.2', - springSecurityCrypto: '5.7.11', - tomcat : '9.0.83', - pact_version: '4.1.11', - httpComponents: '4.5.13', - bouncycastle: '1.73', - ccdCaseDocumentAmClient: '1.7.2', - springFramework : '5.3.27' ] -ext["logback.version"] = '1.2.13' -ext['snakeyaml.version'] = '2.2' -ext['jackson.version'] = '2.16.0' -ext['spring-framework.version'] = '5.3.27' - - - - dependencies { - // integration test - integrationTestImplementation group: 'com.mashape.unirest', name: 'unirest-java', version: versions.unirest - integrationTestImplementation group: 'com.nimbusds', name: 'nimbus-jose-jwt', version: versions.nimbus - integrationTestImplementation group: 'net.serenity-bdd', name: 'serenity-core', version: versions.serenity - integrationTestImplementation group: 'net.serenity-bdd', name: 'serenity-cucumber', version: versions.serenityCucumber - integrationTestImplementation group: 'net.serenity-bdd', name: 'serenity-junit', version: versions.serenity - integrationTestImplementation group: 'net.serenity-bdd', name: 'serenity-rest-assured', version: versions.serenity - integrationTestImplementation group: 'net.serenity-bdd', name: 'serenity-spring', version: versions.serenity - integrationTestImplementation group: 'org.apache.commons', name: 'commons-lang3', version: versions.commonsLang3 - integrationTestImplementation group: 'org.apache.pdfbox', name: 'pdfbox', version: versions.pdfbox - integrationTestImplementation group: 'org.projectlombok', name: 'lombok', version: versions.lombok - integrationTestImplementation group: 'org.skyscreamer', name:'jsonassert', version: versions.jsonAssert - integrationTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test' - integrationTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-web' - - integrationTestImplementation (group: 'com.github.hmcts', name: 'service-auth-provider-java-client', version: versions.serviceTokenGenerator){ - exclude group: 'io.reactivex', module: 'io.reactivex' - exclude group: 'io.reactivex', module: 'rxnetty' - exclude group: 'io.reactivex', module: 'rxnetty-contexts' - exclude group: 'io.reactivex', module: 'rxnetty-servo' - } - - functionalTestImplementation (group: 'com.github.hmcts', name: 'service-auth-provider-java-client', version: versions.serviceTokenGenerator){ - exclude group: 'io.reactivex', module: 'io.reactivex' - exclude group: 'io.reactivex', module: 'rxnetty' - exclude group: 'io.reactivex', module: 'rxnetty-contexts' - exclude group: 'io.reactivex', module: 'rxnetty-servo' - } - - functionalTestImplementation group: 'com.github.hmcts', name:'ccd-case-document-am-client', version: versions.ccdCaseDocumentAmClient - - testImplementation group: 'org.springframework.cloud', name: 'spring-cloud-contract-wiremock', version: '3.1.1' - - implementation (group: 'commons-beanutils', name: 'commons-beanutils', version: versions.commonsBeanUtils) - implementation (group: 'org.springframework.security', name: 'spring-security-rsa', version: versions.spring_security_rsa) { - exclude group: 'org.springframework.security', module: 'spring-security-crypto' - } - - implementation group: 'org.springframework.security', name: 'spring-security-crypto', version: versions.springSecurityCrypto - - implementation group: 'org.springframework.boot', name: 'spring-boot-starter-actuator' - implementation group: 'org.bouncycastle', name: 'bcprov-jdk18on', version: versions.bouncycastle - - implementation group: 'com.github.hmcts', name:'ccd-case-document-am-client', version: versions.ccdCaseDocumentAmClient - - implementation(group: 'org.apache.logging.log4j', name: 'log4j-to-slf4j', version: versions.apacheLogging) - implementation(group: 'org.apache.logging.log4j', name: 'log4j-api', version: versions.apacheLogging) - - implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: versions.springBoot - implementation group: 'commons-io', name: 'commons-io', version: versions.commonsIo - implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.9' - - implementation group: 'commons-fileupload', name: 'commons-fileupload', version: '1.5' - - implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-netflix-ribbon', version: '2.2.10.RELEASE' - implementation (group: 'org.springframework.cloud', name: 'spring-cloud-starter-openfeign', version: versions.springCloud) { - exclude group: 'io.reactivex', module: 'io.reactivex' - exclude group: 'io.reactivex', module: 'rxnetty' - exclude group: 'io.reactivex', module: 'rxnetty-contexts' - exclude group: 'io.reactivex', module: 'rxnetty-servo' - exclude group: 'org.bouncycastle' - } - - implementation group: 'io.github.openfeign', name: 'feign-httpclient', version: versions.feignHttpClient - implementation group: 'org.springframework.hateoas', name: 'spring-hateoas', version: versions.springHateoas - implementation group: 'org.apache.commons', name: 'commons-lang3', version : versions.commonsLang3 - - implementation 'com.github.hmcts.java-logging:logging:6.1.6' - implementation 'com.github.hmcts.java-logging:logging-appinsights:6.1.6' - implementation group: 'org.springframework', name: 'spring-context-support', version: versions.springFramework - implementation ( group: 'com.github.hmcts', name: 'service-auth-provider-java-client', version: versions.serviceTokenGenerator) { - exclude group: 'io.reactivex', module: 'io.reactivex' - exclude group: 'io.reactivex', module: 'rxnetty' - exclude group: 'io.reactivex', module: 'rxnetty-contexts' - exclude group: 'io.reactivex', module: 'rxnetty-servo' - exclude group: 'org.bouncycastle' - } - - implementation group: 'junit', name: 'junit', version: versions.junit - implementation group: 'org.pitest', name: 'pitest', version: versions.pitest - implementation group:'info.solidsoft.gradle.pitest', name: 'gradle-pitest-plugin', version: versions.gradlePitest - implementation group:'org.codehaus.sonar-plugins', name:'sonar-pitest-plugin', version: versions.sonarPitest - implementation group: 'org.apache.httpcomponents', name: 'httpclient', version: versions.httpComponents - implementation (group: 'com.launchdarkly', name: 'launchdarkly-java-server-sdk', version: '7.4.0') - implementation group: 'net.logstash.logback', name: 'logstash-logback-encoder', version:'7.4' - - annotationProcessor("org.projectlombok:lombok:${versions.lombok}") - annotationProcessor "org.springframework.boot:spring-boot-configuration-processor" - implementation group: 'org.projectlombok', name: 'lombok', version: versions.lombok - implementation("org.springframework.boot:spring-boot-starter-validation") - testImplementation group: 'com.github.tomakehurst', name:'wiremock', version: versions.wiremockVersion - testImplementation group: 'io.rest-assured', name: 'rest-assured', version: versions.restAssured - testImplementation group: 'io.rest-assured', name: 'json-path', version: versions.restAssured - testImplementation group: 'io.rest-assured', name: 'xml-path', version: versions.restAssured - testImplementation(group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: versions.springBoot) { - exclude(module: 'commons-logging') - } - testImplementation('org.springframework.security:spring-security-test') - runtimeOnly('org.springframework.boot:spring-boot-devtools') - - runtimeOnly("org.springframework.boot:spring-boot-properties-migrator") - - contractTestImplementation group: 'org.apache.httpcomponents', name: 'httpclient', version: versions.httpComponents - contractTestImplementation group: 'au.com.dius.pact.consumer', name: 'junit5', version: versions.pact_version - contractTestImplementation group: 'au.com.dius.pact.consumer', name: 'java8', version: versions.pact_version - contractTestRuntime group: 'au.com.dius.pact.consumer', name: 'junit5', version: versions.pact_version - contractTestRuntime group: 'au.com.dius.pact.consumer', name: 'java8', version: versions.pact_version - - contractTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test' - contractTestImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2") - contractTestRuntime("org.junit.jupiter:junit-jupiter-engine:5.9.2") - contractTestImplementation('org.junit.jupiter:junit-jupiter-api:5.10.2') - - contractTestImplementation sourceSets.main.runtimeClasspath - contractTestImplementation sourceSets.test.runtimeClasspath - - functionalTestImplementation sourceSets.main.runtimeClasspath - functionalTestImplementation sourceSets.test.runtimeClasspath - smokeTestImplementation sourceSets.main.runtimeClasspath - smokeTestImplementation sourceSets.test.runtimeClasspath - - testImplementation 'com.github.hmcts:fortify-client:1.2.2:all' - testImplementation group: 'io.rest-assured', name: 'rest-assured', version: versions.restAssured - testImplementation group: 'io.rest-assured', name: 'json-path', version: versions.restAssured - testImplementation group: 'io.rest-assured', name: 'xml-path', version: versions.restAssured - testImplementation group: 'org.mockito', name: 'mockito-inline', version: '5.2.0' -} - -task fortifyScan(type: JavaExec) { - mainClass = "uk.gov.hmcts.fortifyclient.FortifyClientMainApp" - classpath += sourceSets.test.runtimeClasspath - jvmArgs = ['--add-opens=java.base/java.lang.reflect=ALL-UNNAMED'] -} -processContractTestResources { - duplicatesStrategy = DuplicatesStrategy.INCLUDE -} -processFunctionalTestResources { - duplicatesStrategy = DuplicatesStrategy.INCLUDE -} -processIntegrationTestResources { - duplicatesStrategy = DuplicatesStrategy.INCLUDE -} - + annotationProcessor group: 'org.projectlombok', name: 'lombok', version: versions.lombok + annotationProcessor group: 'org.springframework.boot', name: 'spring-boot-configuration-processor' + + runtimeOnly group: 'org.springframework.boot', name: 'spring-boot-devtools' + runtimeOnly group: 'org.springframework.boot', name: 'spring-boot-properties-migrator' + + implementation group: 'org.projectlombok', name: 'lombok', version: versions.lombok + implementation group: 'commons-beanutils', name: 'commons-beanutils', version: '1.9.4' + implementation group: 'org.springframework.security', name: 'spring-security-rsa', version: '1.0.12.RELEASE' + implementation group: 'org.springframework.security', name: 'spring-security-crypto', version: '5.7.11' + implementation group: 'org.springframework.boot', name: 'spring-boot-starter-actuator' + implementation group: 'org.bouncycastle', name: 'bcprov-jdk18on', version: '1.73' + implementation group: 'com.github.hmcts', name:'ccd-case-document-am-client', version: versions.ccdCaseDocumentAmClient + implementation group: 'org.apache.logging.log4j', name: 'log4j-to-slf4j', version: versions.apacheLogging + implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: versions.apacheLogging + implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: versions.springBoot + implementation group: 'commons-io', name: 'commons-io', version: '2.7' + implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.9' + implementation group: 'commons-fileupload', name: 'commons-fileupload', version: '1.5' + implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-netflix-ribbon', version: '2.2.10.RELEASE' + implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-openfeign', version: '3.1.6' + implementation group: 'io.github.openfeign', name: 'feign-httpclient', version: '10.2.0' + implementation group: 'org.springframework.hateoas', name: 'spring-hateoas', version: '1.5.5' + implementation group: 'com.github.hmcts.java-logging', name: 'logging', version: '6.1.6' + implementation group: 'com.github.hmcts.java-logging', name: 'logging-appinsights', version: '6.1.6' + implementation group: 'org.springframework', name: 'spring-context-support', version: '5.3.27' + implementation group: 'com.github.hmcts', name: 'service-auth-provider-java-client', version: versions.serviceTokenGenerator + implementation group: 'org.apache.httpcomponents', name: 'httpclient', version: versions.httpComponents + implementation group: 'com.google.guava', name: 'guava', version: '33.4.6-jre' + implementation group: 'com.launchdarkly', name: 'launchdarkly-java-server-sdk', version: '7.4.0' + implementation group: 'net.logstash.logback', name: 'logstash-logback-encoder', version:'7.4' + implementation group: 'org.springframework.boot', name: 'spring-boot-starter-validation' + + testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.9.0' + + testImplementation group: 'org.springframework.cloud', name: 'spring-cloud-contract-wiremock', version: '3.1.1' + testImplementation group: 'com.github.tomakehurst', name:'wiremock', version: '2.27.2' + testImplementation group: 'io.rest-assured', name: 'rest-assured', version: versions.restAssured + testImplementation group: 'io.rest-assured', name: 'json-path', version: versions.restAssured + testImplementation group: 'io.rest-assured', name: 'xml-path', version: versions.restAssured + testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: versions.springBoot + testImplementation group: 'org.springframework.security', name: 'spring-security-test' + testImplementation group: 'com.github.hmcts', name: 'fortify-client', version: '1.4.8', classifier: 'all' + testImplementation group: 'org.mockito', name: 'mockito-inline', version: '5.2.0' + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.9.0' + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.9.0' + + smokeTestImplementation sourceSets.main.runtimeClasspath + smokeTestImplementation sourceSets.test.runtimeClasspath + + contractTestRuntime group: 'au.com.dius.pact.consumer', name: 'java8', version: versions.pact_version + contractTestRuntime group: 'au.com.dius.pact.consumer', name: 'junit5', version: versions.pact_version + contractTestRuntime group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.9.0' + + contractTestImplementation group: 'org.apache.httpcomponents', name: 'httpclient', version: versions.httpComponents + contractTestImplementation group: 'au.com.dius.pact.consumer', name: 'junit5', version: versions.pact_version + contractTestImplementation group: 'au.com.dius.pact.consumer', name: 'java8', version: versions.pact_version + contractTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test' + contractTestImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.9.0' + contractTestImplementation sourceSets.main.runtimeClasspath + contractTestImplementation sourceSets.test.runtimeClasspath -dependencyManagement { - dependencies { - dependency group: 'commons-beanutils', name: 'commons-beanutils', version: '1.9.4' - - dependencySet( - group: 'com.fasterxml.jackson.core', - version: versions.jacksonDatabind - ) { - entry 'jackson-core' - entry 'jackson-databind' - } - - dependency group: 'com.netflix.servo',name: 'servo-core', version: '0.13.2' - - // CVE-2021-30640, CVE-2021-33037 - dependencySet(group: 'org.apache.tomcat.embed', version: versions.tomcat) { - entry 'tomcat-embed-core' - entry 'tomcat-embed-websocket' - entry 'tomcat-embed-el' - } - - // CVE-2021-28170 - dependency group: 'org.glassfish', name: 'jakarta.el', version: '4.0.2' - - //CVE-2022-45688 - dependencySet(group: 'org.json', version: '20230227') { - entry 'json' - } - //CVE-2023-2976 - dependency group: 'com.google.guava', name: 'guava', version: '32.1.2-jre' + functionalTestAnnotationProcessor group: 'org.projectlombok', name: 'lombok', version: versions.lombok - //CVE-2022-1471 - dependencySet(group: 'org.yaml', version: '2.2') { - entry 'snakeyaml' - } - dependency group: 'org.springframework.security', name: 'spring-security-crypto', version: '5.7.5' + functionalTestImplementation group: 'com.github.hmcts', name: 'service-auth-provider-java-client', version: versions.serviceTokenGenerator + functionalTestImplementation group: 'com.github.hmcts', name:'ccd-case-document-am-client', version: versions.ccdCaseDocumentAmClient + functionalTestImplementation sourceSets.main.runtimeClasspath + functionalTestImplementation sourceSets.test.runtimeClasspath + functionalTestImplementation sourceSets.test.output + integrationTestAnnotationProcessor group: 'org.projectlombok', name: 'lombok', version: versions.lombok - } + integrationTestImplementation group: 'com.mashape.unirest', name: 'unirest-java', version: '1.4.9' + integrationTestImplementation group: 'com.nimbusds', name: 'nimbus-jose-jwt', version: '5.1' + integrationTestImplementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.9' + integrationTestImplementation group: 'org.apache.pdfbox', name: 'pdfbox', version: '2.0.24' + integrationTestImplementation group: 'org.projectlombok', name: 'lombok', version: versions.lombok + integrationTestImplementation group: 'org.skyscreamer', name:'jsonassert', version: '1.2.3' + integrationTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test' + integrationTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-web' + integrationTestImplementation group: 'com.github.hmcts', name: 'service-auth-provider-java-client', version: versions.serviceTokenGenerator + integrationTestImplementation sourceSets.test.output } -sourceSets { - integrationTest { - java { - compileClasspath += main.output + test.output - runtimeClasspath += main.output + test.output - srcDir('src/integrationTest/java') - } - resources { - srcDir('src/integrationTest/resources') - } - } +tasks.register('fortifyScan', JavaExec) { + mainClass = "uk.gov.hmcts.fortifyclient.FortifyClientMainApp" + classpath += sourceSets.test.runtimeClasspath + jvmArgs = ['--add-opens=java.base/java.lang.reflect=ALL-UNNAMED'] +} - functionalTest { - java { - compileClasspath += main.output + test.output - runtimeClasspath += main.output + test.output - srcDir('src/functionalTest/java') - } - resources { - srcDir('src/functionalTest/resources') - } - } +processContractTestResources { + duplicatesStrategy = DuplicatesStrategy.INCLUDE +} - contractTest { - java { - compileClasspath += main.output - runtimeClasspath += main.output - srcDir file('src/contractTest/java') - } - resources.srcDir file('src/contractTest/resources') - } - smokeTest { - java { - compileClasspath += main.output + test.output - runtimeClasspath += main.output + test.output - srcDir file('src/smokeTest/java') - } - resources.srcDir file('src/smokeTest/resources') - } +processFunctionalTestResources { + duplicatesStrategy = DuplicatesStrategy.INCLUDE } -dependencies { - integrationTestImplementation(sourceSets.test.output) - integrationTestAnnotationProcessor group: 'org.projectlombok', name: 'lombok', version: versions.lombok - functionalTestImplementation(sourceSets.test.output) - functionalTestAnnotationProcessor group: 'org.projectlombok', name: 'lombok', version: versions.lombok +processIntegrationTestResources { + duplicatesStrategy = DuplicatesStrategy.INCLUDE } -tasks.withType(Test) { - useJUnitPlatform() +tasks.withType(Test).configureEach { + useJUnitPlatform() } -task smoke(type: Test, description: 'Runs the smoke tests.', group: 'Verification') { - setTestClassesDirs(sourceSets.smokeTest.output.classesDirs) - setClasspath(sourceSets.smokeTest.runtimeClasspath) +tasks.register('smoke', Test) { + description = 'Runs the smoke tests.' + group = 'Verification' + setTestClassesDirs(sourceSets.smokeTest.output.classesDirs) + setClasspath(sourceSets.smokeTest.runtimeClasspath) } task bootRunAat(type: BootRun, description: 'Runs the app using AAT config', dependsOn: 'classes', group: 'Application') { - mainClass = 'uk.gov.hmcts.reform.prl.documentgenerator.DocumentGeneratorApplication' - doFirst() { - classpath = sourceSets.main.runtimeClasspath - systemProperties = System.properties - systemProperty 'spring.profiles.active', 'aat' - } + mainClass = 'uk.gov.hmcts.reform.prl.documentgenerator.DocumentGeneratorApplication' + doFirst() { + classpath = sourceSets.main.runtimeClasspath + systemProperties = System.properties + systemProperty 'spring.profiles.active', 'aat' + } } -task functional(type: Test, description: 'Runs the functional tests.', group: 'Verification') { +tasks.register('functional', Test) { + description = 'Runs the functional tests.' + group = 'Verification' setTestClassesDirs(sourceSets.functionalTest.output.classesDirs) setClasspath(sourceSets.functionalTest.runtimeClasspath) - - finalizedBy aggregate } -task integration(type: Test, description: 'Runs the integration tests.', group: 'Verification') { - setTestClassesDirs(sourceSets.integrationTest.output.classesDirs) - setClasspath(sourceSets.integrationTest.runtimeClasspath) - - finalizedBy aggregate +tasks.register('integration', Test) { + description = 'Runs the integration tests.' + group = 'Verification' + setTestClassesDirs(sourceSets.integrationTest.output.classesDirs) + setClasspath(sourceSets.integrationTest.runtimeClasspath) } -task contract(type: Test) { +tasks.register('contract', Test) { + description = 'Runs the contract tests.' group = 'Delivery pipeline' - description = 'Runs the consumer Pact tests' testClassesDirs = sourceSets.contractTest.output.classesDirs classpath = sourceSets.contractTest.runtimeClasspath systemProperty 'pact.rootDir', "pacts" } - -task printVersion { - doLast { - print project.version - } -} - -pitest { - targetClasses = ['uk.gov.hmcts.reform.prl.documentgenerator.*'] - excludedClasses = ['uk.gov.hmcts.reform.prl.documentgenerator.config.*', - 'uk.gov.hmcts.reform.prl.documentgenerator.domain.*', - 'uk.gov.hmcts.reform.prl.documentgenerator.exception.*'] - threads = 10 - outputFormats = ['XML', 'HTML'] - timestampedReports = true - mutationThreshold = 80 -} - -task developAddRelaseSuffix() { - version = "${version}-SNAPSHOT" +jacoco { + toolVersion = "0.8.12" } jacocoTestReport { - executionData(test) + executionData(test) - reports { - xml.getRequired().set(true) - html.getRequired().set(true) - xml.destination file("${buildDir}/reports/jacoco/test/jacocoTestReport.xml") - } + reports { + xml.getRequired().set(true) + html.getRequired().set(true) + xml.outputLocation = file("${layout.buildDirectory.get()}/reports/jacoco/test/jacocoTestReport.xml") + } } jacocoTestCoverageVerification { From c68d01251e7a94edc3994a4b69b7c3b700a35447 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Tue, 1 Apr 2025 10:16:54 +0100 Subject: [PATCH 39/51] Remove pitest from sonar --- build.gradle | 2 -- 1 file changed, 2 deletions(-) diff --git a/build.gradle b/build.gradle index e7272940..e1a72607 100644 --- a/build.gradle +++ b/build.gradle @@ -282,8 +282,6 @@ sonarqube { property "sonar.projectKey", "prl-dgs-api" property "sonar.exclusions", sonarExclusions.join(", ") property "sonar.coverage.jacoco.xmlReportPaths", "${jacocoTestReport.reports.xml.destination.path}" - property "sonar.pitest.mode", "reuseReport" - property "sonar.pitest.reportsDirectory", "build/reports/pitest" } } From 19971ad49a4f7f17a0f376d9b2a13066e7432f83 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Tue, 1 Apr 2025 10:17:30 +0100 Subject: [PATCH 40/51] Reorder pact config to fix pact tests --- build.gradle | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index e1a72607..94ea5e33 100644 --- a/build.gradle +++ b/build.gradle @@ -296,18 +296,13 @@ project.ext { pactVersion = getCheckedOutGitCommitHash() } -task runAndPublishConsumerPactTests(type: Test){ +tasks.register('runAndPublishConsumerPactTests', Test) { logger.lifecycle("Runs pact Tests") testClassesDirs = sourceSets.contractTest.output.classesDirs classpath = sourceSets.contractTest.runtimeClasspath - } -runAndPublishConsumerPactTests.dependsOn contract - -runAndPublishConsumerPactTests.finalizedBy pactPublish - -def getCheckedOutGitCommitHash() { +static def getCheckedOutGitCommitHash() { 'git rev-parse --verify --short HEAD'.execute().text.trim() } @@ -319,3 +314,6 @@ pact { version = project.pactVersion } } + +runAndPublishConsumerPactTests.dependsOn contract +runAndPublishConsumerPactTests.finalizedBy pactPublish From 7acab79cc965d1630a20ee6887e6472b32f49c23 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Tue, 1 Apr 2025 10:30:21 +0100 Subject: [PATCH 41/51] Update suppressions --- config/owasp/suppressions.xml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/config/owasp/suppressions.xml b/config/owasp/suppressions.xml index 00d9a5be..585eabee 100644 --- a/config/owasp/suppressions.xml +++ b/config/owasp/suppressions.xml @@ -11,4 +11,26 @@ CVE-2023-20883 CVE-2023-34055 + + + ^pkg:maven/com\.fasterxml\.jackson\.core/jackson-databind@.*$ + CVE-2023-35116 + + + + ^pkg:maven/ch\.qos\.logback/logback-classic@.*$ + CVE-2023-6378 + CVE-2023-6481 + + + + ^pkg:maven/org\.yaml/snakeyaml@.*$ + CVE-2022-1471 + CVE-2022-25857 + CVE-2022-38749 + CVE-2022-38751 + CVE-2022-38752 + CVE-2022-41854 + CVE-2022-38750 + From b97bd16fe0610c82a064f7a740eeb8b81bda513f Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Tue, 1 Apr 2025 10:41:28 +0100 Subject: [PATCH 42/51] Fix sonar xml output location --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 94ea5e33..46f521b8 100644 --- a/build.gradle +++ b/build.gradle @@ -281,7 +281,7 @@ sonarqube { property "sonar.projectName", "Family Private Law :: prl-dgs" property "sonar.projectKey", "prl-dgs-api" property "sonar.exclusions", sonarExclusions.join(", ") - property "sonar.coverage.jacoco.xmlReportPaths", "${jacocoTestReport.reports.xml.destination.path}" + property "sonar.coverage.jacoco.xmlReportPaths", "${jacocoTestReport.reports.xml.outputLocation.get()}" } } From 3f60c718554aaef775cc54a5279e7a025a129f3f Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Tue, 1 Apr 2025 10:47:21 +0100 Subject: [PATCH 43/51] Update suppressions --- config/owasp/suppressions.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/config/owasp/suppressions.xml b/config/owasp/suppressions.xml index 585eabee..fe989add 100644 --- a/config/owasp/suppressions.xml +++ b/config/owasp/suppressions.xml @@ -17,8 +17,7 @@ CVE-2023-35116 - - ^pkg:maven/ch\.qos\.logback/logback-classic@.*$ + Vulnerability in logback> CVE-2023-6378 CVE-2023-6481 From 92601ae3b6bcfaf61ddea3a7544e68939884eabc Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Tue, 1 Apr 2025 10:56:23 +0100 Subject: [PATCH 44/51] Move junit version to versions block --- build.gradle | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 46f521b8..cc16a134 100644 --- a/build.gradle +++ b/build.gradle @@ -91,6 +91,7 @@ def versions = [ apacheLogging: '2.20.0', ccdCaseDocumentAmClient: '1.7.2', httpComponents: '4.5.13', + junit5: '5.9.0', lombok: '1.18.36', pact_version: '4.1.11', restAssured: '5.5.1', @@ -132,7 +133,7 @@ dependencies { implementation group: 'net.logstash.logback', name: 'logstash-logback-encoder', version:'7.4' implementation group: 'org.springframework.boot', name: 'spring-boot-starter-validation' - testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.9.0' + testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: versions.junit5 testImplementation group: 'org.springframework.cloud', name: 'spring-cloud-contract-wiremock', version: '3.1.1' testImplementation group: 'com.github.tomakehurst', name:'wiremock', version: '2.27.2' @@ -143,21 +144,21 @@ dependencies { testImplementation group: 'org.springframework.security', name: 'spring-security-test' testImplementation group: 'com.github.hmcts', name: 'fortify-client', version: '1.4.8', classifier: 'all' testImplementation group: 'org.mockito', name: 'mockito-inline', version: '5.2.0' - testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.9.0' - testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.9.0' + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: versions.junit5 + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: versions.junit5 smokeTestImplementation sourceSets.main.runtimeClasspath smokeTestImplementation sourceSets.test.runtimeClasspath contractTestRuntime group: 'au.com.dius.pact.consumer', name: 'java8', version: versions.pact_version contractTestRuntime group: 'au.com.dius.pact.consumer', name: 'junit5', version: versions.pact_version - contractTestRuntime group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.9.0' + contractTestRuntime group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: versions.junit5 contractTestImplementation group: 'org.apache.httpcomponents', name: 'httpclient', version: versions.httpComponents contractTestImplementation group: 'au.com.dius.pact.consumer', name: 'junit5', version: versions.pact_version contractTestImplementation group: 'au.com.dius.pact.consumer', name: 'java8', version: versions.pact_version contractTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test' - contractTestImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.9.0' + contractTestImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: versions.junit5 contractTestImplementation sourceSets.main.runtimeClasspath contractTestImplementation sourceSets.test.runtimeClasspath From feaabc21df575eba2f8272ce0cb645ab35b123ed Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Tue, 1 Apr 2025 10:56:43 +0100 Subject: [PATCH 45/51] Bump chart java version --- charts/prl-dgs/Chart.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/charts/prl-dgs/Chart.yaml b/charts/prl-dgs/Chart.yaml index 7904c76b..dfee387a 100644 --- a/charts/prl-dgs/Chart.yaml +++ b/charts/prl-dgs/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 appVersion: "1.0" description: Family Private Law - Document Generator Service name: prl-dgs -version: 0.0.51 +version: 0.0.52 dependencies: - name: java - version: 5.2.0 - repository: "https://hmctspublic.azurecr.io/helm/v1/repo/" + version: 5.3.0 + repository: 'oci://hmctspublic.azurecr.io/helm' From 362da4d85ac888e5b86b0e94d2f765f9cba8a58f Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Wed, 2 Apr 2025 14:32:50 +0100 Subject: [PATCH 46/51] Update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 84aba026..e8119736 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Private Law Document Generator +# Private Law Document Generator Service [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Documentation](https://img.shields.io/static/v1?label=Documentation&message=DGS&color=informational&logo=confluence)](https://tools.hmcts.net/confluence/display/PL/PDF+document+generator) From 20f02f4be5f49f1d4259dd725aa07903e905a817 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Wed, 2 Apr 2025 14:34:19 +0100 Subject: [PATCH 47/51] Move pacts to the contractTests resources folder --- .../reform/prl/documentgenerator/CdamApiConsumerTest.java | 2 +- .../documentgenerator/PdfGenerationServiceConsumerTest.java | 2 +- .../resources/pacts}/prl-dgs-api-CCD_CASE_DOCS_AM_API.json | 4 ++-- ...GeneratorClient-rpePdfService_PDFGenerationEndpointV2.json | 0 4 files changed, 4 insertions(+), 4 deletions(-) rename {pacts => src/contractTest/resources/pacts}/prl-dgs-api-CCD_CASE_DOCS_AM_API.json (90%) rename {pacts => src/contractTest/resources/pacts}/prl_documentGeneratorClient-rpePdfService_PDFGenerationEndpointV2.json (100%) diff --git a/src/contractTest/java/uk/gov/hmcts/reform/prl/documentgenerator/CdamApiConsumerTest.java b/src/contractTest/java/uk/gov/hmcts/reform/prl/documentgenerator/CdamApiConsumerTest.java index e55b0456..9f62f0a2 100644 --- a/src/contractTest/java/uk/gov/hmcts/reform/prl/documentgenerator/CdamApiConsumerTest.java +++ b/src/contractTest/java/uk/gov/hmcts/reform/prl/documentgenerator/CdamApiConsumerTest.java @@ -28,7 +28,7 @@ @ExtendWith(SpringExtension.class) @TestInstance(TestInstance.Lifecycle.PER_CLASS) @PactTestFor(providerName = "CCD_CASE_DOCS_AM_API", port = "5170") -@PactFolder("pacts") +@PactFolder("src/contractTest/resources/pacts") @SpringBootTest("CCD_CASE_DOCS_AM_API = http://localhost:5170") public class CdamApiConsumerTest { diff --git a/src/contractTest/java/uk/gov/hmcts/reform/prl/documentgenerator/PdfGenerationServiceConsumerTest.java b/src/contractTest/java/uk/gov/hmcts/reform/prl/documentgenerator/PdfGenerationServiceConsumerTest.java index 53392cd4..625c65d5 100644 --- a/src/contractTest/java/uk/gov/hmcts/reform/prl/documentgenerator/PdfGenerationServiceConsumerTest.java +++ b/src/contractTest/java/uk/gov/hmcts/reform/prl/documentgenerator/PdfGenerationServiceConsumerTest.java @@ -36,7 +36,7 @@ @ExtendWith(SpringExtension.class) @TestInstance(TestInstance.Lifecycle.PER_CLASS) @PactTestFor(providerName = "rpePdfService_PDFGenerationEndpointV2", port = "8891") -@PactFolder("pacts") +@PactFolder("src/contractTest/resources/pacts") @SpringBootTest("service.pdf-service.uri = http://localhost:8891/pdfs") public class PdfGenerationServiceConsumerTest { diff --git a/pacts/prl-dgs-api-CCD_CASE_DOCS_AM_API.json b/src/contractTest/resources/pacts/prl-dgs-api-CCD_CASE_DOCS_AM_API.json similarity index 90% rename from pacts/prl-dgs-api-CCD_CASE_DOCS_AM_API.json rename to src/contractTest/resources/pacts/prl-dgs-api-CCD_CASE_DOCS_AM_API.json index 02b8279f..14603080 100644 --- a/pacts/prl-dgs-api-CCD_CASE_DOCS_AM_API.json +++ b/src/contractTest/resources/pacts/prl-dgs-api-CCD_CASE_DOCS_AM_API.json @@ -19,7 +19,7 @@ "response": { "status": 200, "headers": { - "Content-Type": "application/vnd\u46AFuk\u1870gov\u6994hmcts\u26F2dm\uE7E0document\u30E7v1haljson;charset=UTF-8" + "Content-Type": "application/vnd\u5EE3uk\u2537gov\u7C40hmcts\uB54Edm\u051Ddocument\uC5E9v1haljson;charset=UTF-8" }, "matchingRules": { "header": { @@ -53,7 +53,7 @@ "response": { "status": 500, "headers": { - "Content-Type": "application/vnd\u5C61uk\uE209gov\uA235hmcts\uC39Bdm\u1943document\u8E03v1haljson;charset=UTF-8" + "Content-Type": "application/vnd\u3211uk\uD708gov\uA009hmcts\u7D48dm\u6DFDdocument\u8E10v11haljson;charset=UTF-8" }, "matchingRules": { "header": { diff --git a/pacts/prl_documentGeneratorClient-rpePdfService_PDFGenerationEndpointV2.json b/src/contractTest/resources/pacts/prl_documentGeneratorClient-rpePdfService_PDFGenerationEndpointV2.json similarity index 100% rename from pacts/prl_documentGeneratorClient-rpePdfService_PDFGenerationEndpointV2.json rename to src/contractTest/resources/pacts/prl_documentGeneratorClient-rpePdfService_PDFGenerationEndpointV2.json From 5761435b8fab08f306078ba3b41b8e942bf2615c Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Wed, 2 Apr 2025 15:04:54 +0100 Subject: [PATCH 48/51] Tidy pact tasks and update tasks to use new gradle syntax --- build.gradle | 86 +++++++++++++++++++++++++--------------------------- 1 file changed, 41 insertions(+), 45 deletions(-) diff --git a/build.gradle b/build.gradle index cc16a134..a827ab3d 100644 --- a/build.gradle +++ b/build.gradle @@ -184,24 +184,24 @@ dependencies { integrationTestImplementation sourceSets.test.output } +tasks.register('bootRunAat', BootRun) { + dependsOn 'classes' + description = 'Runs the app using AAT config.' + group = 'Application' + mainClass = 'uk.gov.hmcts.reform.prl.documentgenerator.DocumentGeneratorApplication' + doFirst() { + classpath = sourceSets.main.runtimeClasspath + systemProperties = System.properties as Map + systemProperty 'spring.profiles.active', 'aat' + } +} + tasks.register('fortifyScan', JavaExec) { mainClass = "uk.gov.hmcts.fortifyclient.FortifyClientMainApp" classpath += sourceSets.test.runtimeClasspath jvmArgs = ['--add-opens=java.base/java.lang.reflect=ALL-UNNAMED'] } -processContractTestResources { - duplicatesStrategy = DuplicatesStrategy.INCLUDE -} - -processFunctionalTestResources { - duplicatesStrategy = DuplicatesStrategy.INCLUDE -} - -processIntegrationTestResources { - duplicatesStrategy = DuplicatesStrategy.INCLUDE -} - tasks.withType(Test).configureEach { useJUnitPlatform() } @@ -213,15 +213,6 @@ tasks.register('smoke', Test) { setClasspath(sourceSets.smokeTest.runtimeClasspath) } -task bootRunAat(type: BootRun, description: 'Runs the app using AAT config', dependsOn: 'classes', group: 'Application') { - mainClass = 'uk.gov.hmcts.reform.prl.documentgenerator.DocumentGeneratorApplication' - doFirst() { - classpath = sourceSets.main.runtimeClasspath - systemProperties = System.properties - systemProperty 'spring.profiles.active', 'aat' - } -} - tasks.register('functional', Test) { description = 'Runs the functional tests.' group = 'Verification' @@ -238,10 +229,31 @@ tasks.register('integration', Test) { tasks.register('contract', Test) { description = 'Runs the contract tests.' - group = 'Delivery pipeline' + group = 'Verification' testClassesDirs = sourceSets.contractTest.output.classesDirs classpath = sourceSets.contractTest.runtimeClasspath - systemProperty 'pact.rootDir', "pacts" + systemProperty 'pact.rootDir', "src/contractTest/resources/pacts" +} + +tasks.register('runAndPublishConsumerPactTests', Test) { + description = 'Runs the contract tests and publishes pacts to the broker.' + logger.lifecycle("Runs the pact Tests") + dependsOn 'contract' + finalizedBy 'pactPublish' + testClassesDirs = sourceSets.contractTest.output.classesDirs + classpath = sourceSets.contractTest.runtimeClasspath +} + +processIntegrationTestResources { + duplicatesStrategy = DuplicatesStrategy.INCLUDE +} + +processFunctionalTestResources { + duplicatesStrategy = DuplicatesStrategy.INCLUDE +} + +processContractTestResources { + duplicatesStrategy = DuplicatesStrategy.INCLUDE } jacoco { @@ -268,8 +280,6 @@ jacocoTestCoverageVerification { } } -project.tasks['sonarqube'].dependsOn jacocoTestReport - def sonarExclusions = [ '**uk/gov/hmcts/reform/prl/documentgenerator/domain/**/*', '**uk/gov/hmcts/reform/prl/documentgenerator/exception/*', @@ -277,6 +287,8 @@ def sonarExclusions = [ '**uk/gov/hmcts/reform/prl/documentgenerator/DocumentGeneratorApplication.java' ] +tasks.sonarqube.dependsOn(jacocoTestReport) + sonarqube { properties { property "sonar.projectName", "Family Private Law :: prl-dgs" @@ -286,35 +298,19 @@ sonarqube { } } -def debug = System.getProperty("debug") -run { - if (debug == 'true') { - jvmArgs = ['-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5010'] - } +static def getCheckedOutGitCommitHash() { + 'git rev-parse --verify --short HEAD'.execute().text.trim() } project.ext { - pactVersion = getCheckedOutGitCommitHash() -} - -tasks.register('runAndPublishConsumerPactTests', Test) { - logger.lifecycle("Runs pact Tests") - testClassesDirs = sourceSets.contractTest.output.classesDirs - classpath = sourceSets.contractTest.runtimeClasspath -} - -static def getCheckedOutGitCommitHash() { - 'git rev-parse --verify --short HEAD'.execute().text.trim() + pactVersion = getCheckedOutGitCommitHash() } pact { publish { - pactDirectory = 'pacts' + pactDirectory = 'src/contractTest/resources/pacts' pactBrokerUrl = System.getenv("PACT_BROKER_FULL_URL") ?: 'http://localhost:80' tags = [System.getenv("PACT_BRANCH_NAME") ?:'Dev'] version = project.pactVersion } } - -runAndPublishConsumerPactTests.dependsOn contract -runAndPublishConsumerPactTests.finalizedBy pactPublish From a2bfd2aab21ce05dc2f5ca3ec52206949e3b1d59 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Wed, 2 Apr 2025 15:21:51 +0100 Subject: [PATCH 49/51] Add jitpack --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a827ab3d..e157b5d8 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,7 @@ java { repositories { mavenLocal() mavenCentral() - maven { url = 'https://jitpack.io' } + jitpack { url = 'https://jitpack.io' } } def configureSourceSet(String name) { From 2692d12d1de623d267e67a3771b80bd0b137b91b Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Wed, 2 Apr 2025 15:22:15 +0100 Subject: [PATCH 50/51] Update pact encoding --- .../resources/pacts/prl-dgs-api-CCD_CASE_DOCS_AM_API.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/contractTest/resources/pacts/prl-dgs-api-CCD_CASE_DOCS_AM_API.json b/src/contractTest/resources/pacts/prl-dgs-api-CCD_CASE_DOCS_AM_API.json index 14603080..7a0782d2 100644 --- a/src/contractTest/resources/pacts/prl-dgs-api-CCD_CASE_DOCS_AM_API.json +++ b/src/contractTest/resources/pacts/prl-dgs-api-CCD_CASE_DOCS_AM_API.json @@ -19,7 +19,7 @@ "response": { "status": 200, "headers": { - "Content-Type": "application/vnd\u5EE3uk\u2537gov\u7C40hmcts\uB54Edm\u051Ddocument\uC5E9v1haljson;charset=UTF-8" + "Content-Type": "application/vnd\uB91Auk\uDF6Dgov\u8E2Dhmcts\u9024dm\u8C79document\u32B4v11halljson;charset=UTF-8" }, "matchingRules": { "header": { @@ -53,7 +53,7 @@ "response": { "status": 500, "headers": { - "Content-Type": "application/vnd\u3211uk\uD708gov\uA009hmcts\u7D48dm\u6DFDdocument\u8E10v11haljson;charset=UTF-8" + "Content-Type": "application/vnd\u054Auk\uD76Cgov\uCC92hmcts\u28DDdm\uF2D3document\u9D3Ev1hallllljson;charset=UTF-8" }, "matchingRules": { "header": { From 382c1b04d043d43f67eb893c5d9f6db103397142 Mon Sep 17 00:00:00 2001 From: Michael1142 Date: Wed, 2 Apr 2025 15:24:01 +0100 Subject: [PATCH 51/51] Add jitpack --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e157b5d8..a827ab3d 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,7 @@ java { repositories { mavenLocal() mavenCentral() - jitpack { url = 'https://jitpack.io' } + maven { url = 'https://jitpack.io' } } def configureSourceSet(String name) {