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

1"""

2 camcops_server/cc_modules/tests/cc_policy_tests.py

3

4===============================================================================

5

6 Copyright (C) 2012, University of Cambridge, Department of Psychiatry.

7 Created by Rudolf Cardinal (rnc1001@cam.ac.uk).

8

9 This file is part of CamCOPS.

10

11 CamCOPS is free software: you can redistribute it and/or modify

12 it under the terms of the GNU General Public License as published by

13 the Free Software Foundation, either version 3 of the License, or

14 (at your option) any later version.

15

16 CamCOPS is distributed in the hope that it will be useful,

17 but WITHOUT ANY WARRANTY; without even the implied warranty of

18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

19 GNU General Public License for more details.

20

21 You should have received a copy of the GNU General Public License

22 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.

23

24===============================================================================

25"""

26

27 import logging

28 from typing import Dict

29

30 from cardinal_pythonlib.logs import BraceStyleAdapter

31 from pendulum import Date

32

33 from camcops_server.cc_modules.cc_policy import TokenizedPolicy

34 from camcops_server.cc_modules.cc_simpleobjects import (

35 BarePatientInfo,

36 IdNumReference,

37)

38 from camcops_server.cc_modules.cc_unittest import ExtendedTestCase

39

40 log = BraceStyleAdapter(logging.getLogger(__name__))

41

42

43# =============================================================================

44# Unit tests

45# =============================================================================

46

47

48 class PolicyTests(ExtendedTestCase):

49 """

50 Unit tests.

51 """

52

53 def test_policies(self) -> None:

54 self.announce("test_policies")

55

56 class TestRig(object):

57 def __init__(

58 self,

59 policy: str,

60 syntactically_valid: bool = None,

61 valid: bool = None,

62 ptinfo_satisfies_id_policy: bool = None,

63 test_critical_single_numerical_id: bool = False,

64 critical_single_numerical_id: int = None,

65 compatible_with_tablet_id_policy: bool = None,

66 is_idnum_mandatory_in_policy: Dict[int, bool] = None,

67 ) -> None:

68 self.policy = policy

69 self.syntactically_valid = syntactically_valid

70 self.valid = valid

71 self.ptinfo_satisfies_id_policy = ptinfo_satisfies_id_policy

72 self.test_critical_single_numerical_id = (

73 test_critical_single_numerical_id

74 )

75 self.critical_single_numerical_id = (

76 critical_single_numerical_id

77 )

78 self.compatible_with_tablet_id_policy = (

79 compatible_with_tablet_id_policy

80 )

81 self.is_idnum_mandatory_in_policy = (

82 is_idnum_mandatory_in_policy or {}

83 ) # type: Dict[int, bool]

84

85 valid_idnums = list(range(1, 10 + 1))

86 # noinspection PyTypeChecker

87 bpi = BarePatientInfo(

88 forename="forename",

89 surname="surname",

90 dob=Date.today(), # random value

91 email="patient@example.com",

92 sex="F",

93 idnum_definitions=[IdNumReference(1, 1), IdNumReference(10, 3)],

94 )

