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
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-08 23:14 +0000
1#!/usr/bin/env python
3"""
4camcops_server/cc_modules/tests/cc_validator_tests.py
6===============================================================================
8 Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
9 Created by Rudolf Cardinal (rnc1001@cam.ac.uk).
11 This file is part of CamCOPS.
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.
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.
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/>.
26===============================================================================
28"""
30from typing import List
31import unittest
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)
51# =============================================================================
52# Unit tests
53# =============================================================================
56class ValidatorTests(unittest.TestCase):
57 """
58 Test our validators.
59 """
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)
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 )
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 )
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 )
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 )
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 )
155 def test_password_validator(self) -> None:
156 self.good_bad(
157 validate_new_password,
158 good=["gibberishfly93", "myotherarmadilloisintheworkshop"],
159 bad=["", " ", "aork", "hastalavista"],
160 )
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 )
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 )