Hide keyboard shortcuts

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 

2 

3""" 

4camcops_server/cc_modules/tests/cc_policy_tests.py 

5 

6=============================================================================== 

7 

8 Copyright (C) 2012-2020 Rudolf Cardinal (rudolf@pobox.com). 

9 

10 This file is part of CamCOPS. 

11 

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. 

16 

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. 

21 

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/>. 

24 

25=============================================================================== 

26""" 

27 

28import logging 

29from typing import Dict 

30 

31from cardinal_pythonlib.logs import BraceStyleAdapter 

32from pendulum import Date 

33 

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 

40 

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

42 

43 

44# ============================================================================= 

45# Unit tests 

46# ============================================================================= 

47 

48class PolicyTests(ExtendedTestCase): 

49 """ 

50 Unit tests. 

51 """ 

52 def test_policies(self) -> None: 

53 self.announce("test_policies") 

54 

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 

75 

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 ] 

249 

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

251 correct_msg = "... correct" 

252 

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) 

258 

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) 

266 

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) 

275 

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) 

294 

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) 

304 

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) 

314 

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)