1
0
mirror of https://github.com/pcvolkmer/etl-processor.git synced 2025-07-01 14:12:55 +00:00

fix: * Status prüfung gIcs überarbeitet.* Tests für GicsConsentService implementiert. * Überschreiben des MTB-File Consent, falls gICS aktiviert ist

This commit is contained in:
Jakub Lidke
2025-05-05 12:28:54 +02:00
parent 2a28a4b3d4
commit 542dc61811
6 changed files with 256 additions and 85 deletions

View File

@ -59,40 +59,3 @@ services:
# POSTGRES_DB: dev # POSTGRES_DB: dev
# POSTGRES_USER: dev # POSTGRES_USER: dev
# POSTGRES_PASSWORD: dev # POSTGRES_PASSWORD: dev
mysql:
image: mysql:8
container_name: gics-mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: root
TZ: Europe/Berlin
ports:
- "3306:3306"
volumes:
- ./sqls:/docker-entrypoint-initdb.d
command: --max_allowed_packet=20M --default-time-zone=Europe/Berlin
gics:
image: registry.diz.uni-marburg.de/ths/gics:2023.1.3
container_name: gics-wildfly
restart: unless-stopped
ports:
- "8090:8080"
- "127.0.0.1:9992:9990"
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
- mysql
consent-data-loader:
image: confluentinc/cp-kafkacat:7.1.12
entrypoint: ["/bin/bash", "-c"]
command: >
"kafkacat -b kafka:19092 -K: -t consent-json -P -l /data/consent-data.ndjson"
volumes:
- ./consent-data.ndjson:/data/consent-data.ndjson:ro
depends_on:
kafka:
condition: service_healthy

View File

