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
« 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
4===============================================================================
6 Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
7 Created by Rudolf Cardinal (rnc1001@cam.ac.uk).
9 This file is part of CamCOPS.
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.
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.
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/>.
24===============================================================================
26"""
28from typing import List
29import unittest
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)
49# =============================================================================
50# Unit tests
51# =============================================================================
54class ValidatorTests(unittest.TestCase):
55 """
56 Test our validators.
57 """
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)
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 )
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 )
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 )
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 )
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 )
153 def test_password_validator(self) -> None:
154 self.good_bad(
155 validate_new_password,
156 good=["gibberishfly93", "myotherarmadilloisintheworkshop"],
157 bad=["", " ", "aork", "hastalavista"],
158 )
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 )
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 )