Skip to content

Commit 61bc62e

Browse files
committed
increase coverage
1 parent 47d718d commit 61bc62e

File tree

3 files changed

+146
-12
lines changed

3 files changed

+146
-12
lines changed

lambdas/shared/src/common/validator/expression_checker.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def validate_expression(self, expression_type: str, rule, field_name, field_valu
105105
case _:
106106
return "Schema expression not found! Check your expression type : " + expression_type
107107

108-
# iso8086 date time validate
108+
# ISO 8601 date/datetime validate (currently date-only)
109109
def _validate_datetime(self, rule, field_name, field_value, row) -> ErrorReport:
110110
try:
111111
datetime.date.fromisoformat(field_value)
@@ -163,7 +163,7 @@ def _validate_integer(self, expression_rule, field_name, field_value, row) -> Er
163163
# "<10" means value must be less than 10
164164

165165
check_value = int(expression_rule)
166-
if field_value != check_value:
166+
if int(field_value) != check_value:
167167
raise RecordError(
168168
ExceptionMessages.RECORD_CHECK_FAILED,
169169
"Value integer check failed",
@@ -306,7 +306,7 @@ def _validate_not_equal(self, expression_rule, field_name, field_value, row) ->
306306
# In Validate
307307
def _validate_in(self, expression_rule, field_name, field_value, row) -> ErrorReport:
308308
try:
309-
if expression_rule.lower() in field_value.lower():
309+
if expression_rule.lower() not in field_value.lower():
310310
raise RecordError(
311311
ExceptionMessages.RECORD_CHECK_FAILED,
312312
"Data not in Value failed",
@@ -335,7 +335,7 @@ def _validate_n_range(self, expression_rule, field_name, field_value, row) -> Er
335335
range1 = float(rule[0])
336336
range2 = float(rule[1])
337337

338-
if range1 <= value >= range2:
338+
if not (range1 <= value <= range2):
339339
raise RecordError(
340340
ExceptionMessages.RECORD_CHECK_FAILED,
341341
"Value range check failed",
@@ -611,8 +611,8 @@ def _validate_gender(self, expressionRule, fieldName, fieldValue, row) -> ErrorR
611611
# PostCode Validate
612612
def _validate_post_code(self, expressionRule, fieldName, fieldValue, row) -> ErrorReport:
613613
try:
614-
regexRule = "^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y]"
615-
"[0-9]{1,2})|(([AZa-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2})$"
614+
# UK postcode regex (allows optional space)
615+
regexRule = r"^(GIR\s?0AA|(?:(?:[A-PR-UWYZ][0-9]{1,2})|(?:[A-PR-UWYZ][A-HK-Y][0-9]{1,2})|(?:[A-PR-UWYZ][0-9][A-HJKS-UW])|(?:[A-PR-UWYZ][A-HK-Y][0-9][ABEHMNPRV-Y]))\s?[0-9][ABD-HJLNP-UW-Z]{2})$"
616616
result = re.search(regexRule, fieldValue)
617617
if not result:
618618
raise RecordError(
@@ -664,7 +664,7 @@ def _validate_only_if(self, expressionRule, fieldName, fieldValue, row) -> Error
664664
conversionList = expressionRule.split("|")
665665
location = conversionList[0]
666666
valueCheck = conversionList[1]
667-
dataValue = self.dataParser.getKeyValue(location)
667+
dataValue = self.data_parser.get_key_value(location)
668668

669669
if dataValue[0] != valueCheck:
670670
raise RecordError(
@@ -690,7 +690,7 @@ def _validate_only_if(self, expressionRule, fieldName, fieldValue, row) -> Error
690690
# Check with Lookup
691691
def _validate_against_lookup(self, expressionRule, fieldName, fieldValue, row) -> ErrorReport:
692692
try:
693-
result = self.dataLookUp.findLookUp(fieldValue)
693+
result = self.data_look_up.find_lookup(fieldValue)
694694
if not result:
695695
raise RecordError(
696696
ExceptionMessages.RECORD_CHECK_FAILED,
@@ -715,7 +715,7 @@ def _validate_against_lookup(self, expressionRule, fieldName, fieldValue, row) -
715715
# Check with Key Lookup
716716
def _validate_against_key(self, expressionRule, fieldName, fieldValue, row) -> ErrorReport:
717717
try:
718-
result = self.KeyData.findKey(expressionRule, fieldValue)
718+
result = self.key_data.findKey(expressionRule, fieldValue)
719719
if not result:
720720
raise RecordError(
721721
ExceptionMessages.KEY_CHECK_FAILED,
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import unittest
2+
3+
from common.validator.expression_checker import ExpressionChecker
4+
from common.validator.record_error import ErrorReport
5+
6+
7+
class DummyParser:
8+
def __init__(self, data=None, raise_on_get=False):
9+
self._data = data or {}
10+
self._raise = raise_on_get
11+
12+
def get_key_value(self, field_name):
13+
if self._raise:
14+
raise RuntimeError("boom")
15+
return [self._data.get(field_name, "")]
16+
17+
18+
class StubLookup:
19+
def __init__(self, raise_on_call=False):
20+
self.raise_on_call = raise_on_call
21+
22+
def find_lookup(self, value):
23+
if self.raise_on_call:
24+
raise RuntimeError("boom")
25+
return "" # force error path
26+
27+
28+
class StubKeyData:
29+
def __init__(self, raise_on_call=False):
30+
self.raise_on_call = raise_on_call
31+
32+
def findKey(self, key_source, field_value):
33+
if self.raise_on_call:
34+
raise RuntimeError("boom")
35+
return False # force error path
36+
37+
38+
class TestExpressionCheckerExceptions(unittest.TestCase):
39+
def make_checker(self, data=None, report=True, raise_on_get=False):
40+
return ExpressionChecker(DummyParser(data, raise_on_get), False, report)
41+
42+
def test_regex_unexpected_true_false(self):
43+
# expression_rule None causes re.search to raise
44+
ec = self.make_checker(report=True)
45+
self.assertIsInstance(ec.validate_expression("REGEX", None, "r", "abc", 1), ErrorReport)
46+
ec2 = self.make_checker(report=False)
47+
self.assertIsNone(ec2.validate_expression("REGEX", None, "r", "abc", 1))
48+
49+
def test_in_unexpected_true_false(self):
50+
ec = self.make_checker(report=True)
51+
self.assertIsInstance(ec.validate_expression("IN", "ab", "f", None, 1), ErrorReport)
52+
ec2 = self.make_checker(report=False)
53+
self.assertIsNone(ec2.validate_expression("IN", "ab", "f", None, 1))
54+
55+
def test_length_unexpected_true_false(self):
56+
ec = self.make_checker(report=True)
57+
self.assertIsInstance(ec.validate_expression("LENGTH", "x", "s", "abcd", 1), ErrorReport)
58+
ec2 = self.make_checker(report=False)
59+
self.assertIsNone(ec2.validate_expression("LENGTH", "x", "s", "abcd", 1))
60+
61+
def test_float_unexpected_true_false(self):
62+
ec = self.make_checker(report=True)
63+
self.assertIsInstance(ec.validate_expression("FLOAT", None, "f", "abc", 1), ErrorReport)
64+
ec2 = self.make_checker(report=False)
65+
self.assertIsNone(ec2.validate_expression("FLOAT", None, "f", "abc", 1))
66+
67+
def test_uuid_unexpected_true_false(self):
68+
ec = self.make_checker(report=True)
69+
self.assertIsInstance(ec.validate_expression("UUID", None, "u", "not-a-uuid", 1), ErrorReport)
70+
ec2 = self.make_checker(report=False)
71+
self.assertIsNone(ec2.validate_expression("UUID", None, "u", "not-a-uuid", 1))
72+
73+
def test_maxobjects_unexpected_true_false(self):
74+
class NoLen:
75+
pass
76+
77+
ec = self.make_checker(report=True)
78+
self.assertIsInstance(ec.validate_expression("MAXOBJECTS", "1", "m", NoLen(), 1), ErrorReport)
79+
ec2 = self.make_checker(report=False)
80+
self.assertIsNone(ec2.validate_expression("MAXOBJECTS", "1", "m", NoLen(), 1))
81+
82+
def test_onlyif_unexpected_true_false(self):
83+
ec = self.make_checker(report=True, raise_on_get=True)
84+
self.assertIsInstance(ec.validate_expression("ONLYIF", "loc|VAL", "f", "x", 1), ErrorReport)
85+
ec2 = self.make_checker(report=False, raise_on_get=True)
86+
self.assertIsNone(ec2.validate_expression("ONLYIF", "loc|VAL", "f", "x", 1))
87+
88+
def test_lookup_unexpected_true_false(self):
89+
ec = self.make_checker(report=True)
90+
ec.data_look_up = StubLookup(raise_on_call=True)
91+
self.assertIsInstance(ec.validate_expression("LOOKUP", None, "l", "x", 1), ErrorReport)
92+
ec2 = self.make_checker(report=False)
93+
ec2.data_look_up = StubLookup(raise_on_call=True)
94+
self.assertIsNone(ec2.validate_expression("LOOKUP", None, "l", "x", 1))
95+
96+
def test_keycheck_unexpected_true_false(self):
97+
ec = self.make_checker(report=True)
98+
ec.key_data = StubKeyData(raise_on_call=True)
99+
self.assertIsInstance(ec.validate_expression("KEYCHECK", "Site", "k", "val", 1), ErrorReport)
100+
ec2 = self.make_checker(report=False)
101+
ec2.key_data = StubKeyData(raise_on_call=True)
102+
self.assertIsNone(ec2.validate_expression("KEYCHECK", "Site", "k", "val", 1))
103+
104+
def test_date_alias_and_upper_lower_edges(self):
105+
ec = self.make_checker({"d": "2025-01-01"})
106+
self.assertIsNone(ec.validate_expression("DATE", None, "d", "2025-01-01", 1))
107+
# unexpected exceptions for .isupper/.islower/St/E
108+
ec2 = self.make_checker()
109+
self.assertIsInstance(ec2.validate_expression("UPPER", None, "u", None, 1), ErrorReport)
110+
self.assertIsInstance(ec2.validate_expression("LOWER", None, "l", None, 1), ErrorReport)
111+
self.assertIsInstance(ec2.validate_expression("STARTSWITH", "a", "s", None, 1), ErrorReport)
112+
self.assertIsInstance(ec2.validate_expression("ENDSWITH", "a", "e", None, 1), ErrorReport)
113+
114+
def test_nrange_out_of_range(self):
115+
ec = self.make_checker()
116+
self.assertIsInstance(ec.validate_expression("NRANGE", "1,10", "n", "11", 1), ErrorReport)
117+
118+
def test_postcode_valid_and_unexpected(self):
119+
ec = self.make_checker()
120+
self.assertIsNone(ec.validate_expression("POSTCODE", None, "p", "EC1A 1BB", 1))
121+
self.assertIsInstance(ec.validate_expression("POSTCODE", None, "p", None, 1), ErrorReport)
122+
123+
def test_nhsnumber_valid_and_unexpected(self):
124+
ec = self.make_checker()
125+
self.assertIsNone(ec.validate_expression("NHSNUMBER", None, "n", "61234567890", 1))
126+
self.assertIsInstance(ec.validate_expression("NHSNUMBER", None, "n", None, 1), ErrorReport)
127+
128+
def test_in_pass_fail(self):
129+
ec = self.make_checker()
130+
self.assertIsNone(ec.validate_expression("IN", "ab", "f", "zzabzz", 1))
131+
self.assertIsInstance(ec.validate_expression("IN", "ab", "f", "zz", 1), ErrorReport)
132+
133+
134+
if __name__ == "__main__":
135+
unittest.main()

lambdas/shared/tests/test_common/validator/test_expression_checker_more.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,10 @@ def test_onlyif_uses_parser(self):
104104
data = {"loc": "VAL"}
105105
ec = self.make_checker(data)
106106
# expressionRule format: location|expected
107-
# Due to current implementation details, both branches return an ErrorReport
108-
# This still exercises the ONLYIF code path.
107+
# Matching value should pass (None), mismatch should return ErrorReport
109108
res1 = ec.validate_expression("ONLYIF", "loc|VAL", "f", "any", 1)
110109
res2 = ec.validate_expression("ONLYIF", "loc|NOPE", "f", "any", 1)
111-
self.assertIsInstance(res1, ErrorReport)
110+
self.assertIsNone(res1)
112111
self.assertIsInstance(res2, ErrorReport)
113112

114113

0 commit comments

Comments
 (0)