Mercurial > p > roundup > code
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']['/'] |