95 test_policies = [

96 TestRig("", syntactically_valid=False, valid=False),

97 TestRig(

98 "sex AND (failure", syntactically_valid=False, valid=False

99 ),

100 TestRig("sex AND NOT", syntactically_valid=False, valid=False),

101 TestRig("OR OR", syntactically_valid=False, valid=False),

102 TestRig("idnum99", syntactically_valid=True, valid=False),

103 TestRig(

104 "sex AND idnum1",

105 syntactically_valid=True,

106 valid=True,

107 ptinfo_satisfies_id_policy=True,

108 test_critical_single_numerical_id=True,

109 critical_single_numerical_id=1,

110 compatible_with_tablet_id_policy=True,

111 is_idnum_mandatory_in_policy={1: True, 3: False},

112 ),

113 TestRig(

114 "sex AND NOT idnum1",

115 syntactically_valid=True,

116 valid=False, # not compatible with tablet policy

117 ptinfo_satisfies_id_policy=False,

118 test_critical_single_numerical_id=True,

119 critical_single_numerical_id=None,

120 compatible_with_tablet_id_policy=False,

121 is_idnum_mandatory_in_policy={1: False, 3: False},

122 ),

123 TestRig(

124 "sex AND NOT idnum2",

125 syntactically_valid=True,

126 valid=False, # not compatible with tablet policy

127 test_critical_single_numerical_id=True,

128 critical_single_numerical_id=None,

129 compatible_with_tablet_id_policy=False,

130 is_idnum_mandatory_in_policy={1: False, 3: False},

131 ),

132 TestRig(

133 "sex AND NOT idnum1 AND idnum3",

134 syntactically_valid=True,

135 valid=True,

136 test_critical_single_numerical_id=True,

137 critical_single_numerical_id=3,

138 compatible_with_tablet_id_policy=True,

139 is_idnum_mandatory_in_policy={1: False, 3: True},

140 ),

141 TestRig(

142 "sex AND NOT idnum2 AND idnum10",

143 syntactically_valid=True,

144 valid=True,

145 test_critical_single_numerical_id=True,

146 critical_single_numerical_id=10,

147 compatible_with_tablet_id_policy=True,

148 is_idnum_mandatory_in_policy={1: False, 3: False},

149 ),

150 TestRig(

151 "sex AND NOT idnum1 AND NOT idnum2 AND NOT idnum3",

152 syntactically_valid=True,

153 valid=False, # not compatible with tablet policy

154 test_critical_single_numerical_id=True,

155 critical_single_numerical_id=None,

156 compatible_with_tablet_id_policy=False,

157 is_idnum_mandatory_in_policy={1: False, 3: False},

158 ),

159 TestRig(

160 "sex AND NOT (idnum1 OR idnum2 OR idnum3)", # same as previous

161 syntactically_valid=True,

162 valid=False, # not compatible with tablet policy

163 test_critical_single_numerical_id=True,

164 critical_single_numerical_id=None,

165 compatible_with_tablet_id_policy=False,

166 is_idnum_mandatory_in_policy={1: False, 3: False},

167 ),

168 TestRig(

169 "NOT (sex OR forename OR surname OR dob OR anyidnum)",

170 syntactically_valid=True,

171 valid=False, # not compatible with tablet policy

172 test_critical_single_numerical_id=True,

173 critical_single_numerical_id=None,

174 compatible_with_tablet_id_policy=False,

175 is_idnum_mandatory_in_policy={1: False, 3: False},

176 ),

177 TestRig(

178 """

179 NOT (sex OR forename OR surname OR dob OR

180 idnum1 OR idnum2 OR idnum3)

181 """,

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 "sex AND idnum1 AND otheridnum AND NOT address",

191 syntactically_valid=True,

192 valid=True,

193 test_critical_single_numerical_id=True,

194 critical_single_numerical_id=None,

195 compatible_with_tablet_id_policy=True,

196 is_idnum_mandatory_in_policy={1: True, 3: False},

197 ),

198 TestRig(

199 "sex AND idnum1 AND NOT (otheridnum OR forename OR surname OR "

200 "dob OR address OR gp OR otherdetails)",

201 syntactically_valid=True,

202 valid=True,

203 test_critical_single_numerical_id=True,

204 critical_single_numerical_id=1,

205 compatible_with_tablet_id_policy=True,

206 is_idnum_mandatory_in_policy={1: True, 3: False},

207 ),

208 TestRig(

209 "forename AND surname AND dob AND sex AND anyidnum",

210 syntactically_valid=True,

211 valid=True,

212 test_critical_single_numerical_id=True,

213 critical_single_numerical_id=None,

214 compatible_with_tablet_id_policy=True,

215 is_idnum_mandatory_in_policy={1: False, 3: False},

216 ),

217 TestRig(

218 "forename AND surname AND email AND sex AND idnum1",

219 syntactically_valid=True,

220 valid=True,

221 test_critical_single_numerical_id=True,

222 critical_single_numerical_id=1,

223 compatible_with_tablet_id_policy=True,

224 is_idnum_mandatory_in_policy={1: True, 3: False},

225 ),

226 TestRig(

227 "email AND sex AND idnum1",

228 syntactically_valid=True,

229 valid=True,

230 ptinfo_satisfies_id_policy=True,

231 test_critical_single_numerical_id=True,

232 critical_single_numerical_id=1,

233 compatible_with_tablet_id_policy=True,

234 is_idnum_mandatory_in_policy={1: True, 3: False},

235 ),

236 ]

