Skip to content

Commit 3cc646d

Browse files
authored
Merge pull request #526 from digital-preservation/DR2-2045_fixOptionRelatedErrorWhenEvaluatingRules
Dr2 2045 fix Option-related error when evaluating rules
2 parents 2b7782d + 6357c5c commit 3cc646d

File tree

9 files changed

+262
-27
lines changed

9 files changed

+262
-27
lines changed

csv-validator-core/src/main/scala/uk/gov/nationalarchives/csv/validator/schema/Rule.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ abstract class Rule(name: String, val argProviders: ArgProvider*) extends Positi
5454
else fail(columnIndex, row, schema)
5555
}
5656

57+
def argProviderHelper(provider: ArgProvider, columnDefinition: ColumnDefinition, columnIndex: Int, row: Row, schema: Schema, cellValue: String): (Option[FilePathBase], FilePathBase) = {
58+
val ruleValue = provider.referenceValue(columnIndex, row, schema)
59+
60+
if (columnDefinition.directives.contains(IgnoreCase())) (ruleValue.map(_.toLowerCase), cellValue.toLowerCase) else (ruleValue, cellValue)
61+
}
5762

5863
def valid(cellValue: String, columnDefinition: ColumnDefinition, columnIndex: Int,
5964
row: Row, schema: Schema, mayBeLast: Option[Boolean] = None): Boolean =

