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

37 statements  

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

1""" 

2camcops_server/cc_modules/tests/cc_validator_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 

28from typing import List 

29import unittest 

30 

31from camcops_server.cc_modules.cc_validators import ( 

32 anchor, 

33 min_max_copies, 

34 one_or_more, 

35 STRING_VALIDATOR_TYPE, 

36 validate_alphanum, 

37 validate_alphanum_underscore, 

38 validate_download_filename, 

39 validate_email, 

40 validate_human_name, 

41 validate_ip_address, 

42 validate_new_password, 

43 validate_restricted_sql_search_literal, 

44 validate_task_tablename, 

45 zero_or_more, 

46) 

47 

48 

49# ============================================================================= 

50# Unit tests 

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

52 

53 

54class ValidatorTests(unittest.TestCase): 

55 """ 

56 Test our validators. 

57 """ 

58 

59 def good_bad( 

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

61 ) -> None: 

62 """ 

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

64 """ 

65 for g in good: 

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

67 try: 

68 validator(g, None) 

69 except ValueError as e: 

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

71 raise 

72 for b in bad: 

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

74 self.assertRaises(ValueError, validator, b) 

75 

76 def test_regex_manipulation(self) -> None: 

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

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

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

80 self.assertEqual( 

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

82 ) 

83 

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

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

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

87 self.assertEqual( 

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

89 ) 

90 

91 def test_generic_validators(self) -> None: 

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

93 self.good_bad( 

94 validate_alphanum_underscore, 

95 good=["hello123_blah"], 

96 bad=["hello!"], 

97 ) 

98 self.good_bad( 

99 validate_human_name, 

100 good=[ 

101 "Al-Assad", 

102 "Al Assad", 

103 "John", 

104 "João", 

105 "タロウ", 

106 "やまだ", 

107 "山田", 

108 "先生", 

109 "мыхаыл", 

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

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

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

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

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

115 "상", 

116 "D'Addario", 

117 "John-Doe", 

118 "P.A.M.", 

119 ], 

120 bad=[ 

121 "hello!", 

122 "' --", 

123 # "<xss>", 

124 # "\"", 

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

126 ], 

127 ) 

128 self.good_bad( 

129 validate_restricted_sql_search_literal, 

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

131 bad=["F200!"], 

132 ) 

133 

134 def test_email_validator(self) -> None: 

135 self.good_bad( 

136 validate_email, 

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

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

139 ) 

140 

141 def test_ip_address_validator(self) -> None: 

142 self.good_bad( 

143 validate_ip_address, 

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

145 bad=[ 

146 "plaintext", 

147 "plain.domain.com", 

148 "two@at@symbols.com", 

149 "999.999.999.999", 

150 ], 

151 ) 

152 

153 def test_password_validator(self) -> None: 

154 self.good_bad( 

155 validate_new_password, 

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

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

158 ) 

159 

160 def test_task_tablename_validator(self) -> None: 

161 self.good_bad( 

162 validate_task_tablename, 

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

164 bad=[ 

165 "7hah", 

166 "thing space", 

167 "table!", 

168 # ... and of course: 

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

170 ], 

171 ) 

172 

173 def test_download_filename_validator(self) -> None: 

174 self.good_bad( 

175 validate_download_filename, 

176 good=[ 

177 "01.tsv", 

178 "_._", 

179 "_blah.txt", 

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

181 ], 

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

183 )