Skip to content

Commit 7274953

Browse files
committed
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]>
1 parent 3b12675 commit 7274953

File tree

2 files changed

+94
-0
lines changed

2 files changed

+94
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ htmlcov/
4444

4545
# Sphinx stuff
4646
/docs/_build
47+
/docs/api/samples
4748

4849
# Selenium test artifacts
4950
/selenium.log

patchwork/tests/api/utils.py

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Patchwork - automated patch tracking system
2+
# Copyright (C) 2018 Stephen Finucane <[email protected]>
3+
#
4+
# SPDX-License-Identifier: GPL-2.0-or-later
5+
6+
import functools
7+
import json
8+
import os
9+
10+
# docs/examples
11+
OUT_DIR = os.path.join(
12+
os.path.dirname(os.path.abspath(__file__)), os.pardir, os.pardir,
13+
os.pardir, 'docs', 'api', 'samples')
14+
15+
_WRITTEN_FILES = {}
16+
17+
18+
def _duplicate_sample(filename, func):
19+
global _WRITTEN_FILES
20+
21+
# sanity check to make sure we're not writing to the same file
22+
# twice
23+
if filename in _WRITTEN_FILES:
24+
# though if tests do this, we simply ignore subsequent
25+
# writes
26+
if _WRITTEN_FILES[filename] == func:
27+
return True
28+
29+
raise Exception(
30+
"Tests '{}' and '{}' write to the same file".format(
31+
_WRITTEN_FILES[filename], func))
32+
33+
_WRITTEN_FILES[filename] = func
34+
35+
return False
36+
37+
38+
def store_samples(filename):
39+
"""Wrapper to store responses and requests generated in tests.
40+
41+
These can be used in documentation. Only the first response or request body
42+
is saved per test.
43+
"""
44+
45+
if not os.path.exists(OUT_DIR):
46+
os.mkdir(OUT_DIR)
47+
48+
def inner(func):
49+
50+
def wrapper(self, *args, **kwargs):
51+
52+
def client_wrapper(orig_func, path, data=None, *orig_args,
53+
**orig_kwargs):
54+
55+
req_filename = filename + '-req.json'
56+
resp_filename = filename + '-resp.json'
57+
58+
# we don't have a request body for GET requests
59+
if orig_func != _get and not _duplicate_sample(
60+
req_filename, func):
61+
with open(os.path.join(OUT_DIR, req_filename), 'w') as fh:
62+
json.dump(data, fh, indent=4, separators=(',', ': '))
63+
64+
resp = orig_func(path, data, *orig_args, **orig_kwargs)
65+
66+
if not _duplicate_sample(resp_filename, func):
67+
with open(os.path.join(OUT_DIR, resp_filename), 'w') as fh:
68+
json.dump(resp.data, fh, indent=4,
69+
separators=(',', ': '))
70+
71+
return resp
72+
73+
# replace client.* with our own implementations
74+
_get = self.client.get
75+
self.client.get = functools.partial(client_wrapper, _get)
76+
_post = self.client.post
77+
self.client.post = functools.partial(client_wrapper, _post)
78+
_put = self.client.put
79+
self.client.put = functools.partial(client_wrapper, _put)
80+
_patch = self.client.patch
81+
self.client.patch = functools.partial(client_wrapper, _patch)
82+
83+
func(self, *args, **kwargs)
84+
85+
# ...then reverse
86+
self.client.patch = _patch
87+
self.client.put = _put
88+
self.client.post = _post
89+
self.client.get = _get
90+
91+
return wrapper
92+
93+
return inner

0 commit comments

Comments
 (0)