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

76 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2025-01-30 13:48 +0000

1""" 

2camcops_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 

27import logging 

28from typing import Dict 

29 

30from cardinal_pythonlib.logs import BraceStyleAdapter 

31from pendulum import Date 

32 

33from camcops_server.cc_modules.cc_policy import TokenizedPolicy 

34from camcops_server.cc_modules.cc_simpleobjects import ( 

35 BarePatientInfo, 

36 IdNumReference, 

37) 

38from camcops_server.cc_modules.cc_unittest import ExtendedTestCase 

39 

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

41 

42 

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

44# Unit tests 

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

46 

47 

48class 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)