]> git.ipfire.org Git - thirdparty/patchwork.git/commitdiff
tests: Add 'store_samples' decorator
authorStephen Finucane <stephen@that.guru>
Sun, 28 Oct 2018 14:40:31 +0000 (14:40 +0000)
committerStephen Finucane <stephen@that.guru>
Sat, 22 Dec 2018 16:13:26 +0000 (16:13 +0000)
We want to start including sample API requests and responses in our
documentation.  Given that these may get out of date over time, we
should really generate these things dynamically. Create a decorator that
will allow us to do just that.

Signed-off-by: Stephen Finucane <stephen@that.guru>
.gitignore
patchwork/tests/api/utils.py [new file with mode: 0644]

index a33d102907a82053bb8126c9ebf9b9daa999951a..1282bc9fda86d8c290fbfc54c9f6ac8e4b179e0a 100644 (file)
@@ -44,6 +44,7 @@ htmlcov/
 
 # Sphinx stuff
 /docs/_build
+/docs/api/samples
 
 # Selenium test artifacts
 /selenium.log
diff --git a/patchwork/tests/api/utils.py b/patchwork/tests/api/utils.py
new file mode 100644 (file)
index 0000000..1097bb0
--- /dev/null
@@ -0,0 +1,93 @@
+# Patchwork - automated patch tracking system
+# Copyright (C) 2018 Stephen Finucane <stephen@that.guru>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import functools
+import json
+import os
+
+# docs/examples
+OUT_DIR = os.path.join(
+    os.path.dirname(os.path.abspath(__file__)), os.pardir, os.pardir,
+    os.pardir, 'docs', 'api', 'samples')
+
+_WRITTEN_FILES = {}
+
+
+def _duplicate_sample(filename, func):
+    global _WRITTEN_FILES
+
+    # sanity check to make sure we're not writing to the same file
+    # twice
+    if filename in _WRITTEN_FILES:
+        # though if tests do this, we simply ignore subsequent
+        # writes
+        if _WRITTEN_FILES[filename] == func:
+            return True
+
+        raise Exception(
+            "Tests '{}' and '{}' write to the same file".format(
+                _WRITTEN_FILES[filename], func))
+
+    _WRITTEN_FILES[filename] = func
+
+    return False
+
+
+def store_samples(filename):
+    """Wrapper to store responses and requests generated in tests.
+
+    These can be used in documentation. Only the first response or request body
+    is saved per test.
+    """
+
+    if not os.path.exists(OUT_DIR):
+        os.mkdir(OUT_DIR)
+
+    def inner(func):
+
+        def wrapper(self, *args, **kwargs):
+
+            def client_wrapper(orig_func, path, data=None, *orig_args,
+                               **orig_kwargs):
+
+                req_filename = filename + '-req.json'
+                resp_filename = filename + '-resp.json'
+
+                # we don't have a request body for GET requests
+                if orig_func != _get and not _duplicate_sample(
+                        req_filename, func):
+                    with open(os.path.join(OUT_DIR, req_filename), 'w') as fh:
+                        json.dump(data, fh, indent=4, separators=(',', ': '))
+
+                resp = orig_func(path, data, *orig_args, **orig_kwargs)
+
+                if not _duplicate_sample(resp_filename, func):
+                    with open(os.path.join(OUT_DIR, resp_filename), 'w') as fh:
+                        json.dump(resp.data, fh, indent=4,
+                                  separators=(',', ': '))
+
+                return resp
+
+            # replace client.* with our own implementations
+            _get = self.client.get
+            self.client.get = functools.partial(client_wrapper, _get)
+            _post = self.client.post
+            self.client.post = functools.partial(client_wrapper, _post)
+            _put = self.client.put
+            self.client.put = functools.partial(client_wrapper, _put)
+            _patch = self.client.patch
+            self.client.patch = functools.partial(client_wrapper, _patch)
+
+            func(self, *args, **kwargs)
+
+            # ...then reverse
+            self.client.patch = _patch
+            self.client.put = _put
+            self.client.post = _post
+            self.client.get = _get
+
+        return wrapper
+
+    return inner