-
Notifications
You must be signed in to change notification settings - Fork 83
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tests: Add 'store_samples' decorator
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 <[email protected]>
- Loading branch information
1 parent
3b12675
commit 7274953
Showing
2 changed files
with
94 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,6 +44,7 @@ htmlcov/ | |
|
||
# Sphinx stuff | ||
/docs/_build | ||
/docs/api/samples | ||
|
||
# Selenium test artifacts | ||
/selenium.log | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
# Patchwork - automated patch tracking system | ||
# Copyright (C) 2018 Stephen Finucane <[email protected]> | ||
# | ||
# 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 |