diff test/rest_common.py @ 5655:207e0f5d551c

Merge in non-conflicting changes from ba67e397f063 including workaround for: https://bugs.python.org/issue27777 1) cgi/client.py: override cgi.FieldStorage's make_file so that file is always created in binary/byte mode. This means that json (and xml) are bytes not strings. 2) rest.py: try harder to find dicttoxml in roundup directory or on sys.path. This just worked under python 2 but python 3 only searches sys.path by default and does not search relative like python 2. 3) test/rest_common.py: workaround for issue27777 Also removed an unneeded case insensitive dict implementation.
author John Rouillard <rouilj@ieee.org>
date Mon, 18 Mar 2019 21:42:33 -0400
parents a02ef29b4242 ba67e397f063
children d26d2590cd8c
line wrap: on
line diff
--- a/test/rest_common.py	Sun Mar 17 21:47:45 2019 -0400
+++ b/test/rest_common.py	Mon Mar 18 21:42:33 2019 -0400
@@ -85,7 +85,7 @@
     def get_header (self, header, not_found=None):
         try:
             return self.headers[header.lower()]
-        except (AttributeError, KeyError):
+        except (AttributeError, KeyError, TypeError):
             return not_found
 
     def testGet(self):
@@ -352,7 +352,7 @@
 
         Run over header only, etag in form only, both,
         each one broke and no etag. Use the put command
-        to triger the etag checking code.
+        to trigger the etag checking code.
         '''
         for mode in ('header', 'etag', 'both',
                      'brokenheader', 'brokenetag', 'none'):
@@ -399,6 +399,11 @@
             else:
                 self.assertEqual(self.dummy_client.response_code, 412)
 
+    def make_file(self, arg=None):
+        ''' work around https://bugs.python.org/issue27777 '''
+        import tempfile
+        return tempfile.TemporaryFile("wb+")
+
     def testDispatch(self):
         """
         run changes through rest dispatch(). This also tests
@@ -406,7 +411,15 @@
         code that changes json payload into something we can
         process.
         """
-        # Set joe's 'realname' using json data.
+
+        # Override the make_file so it is always set to binary
+        # read mode. This is needed so we can send a json 
+        # body.
+        saved_make_file = cgi.FieldStorage.make_file
+        cgi.FieldStorage.make_file = self.make_file
+
+        # TEST #1
+        # PUT: joe's 'realname' using json data.
         # simulate: /rest/data/user/<id>/realname
         # use etag in header
         etag = calculate_etag(self.db.user.getnode(self.joeid))
@@ -417,6 +430,7 @@
         }
         headers={"accept": "application/json",
                  "content-type": env['CONTENT_TYPE'],
+                 "content-length": env['CONTENT_LENGTH'],
                  "etag": etag
         }
         self.headers=headers
@@ -439,6 +453,7 @@
                          'Joe Doe 1')
         del(self.headers)
 
+        # TEST #2
         # Set joe's 'realname' using json data.
         # simulate: /rest/data/user/<id>/realname
         # use etag in payload
@@ -446,17 +461,21 @@
         body=s2b('{ "@etag": "%s", "data": "Joe Doe 2" }'%etag)
         env = { "CONTENT_TYPE": "application/json",
                 "CONTENT_LENGTH": len(body),
-                "REQUEST_METHOD": "PUT"
+                "REQUEST_METHOD": "PUT",
         }
-        headers={"accept": "application/json",
-                 "content-type": env['CONTENT_TYPE']
-        }
-        self.headers=headers
+        self.headers=None  # have FieldStorage get len from env.
         body_file=BytesIO(body)  # FieldStorage needs a file
         form = cgi.FieldStorage(body_file,
-                                headers=headers,
+                                headers=None,
                                 environ=env)
         self.server.client.request.headers.get=self.get_header
+
+        headers={"accept": "application/json",
+                 "content-type": env['CONTENT_TYPE'],
+                 "etag": etag
+        }
+        self.headers=headers # set for dispatch
+
         results = self.server.dispatch('PUT',
                             "/rest/data/user/%s/realname"%self.joeid,
                             form)
@@ -468,12 +487,13 @@
                          'Joe Doe 2')
         del(self.headers)
 
+        # TEST #3
         # change Joe's realname via a normal web form
         # This generates a FieldStorage that looks like:
         #  FieldStorage(None, None, [])
         # use etag from header
         #
-        # also use a GET on the uri via the dispatch to get
+        # Also use GET on the uri via the dispatch to retrieve
         # the results from the db.
         etag = calculate_etag(self.db.user.getnode(self.joeid))
         headers={"etag": etag,
@@ -499,12 +519,14 @@
         self.assertEqual(json_dict['data']['link'],
                          "http://tracker.example/cgi-bin/"
                          "roundup.cgi/bugs/rest/data/user/3/realname") 
-        self.assertEqual(json_dict['data']['type'], "<type 'str'>")
+        self.assertIn(json_dict['data']['type'], ("<class 'str'>",
+                                                  "<type 'str'>"))
         self.assertEqual(json_dict['data']["id"], "3")
         del(self.headers)
 
 
-        # PATCH joe's email address with json
+        # TEST #4
+        # PATCH: joe's email address with json
         # save address so we can use it later
         stored_results = self.server.get_element('user', self.joeid,
                                                  self.empty_form)
@@ -517,7 +539,8 @@
                 "REQUEST_METHOD": "PATCH"
         }
         headers={"accept": "application/json",
-                 "content-type": env['CONTENT_TYPE']
+                 "content-type": env['CONTENT_TYPE'],
+                 "content-length": len(body)
         }
         self.headers=headers
         body_file=BytesIO(body)  # FieldStorage needs a file
@@ -535,7 +558,7 @@
         self.assertEqual(results['data']['attributes']['address'],
                          'demo2@example.com')
 
-        # and set it back
+        # and set it back reusing env and headers from last test
         etag = calculate_etag(self.db.user.getnode(self.joeid))
         body=s2b('{ "address": "%s", "@etag": "%s"}'%(
             stored_results['data']['attributes']['address'],
@@ -557,15 +580,21 @@
                          'random@home.org')
         del(self.headers)
 
-        # POST to create new issue
+        # TEST #5
+        # POST: create new issue
+        # no etag needed
+        # FIXME at some point we probably want to implement
+        # Post Once Only, so we need to add a Post Once Exactly
+        # test and a resubmit as well.
+        etag = "not needed"
         body=b'{ "title": "foo bar", "priority": "critical" }'
-
         env = { "CONTENT_TYPE": "application/json",
                 "CONTENT_LENGTH": len(body),
                 "REQUEST_METHOD": "POST"
         }
         headers={"accept": "application/json",
-                 "content-type": env['CONTENT_TYPE']
+                 "content-type": env['CONTENT_TYPE'],
+                 "content-length": len(body)
         }
         self.headers=headers
         body_file=BytesIO(body)  # FieldStorage needs a file
@@ -588,6 +617,9 @@
                          'foo bar')
         del(self.headers)
 
+        # reset the make_file method in the class
+        cgi.FieldStorage.make_file = saved_make_file
+
     def testPut(self):
         """
         Change joe's 'realname'

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