Coverage for cc_modules/tests/cc_policy_tests.py : 14%

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