comparison test/test_liveserver.py @ 8213:14e92a595828

fix(web) issue2551382 - 409 not 400 errors returned invalid integer values for @verbose, @page_* values in rest uri's generated a 409 (Update Conflict) error not a generic 400 error. Found it when I was working on adding fuzz testing to check error handling for query parameters in REST url's. This also ads the tests in test_liveserver that found the error. Also refactored tst_liveserver to allow resuse of session login method for the new fuzz testing class as well.
author John Rouillard <rouilj@ieee.org>
date Sun, 15 Dec 2024 01:57:42 -0500
parents 3f0f4746dc7e
children 55b0abde56ab
comparison
equal deleted inserted replaced
8212:79b9343794f5 8213:14e92a595828
17 skip_requests = lambda func, *args, **kwargs: func 17 skip_requests = lambda func, *args, **kwargs: func
18 except ImportError: 18 except ImportError:
19 from .pytest_patcher import mark_class 19 from .pytest_patcher import mark_class
20 skip_requests = mark_class(pytest.mark.skip( 20 skip_requests = mark_class(pytest.mark.skip(
21 reason='Skipping liveserver tests: requests library not available')) 21 reason='Skipping liveserver tests: requests library not available'))
22
23 try:
24 import hypothesis
25 skip_hypothesis = lambda func, *args, **kwargs: func
26
27 # ruff: noqa: E402
28 from hypothesis import example, given, settings
29 from hypothesis.strategies import binary, characters, none, one_of, sampled_from, text
30
31 except ImportError:
32 from .pytest_patcher import mark_class
33 skip_hypothesis = mark_class(pytest.mark.skip(
34 reason='Skipping hypothesis liveserver tests: hypothesis library not available'))
35
36 # define a dummy decorator that can take args
37 def noop_decorators_with_args(*args, **kwargs):
38 def noop_decorators(func):
39 def internal():
40 pass
41 return internal
42 return noop_decorators
43
44 # define a dummy strategy
45 def noop_strategy(*args, **kwargs):
46 pass
47
48 # define the decorator functions
49 example = given = settings = noop_decorators_with_args
50 # and stratgies using in decorators
51 binary = characters = none = one_of = sampled_from = text = noop_strategy
52
22 53
23 try: 54 try:
24 import brotli 55 import brotli
25 skip_brotli = lambda func, *args, **kwargs: func 56 skip_brotli = lambda func, *args, **kwargs: func
26 except ImportError: 57 except ImportError:
147 else: 178 else:
148 # wsgiref/validator.py InputWrapper::readline is broke and 179 # wsgiref/validator.py InputWrapper::readline is broke and
149 # doesn't support the max bytes to read argument. 180 # doesn't support the max bytes to read argument.
150 return RequestDispatcher(self.dirname) 181 return RequestDispatcher(self.dirname)
151 182
152 183 class ClientSetup():
153 @skip_requests 184 """ Utility programs for the client querying a server.
154 class BaseTestCases(WsgiSetup): 185 Just a login session at the moment but more to come I am sure.
155 """Class with all tests to run against wsgi server. Is reused when
156 wsgi server is started with various feature flags
157 """ 186 """
158 187
159 def create_login_session(self, username="admin", password="sekrit", 188 def create_login_session(self, username="admin", password="sekrit",
160 return_response=True, expect_login_ok=True): 189 return_response=True, expect_login_ok=True):
161 # Set up session to manage cookies <insert blue monster here> 190 # Set up session to manage cookies <insert blue monster here>
173 session.cookies) 202 session.cookies)
174 203
175 if not return_response: 204 if not return_response:
176 return session 205 return session
177 return session, response 206 return session, response
207
208
209 @skip_hypothesis
210 class FuzzGetUrls(WsgiSetup, ClientSetup):
211
212 _max_examples = 100
213
214 @given(sampled_from(['@verbose', '@page_size', '@page_index']),
215 one_of(characters(),text(min_size=1)))
216 @settings(max_examples=_max_examples,
217 deadline=10000) # 10000ms
218 def test_class_url_param_accepting_integer_values(self, param, value):
219 """Tests all integer args for rest url. @page_* is the
220 same code for all *.
221 """
222 session, _response = self.create_login_session()
223 url = '%s/rest/data/status' % (self.url_base())
224 query = '%s=%s' % (param, value)
225 f = session.get(url, params=query)
226 try:
227 if int(value) >= 0:
228 self.assertEqual(f.status_code, 200)
229 except ValueError:
230 if value in ['#', '&']:
231 self.assertEqual(f.status_code, 200)
232 else:
233 # invalid value for param
234 self.assertEqual(f.status_code, 400)
235
236 @given(sampled_from(['@verbose']),
237 one_of(characters(),text(min_size=1)))
238 @settings(max_examples=_max_examples,
239 deadline=10000) # 10000ms
240 def test_element_url_param_accepting_integer_values(self, param, value):
241 """Tests all integer args for rest url. @page_* is the
242 same code for all *.
243 """
244 session, _response = self.create_login_session()
245 url = '%s/rest/data/status/1' % (self.url_base())
246 query = '%s=%s' % (param, value)
247 f = session.get(url, params=query)
248 try:
249 if int(value) >= 0:
250 self.assertEqual(f.status_code, 200)
251 except ValueError:
252 if value in ['#', '&']:
253 self.assertEqual(f.status_code, 200)
254 else:
255 # invalid value for param
256 self.assertEqual(f.status_code, 400)
257
258
259 @skip_requests
260 class BaseTestCases(WsgiSetup, ClientSetup):
261 """Class with all tests to run against wsgi server. Is reused when
262 wsgi server is started with various feature flags
263 """
178 264
179 def test_cookie_attributes(self): 265 def test_cookie_attributes(self):
180 session, _response = self.create_login_session() 266 session, _response = self.create_login_session()
181 267
182 cookie_box = session.cookies._cookies['localhost.local']['/'] 268 cookie_box = session.cookies._cookies['localhost.local']['/']

Roundup Issue Tracker: http://roundup-tracker.org/