Coverage for cc_modules/tests/cc_policy_tests.py: 14%
77 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-08 23:14 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-08 23:14 +0000
1#!/usr/bin/env python
3"""
4camcops_server/cc_modules/tests/cc_policy_tests.py
6===============================================================================
8 Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
9 Created by Rudolf Cardinal (rnc1001@cam.ac.uk).
11 This file is part of CamCOPS.
13 CamCOPS is free software: you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
18 CamCOPS is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.
26===============================================================================
27"""
29import logging
30from typing import Dict
32from cardinal_pythonlib.logs import BraceStyleAdapter
33from pendulum import Date
35from camcops_server.cc_modules.cc_policy import TokenizedPolicy
36from camcops_server.cc_modules.cc_simpleobjects import (
37 BarePatientInfo,
38 IdNumReference,
39)
40from camcops_server.cc_modules.cc_unittest import ExtendedTestCase
42log = BraceStyleAdapter(logging.getLogger(__name__))
45# =============================================================================
46# Unit tests
47# =============================================================================
50class PolicyTests(ExtendedTestCase):
51 """
52 Unit tests.
53 """
55 def test_policies(self) -> None:
56 self.announce("test_policies")
58 class TestRig(object):
59 def __init__(
60 self,
61 policy: str,
62 syntactically_valid: bool = None,
63 valid: bool = None,
64 ptinfo_satisfies_id_policy: bool = None,
65 test_critical_single_numerical_id: bool = False,
66 critical_single_numerical_id: int = None,
67 compatible_with_tablet_id_policy: bool = None,
68 is_idnum_mandatory_in_policy: Dict[int, bool] = None,
69 ) -> None:
70 self.policy = policy
71 self.syntactically_valid = syntactically_valid
72 self.valid = valid
73 self.ptinfo_satisfies_id_policy = ptinfo_satisfies_id_policy
74 self.test_critical_single_numerical_id = (
75 test_critical_single_numerical_id
76 )
77 self.critical_single_numerical_id = (
78 critical_single_numerical_id
79 )
80 self.compatible_with_tablet_id_policy = (
81 compatible_with_tablet_id_policy
82 )
83 self.is_idnum_mandatory_in_policy = (
84 is_idnum_mandatory_in_policy or {}
85 ) # type: Dict[int, bool]
87 valid_idnums = list(range(1, 10 + 1))
88 # noinspection PyTypeChecker
89 bpi = BarePatientInfo(
90 forename="forename",
91 surname="surname",
92 dob=Date.today(), # random value
93 email="patient@example.com",
94 sex="F",
95 idnum_definitions=[IdNumReference(1, 1), IdNumReference(10, 3)],
96 )
97 test_policies = [
98 TestRig("", syntactically_valid=False, valid=False),
99 TestRig(
100 "sex AND (failure", syntactically_valid=False, valid=False
101 ),
102 TestRig("sex AND NOT", syntactically_valid=False, valid=False),
103 TestRig("OR OR", syntactically_valid=False, valid=False),
104 TestRig("idnum99", syntactically_valid=True, valid=False),
105 TestRig(
106 "sex AND idnum1",
107 syntactically_valid=True,
108 valid=True,
109 ptinfo_satisfies_id_policy=True,
110 test_critical_single_numerical_id=True,
111 critical_single_numerical_id=1,
112 compatible_with_tablet_id_policy=True,
113 is_idnum_mandatory_in_policy={1: True, 3: False},
114 ),
115 TestRig(
116 "sex AND NOT idnum1",
117 syntactically_valid=True,
118 valid=False, # not compatible with tablet policy
119 ptinfo_satisfies_id_policy=False,
120 test_critical_single_numerical_id=True,
121 critical_single_numerical_id=None,
122 compatible_with_tablet_id_policy=False,
123 is_idnum_mandatory_in_policy={1: False, 3: False},
124 ),
125 TestRig(
126 "sex AND NOT idnum2",
127 syntactically_valid=True,
128 valid=False, # not compatible with tablet policy
129 test_critical_single_numerical_id=True,
130 critical_single_numerical_id=None,
131 compatible_with_tablet_id_policy=False,
132 is_idnum_mandatory_in_policy={1: False, 3: False},
133 ),
134 TestRig(
135 "sex AND NOT idnum1 AND idnum3",
136 syntactically_valid=True,
137 valid=True,
138 test_critical_single_numerical_id=True,
139 critical_single_numerical_id=3,
140 compatible_with_tablet_id_policy=True,
141 is_idnum_mandatory_in_policy={1: False, 3: True},
142 ),
143 TestRig(
144 "sex AND NOT idnum2 AND idnum10",
145 syntactically_valid=True,
146 valid=True,
147 test_critical_single_numerical_id=True,
148 critical_single_numerical_id=10,
149 compatible_with_tablet_id_policy=True,
150 is_idnum_mandatory_in_policy={1: False, 3: False},
151 ),
152 TestRig(
153 "sex AND NOT idnum1 AND NOT idnum2 AND NOT idnum3",
154 syntactically_valid=True,
155 valid=False, # not compatible with tablet policy
156 test_critical_single_numerical_id=True,
157 critical_single_numerical_id=None,
158 compatible_with_tablet_id_policy=False,
159 is_idnum_mandatory_in_policy={1: False, 3: False},
160 ),
161 TestRig(
162 "sex AND NOT (idnum1 OR idnum2 OR idnum3)", # same as previous
163 syntactically_valid=True,
164 valid=False, # not compatible with tablet policy
165 test_critical_single_numerical_id=True,
166 critical_single_numerical_id=None,
167 compatible_with_tablet_id_policy=False,
168 is_idnum_mandatory_in_policy={1: False, 3: False},
169 ),
170 TestRig(
171 "NOT (sex OR forename OR surname OR dob OR anyidnum)",
172 syntactically_valid=True,
173 valid=False, # not compatible with tablet policy
174 test_critical_single_numerical_id=True,
175 critical_single_numerical_id=None,
176 compatible_with_tablet_id_policy=False,
177 is_idnum_mandatory_in_policy={1: False, 3: False},
178 ),
179 TestRig(
180 """
181 NOT (sex OR forename OR surname OR dob OR
182 idnum1 OR idnum2 OR idnum3)
183 """,
184 syntactically_valid=True,
185 valid=False, # not compatible with tablet policy
186 test_critical_single_numerical_id=True,
187 critical_single_numerical_id=None,
188 compatible_with_tablet_id_policy=False,
189 is_idnum_mandatory_in_policy={1: False, 3: False},
190 ),
191 TestRig(
192 "sex AND idnum1 AND otheridnum AND NOT address",
193 syntactically_valid=True,
194 valid=True,
195 test_critical_single_numerical_id=True,
196 critical_single_numerical_id=None,
197 compatible_with_tablet_id_policy=True,
198 is_idnum_mandatory_in_policy={1: True, 3: False},
199 ),
200 TestRig(
201 "sex AND idnum1 AND NOT (otheridnum OR forename OR surname OR "
202 "dob OR address OR gp OR otherdetails)",
203 syntactically_valid=True,
204 valid=True,
205 test_critical_single_numerical_id=True,
206 critical_single_numerical_id=1,
207 compatible_with_tablet_id_policy=True,
208 is_idnum_mandatory_in_policy={1: True, 3: False},
209 ),
210 TestRig(
211 "forename AND surname AND dob AND sex AND anyidnum",
212 syntactically_valid=True,
213 valid=True,
214 test_critical_single_numerical_id=True,
215 critical_single_numerical_id=None,
216 compatible_with_tablet_id_policy=True,
217 is_idnum_mandatory_in_policy={1: False, 3: False},
218 ),
219 TestRig(
220 "forename AND surname AND email AND sex AND idnum1",
221 syntactically_valid=True,
222 valid=True,
223 test_critical_single_numerical_id=True,
224 critical_single_numerical_id=1,
225 compatible_with_tablet_id_policy=True,
226 is_idnum_mandatory_in_policy={1: True, 3: False},
227 ),
228 TestRig(
229 "email AND sex AND idnum1",
230 syntactically_valid=True,
231 valid=True,
232 ptinfo_satisfies_id_policy=True,
233 test_critical_single_numerical_id=True,
234 critical_single_numerical_id=1,
235 compatible_with_tablet_id_policy=True,
236 is_idnum_mandatory_in_policy={1: True, 3: False},
237 ),
238 ]
240 test_idnums = [None, -1, 1, 3]
241 correct_msg = "... correct"
243 for tp in test_policies:
244 policy_string = tp.policy
245 log.info("Testing {!r}", policy_string)
246 p = TokenizedPolicy(policy_string)
247 p.set_valid_idnums(valid_idnums)
249 log.debug("Testing is_syntactically_valid()")
250 x = p.is_syntactically_valid()
251 self.assertIsInstance(x, bool)
252 log.info("is_syntactically_valid() -> {!r}".format(x))
253 if tp.syntactically_valid is not None:
254 self.assertEqual(x, tp.syntactically_valid)
255 log.info(correct_msg)
257 log.debug("Testing is_valid()")
258 x = p.is_valid(verbose=True)
259 self.assertIsInstance(x, bool)
260 log.info(
261 "is_valid(valid_idnums={!r}) -> {!r}".format(valid_idnums, x)
262 )
263 if tp.valid is not None:
264 self.assertEqual(x, tp.valid)
265 log.info(correct_msg)
267 log.debug("Testing is_idnum_mandatory_in_policy()")
268 for which_idnum in test_idnums:
269 log.debug("... for which_idnum = {}", which_idnum)
270 x = p.is_idnum_mandatory_in_policy(
271 which_idnum=which_idnum,
272 valid_idnums=valid_idnums,
273 verbose=True,
274 )
275 self.assertIsInstance(x, bool)
276 log.info(
277 "is_idnum_mandatory_in_policy(which_idnum={!r}, "
278 "valid_idnums={!r}) -> {!r}".format(
279 which_idnum, valid_idnums, x
280 )
281 )
282 if tp.is_idnum_mandatory_in_policy:
283 if which_idnum in tp.is_idnum_mandatory_in_policy:
284 self.assertEqual(
285 x, tp.is_idnum_mandatory_in_policy[which_idnum]
286 )
287 log.info(correct_msg)
289 log.debug("Testing find_critical_single_numerical_id()")
290 x = p.find_critical_single_numerical_id(
291 valid_idnums=valid_idnums, verbose=True
292 )
293 self.assertIsInstanceOrNone(x, int)
294 log.info(
295 "find_critical_single_numerical_id(valid_idnums={!r}) "
296 "-> {!r}".format(valid_idnums, x)
297 )
298 if tp.test_critical_single_numerical_id:
299 self.assertEqual(x, tp.critical_single_numerical_id)
300 log.info(correct_msg)
302 log.debug("Testing compatible_with_tablet_id_policy()")
303 x = p.compatible_with_tablet_id_policy(
304 valid_idnums=valid_idnums, verbose=True
305 )
306 self.assertIsInstance(x, bool)
307 log.info(
308 "compatible_with_tablet_id_policy(valid_idnums={!r}) "
309 "-> {!r}".format(valid_idnums, x)
310 )
311 if tp.compatible_with_tablet_id_policy is not None:
312 self.assertEqual(x, tp.compatible_with_tablet_id_policy)
313 log.info(correct_msg)
315 log.debug("Testing satisfies_id_policy()")
316 x = p.satisfies_id_policy(bpi)
317 self.assertIsInstance(x, bool)
318 log.info("satisfies_id_policy(bpi={}) -> {!r}".format(bpi, x))
319 if tp.ptinfo_satisfies_id_policy is not None:
320 self.assertEqual(x, tp.ptinfo_satisfies_id_policy)
321 log.info(correct_msg)