Mercurial > p > roundup > code
changeset 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 | 79b9343794f5 |
| children | 55b0abde56ab |
| files | CHANGES.txt roundup/rest.py test/test_liveserver.py |
| diffstat | 3 files changed, 108 insertions(+), 8 deletions(-) [+] |
line wrap: on
line diff
--- a/CHANGES.txt Sun Dec 15 01:35:51 2024 -0500 +++ b/CHANGES.txt Sun Dec 15 01:57:42 2024 -0500 @@ -41,6 +41,8 @@ for client.py-Client::_serve_file(). (John Rouillard) - issue2551381 - roundup-server parses URI's with multiple '?" incorrectly. (John Rouillard) +- issue2551382 - invalid @verbose, @page_* values in rest uri's + generate 409 not 400 error. (John Rouillard) Features:
--- a/roundup/rest.py Sun Dec 15 01:35:51 2024 -0500 +++ b/roundup/rest.py Sun Dec 15 01:57:42 2024 -0500 @@ -811,10 +811,18 @@ value = form_field.value if key.startswith("@page_"): # serve the paging purpose key = key[6:] - value = int(value) + try: + value = int(value) + except ValueError as e: + raise UsageError("When using @page_%s: %s" % + (key, e.args[0])) page[key] = value elif key == "@verbose": - verbose = int(value) + try: + verbose = int(value) + except ValueError as e: + raise UsageError("When using @verbose: %s" % + (e.args[0])) elif key in ["@fields", "@attrs"]: f = value.split(",") if len(f) == 1: @@ -1129,7 +1137,11 @@ # used only if no @fields/@attrs protected = value.lower() == "true" elif key == "@verbose": - verbose = int(value) + try: + verbose = int(value) + except ValueError as e: + raise UsageError("When using @verbose: %s" % + (e.args[0])) result = {} if props is None:
--- a/test/test_liveserver.py Sun Dec 15 01:35:51 2024 -0500 +++ b/test/test_liveserver.py Sun Dec 15 01:57:42 2024 -0500 @@ -21,6 +21,37 @@ reason='Skipping liveserver tests: requests library not available')) try: + import hypothesis + skip_hypothesis = lambda func, *args, **kwargs: func + + # ruff: noqa: E402 + from hypothesis import example, given, settings + from hypothesis.strategies import binary, characters, none, one_of, sampled_from, text + +except ImportError: + from .pytest_patcher import mark_class + skip_hypothesis = mark_class(pytest.mark.skip( + reason='Skipping hypothesis liveserver tests: hypothesis library not available')) + + # define a dummy decorator that can take args + def noop_decorators_with_args(*args, **kwargs): + def noop_decorators(func): + def internal(): + pass + return internal + return noop_decorators + + # define a dummy strategy + def noop_strategy(*args, **kwargs): + pass + + # define the decorator functions + example = given = settings = noop_decorators_with_args + # and stratgies using in decorators + binary = characters = none = one_of = sampled_from = text = noop_strategy + + +try: import brotli skip_brotli = lambda func, *args, **kwargs: func except ImportError: @@ -149,11 +180,9 @@ # doesn't support the max bytes to read argument. return RequestDispatcher(self.dirname) - -@skip_requests -class BaseTestCases(WsgiSetup): - """Class with all tests to run against wsgi server. Is reused when - wsgi server is started with various feature flags +class ClientSetup(): + """ Utility programs for the client querying a server. + Just a login session at the moment but more to come I am sure. """ def create_login_session(self, username="admin", password="sekrit", @@ -176,6 +205,63 @@ return session return session, response + +@skip_hypothesis +class FuzzGetUrls(WsgiSetup, ClientSetup): + + _max_examples = 100 + + @given(sampled_from(['@verbose', '@page_size', '@page_index']), + one_of(characters(),text(min_size=1))) + @settings(max_examples=_max_examples, + deadline=10000) # 10000ms + def test_class_url_param_accepting_integer_values(self, param, value): + """Tests all integer args for rest url. @page_* is the + same code for all *. + """ + session, _response = self.create_login_session() + url = '%s/rest/data/status' % (self.url_base()) + query = '%s=%s' % (param, value) + f = session.get(url, params=query) + try: + if int(value) >= 0: + self.assertEqual(f.status_code, 200) + except ValueError: + if value in ['#', '&']: + self.assertEqual(f.status_code, 200) + else: + # invalid value for param + self.assertEqual(f.status_code, 400) + + @given(sampled_from(['@verbose']), + one_of(characters(),text(min_size=1))) + @settings(max_examples=_max_examples, + deadline=10000) # 10000ms + def test_element_url_param_accepting_integer_values(self, param, value): + """Tests all integer args for rest url. @page_* is the + same code for all *. + """ + session, _response = self.create_login_session() + url = '%s/rest/data/status/1' % (self.url_base()) + query = '%s=%s' % (param, value) + f = session.get(url, params=query) + try: + if int(value) >= 0: + self.assertEqual(f.status_code, 200) + except ValueError: + if value in ['#', '&']: + self.assertEqual(f.status_code, 200) + else: + # invalid value for param + self.assertEqual(f.status_code, 400) + + +@skip_requests +class BaseTestCases(WsgiSetup, ClientSetup): + """Class with all tests to run against wsgi server. Is reused when + wsgi server is started with various feature flags + """ + def test_cookie_attributes(self): session, _response = self.create_login_session()
