Commit ae740b6a authored by Sapotille Orange's avatar Sapotille Orange Committed by Bergamote Orange
Browse files

feat(dcc): add metrics and logs

parent ccf41259
......@@ -49,6 +49,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
......@@ -98,10 +102,6 @@
<artifactId>cose-java</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
......@@ -162,6 +162,12 @@
<version>1.5.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.github.hakky54</groupId>
<artifactId>logcaptor</artifactId>
<version>2.7.8</version>
<scope>test</scope>
</dependency>
<!-- CUCUMBER -->
<dependency>
......
package fr.gouv.tac.dcclight.controller
import com.fasterxml.jackson.databind.ObjectMapper
import ehn.techiop.hcert.kotlin.chain.Error
import ehn.techiop.hcert.kotlin.chain.VerificationException
import fr.gouv.tac.dcclight.api.AggregateApi
import fr.gouv.tac.dcclight.api.model.CertificatesAggregate
......@@ -8,6 +9,7 @@ import fr.gouv.tac.dcclight.api.model.DcclightsAggregationRequest
import fr.gouv.tac.dcclight.api.model.EncryptedApiResponse
import fr.gouv.tac.dcclight.service.DccDecodingService
import fr.gouv.tac.dcclight.service.GenerationService
import fr.gouv.tac.dcclight.utils.MetricsService
import org.slf4j.LoggerFactory
import org.springframework.http.HttpStatus.OK
import org.springframework.http.ResponseEntity
......@@ -26,7 +28,8 @@ class AggregateController(
val dccDecodingService: DccDecodingService,
val generationService: GenerationService,
val softwareEncryptionTool: SoftwareEncryptionTool,
val objectMapper: ObjectMapper
val objectMapper: ObjectMapper,
val metricsService: MetricsService
) : AggregateApi {
private val log = LoggerFactory.getLogger(this::class.java)
......@@ -34,6 +37,7 @@ class AggregateController(
override fun aggregate(dcclightsAggregationRequest: DcclightsAggregationRequest): ResponseEntity<EncryptedApiResponse> {
val decoder = Base64.getDecoder()
val appPublicKey = decoder.decode(dcclightsAggregationRequest.publicKey)
val key = softwareEncryptionTool.getEncryptionKey(appPublicKey)
val bindingResult: BindingResult = BeanPropertyBindingResult(dcclightsAggregationRequest.certificates, "response")
......@@ -43,7 +47,13 @@ class AggregateController(
softwareEncryptionTool.decrypt(key, decoder.decode(certificate)).toString(UTF_8)
).also {
it.verificationResult.error?.let { error ->
log.error(error.name)
if (error.name.equals(Error.CWT_EXPIRED)) {
metricsService.countAndLogRejectDccWithTechnicalValidityDateInThePast(it)
} else if (error.name.equals(Error.SIGNATURE_INVALID)) {
metricsService.countRejectDccWithWrongSignatureKeys()
} else {
log.warn(error.name)
}
throw VerificationException(error)
}
}
......
......@@ -3,6 +3,7 @@ package fr.gouv.tac.dcclight.service
import ehn.techiop.hcert.kotlin.chain.DecodeResult
import fr.gouv.tac.dcclight.service.error.DccValidationError.INCOMPATIBLE_DCC_COMBINATION
import fr.gouv.tac.dcclight.service.error.reject
import fr.gouv.tac.dcclight.utils.MetricsService
import org.springframework.stereotype.Service
import org.springframework.validation.BindException
import org.springframework.validation.BindingResult
......@@ -14,10 +15,13 @@ class GenerationService(
val dcclightEncodingService: DcclightEncodingService,
val dccEncodingService: DccEncodingService,
val derogationDscBlocklistRulesService: DerogationDscBlocklistRulesService,
val aggregationRulesConfiguration: AggregationSanitaryRulesService
val aggregationRulesConfiguration: AggregationSanitaryRulesService,
val metricsService: MetricsService
) {
fun generateAggregate(decryptedDCCs: List<DecodeResult>, bindingResult: BindingResult): String {
val personHealthData = PersonHealthData(decryptedDCCs, derogationDscBlocklistRulesService, aggregationRulesConfiguration, bindingResult)
val personHealthData = PersonHealthData(decryptedDCCs, derogationDscBlocklistRulesService, aggregationRulesConfiguration, bindingResult, metricsService)
if (personHealthData.isEligibleToDerogation()) {
return personHealthData.derogationDcclight(dcclightEncodingService)
}
......@@ -25,6 +29,7 @@ class GenerationService(
if (aggregationDcc == null) {
bindingResult.reject(INCOMPATIBLE_DCC_COMBINATION)
metricsService.countAndLogRejectDccWithIncompatibleDccCombination(decryptedDCCs)
throw BindException(bindingResult)
}
return aggregationDcc
......
......@@ -31,9 +31,11 @@ import fr.gouv.tac.dcclight.service.error.DccValidationError.MISSING_CERTIFICATE
import fr.gouv.tac.dcclight.service.error.DccValidationError.MISSING_CERTIFICATE_ISSUER
import fr.gouv.tac.dcclight.service.error.DccValidationError.MISSING_GIVEN_NAME_TRANSLITERATED
import fr.gouv.tac.dcclight.service.error.reject
import fr.gouv.tac.dcclight.utils.MetricsService
import kotlinx.datetime.Clock.System.now
import kotlinx.datetime.TimeZone.Companion.currentSystemDefault
import kotlinx.datetime.atStartOfDayIn
import org.apache.commons.codec.digest.DigestUtils
import org.springframework.validation.BindException
import org.springframework.validation.BindingResult
import kotlin.time.Duration.Companion.days
......@@ -45,7 +47,8 @@ class PersonHealthData(
decodeResults: List<DecodeResult>,
private val derogationDscBlocklistRulesService: DerogationDscBlocklistRulesService,
private val aggregationSanitaryRulesService: AggregationSanitaryRulesService,
bindingResult: BindingResult
bindingResult: BindingResult,
private val metricsService: MetricsService
) {
private val vaccinations: List<Vaccination>
......@@ -112,8 +115,10 @@ class PersonHealthData(
if (decodeResults.any { it.chainDecodeResult.eudgc == null }) {
throw IllegalStateException("DecodeResults eudgc fields are not supposed to be empty")
}
if (decodeResults.any { derogationDscBlocklistRulesService.isBlocklisted(it.chainDecodeResult.eudgc!!) }) {
if (decodeResults.any() { derogationDscBlocklistRulesService.isBlocklisted(it.chainDecodeResult.eudgc!!) }) {
bindingResult.reject(BLOCKLISTED_CERTIFICATE)
metricsService.countRejectDccInBlocklist()
throw BindException(bindingResult)
}
val referenceDcc = getFirstCertificateWithGivenNameAndLastName(decodeResults)
verifyDccPersonalInformation(referenceDcc, decodeResults, bindingResult)
......@@ -123,14 +128,15 @@ class PersonHealthData(
if (decodeResults.any { it.chainDecodeResult.eudgc!!.getIssuer().isEmpty() }) {
bindingResult.reject(MISSING_CERTIFICATE_ISSUER)
}
val anyStartWithAggregatePrefix = decodeResults.any {
it.chainDecodeResult.eudgc!!.getIdentifier().startsWith("URN:UVCI:01:FR:DGSAG/")
}
if (anyStartWithAggregatePrefix) {
if (decodeResults.any() { it.chainDecodeResult.eudgc!!.getIdentifier().startsWith("URN:UVCI:01:FR:DGSAG/") }) {
bindingResult.reject(AGGREGATE_CERTIFICATE_NOT_ALLOWED)
metricsService.countRejectDccWithNotAllowedCertificate()
throw BindException(bindingResult)
}
if (decodeResults.any { it.chainDecodeResult.eudgc!!.getProofInstant() > now().plus(1.days) }) {
val firstDccWithDateInFutureMoreThanOneDay = decodeResults.firstOrNull() { it.chainDecodeResult.eudgc!!.getProofInstant() > now().plus(1.days) }
if (firstDccWithDateInFutureMoreThanOneDay != null) {
bindingResult.reject(CERTIFICATE_PROOF_DATE_TOO_FAR_IN_FUTURE)
metricsService.countRejectDccWithDateInFutureMoreThanOneDay(firstDccWithDateInFutureMoreThanOneDay)
}
if (bindingResult.hasErrors()) {
throw BindException(bindingResult)
......@@ -223,38 +229,38 @@ class PersonHealthData(
decodeResults: List<DecodeResult>,
result: BindingResult
) {
if (decodeResults.any { it.chainDecodeResult.eudgc!!.subject.givenNameTransliterated == null }) {
if (decodeResults.any { it.chainDecodeResult.eudgc?.subject?.givenNameTransliterated == null }) {
result.reject(MISSING_GIVEN_NAME_TRANSLITERATED)
}
// directly throw exception to avoid NPE in following code due to String? to String casts
if (result.hasErrors()) {
throw BindException(result)
}
val allMatchReferenceFamilyName =
decodeResults.all {
it.chainDecodeResult.eudgc!!.subject.familyNameTransliterated.trimEnd('<') == referenceDcc.subject.familyNameTransliterated.trimEnd(
'<'
)
}
if (!allMatchReferenceFamilyName) {
val firstInconsistentFamilyName = decodeResults.firstOrNull() {
it.chainDecodeResult.eudgc?.subject?.familyNameTransliterated!!.trimEnd('<') != referenceDcc.subject.familyNameTransliterated.trimEnd('<')
}
if (firstInconsistentFamilyName != null) {
result.reject(INCONSISTENT_FAMILY_NAME_TRANSLITERATED)
metricsService.countAndLogRejectDccWithIncorrecFamilyName(firstInconsistentFamilyName, referenceDcc)
throw BindException(result)
}
val allMatchReferenceGivenName =
decodeResults.all {
it.chainDecodeResult.eudgc!!.subject.givenNameTransliterated!!.trimEnd('<') == referenceDcc.subject.givenNameTransliterated!!.trimEnd(
'<'
)
}
if (!allMatchReferenceGivenName) {
val firstInconsistentGivenName = decodeResults.firstOrNull() {
it.chainDecodeResult.eudgc?.subject?.givenNameTransliterated!!.trimEnd('<') != referenceDcc.subject.givenNameTransliterated!!.trimEnd('<')
}
if (firstInconsistentGivenName != null) {
result.reject(INCONSISTENT_GIVEN_NAME_TRANSLITERATED)
metricsService.countAndLogRejectDccWithIncorrecGivenName(firstInconsistentGivenName, referenceDcc)
throw BindException(result)
}
val allMatchDateOfBirthString = decodeResults
.map { results -> results.chainDecodeResult.eudgc!! }
.all { it.dateOfBirthString == referenceDcc.dateOfBirthString }
if (!allMatchDateOfBirthString) {
result.reject(INCONSISTENT_DATE_OF_BIRTH_STRING)
val firstInconsistentDateOfBirth = decodeResults.map { results -> results.chainDecodeResult.eudgc }.firstOrNull() {
it?.dateOfBirthString != referenceDcc.dateOfBirthString
}
if (result.hasErrors()) {
if (firstInconsistentDateOfBirth != null) {
result.reject(INCONSISTENT_DATE_OF_BIRTH_STRING)
metricsService.countAndLogRejectDccWithIncorrecDateOfBirth(firstInconsistentDateOfBirth, referenceDcc)
throw BindException(result)
}
}
......@@ -263,7 +269,7 @@ class PersonHealthData(
decodeResults.map { it.chainDecodeResult }
.filter { it.eudgc != null }
.map { it.eudgc!! }
.sortedWith(GreenCertificatesDateComparator())
.sortedWith(PersonHealthData.Companion.GreenCertificatesDateComparator())
private fun getFirstCertificateWithGivenNameAndLastName(decodeResults: List<DecodeResult>) =
decodeResults
......@@ -302,6 +308,22 @@ class PersonHealthData(
}
.map { it.chainDecodeResult.eudgc!!.tests!![0]!! }
private fun getConcatenatedIdentifier(): String {
val concatenatedIdentifiers = temporallySortedDcc.joinToString("") {
if (it.hasOneTest()) {
it.tests!![0]!!.certificateIdentifier
} else if (it.hasOneVaccination()) {
it.vaccinations!![0]!!.certificateIdentifier
} else if (it.hasOneRecoveryStatement()) {
it.recoveryStatements!![0]!!.certificateIdentifier
} else {
throw IllegalStateException("Trying to compare at least one certificate with no sanitary proof")
}
}
return "01:FR:DGSAG/" + DigestUtils.sha256Hex(concatenatedIdentifiers).take(19)
}
private fun isDerogationCompatibleTest(it: Test): Boolean {
val now = now()
return !derogationDscBlocklistRulesService.isBlocklisted(it) &&
......
package fr.gouv.tac.dcclight.utils
import ehn.techiop.hcert.kotlin.chain.DecodeResult
import ehn.techiop.hcert.kotlin.data.GreenCertificate
import fr.gouv.tac.dcclight.extension.getProofInstant
import fr.gouv.tac.dcclight.extension.getType
import io.micrometer.core.instrument.Counter
import io.micrometer.core.instrument.MeterRegistry
import kotlinx.datetime.Clock
import kotlinx.datetime.Clock.System.now
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
@Service
class MetricsService(private val meterRegistry: MeterRegistry) {
private val log = LoggerFactory.getLogger(this::class.java)
private val counters: Map<String, Counter> = HashMap()
private val inconsistentFamilyNameCounter = Counter.builder("dcc.request.aggregation.inconsistent.family.name")
.description("Number of inconsistent family name")
.register(meterRegistry)
private val inconsistentGivenNameCounter = Counter.builder("dcc.request.aggregation.inconsistent.given.name")
.description("Number of inconsistent given name")
.register(meterRegistry)
private val inconsistentDateOfBirthCounter = Counter.builder("dcc.request.aggregation.inconsistent.date.of.birth")
.description("Number of inconsistent date of birth")
.register(meterRegistry)
private val incompatibleDccCombinationCounter = Counter.builder("dcc.request.aggregation.incompatible.dcc.combination")
.description("Number of incompatible DCC combination")
.register(meterRegistry)
private val aggregateCertificateNotAllowedCounter = Counter.builder("dcc.request.aggregation.aggregate.certificate.not.allowed")
.description("Number of rejected DCC due to provided certificate coming from a previous aggregation")
.register(meterRegistry)
private val wrongSignatureKeysCounter = Counter.builder("dcc.request.aggregation.decoding.error")
.description("Number of rejected DCC due to wrong signature keys")
.tag("type", "wrong_signature_keys")
.register(meterRegistry)
private val technicalValidityDateInThePastCounter = Counter.builder("dcc.request.aggregation.decoding.error")
.description("Number of rejected DCC due to technical validity date in the past")
.tag("type", "technical_validity_date_in_the_past")
.register(meterRegistry)
private val dccInBlockListCounter = Counter.builder("dcc.request.aggregation.dcc.inblocklist")
.description("Number of DCC rejected due to being on the blocklist")
.register(meterRegistry)
private val dccWithDateInFutureMoreThanOneDayCounter = Counter.builder("dcc.request.aggregation.sanitary.proof.date.in.future")
.description("Number of DCC with a sanitary proof date in more than one day in the future")
.register(meterRegistry)
fun countAndLogRejectDccWithIncorrecFamilyName(firstInconsistentFamilyName: DecodeResult, referenceDcc: GreenCertificate) {
log.info(
"""Transliterated family name is inconsistent across provided dcc list.
DCC type: ${firstInconsistentFamilyName.chainDecodeResult.eudgc?.getType()?.type},
reference family name: ${anonymizeData(referenceDcc.subject.familyNameTransliterated)}
DCC wrong family name: ${anonymizeData(firstInconsistentFamilyName.chainDecodeResult.eudgc?.subject?.familyNameTransliterated!!)},
date: ${Clock.System.now()}
""".trimIndent()
)
inconsistentFamilyNameCounter.increment()
}
fun countAndLogRejectDccWithIncorrecGivenName(firstInconsistentGivenName: DecodeResult, referenceDcc: GreenCertificate) {
log.info(
"""Transliterated given name is inconsistent across provided dcc list.
DCC type: ${firstInconsistentGivenName.chainDecodeResult.eudgc?.getType()?.type},
reference given name: ${anonymizeData(referenceDcc.subject.givenNameTransliterated!!)}
DCC wrong given name: ${anonymizeData(firstInconsistentGivenName.chainDecodeResult.eudgc?.subject?.givenNameTransliterated!!)},
date: ${Clock.System.now()}
""".trimIndent()
)
inconsistentGivenNameCounter.increment()
}
fun countAndLogRejectDccWithIncorrecDateOfBirth(firstInconsistentDateOfBirth: GreenCertificate, referenceDcc: GreenCertificate) {
log.info(
"""Date of birth string is inconsistent across provided dcc list.
DCC type: ${firstInconsistentDateOfBirth.getType()?.type},
reference date of birth: ${anonymizeData(referenceDcc.dateOfBirthString)}
DCC wrong date of birth: ${anonymizeData(firstInconsistentDateOfBirth.dateOfBirthString)},
date: ${Clock.System.now()}
""".trimIndent()
)
inconsistentDateOfBirthCounter.increment()
}
fun countAndLogRejectDccWithIncompatibleDccCombination(decryptedDCCs: List<DecodeResult>) {
log.info(
"""Provided certificates are not compatible with any operation.
date: ${Clock.System.now()},
list of DCC: ${decryptedDCCs.mapIndexed{ index,it -> getDccInformations(it, index) }}
""".trimIndent()
)
incompatibleDccCombinationCounter.increment()
}
fun countRejectDccWithWrongSignatureKeys() {
wrongSignatureKeysCounter.increment()
}
fun countRejectDccWithNotAllowedCertificate() {
aggregateCertificateNotAllowedCounter.increment()
}
fun countAndLogRejectDccWithTechnicalValidityDateInThePast(decryptedDCC: DecodeResult) {
log.info(
"""End of technical validity is in the past.
DCC type: ${decryptedDCC.chainDecodeResult.eudgc?.getType()?.type},
End of validity: ${decryptedDCC.verificationResult.expirationTime},
Date: ${now()}
""".trimIndent()
)
technicalValidityDateInThePastCounter.increment()
}
fun countRejectDccInBlocklist() {
dccInBlockListCounter.increment()
}
fun countRejectDccWithDateInFutureMoreThanOneDay(decryptedDCC: DecodeResult) {
log.info(
"""At least one of the provided certificates has a sanitary proof date in more than one day
DCC type: ${decryptedDCC.chainDecodeResult.eudgc?.getType()?.type},
DCC date: ${decryptedDCC.chainDecodeResult.eudgc?.getProofInstant()},
date: ${now()}
""".trimIndent()
)
dccWithDateInFutureMoreThanOneDayCounter.increment()
}
// fun countAggregationSuccessfullyGenerated(ruleName: String) {
// val formattedRuleName = ruleName.replace("\\s+", ".").lowercase()
// var counter = counters[formattedRuleName];
//
// if(counter == null) {
// counter = Counter.builder("dcc.request.aggregation.successful")
// .description("Number of aggregation successfully generated")
// .tag("rule name", formattedRuleName)
// .register(meterRegistry)
// counters.put(formattedRuleName,counter)
// }
// counter.increment()
//
// }
private fun anonymizeData(name: String): String {
return "[a-zA-Z0-9]".toRegex().replace(name, "#")
}
fun getDccInformations(decryptedDCC: DecodeResult, position: Int): String? {
if (decryptedDCC.chainDecodeResult.eudgc?.getType()?.type!!.equals("v")) {
val vaccinationProof = decryptedDCC.chainDecodeResult.eudgc?.vaccinations!![0]!!
return """
{
DCC type: v,
position: $position,
vaccine code: ${vaccinationProof.vaccine.valueSetEntry.display},
vaccine type: ${vaccinationProof.medicinalProduct.valueSetEntry.display},
vaccine manufacturer: ${vaccinationProof.authorizationHolder.valueSetEntry.display},
dose number: ${vaccinationProof.doseNumber},
total dose number: ${vaccinationProof.doseTotalNumber},
country: ${vaccinationProof.country},
vaccination date: ${vaccinationProof.date}
vaccination certificate issuer: ${vaccinationProof.certificateIssuer}
}
""".trimIndent()
} else if (decryptedDCC.chainDecodeResult.eudgc?.getType()?.type!!.equals("t")) {
val testProof = decryptedDCC.chainDecodeResult.eudgc?.tests!![0]!!
return """
{
DCC type: t,
position: $position,
test type: ${testProof.type.valueSetEntry.display},
test name: ${testProof.nameNaa},
test device: ${testProof.nameRat?.valueSetEntry?.display},
test date sample: ${testProof.dateTimeSample},
test date result: ${testProof.dateTimeResult} ,
test result: ${testProof.resultPositive.valueSetEntry.display},
test country: ${testProof.country}
test certificate issuer: ${testProof.certificateIssuer}
}
""".trimIndent()
} else if (decryptedDCC.chainDecodeResult.eudgc?.getType()?.type!!.equals("r")) {
val recoveryProof = decryptedDCC.chainDecodeResult.eudgc?.recoveryStatements!![0]!!
return """
{
DCC type: r,
position: $position,
recovery date of first positive test: ${recoveryProof.dateOfFirstPositiveTestResult},
country: ${recoveryProof.country},
recovery certificate valid from: ${recoveryProof.certificateValidFrom},
recovery certificate valid until: ${recoveryProof.certificateValidUntil}
recovery certificate issuer: ${recoveryProof.certificateIssuer}
}
""".trimIndent()
} else {
throw IllegalStateException("Trying to compare at least one certificate with no sanitary proof")
}
}
}
......@@ -24,7 +24,7 @@ management:
endpoints:
web:
exposure:
include: health, prometheus, info
include: health, prometheus, info, metrics
endpoint:
health:
probes:
......
......@@ -206,8 +206,6 @@ class TestAndVaccinationCombinationErrorsTest {
.body("message", equalTo("Request body contains invalid attributes"))
.body("errors[0].code", equalTo("INCONSISTENT_FAMILY_NAME_TRANSLITERATED"))
.body("errors[0].message", equalTo("Transliterated family name is inconsistent across provided dcc list"))
.body("errors[1].code", equalTo("INCONSISTENT_GIVEN_NAME_TRANSLITERATED"))
.body("errors[1].message", equalTo("Transliterated given name is inconsistent across provided dcc list"))
}
@ParameterizedTest
......@@ -321,10 +319,6 @@ class TestAndVaccinationCombinationErrorsTest {
.body("message", equalTo("Request body contains invalid attributes"))
.body("errors[0].code", equalTo("INCONSISTENT_FAMILY_NAME_TRANSLITERATED"))
.body("errors[0].message", equalTo("Transliterated family name is inconsistent across provided dcc list"))
.body("errors[1].code", equalTo("INCONSISTENT_GIVEN_NAME_TRANSLITERATED"))
.body("errors[1].message", equalTo("Transliterated given name is inconsistent across provided dcc list"))
.body("errors[2].code", equalTo("INCONSISTENT_DATE_OF_BIRTH_STRING"))
.body("errors[2].message", equalTo("Date of birth string is inconsistent across provided dcc list"))
}
@ParameterizedTest
......
......@@ -17,6 +17,7 @@ import fr.gouv.tac.dcclight.test.defaultPerson
import fr.gouv.tac.dcclight.test.defaultTest
import fr.gouv.tac.dcclight.test.defaultVaccination
import fr.gouv.tac.dcclight.test.entryOf
import fr.gouv.tac.dcclight.utils.MetricsService
import kotlinx.datetime.Clock.System.now
import kotlinx.datetime.TimeZone.Companion.currentSystemDefault
import kotlinx.datetime.toLocalDateTime
......@@ -47,6 +48,7 @@ class PersonHealthDataTest {
private val aggregationSanitaryRulesServiceMock: AggregationSanitaryRulesService = mock(
AggregationSanitaryRulesService::class.java
)
private val metricsService: MetricsService = mock(MetricsService::class.java)
@TestInstance(PER_CLASS)
@Nested
......@@ -73,7 +75,8 @@ class PersonHealthDataTest {
list,
derogationDscBlocklistRulesServiceMock,
aggregationSanitaryRulesServiceMock,
bindingResult
bindingResult,
metricsService
)
Assertions.assertTrue(personHealthData.hasCompliantTestInLastDay())
}
......@@ -99,7 +102,8 @@ class PersonHealthDataTest {
list,
derogationDscBlocklistRulesServiceMock,
aggregationSanitaryRulesServiceMock,
bindingResult
bindingResult,
metricsService
)
Assertions.assertTrue(personHealthData.hasCompliantTestInLastDay())
}
......@@ -126,7 +130,8 @@ class PersonHealthDataTest {
list,
derogationDscBlocklistRulesServiceMock,
aggregationSanitaryRulesServiceMock,
bindingResult
bindingResult,
metricsService
)
Assertions.assertFalse(personHealthData.hasCompliantTestInLastDay())
}
......@@ -153,7 +158,8 @@ class PersonHealthDataTest {
list,
derogationDscBlocklistRulesServiceMock,
aggregationSanitaryRulesServiceMock,
bindingResult
bindingResult,
metricsService
)
Assertions.assertFalse(personHealthData.hasCompliantTestInLastDay())
}
......@@ -180,7 +186,8 @@ class PersonHealthDataTest {
list,
derogationDscBlocklistRulesServiceMock,
aggregationSanitaryRulesServiceMock,
bindingResult
bindingResult,
metricsService
)
Assertions.assertTrue(personHealthData.hasCompliantTestInLastDay())
}
......@@ -206,7 +213,8 @@ class PersonHealthDataTest {
list,
derogationDscBlocklistRulesServiceMock,
aggregationSanitaryRulesServiceMock,
bindingResult
bindingResult,
metricsService
)
Assertions.assertFalse(personHealthData.hasCompliantTestInLastDay())
}
......@@ -233,7 +241,8 @@ class PersonHealthDataTest {
list,
derogationDscBlocklistRulesServiceMock,
aggregationSanitaryRulesServiceMock,
bindingResult
bindingResult,
metricsService
)
Assertions.assertFalse(personHealthData.hasCompliantTestInLastDay())
}
......@@ -260,7 +269,8 @@ class PersonHealthDataTest {
list,
derogationDscBlocklistRulesServiceMock,
aggregationSanitaryRulesServiceMock,
bindingResult
bindingResult,
metricsService
)
Assertions.assertFalse(personHealthData.hasCompliantTestInLastDay())
}
......@@ -293,7 +303,8 @@ class PersonHealthDataTest {
list,
derogationDscBlocklistRulesServiceMock,
aggregationSanitaryRulesServiceMock,
bindingResult
bindingResult,