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

1#!/usr/bin/env python 

2 

3""" 

4camcops_server/cc_modules/tests/cc_policy_tests.py 

5 

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

7 

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

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

10 

11 This file is part of CamCOPS. 

12 

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. 

17 

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. 

22 

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

25 

26=============================================================================== 

27""" 

28 

29import logging 

30from typing import Dict 

31 

32from cardinal_pythonlib.logs import BraceStyleAdapter 

33from pendulum import Date 

34 

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 

41 

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

43 

44 

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

46# Unit tests 

47# ============================================================================= 

48 

49 

50class PolicyTests(ExtendedTestCase): 

51 """ 

52 Unit tests. 

53 """ 

54 

55 def test_policies(self) -> None: 

56 self.announce("test_policies") 

57 

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] 

86 

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 ] 

239 

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

241 correct_msg = "... correct" 

242 

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) 

248 

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) 

256 

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) 

266 

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) 

288 

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) 

301 

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) 

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)