@ -1,12 +1,14 @@
package dev.dnpm.etl.processor.consent; package dev.dnpm.etl.processor.consent;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.DataFormatException;
import dev.dnpm.etl.processor.config.AppFhirConfig; import dev.dnpm.etl.processor.config.AppFhirConfig;
import dev.dnpm.etl.processor.config.GIcsConfigProperties; import dev.dnpm.etl.processor.config.GIcsConfigProperties;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.OperationOutcome;
import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent; import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.StringType;
@ -31,7 +33,7 @@ public class GicsConsentService implements ICheckConsent {
private final GIcsConfigProperties gIcsConfigProperties; private final GIcsConfigProperties gIcsConfigProperties;
public final String IS_CONSENTED_PATH = "/ttp-fhir/fhir/gics/$isConsented"; public static final String IS_CONSENTED_ENDPOINT = "/$isConsented";
private final RetryTemplate retryTemplate; private final RetryTemplate retryTemplate;
private final RestTemplate restTemplate; private final RestTemplate restTemplate;
private final FhirContext fhirContext; private final FhirContext fhirContext;
@ -56,8 +58,7 @@ public class GicsConsentService implements ICheckConsent {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"gICS base URL is empty - should call gICS with false configuration."); "gICS base URL is empty - should call gICS with false configuration.");
} }
url = UriComponentsBuilder.fromHttpUrl(gIcsBaseUri) url = UriComponentsBuilder.fromHttpUrl(gIcsBaseUri).path(IS_CONSENTED_ENDPOINT)
.path(IS_CONSENTED_PATH)
.toUriString(); .toUriString();
} }
return url; return url;
@ -84,14 +85,20 @@ public class GicsConsentService implements ICheckConsent {
.setSystem(configProperties.getPersonIdentifierSystem()))); .setSystem(configProperties.getPersonIdentifierSystem())));
result.addParameter(new ParametersParameterComponent().setName("domain") result.addParameter(new ParametersParameterComponent().setName("domain")
.setValue(new StringType().setValue(configProperties.getConsentDomainName()))); .setValue(new StringType().setValue(configProperties.getConsentDomainName())));
result.addParameter(new ParametersParameterComponent().setName("policy") result.addParameter(new ParametersParameterComponent().setName("policy").setValue(
.setValue(new Coding().setCode(configProperties.getPolicyCode()) new Coding().setCode(configProperties.getPolicyCode())
.setSystem(configProperties.getPolicySystem()))); .setSystem(configProperties.getPolicySystem())));
/*
* is mandatory parameter, but we ignore it via additional configuration parameter
* 'ignoreVersionNumber'.
*/
result.addParameter(new ParametersParameterComponent().setName("version") result.addParameter(new ParametersParameterComponent().setName("version")
.setValue(new StringType().setValue(configProperties.getParameterVersion()))); .setValue(new StringType().setValue("1.1")));
/* add config parameter with: /* add config parameter with:
* ignoreVersionNumber -> true * ignoreVersionNumber -> true ->> Reason is we cannot know which policy version each patient
* has possibly signed or not, therefore we are happy with any version found.
* unknownStateIsConsideredAsDecline -> true * unknownStateIsConsideredAsDecline -> true
*/ */
var config = new ParametersParameterComponent().setName("config").addPart( var config = new ParametersParameterComponent().setName("config").addPart(
@ -110,9 +117,10 @@ public class GicsConsentService implements ICheckConsent {
HttpEntity<String> requestEntity = new HttpEntity<>(parameterAsXml, this.httpHeader); HttpEntity<String> requestEntity = new HttpEntity<>(parameterAsXml, this.httpHeader);
ResponseEntity<String> responseEntity; ResponseEntity<String> responseEntity;
try { try {
var url = getGicsUri();
responseEntity = retryTemplate.execute( responseEntity = retryTemplate.execute(
ctx -> restTemplate.exchange(getGicsUri(), HttpMethod.POST, requestEntity, ctx -> restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class));
String.class));
} catch (RestClientException e) { } catch (RestClientException e) {
var msg = String.format("Get consents status request failed reason: '%s", var msg = String.format("Get consents status request failed reason: '%s",
e.getMessage()); e.getMessage());
@ -123,8 +131,7 @@ public class GicsConsentService implements ICheckConsent {
var msg = String.format( var msg = String.format(
"Get consents status process has been terminated. termination reason: '%s", "Get consents status process has been terminated. termination reason: '%s",
terminatedRetryException.getMessage()); terminatedRetryException.getMessage());
log.error(msg log.error(msg);
);
return null; return null;
} }
@ -153,18 +160,31 @@ public class GicsConsentService implements ICheckConsent {
if (consentStatusResponse == null) { if (consentStatusResponse == null) {
return TtpConsentStatus.FAILED_TO_ASK; return TtpConsentStatus.FAILED_TO_ASK;
} }
var responseParameters = fhirContext.newJsonParser() try {
.parseResource(Parameters.class, consentStatusResponse); var response = fhirContext.newJsonParser().parseResource(consentStatusResponse);
var responseValue = responseParameters.getParameter("consented").getValue(); if (response instanceof Parameters responseParameters) {
var isConsented = responseValue.castToBoolean(responseValue);
if (!isConsented.hasValue()) { var responseValue = responseParameters.getParameter("consented").getValue();
return TtpConsentStatus.FAILED_TO_ASK; var isConsented = responseValue.castToBoolean(responseValue);
} if (!isConsented.hasValue()) {
if (isConsented.booleanValue()) { return TtpConsentStatus.FAILED_TO_ASK;
return TtpConsentStatus.CONSENTED; }
} else { if (isConsented.booleanValue()) {
return TtpConsentStatus.CONSENT_MISSING_OR_REJECTED; return TtpConsentStatus.CONSENTED;
} else {
return TtpConsentStatus.CONSENT_MISSING_OR_REJECTED;
}
} else if (response instanceof OperationOutcome outcome) {
log.error(
"failed to get consent status from ttp. probably configuration error. outcome: ",
fhirContext.newJsonParser().encodeToString(outcome));
}
} catch (DataFormatException dfe) {
log.error("failed to parse response to FHIR R4 resource.", dfe);
} }
return TtpConsentStatus.FAILED_TO_ASK;
} }
} }

View File

