diff 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
line wrap: on
line diff
--- 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()
 

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