csv-validator-core/src/main/scala/uk/gov/nationalarchives/csv/validator/schema/v1_0/Rule.scala

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -128,47 +128,36 @@ case class FileExistsRule(pathSubstitutions: List[(String,String)], enforceCaseS
128128

129129
case class InRule(inValue: ArgProvider) extends Rule("in", Seq(inValue): _*) {
130130
override def valid(cellValue: String, columnDefinition: ColumnDefinition, columnIndex: Int, row: Row, schema: Schema, mayBeLast: Option[Boolean] = None): Boolean = {
131-
val ruleValue = inValue.referenceValue(columnIndex, row, schema)
132-
133-
val (rv, cv) = if (columnDefinition.directives.contains(IgnoreCase())) (ruleValue.get.toLowerCase, cellValue.toLowerCase) else (ruleValue.get, cellValue)
134-
rv contains cv
131+
val (potentialRv, cv) = argProviderHelper(inValue, columnDefinition, columnIndex, row, schema, cellValue)
132+
potentialRv.exists(_.contains(cv))
135133
}
136134
}
137135

138136
case class IsRule(isValue: ArgProvider) extends Rule("is", Seq(isValue): _*) {
139137
override def valid(cellValue: String, columnDefinition: ColumnDefinition, columnIndex: Int, row: Row, schema: Schema, mayBeLast: Option[Boolean] = None): Boolean = {
140-
val ruleValue = isValue.referenceValue(columnIndex, row, schema)
141-
142-
val (rv, cv) = if (columnDefinition.directives.contains(IgnoreCase())) (ruleValue.get.toLowerCase, cellValue.toLowerCase) else (ruleValue.get, cellValue)
143-
cv == rv
138+
val (potentialRv, cv) = argProviderHelper(isValue, columnDefinition, columnIndex, row, schema, cellValue)
139+
potentialRv.contains(cv)
144140
}
145141
}
146142

147-
148143
case class NotRule(notValue: ArgProvider) extends Rule("not", Seq(notValue): _*) {
149144
override def valid(cellValue: String, columnDefinition: ColumnDefinition, columnIndex: Int, row: Row, schema: Schema,mayBeLast: Option[Boolean] = None): Boolean = {
150-
val ruleValue = notValue.referenceValue(columnIndex, row, schema)
151-
152-
val (rv, cv) = if (columnDefinition.directives.contains(IgnoreCase())) (ruleValue.get.toLowerCase, cellValue.toLowerCase) else (ruleValue.get, cellValue)
153-
cv != rv
145+
val (potentialRv, cv) = argProviderHelper(notValue, columnDefinition, columnIndex, row, schema, cellValue)
146+
!potentialRv.contains(cv)
154147
}
155148
}
156149

157150
case class StartsRule(startsValue: ArgProvider) extends Rule("starts", Seq(startsValue): _*) {
158151
override def valid(cellValue: String, columnDefinition: ColumnDefinition, columnIndex: Int, row: Row, schema: Schema, mayBeLast: Option[Boolean] = None): Boolean = {
159-
val ruleValue = startsValue.referenceValue(columnIndex, row, schema)
160-
161-
val (rv, cv) = if (columnDefinition.directives.contains(IgnoreCase())) (ruleValue.get.toLowerCase, cellValue.toLowerCase) else (ruleValue.get, cellValue)
162-
cv startsWith rv
152+
val (potentialRv, cv) = argProviderHelper(startsValue, columnDefinition, columnIndex, row, schema, cellValue)
153+
potentialRv.exists(rv => cv.startsWith(rv))
163154
}
164155
}
165156

166157
case class EndsRule(endsValue: ArgProvider) extends Rule("ends", Seq(endsValue): _*) {
167158
override def valid(cellValue: String, columnDefinition: ColumnDefinition, columnIndex: Int, row: Row, schema: Schema, mayBeLast: Option[Boolean] = None): Boolean = {
168-
val ruleValue = endsValue.referenceValue(columnIndex, row, schema)
169-
170-
val (rv, cv) = if (columnDefinition.directives.contains(IgnoreCase())) (ruleValue.get.toLowerCase, cellValue.toLowerCase) else (ruleValue.get, cellValue)
171-
cv endsWith rv
159+
val (potentialRv, cv) = argProviderHelper(endsValue, columnDefinition, columnIndex, row, schema, cellValue)
160+
potentialRv.exists(rv => cv.endsWith(rv))
172161
}
173162
}
174163

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright (c) 2013, The National Archives <[email protected]>
3+
* https://www.nationalarchives.gov.uk
4+
*
5+
* This Source Code Form is subject to the terms of the Mozilla Public
6+
* License, v. 2.0. If a copy of the MPL was not distributed with this
7+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
8+
*/
9+
package uk.gov.nationalarchives.csv.validator.schema.v1_0
10+
11+
import cats.data.Validated
12+
import org.junit.runner.RunWith
13+
import org.specs2.mutable.Specification
14+
import org.specs2.runner.JUnitRunner
15+
import uk.gov.nationalarchives.csv.validator.metadata.{Cell, Row}
16+
import uk.gov.nationalarchives.csv.validator.schema._
17+
18+
@RunWith(classOf[JUnitRunner])
19+
class EndsRuleSpec extends Specification {
20+
21+
"EndsRule with a string literal behaviour" should {
22+
val globalDirsOne = List(TotalColumns(1))
23+
24+
"succeed if cell ends with endsRule value" in {
25+
val endsRule = EndsRule(Literal(Some("world")))
26+
27+
endsRule.evaluate(0, Row(List(Cell("hello world")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) mustEqual Validated.Valid(true)
28+
}
29+
30+
"fail if cell does not end with endsRule value" in {
31+
val endsRule = EndsRule(Literal(Some("hello world")))
32+
endsRule.evaluate(0, Row(List(Cell("hello")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) must beLike {
33+
case Validated.Invalid(messages) => messages.head mustEqual """ends("hello world") fails for line: 1, column: column1, value: "hello""""
34+
}
35+
}
36+
37+
"succeed if endsRule is the same as value" in {
38+
val endsRule = EndsRule(Literal(Some("hello world")))
39+
endsRule.evaluate(0, Row(List(Cell("hello world")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) mustEqual Validated.Valid(true)
40+
}
41+
42+
"succeed if endsRule's column reference does exist" in {
43+
val endsRule = EndsRule(ColumnReference(NamedColumnIdentifier("column1")))
44+
45+
endsRule.evaluate(0, Row(List(Cell("hello world")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) mustEqual Validated.Valid(true)
46+
}
47+
48+
"fail if endsRule's column reference doesn't exist" in {
49+
val endsRule = EndsRule(ColumnReference(NamedColumnIdentifier("nonExistentColumn")))
50+
51+
endsRule.evaluate(0, Row(List(Cell("hello world today")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) must beLike {
52+
case Validated.Invalid(messages) => messages.head mustEqual """ends($nonExistentColumn) fails for line: 1, column: column1, value: "hello world today""""
53+
}
54+
}
55+
56+
"succeed with @ignoreCase" in {
57+
val endsRule = EndsRule(Literal(Some("hello world")))
58+
endsRule.evaluate(0, Row(List(Cell("hello WORLD")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"), Nil, List(IgnoreCase()))))) mustEqual Validated.Valid(true)
59+
}
60+
}
61+
}

csv-validator-core/src/test/scala/uk/gov/nationalarchives/csv/validator/schema/v1_0/InRuleSpec.scala

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,34 @@ class InRuleSpec extends Specification {
2323
val globalDirsOne = List(TotalColumns(1))
2424

2525
"succeed if inRule is embedded in value" in {
26-
val inRule = InRule(Literal(Some("myhello world today")))
26+
val inRule = InRule(Literal(Some("hello world today")))
2727
inRule.evaluate(0, Row(List(Cell("hello world")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) mustEqual Validated.Valid(true)
2828
}
2929

30+
"fail if inRule is not in value" in {
31+
val inRule = InRule(Literal(Some("hello world")))
32+
33+
inRule.evaluate(0, Row(List(Cell("hello world today")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) must beLike {
34+
case Validated.Invalid(messages) => messages.head mustEqual """in("hello world") fails for line: 1, column: column1, value: "hello world today""""
35+
}
36+
}
37+
3038
"succeed if inRule is the same as value" in {
3139
val inRule = InRule(Literal(Some("hello world")))
3240
inRule.evaluate(0, Row(List(Cell("hello world")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) mustEqual Validated.Valid(true)
3341
}
3442

35-
"fail if inRule is not in value" in {
36-
val inRule = InRule(Literal(Some("hello world")))
43+
"succeed if inRule's column reference does exist" in {
44+
val inRule = InRule(ColumnReference(NamedColumnIdentifier("column1")))
45+
46+
inRule.evaluate(0, Row(List(Cell("hello world")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) mustEqual Validated.Valid(true)
47+
}
48+
49+
"fail if inRule's column reference doesn't exist" in {
50+
val inRule = InRule(ColumnReference(NamedColumnIdentifier("nonExistentColumn")))
3751

3852
inRule.evaluate(0, Row(List(Cell("hello world today")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) must beLike {
39-
case Validated.Invalid(messages) => messages.head mustEqual """in("hello world") fails for line: 1, column: column1, value: "hello world today""""
53+
case Validated.Invalid(messages) => messages.head mustEqual """in($nonExistentColumn) fails for line: 1, column: column1, value: "hello world today""""
4054
}
4155
}
4256

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright (c) 2013, The National Archives <[email protected]>
3+
* https://www.nationalarchives.gov.uk
4+
*
5+
* This Source Code Form is subject to the terms of the Mozilla Public
6+
* License, v. 2.0. If a copy of the MPL was not distributed with this
7+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
8+
*/
9+
package uk.gov.nationalarchives.csv.validator.schema.v1_0
10+
11+
import cats.data.Validated
12+
import org.junit.runner.RunWith
13+
import org.specs2.mutable.Specification
14+
import org.specs2.runner.JUnitRunner
15+
import uk.gov.nationalarchives.csv.validator.metadata.{Cell, Row}
16+
import uk.gov.nationalarchives.csv.validator.schema._
17+
18+
@RunWith(classOf[JUnitRunner])
19+
class IsRuleSpec extends Specification {
20+
21+
"IsRule with a string literal behaviour" should {
22+
val globalDirsOne = List(TotalColumns(1))
23+
24+
"succeed if isRule is the same as value" in {
25+
val isRule = IsRule(Literal(Some("hello world")))
26+
isRule.evaluate(0, Row(List(Cell("hello world")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) mustEqual Validated.Valid(true)
27+
}
28+
29+
"fail if isRule is not the same as value" in {
30+
val isRule = IsRule(Literal(Some("completely different value")))
31+
isRule.evaluate(0, Row(List(Cell("hello world")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) must beLike {
32+
case Validated.Invalid(messages) => messages.head mustEqual """is("completely different value") fails for line: 1, column: column1, value: "hello world""""
33+
}
34+
}
35+
36+
"fail if isRule is embedded in value" in {
37+
val isRule = IsRule(Literal(Some("hello world today")))
38+
isRule.evaluate(0, Row(List(Cell("hello world")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) must beLike {
39+
case Validated.Invalid(messages) => messages.head mustEqual """is("hello world today") fails for line: 1, column: column1, value: "hello world""""
40+
}
41+
}
42+
43+
"fail if isRule is not in value" in {
44+
val isRule = IsRule(Literal(Some("hello world")))
45+
isRule.evaluate(0, Row(List(Cell("hello world today")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) must beLike {
46+
case Validated.Invalid(messages) => messages.head mustEqual """is("hello world") fails for line: 1, column: column1, value: "hello world today""""
47+
}
48+
}
49+
50+
"succeed with @ignoreCase" in {
51+
val isRule = IsRule(Literal(Some("hello world")))
52+
isRule.evaluate(0, Row(List(Cell("hello WORLD")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"), Nil, List(IgnoreCase()))))) mustEqual Validated.Valid(true)
53+
}
54+
}
55+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) 2013, The National Archives <[email protected]>
3+
* https://www.nationalarchives.gov.uk
4+
*
5+
* This Source Code Form is subject to the terms of the Mozilla Public
6+
* License, v. 2.0. If a copy of the MPL was not distributed with this
7+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
8+
*/
9+
package uk.gov.nationalarchives.csv.validator.schema.v1_0
10+
11+
import cats.data.Validated
12+
import org.junit.runner.RunWith
13+
import org.specs2.mutable.Specification
14+
import org.specs2.runner.JUnitRunner
15+
import uk.gov.nationalarchives.csv.validator.metadata.{Cell, Row}
16+
import uk.gov.nationalarchives.csv.validator.schema._
17+
18+
@RunWith(classOf[JUnitRunner])
19+
class NotRuleSpec extends Specification {
20+
21+
"NotRule with a string literal behaviour" should {
22+
val globalDirsOne = List(TotalColumns(1))
23+
24+
"succeed if notRule is not the same as value" in {
25+
val notRule = NotRule(Literal(Some("completely different value")))
26+
notRule.evaluate(0, Row(List(Cell("hello world")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) mustEqual Validated.Valid(true)
27+
}
28+
29+
"succeed if notRule is the similar to value" in {
30+
val notRule = NotRule(Literal(Some("hello world ")))
31+
notRule.evaluate(0, Row(List(Cell("hello world")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) mustEqual Validated.Valid(true)
32+
}
33+
34+
"fail if notRule is the same as value" in {
35+
val notRule = NotRule(Literal(Some("hello world")))
36+
notRule.evaluate(0, Row(List(Cell("hello world")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) must beLike {
37+
case Validated.Invalid(messages) => messages.head mustEqual """not("hello world") fails for line: 1, column: column1, value: "hello world""""
38+
}
39+
}
40+
41+
"fail with @ignoreCase" in {
42+
val notRule = NotRule(Literal(Some("hello world")))
43+
notRule.evaluate(0, Row(List(Cell("hello WORLD")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"), Nil, List(IgnoreCase()))))) must beLike {
44+
case Validated.Invalid(messages) => messages.head mustEqual """not("hello world") fails for line: 1, column: column1, value: "hello WORLD""""
45+
}
46+
}
47+
}
48+
}

csv-validator-core/src/test/scala/uk/gov/nationalarchives/csv/validator/schema/v1_0/OrRuleSpec.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ class OrRuleSpec extends Specification {
6767

6868
val orRule = OrRule(leftInRule, rightInRule)
6969

70-
orRule.evaluate(0, Row(List(Cell("UK")), 1), schema) must throwA[NoSuchElementException]
70+
orRule.evaluate(0, Row(List(Cell("UK")), 1), schema) must beLike {
71+
case Validated.Invalid(messages) => messages.head mustEqual """in($ConfigurableCountry) or in("France") fails for line: 1, column: Country, value: "UK""""
72+
}
7173
}
7274

7375
"succeed when 3 'or' rules valid for right rule" in {

csv-validator-core/src/test/scala/uk/gov/nationalarchives/csv/validator/schema/v1_0/SchemaParserSpecs.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class SchemaParserSpecs extends SchemaSpecBase {
5959
LastName: @IgnoreCase regex ("[a]")"""
6060

6161
parse(new StringReader(schema)) must beLike {
62-
case Failure(messages, _) => messages mustEqual "Invalid column directive"
62+
case Failure(messages, _) => messages mustEqual "Invalid column definition"
6363
}
6464
}
6565

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright (c) 2013, The National Archives <[email protected]>
3+
* https://www.nationalarchives.gov.uk
4+
*
5+
* This Source Code Form is subject to the terms of the Mozilla Public
6+
* License, v. 2.0. If a copy of the MPL was not distributed with this
7+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
8+
*/
9+
package uk.gov.nationalarchives.csv.validator.schema.v1_0
10+
11+
import cats.data.Validated
12+
import org.junit.runner.RunWith
13+
import org.specs2.mutable.Specification
14+
import org.specs2.runner.JUnitRunner
15+
import uk.gov.nationalarchives.csv.validator.metadata.{Cell, Row}
16+
import uk.gov.nationalarchives.csv.validator.schema._
17+
18+
@RunWith(classOf[JUnitRunner])
19+
class StartsRuleSpec extends Specification {
20+
21+
"StartsRule with a string literal behaviour" should {
22+
val globalDirsOne = List(TotalColumns(1))
23+
24+
"succeed if cell starts with startsRule value" in {
25+
val startsRule = StartsRule(Literal(Some("hello world")))
26+
27+
startsRule.evaluate(0, Row(List(Cell("hello world today")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) mustEqual Validated.Valid(true)
28+
}
29+
30+
"fail if cell does not start with startsRule value" in {
31+
val startsRule = StartsRule(Literal(Some("hello world today")))
32+
startsRule.evaluate(0, Row(List(Cell("hello world")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) must beLike {
33+
case Validated.Invalid(messages) => messages.head mustEqual """starts("hello world today") fails for line: 1, column: column1, value: "hello world""""
34+
}
35+
}
36+
37+
"succeed if startsRule is the same as value" in {
38+
val startsRule = StartsRule(Literal(Some("hello world")))
39+
startsRule.evaluate(0, Row(List(Cell("hello world")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) mustEqual Validated.Valid(true)
40+
}
41+
42+
"succeed if startsRule's column reference does exist" in {
43+
val startsRule = StartsRule(ColumnReference(NamedColumnIdentifier("column1")))
44+
45+
startsRule.evaluate(0, Row(List(Cell("hello world")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) mustEqual Validated.Valid(true)
46+
}
47+
48+
"fail if startsRule's column reference doesn't exist" in {
49+
val startsRule = StartsRule(ColumnReference(NamedColumnIdentifier("nonExistentColumn")))
50+
51+
startsRule.evaluate(0, Row(List(Cell("hello world today")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"))))) must beLike {
52+
case Validated.Invalid(messages) => messages.head mustEqual """starts($nonExistentColumn) fails for line: 1, column: column1, value: "hello world today""""
53+
}
54+
}
55+
56+
"succeed with @ignoreCase" in {
57+
val startsRule = StartsRule(Literal(Some("hello world")))
58+
startsRule.evaluate(0, Row(List(Cell("hello WORLD")), 1), Schema(globalDirsOne, List(ColumnDefinition(NamedColumnIdentifier("column1"), Nil, List(IgnoreCase()))))) mustEqual Validated.Valid(true)
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)