From patchwork Tue Oct 30 11:31:41 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 990759 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42kq9x0d4Wz9s7W for ; Tue, 30 Oct 2018 22:34:13 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=that.guru Authentication-Results: ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="eWIdNQf3"; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 42kq9w5zJHzDrSM for ; Tue, 30 Oct 2018 22:34:12 +1100 (AEDT) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=that.guru Authentication-Results: lists.ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="eWIdNQf3"; dkim-atps=neutral X-Original-To: patchwork@lists.ozlabs.org Delivered-To: patchwork@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (mailfrom) smtp.mailfrom=that.guru (client-ip=185.234.75.16; helo=relay016.mxrelay.co; envelope-from=stephen@that.guru; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=that.guru Authentication-Results: lists.ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="eWIdNQf3"; dkim-atps=neutral Received: from relay016.mxrelay.co (relay016.mxrelay.co [185.234.75.16]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 42kq81543MzF100 for ; Tue, 30 Oct 2018 22:32:33 +1100 (AEDT) Received: from filter001.mxrelay.co (filter001.mxrelay.co [64.52.23.203]) by relay016.mxrelay.co (Postfix) with ESMTP id C72503F1DD for ; Tue, 30 Oct 2018 11:32:00 +0000 (UTC) Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter001.mxrelay.co (Postfix) with ESMTPS id 01F4C100065 for ; Tue, 30 Oct 2018 11:32:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=that.guru; s=default; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From: Sender:Reply-To:MIME-Version:Content-Type:Content-Transfer-Encoding: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=A9Bf3Ujp0Rb8Wyt+fVSedTIGkMO21hcwqrHfFdWvsPE=; b=eWIdNQf300fghZXDjQuAV6f/um 2/ZtR/Dvvqn3gKOilmwRnWFoxyVQzaRGTKsIDS0iujqvxAzA22yg2ggitUi8DsybRoAPg7r0nOMn+ GLbLVt+My7b603TZyoVF9ok16fLYUWCQ3sfsl4JEU9hYAlOZXNSuB+g1WaL/rbo6cd/GhKL6nsWsp QtB0dKhoXR1AwBkRDLtxriG+Y2xBa96w50F7f+9NAb0RDPiTNdvhqS+YpYDKPkmOvJ1rmz5v5XLwH r7eYF0u0h/R1qopY22JR0jipq2ZUE8wbToWPRde/B9wGDKcNUoPvgpX/qqydKw3Tjv890QQMxhhUt KByahwHg==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 01/13] tests: Add 'store_samples' decorator Date: Tue, 30 Oct 2018 11:31:41 +0000 Message-Id: <20181030113153.7855-2-stephen@that.guru> X-Mailer: git-send-email 2.17.2 In-Reply-To: <20181030113153.7855-1-stephen@that.guru> References: <20181030113153.7855-1-stephen@that.guru> X-AuthUser: stephen@that.guru X-BeenThere: patchwork@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Patchwork development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Patchwork" 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 --- .gitignore | 1 + patchwork/tests/api/utils.py | 93 ++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 patchwork/tests/api/utils.py diff --git a/.gitignore b/.gitignore index a33d1029..1282bc9f 100644 --- a/.gitignore +++ b/.gitignore @@ -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 index 00000000..1097bb07 --- /dev/null +++ b/patchwork/tests/api/utils.py @@ -0,0 +1,93 @@ +# Patchwork - automated patch tracking system +# Copyright (C) 2018 Stephen Finucane +# +# 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