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

1"""

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

28 from typing import List

29 import unittest

30

31 from 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

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