237

238 test_idnums = [None, -1, 1, 3]

239 correct_msg = "... correct"

240

241 for tp in test_policies:

242 policy_string = tp.policy

243 log.info("Testing {!r}", policy_string)

244 p = TokenizedPolicy(policy_string)

245 p.set_valid_idnums(valid_idnums)

246

247 log.debug("Testing is_syntactically_valid()")

248 x = p.is_syntactically_valid()

249 self.assertIsInstance(x, bool)

250 log.info("is_syntactically_valid() -> {!r}".format(x))

251 if tp.syntactically_valid is not None:

252 self.assertEqual(x, tp.syntactically_valid)

253 log.info(correct_msg)

254

255 log.debug("Testing is_valid()")

256 x = p.is_valid(verbose=True)

257 self.assertIsInstance(x, bool)

258 log.info(

259 "is_valid(valid_idnums={!r}) -> {!r}".format(valid_idnums, x)

260 )

261 if tp.valid is not None:

262 self.assertEqual(x, tp.valid)

263 log.info(correct_msg)

264

265 log.debug("Testing is_idnum_mandatory_in_policy()")

266 for which_idnum in test_idnums:

267 log.debug("... for which_idnum = {}", which_idnum)

268 x = p.is_idnum_mandatory_in_policy(

269 which_idnum=which_idnum,

270 valid_idnums=valid_idnums,

271 verbose=True,

272 )

273 self.assertIsInstance(x, bool)

274 log.info(

275 "is_idnum_mandatory_in_policy(which_idnum={!r}, "

276 "valid_idnums={!r}) -> {!r}".format(

277 which_idnum, valid_idnums, x

278 )

279 )

280 if tp.is_idnum_mandatory_in_policy:

281 if which_idnum in tp.is_idnum_mandatory_in_policy:

282 self.assertEqual(

283 x, tp.is_idnum_mandatory_in_policy[which_idnum]

284 )

285 log.info(correct_msg)

286

287 log.debug("Testing find_critical_single_numerical_id()")

288 x = p.find_critical_single_numerical_id(

289 valid_idnums=valid_idnums, verbose=True

290 )

291 self.assertIsInstanceOrNone(x, int)

292 log.info(

293 "find_critical_single_numerical_id(valid_idnums={!r}) "

294 "-> {!r}".format(valid_idnums, x)

295 )

296 if tp.test_critical_single_numerical_id:

297 self.assertEqual(x, tp.critical_single_numerical_id)

298 log.info(correct_msg)

299

300 log.debug("Testing compatible_with_tablet_id_policy()")

301 x = p.compatible_with_tablet_id_policy(

302 valid_idnums=valid_idnums, verbose=True

303 )

304 self.assertIsInstance(x, bool)

305 log.info(

306 "compatible_with_tablet_id_policy(valid_idnums={!r}) "

307 "-> {!r}".format(valid_idnums, x)

308 )

309 if tp.compatible_with_tablet_id_policy is not None:

310 self.assertEqual(x, tp.compatible_with_tablet_id_policy)

311 log.info(correct_msg)

312

313 log.debug("Testing satisfies_id_policy()")

314 x = p.satisfies_id_policy(bpi)

315 self.assertIsInstance(x, bool)

316 log.info("satisfies_id_policy(bpi={}) -> {!r}".format(bpi, x))

317 if tp.ptinfo_satisfies_id_policy is not None:

318 self.assertEqual(x, tp.ptinfo_satisfies_id_policy)

319 log.info(correct_msg)