@ -92,8 +92,7 @@ data class GIcsConfigProperties(
/** /**
* Consent Policy which should be used for consent check * Consent Policy which should be used for consent check
*/ */
val policySystem: String = "urn:oid:2.16.840.1.113883.3.1937.777.24.5.3", val policySystem: String = "urn:oid:2.16.840.1.113883.3.1937.777.24.5.3"
val parameterVersion: String = "1.1"
) { ) {
companion object { companion object {
const val NAME = "app.consent.gics" const val NAME = "app.consent.gics"

View File

@ -45,20 +45,11 @@ class MtbFileRestController(
return ResponseEntity.ok("Test") return ResponseEntity.ok("Test")
} }
@PostMapping( consumes = [ MediaType.APPLICATION_JSON_VALUE ] ) @PostMapping(consumes = [MediaType.APPLICATION_JSON_VALUE])
fun mtbFile(@RequestBody mtbFile: MtbFile): ResponseEntity<Unit> { fun mtbFile(@RequestBody mtbFile: MtbFile): ResponseEntity<Unit> {
var ttpConsentStatus = constService.isConsented(mtbFile.patient.id) val consentStatusBooleanPair = checkConsentStatus(mtbFile)
var ttpConsentStatus = consentStatusBooleanPair.first
// received status REJECTED overrides TTP value. Also in case of disabled consent service, val isConsentOK = consentStatusBooleanPair.second
// we need to override IGNORED status
if (mtbFile.consent.status == Consent.Status.REJECTED) ttpConsentStatus =
TtpConsentStatus.CONSENT_MISSING_OR_REJECTED
val isConsentOK = mtbFile.consent.status == Consent.Status.ACTIVE && (ttpConsentStatus.equals(
TtpConsentStatus.CONSENTED
) || ttpConsentStatus.equals(
TtpConsentStatus.IGNORED
))
if (isConsentOK) { if (isConsentOK) {
logger.debug("Accepted MTB File (bwHC V1) for processing") logger.debug("Accepted MTB File (bwHC V1) for processing")
requestProcessor.processMtbFile(mtbFile) requestProcessor.processMtbFile(mtbFile)
@ -71,7 +62,22 @@ class MtbFileRestController(
return ResponseEntity.accepted().build() return ResponseEntity.accepted().build()
} }
@PostMapping( consumes = [ CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE] ) private fun checkConsentStatus(mtbFile: MtbFile): Pair<TtpConsentStatus, Boolean> {
var ttpConsentStatus = constService.isConsented(mtbFile.patient.id)
val isConsentOK =
(ttpConsentStatus.equals(TtpConsentStatus.IGNORED) && mtbFile.consent.status == Consent.Status.ACTIVE) ||
ttpConsentStatus.equals(
TtpConsentStatus.CONSENTED
)
if (ttpConsentStatus.equals(TtpConsentStatus.IGNORED) && mtbFile.consent.status == Consent.Status.REJECTED) {
// in case ttp check is disabled - we propagate rejected status anyway
ttpConsentStatus = TtpConsentStatus.CONSENT_MISSING_OR_REJECTED
}
return Pair(ttpConsentStatus, isConsentOK)
}
@PostMapping(consumes = [CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE])
fun mtbFile(@RequestBody mtbFile: Mtb): ResponseEntity<Unit> { fun mtbFile(@RequestBody mtbFile: Mtb): ResponseEntity<Unit> {
logger.debug("Accepted MTB File (DNPM V2) for processing") logger.debug("Accepted MTB File (DNPM V2) for processing")
requestProcessor.processMtbFile(mtbFile) requestProcessor.processMtbFile(mtbFile)

View File

@ -1,14 +1,102 @@
package dev.dnpm.etl.processor.consent; package dev.dnpm.etl.processor.consent;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.dnpm.etl.processor.config.AppConfiguration;
import dev.dnpm.etl.processor.config.AppFhirConfig;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.OperationOutcome;
import org.hl7.fhir.r4.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.r4.model.OperationOutcome.IssueType;
import org.hl7.fhir.r4.model.OperationOutcome.OperationOutcomeIssueComponent;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.client.MockRestServiceServer;
//@ExtendWith(MockitoExtension.class)
@ContextConfiguration(classes = {GicsConsentService.class,
AppConfiguration.class, ObjectMapper.class})
@TestPropertySource(properties = {"app.consent.gics.enabled=true",
"app.consent.gics.gIcsBaseUri=http://localhost:8090/ttp-fhir/fhir/gics"})
@RestClientTest
public class GicsConsentServiceTest { public class GicsConsentServiceTest {
public static final String GICS_BASE_URI = "http://localhost:8090/ttp-fhir/fhir/gics";
@Autowired
MockRestServiceServer mockRestServiceServer;
@Autowired
GicsConsentService gicsConsentService;
@Autowired
AppConfiguration appConfiguration;
@Autowired
AppFhirConfig appFhirConfig;
@BeforeEach
public void setUp() {
mockRestServiceServer = MockRestServiceServer.createServer(appConfiguration.restTemplate());
}
@Test @Test
void isConsented() { void isConsented() {
final Parameters responseConsented = new Parameters().addParameter(
new ParametersParameterComponent().setName("consented")
.setValue(new BooleanType().setValue(true)));
mockRestServiceServer.expect(
requestTo("http://localhost:8090/ttp-fhir/fhir/gics" + GicsConsentService.IS_CONSENTED_ENDPOINT)).andRespond(
withSuccess(appFhirConfig.fhirContext().newJsonParser()
.encodeResourceToString(responseConsented),
MediaType.APPLICATION_JSON));
var consentStatus = gicsConsentService.isConsented("123456");
assertThat(consentStatus).isEqualTo(TtpConsentStatus.CONSENTED);
} }
@Test
void consentRevoced() {
final Parameters responseRevoced = new Parameters().addParameter(
new ParametersParameterComponent().setName("consented")
.setValue(new BooleanType().setValue(false)));
mockRestServiceServer.expect(
requestTo("http://localhost:8090/ttp-fhir/fhir/gics" + GicsConsentService.IS_CONSENTED_ENDPOINT)).andRespond(
withSuccess(appFhirConfig.fhirContext().newJsonParser()
.encodeResourceToString(responseRevoced),
MediaType.APPLICATION_JSON));
var consentStatus = gicsConsentService.isConsented("123456");
assertThat(consentStatus).isEqualTo(TtpConsentStatus.CONSENT_MISSING_OR_REJECTED);
}
@Test
void gicsParameterInvalid() {
final OperationOutcome responseErrorOutcome = new OperationOutcome().addIssue(
new OperationOutcomeIssueComponent().setSeverity(
IssueSeverity.ERROR).setCode(IssueType.PROCESSING)
.setDiagnostics("Invalid policy parameter..."));
mockRestServiceServer.expect(
requestTo(GICS_BASE_URI + GicsConsentService.IS_CONSENTED_ENDPOINT)).andRespond(
withSuccess(appFhirConfig.fhirContext().newJsonParser()
.encodeResourceToString(responseErrorOutcome),
MediaType.APPLICATION_JSON));
var consentStatus = gicsConsentService.isConsented("123456");
assertThat(consentStatus).isEqualTo(TtpConsentStatus.FAILED_TO_ASK);
}
} }

View File

@ -21,22 +21,29 @@ package dev.dnpm.etl.processor.input
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import de.ukw.ccc.bwhc.dto.* import de.ukw.ccc.bwhc.dto.*
import dev.dnpm.etl.processor.consent.ConsentCheckedIgnored import de.ukw.ccc.bwhc.dto.Consent.Status
import dev.dnpm.etl.processor.consent.TtpConsentStatus
import dev.dnpm.etl.processor.CustomMediaType import dev.dnpm.etl.processor.CustomMediaType
import dev.dnpm.etl.processor.consent.ConsentCheckedIgnored
import dev.dnpm.etl.processor.consent.GicsConsentService
import dev.dnpm.etl.processor.consent.TtpConsentStatus
import dev.dnpm.etl.processor.services.RequestProcessor import dev.dnpm.etl.processor.services.RequestProcessor
import dev.pcvolkmer.mv64e.mtb.Mtb import dev.pcvolkmer.mv64e.mtb.Mtb
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.* import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.junit.jupiter.MockitoExtension import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.any import org.mockito.kotlin.any
import org.mockito.kotlin.anyValueClass import org.mockito.kotlin.anyValueClass
import org.mockito.kotlin.whenever
import org.springframework.core.io.ClassPathResource import org.springframework.core.io.ClassPathResource
import org.springframework.http.MediaType import org.springframework.http.MediaType
import org.springframework.test.context.TestPropertySource
import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.delete import org.springframework.test.web.servlet.delete
import org.springframework.test.web.servlet.post import org.springframework.test.web.servlet.post
@ -54,6 +61,7 @@ class MtbFileRestControllerTest {
private lateinit var requestProcessor: RequestProcessor private lateinit var requestProcessor: RequestProcessor
@BeforeEach @BeforeEach
fun setup( fun setup(
@Mock requestProcessor: RequestProcessor @Mock requestProcessor: RequestProcessor
@ -66,7 +74,7 @@ class MtbFileRestControllerTest {
@Test @Test
fun shouldProcessPostRequest() { fun shouldProcessPostRequest() {
mockMvc.post("/mtbfile") { mockMvc.post("/mtbfile") {
content = objectMapper.writeValueAsString(bwhcMtbFileContent(Consent.Status.ACTIVE)) content = objectMapper.writeValueAsString(bwhcMtbFileContent(Status.ACTIVE))
contentType = MediaType.APPLICATION_JSON contentType = MediaType.APPLICATION_JSON
}.andExpect { }.andExpect {
status { status {
@ -81,7 +89,7 @@ class MtbFileRestControllerTest {
fun shouldProcessPostRequestWithRejectedConsent() { fun shouldProcessPostRequestWithRejectedConsent() {
mockMvc.post("/mtbfile") { mockMvc.post("/mtbfile") {
content = content =
objectMapper.writeValueAsString(bwhcMtbFileContent(Consent.Status.REJECTED)) objectMapper.writeValueAsString(bwhcMtbFileContent(Status.REJECTED))
contentType = MediaType.APPLICATION_JSON contentType = MediaType.APPLICATION_JSON
}.andExpect { }.andExpect {
status { status {
@ -110,6 +118,93 @@ class MtbFileRestControllerTest {
} }
} }
@TestPropertySource(
properties = ["app.consent.gics.enabled=true",
"app.consent.gics.gIcsBaseUri=http://localhost:8090/ttp-fhir/fhir/gics"]
)
@Nested
inner class BwhcRequestsCheckConsentViaTtp {
private lateinit var mockMvc: MockMvc
private lateinit var requestProcessor: RequestProcessor
private lateinit var gicsConsentService: GicsConsentService
@BeforeEach
fun setup(
@Mock requestProcessor: RequestProcessor,
@Mock gicsConsentService: GicsConsentService
) {
this.requestProcessor = requestProcessor
val controller = MtbFileRestController(requestProcessor, gicsConsentService)
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
this.gicsConsentService = gicsConsentService
}
@ParameterizedTest
@ValueSource(strings = ["ACTIVE", "REJECTED"])
fun shouldProcessPostRequest(status: String) {
whenever(gicsConsentService.isConsented(any())).thenReturn(TtpConsentStatus.CONSENTED)
mockMvc.post("/mtbfile") {
content =
objectMapper.writeValueAsString(bwhcMtbFileContent(Status.valueOf(status)))
contentType = MediaType.APPLICATION_JSON
}.andExpect {
status {
isAccepted()
}
}
verify(requestProcessor, times(1)).processMtbFile(any<MtbFile>())
}
@ParameterizedTest
@ValueSource(strings = ["ACTIVE", "REJECTED"])
fun shouldProcessPostRequestWithRejectedConsent(status: String) {
whenever(gicsConsentService.isConsented(any())).thenReturn(TtpConsentStatus.CONSENT_MISSING_OR_REJECTED)
mockMvc.post("/mtbfile") {
content =
objectMapper.writeValueAsString(bwhcMtbFileContent(Status.valueOf(status)))
contentType = MediaType.APPLICATION_JSON
}.andExpect {
status {
isAccepted()
}
}
// consent status from ttp should override file consent value
verify(requestProcessor, times(1)).processDeletion(
anyValueClass(),
org.mockito.kotlin.eq(TtpConsentStatus.CONSENT_MISSING_OR_REJECTED)
)
}
@Test
fun shouldProcessDeleteRequest() {
mockMvc.delete("/mtbfile/TEST_12345678").andExpect {
status {
isAccepted()
}
}
verify(requestProcessor, times(1)).processDeletion(
anyValueClass(),
org.mockito.kotlin.eq(TtpConsentStatus.IGNORED)
)
verify(gicsConsentService, times(0)).isConsented(any())
}
}
@Nested @Nested
inner class BwhcRequestsWithAlias { inner class BwhcRequestsWithAlias {
@ -129,7 +224,7 @@ class MtbFileRestControllerTest {
@Test @Test
fun shouldProcessPostRequest() { fun shouldProcessPostRequest() {
mockMvc.post("/mtb") { mockMvc.post("/mtb") {
content = objectMapper.writeValueAsString(bwhcMtbFileContent(Consent.Status.ACTIVE)) content = objectMapper.writeValueAsString(bwhcMtbFileContent(Status.ACTIVE))
contentType = MediaType.APPLICATION_JSON contentType = MediaType.APPLICATION_JSON
}.andExpect { }.andExpect {
status { status {
@ -144,7 +239,7 @@ class MtbFileRestControllerTest {
fun shouldProcessPostRequestWithRejectedConsent() { fun shouldProcessPostRequestWithRejectedConsent() {
mockMvc.post("/mtb") { mockMvc.post("/mtb") {
content = content =
objectMapper.writeValueAsString(bwhcMtbFileContent(Consent.Status.REJECTED)) objectMapper.writeValueAsString(bwhcMtbFileContent(Status.REJECTED))
contentType = MediaType.APPLICATION_JSON contentType = MediaType.APPLICATION_JSON
}.andExpect { }.andExpect {
status { status {
@ -212,7 +307,7 @@ class MtbFileRestControllerTest {
} }
companion object { companion object {
fun bwhcMtbFileContent(consentStatus: Consent.Status) = MtbFile.builder() fun bwhcMtbFileContent(consentStatus: Status) = MtbFile.builder()
.withPatient( .withPatient(
Patient.builder() Patient.builder()
.withId("TEST_12345678") .withId("TEST_12345678")