Coverage for cc_modules/tests/cc_validator_tests.py: 34%

38 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_validator_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""" 

29 

30from typing import List 

31import unittest 

32 

33from camcops_server.cc_modules.cc_validators import ( 

34 anchor, 

35 min_max_copies, 

36 one_or_more, 

37 STRING_VALIDATOR_TYPE, 

38 validate_alphanum, 

39 validate_alphanum_underscore, 

40 validate_download_filename, 

41 validate_email, 

42 validate_human_name, 

43 validate_ip_address, 

44 validate_new_password, 

45 validate_restricted_sql_search_literal, 

46 validate_task_tablename, 

47 zero_or_more, 

48) 

49 

50 

51# ============================================================================= 

52# Unit tests 

53# ============================================================================= 

54 

55 

56class ValidatorTests(unittest.TestCase): 

57 """ 

58 Test our validators. 

59 """ 

60 

61 def good_bad( 

62 self, validator: STRING_VALIDATOR_TYPE, good: List[str], bad: List[str] 

63 ) -> None: 

64 """ 

65 Test a validator with a bunch of known-good and known-bad strings. 

66 """ 

67 for g in good: 

68 # print(f"Testing good: {g!r}") 

69 try: 

70 validator(g, None) 

71 except ValueError as e: 

72 print(f"Validator failed for good value {g!r}: {e}") 

73 raise 

74 for b in bad: 

75 # print(f"Testing bad: {b!r}") 

76 self.assertRaises(ValueError, validator, b) 

77 

78 def test_regex_manipulation(self) -> None: 

79 self.assertEqual(anchor("x"), "^x$") 

80 self.assertEqual(anchor("x", anchor_start=False), "x$") 

81 self.assertEqual(anchor("x", anchor_end=False), "^x") 

82 self.assertEqual( 

83 anchor("x", anchor_start=False, anchor_end=False), "x" 

84 ) 

85 

86 self.assertEqual(zero_or_more("x"), "x*") 

87 self.assertEqual(one_or_more("x"), "x+") 

88 self.assertEqual(min_max_copies("x", max_count=5), "x{1,5}") 

89 self.assertEqual( 

90 min_max_copies("x", min_count=0, max_count=5), "x{0,5}" 

91 ) 

92 

93 def test_generic_validators(self) -> None: 

94 self.good_bad(validate_alphanum, good=["hello123"], bad=["hello!"]) 

95 self.good_bad( 

96 validate_alphanum_underscore, 

97 good=["hello123_blah"], 

98 bad=["hello!"], 

99 ) 

100 self.good_bad( 

101 validate_human_name, 

102 good=[ 

103 "Al-Assad", 

104 "Al Assad", 

105 "John", 

106 "João", 

107 "タロウ", 

108 "やまだ", 

109 "山田", 

110 "先生", 

111 "мыхаыл", 

112 "Θεοκλεια", 

113 # NOT WORKING: "आकाङ्क्षा", 

114 "علاء الدين", 

115 # NOT WORKING: "אַבְרָהָם", 

116 # NOT WORKING: "മലയാളം", 

117 "상", 

118 "D'Addario", 

119 "John-Doe", 

120 "P.A.M.", 

121 ], 

122 bad=[ 

123 "hello!", 

124 "' --", 

125 # "<xss>", 

126 # "\"", 

127 # "Robert'); DROP TABLE students;--", 

128 ], 

129 ) 

130 self.good_bad( 

131 validate_restricted_sql_search_literal, 

132 good=["F20%", "F2_0", "F200"], 

133 bad=["F200!"], 

134 ) 

135 

136 def test_email_validator(self) -> None: 

137 self.good_bad( 

138 validate_email, 

139 good=["blah@somewhere.com", "r&d@sillydomain.co.uk"], 

140 bad=["plaintext", "plain.domain.com", "two@at@symbols.com"], 

141 ) 

142 

143 def test_ip_address_validator(self) -> None: 

144 self.good_bad( 

145 validate_ip_address, 

146 good=["127.0.0.1", "131.141.8.42", "0.0.0.0"], 

147 bad=[ 

148 "plaintext", 

149 "plain.domain.com", 

150 "two@at@symbols.com", 

151 "999.999.999.999", 

152 ], 

153 ) 

154 

155 def test_password_validator(self) -> None: 

156 self.good_bad( 

157 validate_new_password, 

158 good=["gibberishfly93", "myotherarmadilloisintheworkshop"], 

159 bad=["", " ", "aork", "hastalavista"], 

160 ) 

161 

162 def test_task_tablename_validator(self) -> None: 

163 self.good_bad( 

164 validate_task_tablename, 

165 good=["phq9", "gad7_with_extra_bits"], 

166 bad=[ 

167 "7hah", 

168 "thing space", 

169 "table!", 

170 # ... and of course: 

171 "Robert'); DROP TABLE students;--", 

172 ], 

173 ) 

174 

175 def test_download_filename_validator(self) -> None: 

176 self.good_bad( 

177 validate_download_filename, 

178 good=[ 

179 "01.tsv", 

180 "_._", 

181 "_blah.txt", 

182 "CamCOPS_dump_2021-06-04T100622.zip", 

183 ], 

184 bad=["/etc/passwd", "_", "a", r"C:\autoexec.bat"], 

185 )