Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert S303 to Q303 for 2025 #4897

Merged
merged 2 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
315 changes: 315 additions & 0 deletions common/src/main/resources/2025EditsDescriptions.txt

Large diffs are not rendered by default.

315 changes: 315 additions & 0 deletions common/src/main/resources/2025QuarterlyEditsDescriptions.txt

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ private class HmdaFileValidationHttpApi(implicit mat: Materializer) {

case "validity" =>
checkValidity(ts, ts.LEI, ctx, TsValidationError)

case "quality" =>
checkQuality(ts, ts.LEI, ctx)
}

val maybeErrors = validation.leftMap(xs => xs.toList).toEither
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import hmda.model.filing.ts.TransmittalSheet
import hmda.validation.context.ValidationContext
import hmda.validation.rules.ts.syntactical.{ S300, S303, S302 }
import hmda.validation.rules.ts.validity._
import hmda.validation.rules.ts.quality._
// $COVERAGE-OFF$
private[engine] object TsEngine2025 extends ValidationEngine[TransmittalSheet] {

override def syntacticalChecks(ctx: ValidationContext) = Vector(
S300,
S302.withContext(ctx),
S303.withContext(ctx)
S302.withContext(ctx)
)

override def validityChecks(ctx: ValidationContext) = Vector(
Expand All @@ -27,5 +27,9 @@ private[engine] object TsEngine2025 extends ValidationEngine[TransmittalSheet] {
V719_2
)

override def qualityChecks(ctx: ValidationContext) = Vector(
Q303.withContext(ctx)
)

}
// $COVERAGE-ON$
13 changes: 13 additions & 0 deletions hmda/src/main/scala/hmda/validation/filing/ValidationFlow.scala
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ object ValidationFlow extends ColumnDataFormatter {
validationEngine.checkSyntactical(ts, ts.LEI, validationContext, TsValidationError)
case "validity" =>
validationEngine.checkValidity(ts, ts.LEI, validationContext, TsValidationError)
case "quality" =>
validationEngine.checkQuality(ts, ts.LEI, validationContext)
}
(ts, errors)
}
Expand Down Expand Up @@ -153,6 +155,17 @@ object ValidationFlow extends ColumnDataFormatter {
)
): _*
)
case "Q303" =>
ListMap(
affectedFields.map(field =>
(
field,
"Provided: " + ts.valueOf(field) + ", Expected: " + institution
.getOrElse(Institution.empty)
.valueOf(field)
)
): _*
)
case "V718" =>
val quarter =
period.quarter match {
Expand Down
24 changes: 24 additions & 0 deletions hmda/src/main/scala/hmda/validation/rules/ts/quality/Q303.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package hmda.validation.rules.ts.quality

import hmda.model.filing.ts.TransmittalSheet
import hmda.model.institution.Institution
import hmda.validation.context.ValidationContext
import hmda.validation.dsl.ValidationResult
import hmda.validation.rules.{ EditCheck, IfInstitutionPresentIn }
import hmda.validation.dsl.PredicateCommon._
import hmda.validation.dsl.PredicateSyntax._

object Q303 {
def withContext(ctx: ValidationContext): EditCheck[TransmittalSheet] =
IfInstitutionPresentIn(ctx) { new Q303(_) }

}

class Q303 private (institution: Institution) extends EditCheck[TransmittalSheet] {
override def name: String = "Q303"

override def apply(ts: TransmittalSheet): ValidationResult =
(ts.LEI.toLowerCase is equalTo(institution.LEI.toLowerCase)) and
(ts.agency.code is equalTo(institution.agency.code)) and
(ts.taxId is equalTo(institution.taxId.getOrElse("")))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package hmda.validation.rules.ts.quality

import hmda.model.filing.ts.TransmittalSheet
import hmda.model.institution._
import hmda.validation.rules.EditCheck
import hmda.validation.rules.ts.TsEditCheckSpec
import hmda.model.filing.ts.TsGenerators._
import hmda.validation.context.ValidationContext

class Q303Spec extends TsEditCheckSpec {

private var institution: Institution = _

override def check: EditCheck[TransmittalSheet] =
Q303.withContext(ValidationContext(Some(institution)))

property("Pass when LEI, Agency and Tax ID are reported correctly") {
forAll(tsGen) { ts =>
whenInstitution(ts.LEI, ts.agency, ts.taxId)
ts.mustPass
}
}

property("Pass when LEI is a different case") {
forAll(tsGen) { ts =>
whenInstitution(ts.LEI.toLowerCase, ts.agency, ts.taxId)
ts.mustPass
val upperCaseTs = ts.copy(LEI = ts.LEI.toUpperCase)
upperCaseTs.mustPass
}
}

property("Fail when LEI is reported incorrectly") {
forAll(tsGen) { ts =>
whenInstitution(ts.LEI + "x", ts.agency, ts.taxId)
ts.mustFail
}
}

property("Fail when Agency is reported incorrectly") {
forAll(tsGen) { ts =>
whenever(ts.agency != CFPB) {
whenInstitution(ts.LEI, CFPB, ts.taxId)
ts.mustFail
}
}
}

property("Fail when Tax ID is reported incorrectly") {
forAll(tsGen) { ts =>
whenInstitution(ts.LEI, ts.agency, ts.taxId + "x")
ts.mustFail
}
}

property("Fail when LEI, Agency and Tax ID are reported incorrectly") {
forAll(tsGen) { ts =>
whenever(ts.agency != CFPB) {
whenInstitution(ts.LEI + "x", CFPB, ts.taxId + "x")
ts.mustFail
}
}
}
private def whenInstitution(lei: String,
agency: Agency,
taxId: String): Unit = {
institution = Institution.empty.copy(
LEI = lei,
agency = agency,
taxId = Some(taxId)
)
}

}
Loading