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 From patchwork Tue Oct 30 11:31:42 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 990755 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42kq8b0jQZz9s7W for ; Tue, 30 Oct 2018 22:33:03 +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="gbGZrjpb"; 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 42kq8Z5kstzF1PT for ; Tue, 30 Oct 2018 22:33:02 +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="gbGZrjpb"; 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.12; helo=relay012.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="gbGZrjpb"; dkim-atps=neutral Received: from relay012.mxrelay.co (relay012.mxrelay.co [185.234.75.12]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 42kq7z45b4zF1Q6 for ; Tue, 30 Oct 2018 22:32:31 +1100 (AEDT) Received: from filter002.mxroute.com (unknown [185.133.192.179]) by relay012.mxrelay.co (Postfix) with ESMTP id 4D1CC3F727 for ; Tue, 30 Oct 2018 11:31:59 +0000 (UTC) Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter002.mxroute.com (Postfix) with ESMTPS id 26D5A3F546 for ; Tue, 30 Oct 2018 11:31:59 +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=1oJEqYTf848MXIcRRVnZS0AhfsPn4d+hK9UGVYqYNkU=; b=gbGZrjpbp5lztsnhrrlPcILgpm WPmYUO5OpLF7rq4Ykl/8IBB6zc2gQgyY0wWGM3aI0PILRinlpRr+1+8sgNgi8kQX57il/gL6eC3uf o2SRqzu7Ry2kJjQMdd8nKY7k9dpzy8++E92HKnM0zioZiAFIySQX/RrTsxEdM3FFLcQbV9/SyrPWG 2oMsvY8F21witWtKtXNJlog7HwoF7TYDUxI3DOOdsIonBHprdLjvW4/DZOIoxfK+bqfMrR5NYPukb xQ2Zc27n2Me9IKwHwcYkh2n6HWKpgdYKixjB/Pekp4vRj4EyBHXRZ2RsBwc6H66SimT15wEHytrpQ wUFZz0/g==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 02/13] tests: Add 'store_samples' decorator to 'test_bundle' Date: Tue, 30 Oct 2018 11:31:42 +0000 Message-Id: <20181030113153.7855-3-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" Add the decorator to the 'test_bundle' test class. This involves splitting up the test cases so that each test case we care about makes only a single request. We also add a missing test to ensure private bundles cannot be shown by anyone but the owner. Signed-off-by: Stephen Finucane --- patchwork/tests/api/test_bundle.py | 76 ++++++++++++++++++++++++++---- 1 file changed, 67 insertions(+), 9 deletions(-) diff --git a/patchwork/tests/api/test_bundle.py b/patchwork/tests/api/test_bundle.py index 2de4f99d..e33c25ef 100644 --- a/patchwork/tests/api/test_bundle.py +++ b/patchwork/tests/api/test_bundle.py @@ -8,6 +8,7 @@ import unittest from django.conf import settings from django.urls import reverse +from patchwork.tests.api import utils from patchwork.tests.utils import create_bundle from patchwork.tests.utils import create_maintainer from patchwork.tests.utils import create_project @@ -53,12 +54,13 @@ class TestBundleAPI(APITestCase): self.assertEqual(bundle_obj.project.id, bundle_json['project']['id']) - def test_list(self): - """Validate we can list bundles.""" + def test_list_empty(self): + """List bundles when none are present.""" resp = self.client.get(self.api_url()) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(0, len(resp.data)) + def _create_bundles(self): user = create_user(username='myuser') project = create_project(linkname='myproject') bundle_public = create_bundle(public=True, owner=user, @@ -66,6 +68,12 @@ class TestBundleAPI(APITestCase): bundle_private = create_bundle(public=False, owner=user, project=project) + return user, project, bundle_public, bundle_private + + def test_list_anonymous(self): + """List bundles as anonymous user.""" + user, project, bundle_public, _ = self._create_bundles() + # anonymous users # should only see the public bundle resp = self.client.get(self.api_url()) @@ -74,6 +82,11 @@ class TestBundleAPI(APITestCase): bundle_rsp = resp.data[0] self.assertSerialized(bundle_public, bundle_rsp) + @utils.store_samples('bundle-list') + def test_list_authenticated(self): + """List bundles as an authenticated user.""" + user, project, bundle_public, bundle_private = self._create_bundles() + # authenticated user # should see the public and private bundle self.client.force_authenticate(user=user) @@ -84,14 +97,24 @@ class TestBundleAPI(APITestCase): resp.data, [bundle_public, bundle_private]): self.assertSerialized(bundle_obj, bundle_rsp) + def test_list_filter_project(self): + """Filter bundles by project.""" + user, project, bundle_public, bundle_private = self._create_bundles() + # test filtering by project + self.client.force_authenticate(user=user) resp = self.client.get(self.api_url(), {'project': 'myproject'}) self.assertEqual([bundle_public.id, bundle_private.id], [x['id'] for x in resp.data]) resp = self.client.get(self.api_url(), {'project': 'invalidproject'}) self.assertEqual(0, len(resp.data)) + def test_list_filter_owner(self): + """Filter bundles by owner.""" + user, project, bundle_public, bundle_private = self._create_bundles() + # test filtering by owner, both ID and username + self.client.force_authenticate(user=user) resp = self.client.get(self.api_url(), {'owner': user.id}) self.assertEqual([bundle_public.id, bundle_private.id], [x['id'] for x in resp.data]) @@ -101,28 +124,63 @@ class TestBundleAPI(APITestCase): resp = self.client.get(self.api_url(), {'owner': 'otheruser'}) self.assertEqual(0, len(resp.data)) + @utils.store_samples('bundle-list-1.0') def test_list_version_1_0(self): - """Validate that newer fields are dropped for older API versions.""" - create_bundle(public=True) + """List bundles using API v1.0. + + Validate that newer fields are dropped for older API versions. + """ + user, _, _, _ = self._create_bundles() + self.client.force_authenticate(user=user) resp = self.client.get(self.api_url(version='1.0')) self.assertEqual(status.HTTP_200_OK, resp.status_code) - self.assertEqual(1, len(resp.data)) + self.assertEqual(2, len(resp.data)) self.assertIn('url', resp.data[0]) self.assertNotIn('web_url', resp.data[0]) - def test_detail(self): - """Validate we can get a specific bundle.""" - bundle = create_bundle(public=True) + def test_detail_anonymous_public(self): + """Show public bundle as anonymous user. + + Validate we can get a public bundle. + """ + user, _, bundle, _ = self._create_bundles() resp = self.client.get(self.api_url(bundle.id)) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertSerialized(bundle, resp.data) + @utils.store_samples('bundle-detail-error-not-found') + def test_detail_anonymous_private(self): + """Show private bundle as anonymous user. + + Validate we cannot get a private bundle if we're not the owner. + """ + user, _, _, bundle = self._create_bundles() + + resp = self.client.get(self.api_url(bundle.id)) + self.assertEqual(status.HTTP_404_NOT_FOUND, resp.status_code) + + @utils.store_samples('bundle-detail') + def test_detail_authenticated(self): + """Show private bundle as authenticated user. + + Validate we can get a private bundle if we're the owner. + """ + user, _, _, bundle = self._create_bundles() + + self.client.force_authenticate(user=user) + resp = self.client.get(self.api_url(bundle.id)) + self.assertEqual(status.HTTP_200_OK, resp.status_code) + self.assertSerialized(bundle, resp.data) + + @utils.store_samples('bundle-detail-1.0') def test_detail_version_1_0(self): - bundle = create_bundle(public=True) + """Show bundle using API v1.0.""" + user, _, bundle, _ = self._create_bundles() resp = self.client.get(self.api_url(bundle.id, version='1.0')) + self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertIn('url', resp.data) self.assertNotIn('web_url', resp.data) From patchwork Tue Oct 30 11:31:43 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 990756 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 42kq950s15z9s7W for ; Tue, 30 Oct 2018 22:33:29 +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="h6XzkyCX"; 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 42kq945zZpzDrSM for ; Tue, 30 Oct 2018 22:33:28 +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="h6XzkyCX"; 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.20; helo=relay020.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="h6XzkyCX"; dkim-atps=neutral Received: from relay020.mxrelay.co (relay020.mxrelay.co [185.234.75.20]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 42kq801VVmzF1PT for ; Tue, 30 Oct 2018 22:32:32 +1100 (AEDT) Received: from filter002.mxroute.com (unknown [185.133.192.179]) by relay020.mxrelay.co (Postfix) with ESMTP id 9CCEE42C6B for ; Tue, 30 Oct 2018 11:31:59 +0000 (UTC) Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter002.mxroute.com (Postfix) with ESMTPS id 7D2D43F546 for ; Tue, 30 Oct 2018 11:31:59 +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=4/q370/GNV5PpIgu8cdOT1AHKVsBGRagK/1PUeLUVW8=; b=h6XzkyCXbFjE1/EVsSo4nskqXz IDtsz34vGsKy6apRPAO+hYYUxNSxCrv4LobA6PdrqZDiiQ5Qs9mXr/ODYy1tLzU0fWeXR7sgVzZNk cmGuJJIIY4wKCGiLoDXzzCKh3FxZpDA66m+Fxd1osg9trythRzfz9G1R2SnDKgo7WLRbOubI5M58Y m5jvaGz0niSTJKL7BcpYanDxruR35GAJSwpBLVNW69Kl7HPY7ENgo43KSPuFARrYKaZEX5+lgSMzE qK8gnm7fezG1OTae7OWCjd1vX1MilEArU236bSjsMWKcNgRU6e0RGe/SGdDAhd6dFQH0E6TITekEg Tp/XpEjg==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 03/13] tests: Add 'store_samples' decorator to 'test_project' Date: Tue, 30 Oct 2018 11:31:43 +0000 Message-Id: <20181030113153.7855-4-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" Signed-off-by: Stephen Finucane --- patchwork/tests/api/test_project.py | 110 ++++++++++++++++++++-------- 1 file changed, 81 insertions(+), 29 deletions(-) diff --git a/patchwork/tests/api/test_project.py b/patchwork/tests/api/test_project.py index 557c1e07..c1eb3ded 100644 --- a/patchwork/tests/api/test_project.py +++ b/patchwork/tests/api/test_project.py @@ -9,6 +9,7 @@ from django.conf import settings from django.urls import reverse from patchwork.models import Project +from patchwork.tests.api import utils from patchwork.tests.utils import create_maintainer from patchwork.tests.utils import create_project from patchwork.tests.utils import create_user @@ -33,7 +34,8 @@ class TestProjectAPI(APITestCase): if item is None: return reverse('api-project-list', kwargs=kwargs) - return reverse('api-project-detail', args=[item], kwargs=kwargs) + kwargs['pk'] = item + return reverse('api-project-detail', kwargs=kwargs) def assertSerialized(self, project_obj, project_json): self.assertEqual(project_obj.id, project_json['id']) @@ -48,67 +50,93 @@ class TestProjectAPI(APITestCase): self.assertEqual(len(project_json['maintainers']), project_obj.maintainer_project.all().count()) - def test_list(self): - """Validate we can list the default test project.""" + def test_list_empty(self): + """List projects when none are present.""" + resp = self.client.get(self.api_url()) + self.assertEqual(status.HTTP_200_OK, resp.status_code) + self.assertEqual(0, len(resp.data)) + + def test_list_anonymous(self): + """List projects as anonymous user.""" project = create_project() - # anonymous user resp = self.client.get(self.api_url()) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(1, len(resp.data)) self.assertSerialized(project, resp.data[0]) - # maintainer + @utils.store_samples('project-list') + def test_list_authenticated(self): + """List projects as an authenticated user.""" + project = create_project() user = create_maintainer(project) + self.client.force_authenticate(user=user) resp = self.client.get(self.api_url()) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(1, len(resp.data)) self.assertSerialized(project, resp.data[0]) - # test old version of API + @utils.store_samples('project-list-1.0') + def test_list_version_1_0(self): + """List projects using API v1.0. + + Validate that newer fields are dropped for older API versions. + """ + create_project() + resp = self.client.get(self.api_url(version='1.0')) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(1, len(resp.data)) self.assertNotIn('subject_match', resp.data[0]) - def test_detail(self): - """Validate we can get a specific project.""" - project = create_project() + @utils.store_samples('project-detail') + def test_detail_by_id(self): + """Show project using ID lookup. - resp = self.client.get(self.api_url(project.id)) - self.assertEqual(status.HTTP_200_OK, resp.status_code) - self.assertEqual(project.name, resp.data['name']) - - # make sure we can look up by linkname - resp = self.client.get(self.api_url(resp.data['link_name'])) - self.assertEqual(status.HTTP_200_OK, resp.status_code) - self.assertSerialized(project, resp.data) - - def test_get_by_id(self): - """Validate that it's possible to filter by pk.""" + Validate that it's possible to filter by pk. + """ project = create_project() resp = self.client.get(self.api_url(project.pk)) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertSerialized(project, resp.data) - def test_get_by_linkname(self): - """Validate that it's possible to filter by linkname.""" + def test_detail_by_linkname(self): + """Show project using linkname lookup. + + Validate that it's possible to filter by linkname. + """ project = create_project(linkname='project', name='Sample project') resp = self.client.get(self.api_url('project')) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertSerialized(project, resp.data) - def test_get_by_numeric_linkname(self): - """Validate we try to do the right thing for numeric linkname""" + def test_detail_by_numeric_linkname(self): + """Show project using numeric linkname lookup. + + Validate we try to do the right thing for numeric linkname. + """ project = create_project(linkname='12345') resp = self.client.get(self.api_url('12345')) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertSerialized(project, resp.data) + @utils.store_samples('project-detail-1.0') + def test_detail_version_1_0(self): + """Show project using API v1.0. + + Validate that newer fields are dropped for older API versions. + """ + project = create_project() + + resp = self.client.get(self.api_url(project.pk, version='1.0')) + self.assertEqual(status.HTTP_200_OK, resp.status_code) + self.assertIn('name', resp.data) + self.assertNotIn('subject_match', resp.data) + def test_create(self): """Ensure creations are rejected.""" project = create_project() @@ -126,8 +154,11 @@ class TestProjectAPI(APITestCase): resp = self.client.post(self.api_url(), data) self.assertEqual(status.HTTP_405_METHOD_NOT_ALLOWED, resp.status_code) - def test_update(self): - """Ensure updates can be performed by maintainers.""" + def test_update_anonymous(self): + """Update project as anonymous user. + + Ensure updates can only be performed by maintainers. + """ project = create_project() data = {'web_url': 'TEST'} @@ -135,20 +166,41 @@ class TestProjectAPI(APITestCase): resp = self.client.patch(self.api_url(project.id), data) self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code) - # a normal user + @utils.store_samples('project-update-error-forbidden') + def test_update_non_maintainer(self): + """Update project as normal user. + + Ensure updates can only be performed by maintainers. + """ + project = create_project() + data = {'web_url': 'TEST'} + user = create_user() self.client.force_authenticate(user=user) resp = self.client.patch(self.api_url(project.id), data) self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code) - # a maintainer + @utils.store_samples('project-update') + def test_update_maintainer(self): + """Update project as maintainer. + + Ensure updates can only be performed by maintainers. + """ + project = create_project() + data = {'web_url': 'TEST'} + user = create_maintainer(project) self.client.force_authenticate(user=user) resp = self.client.patch(self.api_url(project.id), data) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(resp.data['web_url'], 'TEST') - # ...with the exception of some read-only fields + def test_update_readonly_field(self): + """Update read-only fields.""" + project = create_project() + + user = create_maintainer(project) + self.client.force_authenticate(user=user) resp = self.client.patch(self.api_url(project.id), { 'link_name': 'test'}) # NOTE(stephenfin): This actually returns HTTP 200 due to From patchwork Tue Oct 30 11:31:44 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 990765 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 42kqCP4WfDz9s7W for ; Tue, 30 Oct 2018 22:35:29 +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="T9UNPTC7"; 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 42kqCP2j0HzF1PT for ; Tue, 30 Oct 2018 22:35:29 +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="T9UNPTC7"; 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.3; helo=relay-direct21.mxroute.com; 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="T9UNPTC7"; dkim-atps=neutral Received: from relay-direct21.mxroute.com (relay-direct21.mxroute.com [185.234.75.3]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 42kq8315wGzF1Pt for ; Tue, 30 Oct 2018 22:32:35 +1100 (AEDT) Received: from filter001.mxrelay.co (filter001.mxrelay.co [64.52.23.203]) by relay-direct21.mxroute.com (Postfix) with ESMTP id 0887A3F345 for ; Tue, 30 Oct 2018 11:32:02 +0000 (UTC) Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter001.mxrelay.co (Postfix) with ESMTPS id 23547100065 for ; Tue, 30 Oct 2018 11:32:01 +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=1IlmW36EebKEz7vfuRvSW0nWjbWimqHrLFwNeH5tHnI=; b=T9UNPTC7aUnezpbCSvU06nFN4q JwH2+hOLnmC4I3jQhGaH2T6lNPa+fef/qzY9MZFnAY429pqaM0g9k6Mj2eqhvi9XC+FhR1QmNAJPY Ylrbod/ih8b60oRllZX1TE/3y/geL+XP7VsRG03D7R70aThTHpTdv9KN2iGngseY2W26A94PPmkO/ rwOK/9bpx7l3jithYvrsQ+WdRsIjFjyURSaq2KbjDf/XlxusYvqg3vmPfly4QRO4uR0V7c9twqP3A 5FeOOfLRypXXj9bBm7HMgFjOfX6galU5fxYZPP/kzV49cCh1zGz1kTgqL2YzJJyg6brPkCXDa8rq1 6RT+quFA==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 04/13] tests: Add 'store_samples' decorator to 'test_people' Date: Tue, 30 Oct 2018 11:31:44 +0000 Message-Id: <20181030113153.7855-5-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" Signed-off-by: Stephen Finucane --- patchwork/tests/api/test_person.py | 37 +++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/patchwork/tests/api/test_person.py b/patchwork/tests/api/test_person.py index 3a89e73e..aad37a7a 100644 --- a/patchwork/tests/api/test_person.py +++ b/patchwork/tests/api/test_person.py @@ -8,6 +8,7 @@ import unittest from django.conf import settings from django.urls import reverse +from patchwork.tests.api import utils from patchwork.tests.utils import create_maintainer from patchwork.tests.utils import create_person from patchwork.tests.utils import create_user @@ -42,31 +43,49 @@ class TestPersonAPI(APITestCase): self.assertEqual(person_obj.user.id, person_json['user']['id']) - def test_list(self): - """This API requires authenticated users.""" - person = create_person() + def test_list_empty(self): + """List people when none are present.""" + # authentication is required + user = create_user(link_person=False) + + self.client.force_authenticate(user=user) + resp = self.client.get(self.api_url()) + self.assertEqual(status.HTTP_200_OK, resp.status_code) + self.assertEqual(0, len(resp.data)) + + @utils.store_samples('people-list-error-forbidden') + def test_list_anonymous(self): + """List people as anonymous user.""" + create_person() # anonymous user resp = self.client.get(self.api_url()) self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code) - # authenticated user + @utils.store_samples('people-list') + def test_list_authenticated(self): + """List people as an authenticated user.""" + person = create_person() user = create_user(link_person=False) - self.client.force_authenticate(user=user) + self.client.force_authenticate(user=user) resp = self.client.get(self.api_url()) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(1, len(resp.data)) self.assertSerialized(person, resp.data[0]) - def test_detail(self): + @utils.store_samples('people-detail-error-forbidden') + def test_detail_anonymous(self): + """Show person as anonymous user.""" person = create_person() # anonymous user resp = self.client.get(self.api_url(person.id)) self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code) - # authenticated, unlinked user + def test_detail_unlinked(self): + """Show unlinked person as authenticted user.""" + person = create_person() user = create_user(link_person=False) self.client.force_authenticate(user=user) @@ -74,7 +93,9 @@ class TestPersonAPI(APITestCase): self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertSerialized(person, resp.data, has_user=False) - # authenticated, linked user + @utils.store_samples('people-detail') + def test_detail_linked(self): + """Show linked person as authenticated user.""" user = create_user(link_person=True) person = user.person_set.all().first() self.client.force_authenticate(user=user) From patchwork Tue Oct 30 11:31:45 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 990758 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 42kq9V13Dnz9s7W for ; Tue, 30 Oct 2018 22:33:50 +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="IKOXKhm0"; 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 42kq9T5w0BzDrYb for ; Tue, 30 Oct 2018 22:33:49 +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="IKOXKhm0"; 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.15; helo=relay015.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="IKOXKhm0"; dkim-atps=neutral Received: from relay015.mxrelay.co (relay015.mxrelay.co [185.234.75.15]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 42kq813bD2zDrSM for ; Tue, 30 Oct 2018 22:32:33 +1100 (AEDT) Received: from filter002.mxroute.com (unknown [185.133.192.179]) by relay015.mxrelay.co (Postfix) with ESMTP id 85DD642CDF for ; Tue, 30 Oct 2018 11:32:00 +0000 (UTC) Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter002.mxroute.com (Postfix) with ESMTPS id 68F073F546 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=ouHtdb7uMLLhbSi/wdMW+Z7RpLrm7mdruZ/tCDwi/wE=; b=IKOXKhm0bOL0qL8pQZd2IuOBx5 XikpdV549Ih4HDXECrnoC9qRGhUG9pYo4w4b0SsnGarvdVQ/9KUNAo0OfbNucvJVH5t7Mn7rtH3Hm qv0lPq3x8z2gkhT15xo3i/bvmncoEUd+NgDHsNa2LZdvzNDIV30GRFYMLU2Usy0IJ3C1yXOBOtIOU k/oi1gL/rPtWqKYuvvh9Mx9LfKY063oCGHxAA9ujBPqArO8H4Jqin3H5JPvJ5PbhUeM0AjzlATHZr 1XAEjZOM5tPH1uBS9ukHkhJixomAZhJ/hk+Q9Psg5JN1Zeejli9U2huBVyamBEJtD80BTFp+UYHek JZgfxH5A==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 05/13] tests: Add 'store_samples' decorator to 'test_user' Date: Tue, 30 Oct 2018 11:31:45 +0000 Message-Id: <20181030113153.7855-6-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" Signed-off-by: Stephen Finucane --- patchwork/tests/api/test_user.py | 65 ++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/patchwork/tests/api/test_user.py b/patchwork/tests/api/test_user.py index 225b0778..c6114ee6 100644 --- a/patchwork/tests/api/test_user.py +++ b/patchwork/tests/api/test_user.py @@ -8,6 +8,7 @@ import unittest from django.conf import settings from django.urls import reverse +from patchwork.tests.api import utils from patchwork.tests.utils import create_maintainer from patchwork.tests.utils import create_user @@ -35,28 +36,78 @@ class TestUserAPI(APITestCase): self.assertNotIn('password', user_json) self.assertNotIn('is_superuser', user_json) - def test_list(self): - """This API requires authenticated users.""" - # anonymous users + @utils.store_samples('users-list-error-forbidden') + def test_list_anonymous(self): + """List users as anonymous user.""" + create_user() + resp = self.client.get(self.api_url()) self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code) - # authenticated user + @utils.store_samples('users-list') + def test_list_authenticated(self): + """List users as authenticated user.""" user = create_user() - self.client.force_authenticate(user=user) + self.client.force_authenticate(user=user) resp = self.client.get(self.api_url()) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(1, len(resp.data)) self.assertSerialized(user, resp.data[0]) - def test_update(self): - """Ensure updates are allowed.""" + @utils.store_samples('users-detail-error-forbidden') + def test_detail_anonymous(self): + """Show user as anonymous user.""" + user = create_user() + + resp = self.client.get(self.api_url(user.id)) + self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code) + + @utils.store_samples('users-detail') + def test_detail_authenticated(self): + """Show user as authenticated user.""" + user = create_user() + + self.client.force_authenticate(user=user) + resp = self.client.get(self.api_url(user.id)) + self.assertEqual(status.HTTP_200_OK, resp.status_code) + self.assertSerialized(user, resp.data) + + @utils.store_samples('users-update-error-forbidden') + def test_update_anonymous(self): + """Update user as anonymous user.""" + user = create_user() + + resp = self.client.patch(self.api_url(user.id), {'first_name': 'Tan'}) + self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code) + + def test_update_other_user(self): + """Update user as another, non-maintainer user.""" + user_a = create_user() + user_b = create_user() + + self.client.force_authenticate(user=user_a) + resp = self.client.patch(self.api_url(user_b.id), + {'first_name': 'Tan'}) + self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code) + + def test_update_maintainer(self): + """Update user as maintainer.""" user = create_maintainer() user.is_superuser = True user.save() + self.client.force_authenticate(user=user) + resp = self.client.patch(self.api_url(user.id), {'first_name': 'Tan'}) + self.assertEqual(status.HTTP_200_OK, resp.status_code) + self.assertSerialized(user, resp.data) + @utils.store_samples('users-update') + def test_update_self(self): + """Update user as self.""" + user = create_user() + + self.client.force_authenticate(user=user) resp = self.client.patch(self.api_url(user.id), {'first_name': 'Tan'}) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertSerialized(user, resp.data) From patchwork Tue Oct 30 11:31:46 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 990766 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42kqD52bYLz9s7W for ; Tue, 30 Oct 2018 22:36:05 +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="bUHbm3y9"; 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 42kqD50KL9zDrYb for ; Tue, 30 Oct 2018 22:36:05 +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="bUHbm3y9"; 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.19; helo=relay019.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="bUHbm3y9"; dkim-atps=neutral Received: from relay019.mxrelay.co (relay019.mxrelay.co [185.234.75.19]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 42kq841ZgwzF1Pt for ; Tue, 30 Oct 2018 22:32:36 +1100 (AEDT) Received: from filter001.mxrelay.co (filter001.mxrelay.co [64.52.23.203]) by relay019.mxrelay.co (Postfix) with ESMTP id 7D87F43200 for ; Tue, 30 Oct 2018 11:32:03 +0000 (UTC) Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter001.mxrelay.co (Postfix) with ESMTPS id A5CCD100065 for ; Tue, 30 Oct 2018 11:32:02 +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=Poct0nZS5gp6DwhkyBbgCLK4TeTRfrODmoxalke7zEo=; b=bUHbm3y9k1fem1nB/dB8BPZz2J GU45EiGbO4Eyi4jC9UfsRqXs8HdP0x3NtRF+GYkCshwHNSc1uh4pQEFNeefawyU0q6R5in/ZQxPBx Mnxf20JTEHm0VD1HnoqWXuTR+6WWw6YLEx1K453YZPWruoMPmvIZznEoX/lTHXEHXgvML4qzJ4rzq UFXb1iQWXcEKnqOOwHo2glzcDPwd26RC8qHpWi2eS6C/P0NHrQ1mJV6TTtvwK3sjzpdNOl5s38ba/ +qfP4rjBbzRPieiToXZi2ezuh+kZJneIRrdv6ocK0zayXvjFyqueZlEHAXXGE13t2QdLIVDyvsJCN LWUkerww==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 06/13] tests: Add 'store_samples' decorator to 'test_patch' Date: Tue, 30 Oct 2018 11:31:46 +0000 Message-Id: <20181030113153.7855-7-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" Signed-off-by: Stephen Finucane --- patchwork/tests/api/test_patch.py | 145 +++++++++++++++++++++--------- 1 file changed, 102 insertions(+), 43 deletions(-) diff --git a/patchwork/tests/api/test_patch.py b/patchwork/tests/api/test_patch.py index df30d510..7d34ba09 100644 --- a/patchwork/tests/api/test_patch.py +++ b/patchwork/tests/api/test_patch.py @@ -11,6 +11,7 @@ from django.conf import settings from django.urls import reverse from patchwork.models import Patch +from patchwork.tests.api import utils from patchwork.tests.utils import create_maintainer from patchwork.tests.utils import create_patch from patchwork.tests.utils import create_person @@ -58,73 +59,98 @@ class TestPatchAPI(APITestCase): self.assertEqual(patch_obj.project.id, patch_json['project']['id']) - def test_list(self): - """Validate we can list a patch.""" + def test_list_empty(self): + """List patches when none are present.""" resp = self.client.get(self.api_url()) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(0, len(resp.data)) + def _create_patch(self): person_obj = create_person(email='test@example.com') project_obj = create_project(linkname='myproject') state_obj = create_state(name='Under Review') patch_obj = create_patch(state=state_obj, project=project_obj, submitter=person_obj) - # anonymous user + return patch_obj + + def test_list_anonymous(self): + """List patches as anonymous user.""" + patch = self._create_patch() + resp = self.client.get(self.api_url()) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(1, len(resp.data)) patch_rsp = resp.data[0] - self.assertSerialized(patch_obj, patch_rsp) + self.assertSerialized(patch, patch_rsp) self.assertNotIn('headers', patch_rsp) self.assertNotIn('content', patch_rsp) self.assertNotIn('diff', patch_rsp) - # authenticated user + @utils.store_samples('patch-list') + def test_list_authenticated(self): + """List patches as an authenticated user.""" + patch = self._create_patch() user = create_user() + self.client.force_authenticate(user=user) resp = self.client.get(self.api_url()) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(1, len(resp.data)) patch_rsp = resp.data[0] - self.assertSerialized(patch_obj, patch_rsp) + self.assertSerialized(patch, patch_rsp) - # test filtering by state - resp = self.client.get(self.api_url(), {'state': 'under-review'}) - self.assertEqual([patch_obj.id], [x['id'] for x in resp.data]) - resp = self.client.get(self.api_url(), {'state': 'missing-state'}) - self.assertEqual(0, len(resp.data)) + def test_list_filter_state(self): + """Filter patches by state.""" + self._create_patch() + user = create_user() + + state_obj_b = create_state(name='New') + create_patch(state=state_obj_b) + state_obj_c = create_state(name='RFC') + create_patch(state=state_obj_c) + + self.client.force_authenticate(user=user) + resp = self.client.get(self.api_url(), [('state', 'under-review'), + ('state', 'new')]) + self.assertEqual(2, len(resp.data)) + + def test_list_filter_project(self): + """Filter patches by project.""" + patch = self._create_patch() + user = create_user() + + self.client.force_authenticate(user=user) - # test filtering by project resp = self.client.get(self.api_url(), {'project': 'myproject'}) - self.assertEqual([patch_obj.id], [x['id'] for x in resp.data]) + self.assertEqual([patch.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), {'project': 'invalidproject'}) self.assertEqual(0, len(resp.data)) + def test_list_filter_submitter(self): + """Filter patches by submitter.""" + patch = self._create_patch() + submitter = patch.submitter + user = create_user() + + self.client.force_authenticate(user=user) + # test filtering by submitter, both ID and email - resp = self.client.get(self.api_url(), {'submitter': person_obj.id}) - self.assertEqual([patch_obj.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), {'submitter': submitter.id}) + self.assertEqual([patch.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), { 'submitter': 'test@example.com'}) - self.assertEqual([patch_obj.id], [x['id'] for x in resp.data]) + self.assertEqual([patch.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), { 'submitter': 'test@example.org'}) self.assertEqual(0, len(resp.data)) - state_obj_b = create_state(name='New') - create_patch(state=state_obj_b) - state_obj_c = create_state(name='RFC') - create_patch(state=state_obj_c) - - resp = self.client.get(self.api_url()) - self.assertEqual(3, len(resp.data)) - resp = self.client.get(self.api_url(), [('state', 'under-review')]) - self.assertEqual(1, len(resp.data)) - resp = self.client.get(self.api_url(), [('state', 'under-review'), - ('state', 'new')]) - self.assertEqual(2, len(resp.data)) - + @utils.store_samples('patch-list-1-0') def test_list_version_1_0(self): + """List patches using API v1.0.""" create_patch() resp = self.client.get(self.api_url(version='1.0')) @@ -133,8 +159,9 @@ class TestPatchAPI(APITestCase): self.assertIn('url', resp.data[0]) self.assertNotIn('web_url', resp.data[0]) + @utils.store_samples('patch-detail') def test_detail(self): - """Validate we can get a specific patch.""" + """Show a specific patch.""" patch = create_patch( content='Reviewed-by: Test User \n', headers='Received: from somewhere\nReceived: from another place' @@ -154,6 +181,7 @@ class TestPatchAPI(APITestCase): self.assertEqual(patch.diff, resp.data['diff']) self.assertEqual(0, len(resp.data['tags'])) + @utils.store_samples('patch-detail-1-0') def test_detail_version_1_0(self): patch = create_patch() @@ -185,27 +213,45 @@ class TestPatchAPI(APITestCase): resp = self.client.post(self.api_url(), patch) self.assertEqual(status.HTTP_405_METHOD_NOT_ALLOWED, resp.status_code) - def test_update(self): - """Ensure updates can be performed by maintainers.""" - project = create_project() - patch = create_patch(project=project) + @utils.store_samples('patch-update-error-forbidden') + def test_update_anonymous(self): + """Update patch as anonymous user. + + Ensure updates can be performed by maintainers. + """ + patch = create_patch() state = create_state() - # anonymous user resp = self.client.patch(self.api_url(patch.id), {'state': state.name}) self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code) - # authenticated user + def test_update_non_maintainer(self): + """Update patch as non-maintainer. + + Ensure updates can be performed by maintainers. + """ + patch = create_patch() + state = create_state() user = create_user() + self.client.force_authenticate(user=user) resp = self.client.patch(self.api_url(patch.id), {'state': state.name}) self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code) - # maintainer + @utils.store_samples('patch-update') + def test_update_maintainer(self): + """Update patch as maintainer. + + Ensure updates can be performed by maintainers. + """ + project = create_project() + patch = create_patch(project=project) + state = create_state() user = create_maintainer(project) + self.client.force_authenticate(user=user) - resp = self.client.patch(self.api_url(patch.id), { - 'state': state.name, 'delegate': user.id}) + resp = self.client.patch(self.api_url(patch.id), + {'state': state.name, 'delegate': user.id}) self.assertEqual(status.HTTP_200_OK, resp.status_code, resp) self.assertEqual(Patch.objects.get(id=patch.id).state, state) self.assertEqual(Patch.objects.get(id=patch.id).delegate, user) @@ -217,22 +263,35 @@ class TestPatchAPI(APITestCase): self.assertEqual(status.HTTP_200_OK, resp.status_code, resp) self.assertIsNone(Patch.objects.get(id=patch.id).delegate) - def test_update_invalid(self): - """Ensure we handle invalid Patch updates.""" + @utils.store_samples('patch-update-error-bad-request') + def test_update_invalid_state(self): + """Update patch with invalid fields. + + Ensure we handle invalid Patch updates. + """ project = create_project() state = create_state() patch = create_patch(project=project, state=state) user = create_maintainer(project) - # invalid state self.client.force_authenticate(user=user) resp = self.client.patch(self.api_url(patch.id), {'state': 'foobar'}) self.assertEqual(status.HTTP_400_BAD_REQUEST, resp.status_code) self.assertContains(resp, 'Expected one of: %s.' % state.name, status_code=status.HTTP_400_BAD_REQUEST) - # invalid delegate + def test_update_invalid_delegate(self): + """Update patch with invalid fields. + + Ensure we handle invalid Patch updates. + """ + project = create_project() + state = create_state() + patch = create_patch(project=project, state=state) + user_a = create_maintainer(project) user_b = create_user() + + self.client.force_authenticate(user=user_a) resp = self.client.patch(self.api_url(patch.id), {'delegate': user_b.id}) self.assertEqual(status.HTTP_400_BAD_REQUEST, resp.status_code) From patchwork Tue Oct 30 11:31:47 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 990763 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 42kqBy2NmKz9s7W for ; Tue, 30 Oct 2018 22:35:06 +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="R6BpFlvx"; 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 42kqBx63jRzDrPg for ; Tue, 30 Oct 2018 22:35:05 +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="R6BpFlvx"; 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.19; helo=relay019.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="R6BpFlvx"; dkim-atps=neutral Received: from relay019.mxrelay.co (relay019.mxrelay.co [185.234.75.19]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 42kq831QDRzF1Q6 for ; Tue, 30 Oct 2018 22:32:35 +1100 (AEDT) Received: from filter002.mxroute.com (unknown [185.133.192.179]) by relay019.mxrelay.co (Postfix) with ESMTP id EB5B44058D for ; Tue, 30 Oct 2018 11:32:02 +0000 (UTC) Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter002.mxroute.com (Postfix) with ESMTPS id B7B9B3F3DB for ; Tue, 30 Oct 2018 11:32:02 +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=pwTffWyKJ/3t1SnwArESOYg2Xo7WgLnJKo+kvR+GX6U=; b=R6BpFlvxBm7dZF0vuIG0rVSadD 7eSvh4ZYjeLBL3npv8B8/yssGtKgiHGsggsYQb/kEvPwj6YPOl+5DMzCR3OTaY5eteXua9cGi/ch6 rFpSMh+8eKM60q2b59bB7DokJEeNwJrckOQ10Nx7qAlzr4+meLD7RMR9zjOovVmhtCRcbDn3SAZuO l3ExuS2mQWncmaCG0WX1ytpT/urXRFLxZO1YoJBEAfSemEtKnfOwAFJ4bVLrqw+LDvWQ13es0ieU2 JWepkZc3aao/8t9wa0S91cS66KbzXOPFq2PySKAYj0m1AqeFrCmHdg61VkQBmTNvZUrxCARhzt0jA 0OuT1RBw==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 07/13] tests: Add 'store_samples' decorator to 'test_cover' Date: Tue, 30 Oct 2018 11:31:47 +0000 Message-Id: <20181030113153.7855-8-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" Signed-off-by: Stephen Finucane --- patchwork/tests/api/test_cover.py | 51 +++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/patchwork/tests/api/test_cover.py b/patchwork/tests/api/test_cover.py index a5686292..152c343e 100644 --- a/patchwork/tests/api/test_cover.py +++ b/patchwork/tests/api/test_cover.py @@ -9,6 +9,7 @@ import unittest from django.conf import settings from django.urls import reverse +from patchwork.tests.api import utils from patchwork.tests.utils import create_cover from patchwork.tests.utils import create_maintainer from patchwork.tests.utils import create_person @@ -51,46 +52,62 @@ class TestCoverLetterAPI(APITestCase): self.assertEqual(cover_obj.submitter.id, cover_json['submitter']['id']) - def test_list(self): - """Validate we can list cover letters.""" + def test_list_empty(self): + """List patches when none are present.""" resp = self.client.get(self.api_url()) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(0, len(resp.data)) - person_obj = create_person(email='test@example.com') - project_obj = create_project(linkname='myproject') - cover_obj = create_cover(project=project_obj, submitter=person_obj) + def test_list_anonymous(self): + """List patches as anonymous user.""" + cover = create_cover() - # anonymous user resp = self.client.get(self.api_url()) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(1, len(resp.data)) - self.assertSerialized(cover_obj, resp.data[0]) + self.assertSerialized(cover, resp.data[0]) - # authenticated user + @utils.store_samples('cover-list') + def test_list_authenticated(self): + """List patches as an authenticated user.""" + cover = create_cover() user = create_user() + self.client.force_authenticate(user=user) resp = self.client.get(self.api_url()) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(1, len(resp.data)) - self.assertSerialized(cover_obj, resp.data[0]) + self.assertSerialized(cover, resp.data[0]) + + def test_list_filter_project(self): + """Filter cover letters by project.""" + cover = create_cover() + project = cover.project + + resp = self.client.get(self.api_url(), {'project': project.linkname}) + self.assertEqual([cover.id], [x['id'] for x in resp.data]) - # test filtering by project - resp = self.client.get(self.api_url(), {'project': 'myproject'}) - self.assertEqual([cover_obj.id], [x['id'] for x in resp.data]) resp = self.client.get(self.api_url(), {'project': 'invalidproject'}) self.assertEqual(0, len(resp.data)) + def test_list_filter_submitter(self): + """Filter cover letter by submitter.""" + cover = create_cover() + submitter = cover.submitter + # test filtering by submitter, both ID and email - resp = self.client.get(self.api_url(), {'submitter': person_obj.id}) - self.assertEqual([cover_obj.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), {'submitter': submitter.id}) + self.assertEqual([cover.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), { - 'submitter': 'test@example.com'}) - self.assertEqual([cover_obj.id], [x['id'] for x in resp.data]) + 'submitter': submitter.email}) + self.assertEqual([cover.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), { 'submitter': 'test@example.org'}) self.assertEqual(0, len(resp.data)) + @utils.store_samples('cover-list-1-0') def test_list_version_1_0(self): create_cover() @@ -101,6 +118,7 @@ class TestCoverLetterAPI(APITestCase): self.assertNotIn('mbox', resp.data[0]) self.assertNotIn('web_url', resp.data[0]) + @utils.store_samples('cover-detail') def test_detail(self): """Validate we can get a specific cover letter.""" cover_obj = create_cover( @@ -118,6 +136,7 @@ class TestCoverLetterAPI(APITestCase): for key, value in parsed_headers.items(): self.assertIn(value, resp.data['headers'][key]) + @utils.store_samples('cover-detail-1-0') def test_detail_version_1_0(self): cover = create_cover() From patchwork Tue Oct 30 11:31:48 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 990768 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42kqFK6Hhhz9s7W for ; Tue, 30 Oct 2018 22:37:09 +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="DRzat5hG"; 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 42kqFK4BRYzDr4R for ; Tue, 30 Oct 2018 22:37:09 +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="DRzat5hG"; 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.17; helo=relay017.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="DRzat5hG"; dkim-atps=neutral Received: from relay017.mxrelay.co (relay017.mxrelay.co [185.234.75.17]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 42kq860T58zDrhk for ; Tue, 30 Oct 2018 22:32:38 +1100 (AEDT) Received: from filter001.mxrelay.co (filter001.mxrelay.co [64.52.23.203]) by relay017.mxrelay.co (Postfix) with ESMTP id 80AFF42C61 for ; Tue, 30 Oct 2018 11:32:05 +0000 (UTC) Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter001.mxrelay.co (Postfix) with ESMTPS id AA7D5100065 for ; Tue, 30 Oct 2018 11:32:04 +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=gmOmgTU9uW4DKUt9lVxx0HczLBHfIDtYWZk8fB63k2E=; b=DRzat5hGyavrfsfqmemDQ8/u8a bHBezF1tYm4rH2VqN6KIXg7dO3Srmi2t1ADSaySMHcOIGbQcZHf/KxfPjsGx1rqXi9x2zzhNJ202M S5w4O+KJeKjC+NxVb5CytRDl/shj8isTGTO1wdaBwbstD928qZxuKy5oS4HnzFd/slqUf898V84WO nszQAGMTtoOfQJphtSrGXwcq2Sjzmk20Bzj2xDeOGiIen8yZznftOdrogMhpQkENLRZDen7E04xiN znJxTpWpsa0LoQOsGqhXUxayyv9orecas8YVeuS8YvGbIanZF8djTt0peSkT3GNC0FATXg/NBREyP /wjxXFYA==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 08/13] tests: Add 'store_samples' decorator to 'test_series' Date: Tue, 30 Oct 2018 11:31:48 +0000 Message-Id: <20181030113153.7855-9-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" Signed-off-by: Stephen Finucane --- patchwork/tests/api/test_series.py | 65 +++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 19 deletions(-) diff --git a/patchwork/tests/api/test_series.py b/patchwork/tests/api/test_series.py index 21ce2547..b377880a 100644 --- a/patchwork/tests/api/test_series.py +++ b/patchwork/tests/api/test_series.py @@ -8,6 +8,7 @@ import unittest from django.conf import settings from django.urls import reverse +from patchwork.tests.api import utils from patchwork.tests.utils import create_cover from patchwork.tests.utils import create_maintainer from patchwork.tests.utils import create_patch @@ -61,53 +62,77 @@ class TestSeriesAPI(APITestCase): self.assertEqual(series_obj.patches.count(), len(series_json['patches'])) - def test_list(self): - """Validate we can list series.""" + def test_list_empty(self): + """List series when none are present.""" resp = self.client.get(self.api_url()) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(0, len(resp.data)) + def _create_series(self): project_obj = create_project(linkname='myproject') person_obj = create_person(email='test@example.com') series_obj = create_series(project=project_obj, submitter=person_obj) create_cover(series=series_obj) create_patch(series=series_obj) - # anonymous users + return series_obj + + def test_list_anonymous(self): + """List patches as anonymous user.""" + series = self._create_series() + resp = self.client.get(self.api_url()) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(1, len(resp.data)) series_rsp = resp.data[0] - self.assertSerialized(series_obj, series_rsp) + self.assertSerialized(series, series_rsp) - # authenticated user + @utils.store_samples('series-list') + def test_list_authenticated(self): + """List series as an authenticated user.""" + series = self._create_series() user = create_user() + self.client.force_authenticate(user=user) resp = self.client.get(self.api_url()) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(1, len(resp.data)) series_rsp = resp.data[0] - self.assertSerialized(series_obj, series_rsp) + self.assertSerialized(series, series_rsp) + + def test_list_filter_project(self): + """Filter series by project.""" + series = self._create_series() - # test filtering by project resp = self.client.get(self.api_url(), {'project': 'myproject'}) - self.assertEqual([series_obj.id], [x['id'] for x in resp.data]) + self.assertEqual([series.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), {'project': 'invalidproject'}) self.assertEqual(0, len(resp.data)) - # test filtering by owner, both ID and email - resp = self.client.get(self.api_url(), {'submitter': person_obj.id}) - self.assertEqual([series_obj.id], [x['id'] for x in resp.data]) + def test_list_filter_owner(self): + """Filter series by owner.""" + series = self._create_series() + submitter = series.submitter + + resp = self.client.get(self.api_url(), {'submitter': submitter.id}) + self.assertEqual([series.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), { 'submitter': 'test@example.com'}) - self.assertEqual([series_obj.id], [x['id'] for x in resp.data]) + self.assertEqual([series.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), { 'submitter': 'test@example.org'}) self.assertEqual(0, len(resp.data)) - def test_list_old_version(self): - """Validate that newer fields are dropped for older API versions.""" - create_series() + @utils.store_samples('series-list-1-0') + def test_list_version_1_0(self): + """List patches using API v1.0. + + Validate that newer fields are dropped for older API versions. + """ + self._create_series() resp = self.client.get(self.api_url(version='1.0')) self.assertEqual(status.HTTP_200_OK, resp.status_code) @@ -115,17 +140,19 @@ class TestSeriesAPI(APITestCase): self.assertIn('url', resp.data[0]) self.assertNotIn('web_url', resp.data[0]) + @utils.store_samples('series-detail') def test_detail(self): - """Validate we can get a specific series.""" - series = create_series() - create_cover(series=series) + """Show series.""" + series = self._create_series() resp = self.client.get(self.api_url(series.id)) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertSerialized(series, resp.data) + @utils.store_samples('series-detail-1-0') def test_detail_version_1_0(self): - series = create_series() + """Show series using API v1.0.""" + series = self._create_series() resp = self.client.get(self.api_url(series.id, version='1.0')) self.assertIn('url', resp.data) From patchwork Tue Oct 30 11:31:49 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 990754 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42kq862Vjzz9s7W for ; Tue, 30 Oct 2018 22:32:38 +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="O6DP0Xuf"; 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 42kq860R0CzDrPw for ; Tue, 30 Oct 2018 22:32:38 +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="O6DP0Xuf"; 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=159.100.240.208; helo=relay-ext2.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="O6DP0Xuf"; dkim-atps=neutral Received: from relay-ext2.mxrelay.co (relay-ext2.mxrelay.co [159.100.240.208]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 42kq7Z2sNkzDrPw for ; Tue, 30 Oct 2018 22:32:10 +1100 (AEDT) Received: from filter001.mxrelay.co (filter001.mxrelay.co [64.52.23.203]) by relay-ext2.mxrelay.co (Postfix) with ESMTP id 3F4BF3FC3F for ; Tue, 30 Oct 2018 11:32:06 +0000 (UTC) Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter001.mxrelay.co (Postfix) with ESMTPS id 25BE31000C0 for ; Tue, 30 Oct 2018 11:32:05 +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=JrnvKkAUxkakWQDiHuW2UfIT0xoQLhVhfEwrxv2XmNM=; b=O6DP0XufCZUDmWOQuroDUV0klC K+detyMYjEJx31HrvGcX9PsRbFkag9+1p7xBCsS+OqKFx+F7TuKy/C96ihbwHmiwvaQBOwY26SRo4 gjc538MEHCdUgUYkchVEeVmmVfktao6fFaRdU6uioeMOVVgApJWNKN+DIUar3yJPf4TphmXXu9TcV f7ZUkdBhfBFi9D4kzWQItL+GG/Ll13Tr5/3VNCkI96IKmdztQjg6Yw0uHSWuGb9EPx/9inWwbq7u2 dfUCinr6/9Cr4dl+B3zUHfe27KXvrib6WB7RGWUcYCPc7KESHyjEXcBv78CSj39GL/MSd0xGm9OQq pqBjHyag==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 09/13] tests: Add 'store_samples' decorator to 'test_comment' Date: Tue, 30 Oct 2018 11:31:49 +0000 Message-Id: <20181030113153.7855-10-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" Signed-off-by: Stephen Finucane --- patchwork/tests/api/test_comment.py | 57 ++++++++++++++++++----------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/patchwork/tests/api/test_comment.py b/patchwork/tests/api/test_comment.py index a0aec594..2ed2a35c 100644 --- a/patchwork/tests/api/test_comment.py +++ b/patchwork/tests/api/test_comment.py @@ -9,6 +9,7 @@ from django.conf import settings from django.urls import NoReverseMatch from django.urls import reverse +from patchwork.tests.api import utils from patchwork.tests.utils import create_comment from patchwork.tests.utils import create_cover from patchwork.tests.utils import create_patch @@ -40,26 +41,32 @@ class TestCoverComments(APITestCase): comment_json['submitter']['id']) self.assertIn(SAMPLE_CONTENT, comment_json['content']) - def test_list(self): - cover_obj = create_cover() - resp = self.client.get(self.api_url(cover_obj)) + def test_list_empty(self): + """List cover letter comments when none are present.""" + patch = create_cover() + resp = self.client.get(self.api_url(patch)) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(0, len(resp.data)) - comment_obj = create_comment(submission=cover_obj) - resp = self.client.get(self.api_url(cover_obj)) + @utils.store_samples('cover-comment-list') + def test_list(self): + """List cover letter comments.""" + patch = create_cover() + comment = create_comment(submission=patch) + + resp = self.client.get(self.api_url(patch)) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(1, len(resp.data)) - self.assertSerialized(comment_obj, resp.data[0]) + self.assertSerialized(comment, resp.data[0]) - create_comment(submission=cover_obj) - resp = self.client.get(self.api_url(cover_obj)) - self.assertEqual(status.HTTP_200_OK, resp.status_code) - self.assertEqual(2, len(resp.data)) + def test_list_version_1_0(self): + """List cover letter comments using API v1.0.""" + patch = create_cover() + create_comment(submission=patch) # check we can't access comments using the old version of the API with self.assertRaises(NoReverseMatch): - self.client.get(self.api_url(cover_obj, version='1.0')) + self.client.get(self.api_url(patch, version='1.0')) @unittest.skipUnless(settings.ENABLE_REST_API, 'requires ENABLE_REST_API') @@ -79,23 +86,29 @@ class TestPatchComments(APITestCase): comment_json['submitter']['id']) self.assertIn(SAMPLE_CONTENT, comment_json['content']) - def test_list(self): - patch_obj = create_patch() - resp = self.client.get(self.api_url(patch_obj)) + def test_list_empty(self): + """List patch comments when none are present.""" + patch = create_patch() + resp = self.client.get(self.api_url(patch)) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(0, len(resp.data)) - comment_obj = create_comment(submission=patch_obj) - resp = self.client.get(self.api_url(patch_obj)) + @utils.store_samples('patch-comment-list') + def test_list(self): + """List patch comments.""" + patch = create_patch() + comment = create_comment(submission=patch) + + resp = self.client.get(self.api_url(patch)) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(1, len(resp.data)) - self.assertSerialized(comment_obj, resp.data[0]) + self.assertSerialized(comment, resp.data[0]) - create_comment(submission=patch_obj) - resp = self.client.get(self.api_url(patch_obj)) - self.assertEqual(status.HTTP_200_OK, resp.status_code) - self.assertEqual(2, len(resp.data)) + def test_list_version_1_0(self): + """List patch comments using API v1.0.""" + patch = create_patch() + create_comment(submission=patch) # check we can't access comments using the old version of the API with self.assertRaises(NoReverseMatch): - self.client.get(self.api_url(patch_obj, version='1.0')) + self.client.get(self.api_url(patch, version='1.0')) From patchwork Tue Oct 30 11:31:50 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 990767 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42kqDl6ZJtz9s89 for ; Tue, 30 Oct 2018 22:36:39 +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="HODP4rx7"; 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 42kqDl1p7YzDr6Q for ; Tue, 30 Oct 2018 22:36:39 +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="HODP4rx7"; 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.18; helo=relay018.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="HODP4rx7"; dkim-atps=neutral Received: from relay018.mxrelay.co (relay018.mxrelay.co [185.234.75.18]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 42kq85172NzF1QC for ; Tue, 30 Oct 2018 22:32:37 +1100 (AEDT) Received: from filter002.mxroute.com (unknown [185.133.192.179]) by relay018.mxrelay.co (Postfix) with ESMTP id C89173F1C9 for ; Tue, 30 Oct 2018 11:32:04 +0000 (UTC) Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter002.mxroute.com (Postfix) with ESMTPS id AC7373F3DB for ; Tue, 30 Oct 2018 11:32:04 +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=fys0VRnLwLWYkpJVVQUX6XZTLOS6PoXQQ3HjnWCLmPM=; b=HODP4rx7VlOY8/UkKrsMz0n5Lq ehUYEjjzfjBSh+EGjOAFk6MD8xtfa0hL66mxFivZlHXiXGEOKun82H60Cqfnt0JbXEEYobqZe5xsN BpYU0iUiTetC/pc2wYRV5JtardBYFrozSCe5U24y53nOai7e5GV1FUMdfAxgoVzPBPzArKgyGhytx ZLYZJeaOHoeZyQS5Cfo6KUZHhOr1cCFQEmghLSqde94Tq/7gBmDLxg68Lhrd7KBL1RBwbAvMXbkWI Se93tLf4xWeBV654D4j3PmenNvCO3QA2FOhVgvz791FXJZqR5OOyr+7FAxpqxBTZ0Nq2c5sKCgIZM vt0r8lEQ==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 10/13] tests: Add 'store_samples' decorator to 'test_check' Date: Tue, 30 Oct 2018 11:31:50 +0000 Message-Id: <20181030113153.7855-11-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" Signed-off-by: Stephen Finucane --- patchwork/tests/api/test_check.py | 54 +++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/patchwork/tests/api/test_check.py b/patchwork/tests/api/test_check.py index 783a6154..42a6231a 100644 --- a/patchwork/tests/api/test_check.py +++ b/patchwork/tests/api/test_check.py @@ -9,6 +9,7 @@ from django.conf import settings from django.urls import reverse from patchwork.models import Check +from patchwork.tests.api import utils from patchwork.tests.utils import create_check from patchwork.tests.utils import create_patch from patchwork.tests.utils import create_maintainer @@ -55,12 +56,15 @@ class TestCheckAPI(APITestCase): self.assertEqual(check_obj.description, check_json['description']) self.assertEqual(check_obj.user.id, check_json['user']['id']) - def test_list(self): - """Validate we can list checks on a patch.""" + def test_list_empty(self): + """List checks when none are present.""" resp = self.client.get(self.api_url()) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertEqual(0, len(resp.data)) + @utils.store_samples('check-list') + def test_list(self): + """List checks.""" check_obj = self._create_check() self._create_check(create_patch()) # second, unrelated patch @@ -69,23 +73,29 @@ class TestCheckAPI(APITestCase): self.assertEqual(1, len(resp.data)) self.assertSerialized(check_obj, resp.data[0]) + def test_list_filter_user(self): + """Filter checks by user.""" + check_obj = self._create_check() + # test filtering by owner, both ID and username resp = self.client.get(self.api_url(), {'user': self.user.id}) self.assertEqual([check_obj.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), {'user': self.user.username}) self.assertEqual([check_obj.id], [x['id'] for x in resp.data]) + resp = self.client.get(self.api_url(), {'user': 'otheruser'}) self.assertEqual(0, len(resp.data)) + @utils.store_samples('check-detail') def test_detail(self): - """Validate we can get a specific check.""" + """Show a check.""" check = self._create_check() resp = self.client.get(self.api_url(check)) self.assertEqual(status.HTTP_200_OK, resp.status_code) self.assertSerialized(check, resp.data) - def test_create(self): - """Ensure creations can be performed by user of patch.""" + def _test_create(self, user): check = { 'state': 'success', 'target_url': 'http://t.co', @@ -93,19 +103,37 @@ class TestCheckAPI(APITestCase): 'context': 'context', } - self.client.force_authenticate(user=self.user) - resp = self.client.post(self.api_url(), check) - self.assertEqual(status.HTTP_201_CREATED, resp.status_code) - self.assertEqual(1, Check.objects.all().count()) - self.assertSerialized(Check.objects.first(), resp.data) + self.client.force_authenticate(user=user) + return self.client.post(self.api_url(), check) + + @utils.store_samples('check-create-error-forbidden') + def test_create_non_maintainer(self): + """Create a check as a non-maintainer. + Ensure creations can only be performed by maintainers. + """ user = create_user() - self.client.force_authenticate(user=user) - resp = self.client.post(self.api_url(), check) + + resp = self._test_create(user=user) self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code) + @utils.store_samples('check-create') + def test_create_maintainer(self): + """Create a check as a maintainer. + + Ensure creations can only be performed by maintainers. + """ + resp = self._test_create(user=self.user) + self.assertEqual(status.HTTP_201_CREATED, resp.status_code) + self.assertEqual(1, Check.objects.all().count()) + self.assertSerialized(Check.objects.first(), resp.data) + + @utils.store_samples('check-create-error-bad-request') def test_create_invalid(self): - """Ensure we handle invalid check states.""" + """Create a check using invalid values. + + Ensure we handle invalid check states. + """ check = { 'state': 'this-is-not-a-valid-state', 'target_url': 'http://t.co', From patchwork Tue Oct 30 11:31:51 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 990771 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 42kqGH4hqKz9s7W for ; Tue, 30 Oct 2018 22:37:59 +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="D1AjTerl"; 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 42kqGH31KQzDr4R for ; Tue, 30 Oct 2018 22:37:59 +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="D1AjTerl"; 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.19; helo=relay019.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="D1AjTerl"; dkim-atps=neutral Received: from relay019.mxrelay.co (relay019.mxrelay.co [185.234.75.19]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 42kq875N9kzDrPg for ; Tue, 30 Oct 2018 22:32:39 +1100 (AEDT) Received: from filter001.mxrelay.co (filter001.mxrelay.co [64.52.23.203]) by relay019.mxrelay.co (Postfix) with ESMTP id 9229943202 for ; Tue, 30 Oct 2018 11:32:06 +0000 (UTC) Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter001.mxrelay.co (Postfix) with ESMTPS id BEFC11000C3 for ; Tue, 30 Oct 2018 11:32:05 +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=9bR1QkqWo1TZ+WPb/DXvryBvVUUm2juAqP8ayJHXak8=; b=D1AjTerl+Y+usO+UxjztZsoMGr iYhF9rc7+yLrFsy+Uw60/FDUK4qj++zbgplGp+dXdUcEDw4xKMNvs3XC2YRRW1vf2hofIHPzFDrBI 5UeyKh0ryu1TH4oyS/oKBEgymOM9Dn2HVwt/7XY9O/akfSaUvjMXr8edgJjvV/k0N+N9Z861edR+R HKfv+XVEFWvpVl+TogFDxqSuTQXmeCHlV+2gr07WBQQ7nnr2Jqi3BWJaiBlJRmdamKdyOvhfBR9Lw DxkiCxRpPTO5sauRQysDgElKroAyBglNl1vn4FESh/k5MzFniSnN7hs6R8kdy8StgORBf5NfBh8oR wmrfwrLg==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 11/13] tests: Add tests for '/events' resource Date: Tue, 30 Oct 2018 11:31:51 +0000 Message-Id: <20181030113153.7855-12-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" This highlights an issue that will be resolved in a future change. Signed-off-by: Stephen Finucane --- patchwork/tests/api/test_event.py | 169 ++++++++++++++++++++++++++++++ patchwork/tests/utils.py | 10 ++ 2 files changed, 179 insertions(+) create mode 100644 patchwork/tests/api/test_event.py diff --git a/patchwork/tests/api/test_event.py b/patchwork/tests/api/test_event.py new file mode 100644 index 00000000..0a36dbd3 --- /dev/null +++ b/patchwork/tests/api/test_event.py @@ -0,0 +1,169 @@ +# Patchwork - automated patch tracking system +# Copyright (C) 2018 Stephen Finucane +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import unittest + +from django.conf import settings +from django.urls import reverse + +from patchwork.models import Event +from patchwork.tests.utils import create_check +from patchwork.tests.utils import create_cover +from patchwork.tests.utils import create_maintainer +from patchwork.tests.utils import create_patch +from patchwork.tests.utils import create_series +from patchwork.tests.utils import create_state + +if settings.ENABLE_REST_API: + from rest_framework import status + from rest_framework.test import APITestCase +else: + # stub out APITestCase + from django.test import TestCase + APITestCase = TestCase # noqa + + +@unittest.skipUnless(settings.ENABLE_REST_API, 'requires ENABLE_REST_API') +class TestEventAPI(APITestCase): + + @staticmethod + def api_url(version=None): + kwargs = {} + if version: + kwargs['version'] = version + + return reverse('api-event-list', kwargs=kwargs) + + def assertSerialized(self, event_obj, event_json): + self.assertEqual(event_obj.id, event_json['id']) + self.assertEqual(event_obj.category, event_json['category']) + + # nested fields + + self.assertEqual(event_obj.project.id, + event_json['project']['id']) + + # TODO(stephenfin): Check other fields + + def test_list_empty(self): + """List events when none are present.""" + resp = self.client.get(self.api_url()) + self.assertEqual(status.HTTP_200_OK, resp.status_code) + self.assertEqual(0, len(resp.data)) + + def _create_events(self): + """Create sample events. + + This one's a bit weird. While we could generate event models ourselves, + it seems wiser to test the event machinery as many times as possible. + As a result, we actually create a load of *other* objects, which will + raise signals and trigger the remainder. + """ + # series-created + series = create_series() + # patch-created, patch-completed, series-completed + patch = create_patch(series=series) + # cover-created + create_cover(series=series) + # check-created + create_check(patch=patch) + # patch-delegated, patch-state-changed + user = create_maintainer(project=patch.project) + state = create_state() + patch.delegate = user + patch.state = state + patch.save() + + return Event.objects.all() + + def test_list(self): + """List events.""" + events = self._create_events() + + resp = self.client.get(self.api_url()) + self.assertEqual(status.HTTP_200_OK, resp.status_code) + # FIXME(stephenfin): This should actually return 8 events but + # 'series-completed' events are not currently being generated + self.assertEqual(7, len(resp.data), [x['category'] for x in resp.data]) + for event_rsp in resp.data: + event_obj = events.get(category=event_rsp['category']) + self.assertSerialized(event_obj, event_rsp) + + def test_list_filter_project(self): + """Filter events by project.""" + events = self._create_events() + project = events[0].project + create_series() # create series in a random project + + resp = self.client.get(self.api_url(), {'project': project.pk}) + # All but one event belongs to the same project + # FIXME(stephenfin): This should actually return 8 events but + # 'series-completed' events are not currently being generated + self.assertEqual(7, len(resp.data)) + + resp = self.client.get(self.api_url(), {'project': 'invalidproject'}) + self.assertEqual(0, len(resp.data)) + + def test_list_filter_category(self): + """Filter events by category.""" + events = self._create_events() + + resp = self.client.get(self.api_url(), + {'category': events[0].category}) + # There should only be one + self.assertEqual(1, len(resp.data)) + + resp = self.client.get(self.api_url(), {'category': 'foo-bar'}) + self.assertEqual(0, len(resp.data)) + + def test_list_filter_patch(self): + """Filter events by patch.""" + events = self._create_events() + + patch = events.get(category='patch-created').patch + resp = self.client.get(self.api_url(), {'patch': patch.pk}) + # There should be five - patch-created, patch-completed, check-created, + # patch-state-changed and patch-delegated + self.assertEqual(5, len(resp.data)) + + resp = self.client.get(self.api_url(), {'patch': 999999}) + self.assertEqual(0, len(resp.data)) + + def test_list_filter_cover(self): + """Filter events by cover.""" + events = self._create_events() + + cover = events.get(category='cover-created').cover + resp = self.client.get(self.api_url(), {'cover': cover.pk}) + # There should only be one - cover-created + self.assertEqual(1, len(resp.data)) + + resp = self.client.get(self.api_url(), {'cover': 999999}) + self.assertEqual(0, len(resp.data)) + + def test_list_filter_series(self): + """Filter events by series.""" + events = self._create_events() + + series = events.get(category='series-created').series + resp = self.client.get(self.api_url(), {'series': series.pk}) + # There should be three - series-created, patch-completed and + # series-completed + # FIXME(stephenfin): This should actually return 3 events but + # 'series-completed' events are not currently being generated + self.assertEqual(2, len(resp.data)) + + resp = self.client.get(self.api_url(), {'series': 999999}) + self.assertEqual(0, len(resp.data)) + + def test_create(self): + """Ensure creates aren't allowed""" + user = create_maintainer() + user.is_superuser = True + user.save() + + self.client.force_authenticate(user=user) + resp = self.client.post(self.api_url(), {'category': 'patch-created'}) + self.assertEqual(status.HTTP_405_METHOD_NOT_ALLOWED, resp.status_code) diff --git a/patchwork/tests/utils.py b/patchwork/tests/utils.py index c8acda40..e15ea561 100644 --- a/patchwork/tests/utils.py +++ b/patchwork/tests/utils.py @@ -155,6 +155,11 @@ def create_patch(**kwargs): series = kwargs.pop('series', None) number = kwargs.pop('number', None) + # NOTE(stephenfin): We overwrite the provided project, if there is one, to + # maintain some degree of sanity + if series: + kwargs['project'] = series.project + values = { 'submitter': create_person() if 'submitter' not in kwargs else None, 'delegate': None, @@ -192,6 +197,11 @@ def create_cover(**kwargs): # [1] https://stackoverflow.com/q/43119575/ series = kwargs.pop('series', None) + # NOTE(stephenfin): We overwrite the provided project, if there is one, to + # maintain some degree of sanity + if series: + kwargs['project'] = series.project + values = { 'submitter': create_person() if 'person' not in kwargs else None, 'project': create_project() if 'project' not in kwargs else None, From patchwork Tue Oct 30 11:31:52 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 990753 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 42kq7g2ghzz9s89 for ; Tue, 30 Oct 2018 22:32:15 +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="lAScnc/2"; 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 42kq7g0pgzzF0yr for ; Tue, 30 Oct 2018 22:32:15 +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="lAScnc/2"; 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=159.100.240.208; helo=relay-ext2.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="lAScnc/2"; dkim-atps=neutral X-Greylist: delayed 758 seconds by postgrey-1.36 at bilbo; Tue, 30 Oct 2018 22:32:10 AEDT Received: from relay-ext2.mxrelay.co (relay-ext2.mxrelay.co [159.100.240.208]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 42kq7Z2Mt9zDrPg for ; Tue, 30 Oct 2018 22:32:10 +1100 (AEDT) Received: from filter001.mxrelay.co (filter001.mxrelay.co [64.52.23.203]) by relay-ext2.mxrelay.co (Postfix) with ESMTP id 5B5643FC43 for ; Tue, 30 Oct 2018 11:32:07 +0000 (UTC) Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter001.mxrelay.co (Postfix) with ESMTPS id 49510100065 for ; Tue, 30 Oct 2018 11:32:06 +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=+JFFvhEcu9OSiSjHfFA0jj/Dvmy3heH2NxzfBSLMbGI=; b=lAScnc/2rBX8Ljk5afaIpGx5aX ELE6hsBanzsi6bPAxY4nNSulCy9EdApwwaSdo1bRF0xPWhsT1MTQH2ZuudRhM5eICKmooLIV3pmbm hKOgkhIsytjKlpSKPPhIGiCrRUjXMb/fb2F5Nv7pR55Drrwotn3eS72iA5L66VuP1VX+TwYACRyMP /NLZ/4poLm9XwJT7m0N78NZ9TU4vkjltMF5jhXNgowd8GBi2bpfE61Xte4U+NY77PTOrgWdl+q5+Y n8+4W0lxqZf+RJgc0UMFOGsU5/7NiMFEQJODL6gXbrhKvVRgZKOi23m7emA9gYjlz4hiGwG6jeEkc SZn3HzSg==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 12/13] tests: Add 'store_samples' decorator to 'test_event' Date: Tue, 30 Oct 2018 11:31:52 +0000 Message-Id: <20181030113153.7855-13-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" Signed-off-by: Stephen Finucane --- patchwork/tests/api/test_event.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/patchwork/tests/api/test_event.py b/patchwork/tests/api/test_event.py index 0a36dbd3..fd32bc8c 100644 --- a/patchwork/tests/api/test_event.py +++ b/patchwork/tests/api/test_event.py @@ -9,6 +9,7 @@ from django.conf import settings from django.urls import reverse from patchwork.models import Event +from patchwork.tests.api import utils from patchwork.tests.utils import create_check from patchwork.tests.utils import create_cover from patchwork.tests.utils import create_maintainer @@ -78,6 +79,7 @@ class TestEventAPI(APITestCase): return Event.objects.all() + @utils.store_samples('event-list') def test_list(self): """List events.""" events = self._create_events() From patchwork Tue Oct 30 11:31:53 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 990769 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 42kqFp0Qj1z9s7W for ; Tue, 30 Oct 2018 22:37:34 +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="aTJaI0MD"; 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 42kqFn5s2MzF1Pt for ; Tue, 30 Oct 2018 22:37:33 +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="aTJaI0MD"; 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.12; helo=relay012.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="aTJaI0MD"; dkim-atps=neutral Received: from relay012.mxrelay.co (relay012.mxrelay.co [185.234.75.12]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 42kq862FhdzF1QC for ; Tue, 30 Oct 2018 22:32:38 +1100 (AEDT) Received: from filter002.mxroute.com (unknown [185.133.192.179]) by relay012.mxrelay.co (Postfix) with ESMTP id CDBD33F9FF for ; Tue, 30 Oct 2018 11:32:05 +0000 (UTC) Received: from one.mxroute.com (one.mxroute.com [195.201.59.211]) by filter002.mxroute.com (Postfix) with ESMTPS id B470C3F3DB for ; Tue, 30 Oct 2018 11:32:05 +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=m2onPd1b12aUPWN4UBYgI1nVAoe6qvIayMkpkMRrKyQ=; b=aTJaI0MDwDqce7P998e79WKCDT IMMd+4U2nizakqOt0mrQH7AUk26G96WsZTRdVeDa6wsBpH0HnHtEgdsi05XZNAeT9TRytLt4KGEEp 8f4gxhw9py+uxXIbJAxfmWKOVDoYmRceR92kqpQaRmDENU3mQxJNYJxkKOeT45yxNKjIB//c8Tk8P gp3+tDRPs24Ygb1l8xZoVQYHarc9+ESZNXuJBPl14Yb/MgVkRIfl+rsljb4d0otdXLVPqWL+kEwXj oL5j6Wg8Hs16nF8DXI9rVDiSA8ALjEej9GkIQ85my5rLGY40WO7ippgSkip2+hIa46mF/DycsGNrd 11yVYpBQ==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 13/13] signals: Fix 'series-completed' event Date: Tue, 30 Oct 2018 11:31:53 +0000 Message-Id: <20181030113153.7855-14-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" I'm not sure how I ever intended this to work and there were no tests verifying things. Fix it now and add tests to prevent it regressing. Signed-off-by: Stephen Finucane Fixes: 76505e91 ("models: Convert Series-Patch relationship to 1:N") --- patchwork/signals.py | 21 +++++++++++++++----- patchwork/tests/api/test_event.py | 12 +++-------- patchwork/tests/test_events.py | 33 +++++++++++++++++++++++++++---- 3 files changed, 48 insertions(+), 18 deletions(-) diff --git a/patchwork/signals.py b/patchwork/signals.py index 536b177e..666199b6 100644 --- a/patchwork/signals.py +++ b/patchwork/signals.py @@ -210,8 +210,8 @@ def create_series_created_event(sender, instance, created, raw, **kwargs): create_event(instance) -@receiver(post_save, sender=Patch) -def create_series_completed_event(sender, instance, created, raw, **kwargs): +@receiver(pre_save, sender=Patch) +def create_series_completed_event(sender, instance, raw, **kwargs): # NOTE(stephenfin): It's actually possible for this event to be fired # multiple times for a given series. To trigger this case, you would need @@ -225,9 +225,20 @@ def create_series_completed_event(sender, instance, created, raw, **kwargs): project=series.project, series=series) - # don't trigger for items loaded from fixtures or existing items - if raw or not created: + # don't trigger for items loaded from fixtures, new items or items that + # (still) don't have a series + if raw or not instance.pk or not instance.series: + return + + orig_patch = Patch.objects.get(pk=instance.pk) + + # we don't currently allow users to change a series (though this might + # change in the future) meaning if the patch already had a series, there's + # nothing to notify about + if orig_patch.series: return - if instance.series and instance.series.received_all: + # we can't use "series.received_all" here since we haven't actually saved + # the instance yet so we duplicate that logic here but with an offset + if (instance.series.received_total + 1) >= instance.series.total: create_event(instance.series) diff --git a/patchwork/tests/api/test_event.py b/patchwork/tests/api/test_event.py index fd32bc8c..a2e89f53 100644 --- a/patchwork/tests/api/test_event.py +++ b/patchwork/tests/api/test_event.py @@ -86,9 +86,7 @@ class TestEventAPI(APITestCase): resp = self.client.get(self.api_url()) self.assertEqual(status.HTTP_200_OK, resp.status_code) - # FIXME(stephenfin): This should actually return 8 events but - # 'series-completed' events are not currently being generated - self.assertEqual(7, len(resp.data), [x['category'] for x in resp.data]) + self.assertEqual(8, len(resp.data), [x['category'] for x in resp.data]) for event_rsp in resp.data: event_obj = events.get(category=event_rsp['category']) self.assertSerialized(event_obj, event_rsp) @@ -101,9 +99,7 @@ class TestEventAPI(APITestCase): resp = self.client.get(self.api_url(), {'project': project.pk}) # All but one event belongs to the same project - # FIXME(stephenfin): This should actually return 8 events but - # 'series-completed' events are not currently being generated - self.assertEqual(7, len(resp.data)) + self.assertEqual(8, len(resp.data)) resp = self.client.get(self.api_url(), {'project': 'invalidproject'}) self.assertEqual(0, len(resp.data)) @@ -153,9 +149,7 @@ class TestEventAPI(APITestCase): resp = self.client.get(self.api_url(), {'series': series.pk}) # There should be three - series-created, patch-completed and # series-completed - # FIXME(stephenfin): This should actually return 3 events but - # 'series-completed' events are not currently being generated - self.assertEqual(2, len(resp.data)) + self.assertEqual(3, len(resp.data)) resp = self.client.get(self.api_url(), {'series': 999999}) self.assertEqual(0, len(resp.data)) diff --git a/patchwork/tests/test_events.py b/patchwork/tests/test_events.py index 7d03d65d..fc82022e 100644 --- a/patchwork/tests/test_events.py +++ b/patchwork/tests/test_events.py @@ -28,7 +28,7 @@ class _BaseTestCase(TestCase): self.assertIsNone(field) -class PatchCreateTest(_BaseTestCase): +class PatchCreatedTest(_BaseTestCase): def test_patch_created(self): """No series, so patch dependencies implicitly exist.""" @@ -165,7 +165,7 @@ class PatchChangedTest(_BaseTestCase): self.assertEventFields(events[3], previous_delegate=delegate_b) -class CheckCreateTest(_BaseTestCase): +class CheckCreatedTest(_BaseTestCase): def test_check_created(self): check = utils.create_check() @@ -176,7 +176,7 @@ class CheckCreateTest(_BaseTestCase): self.assertEventFields(events[0]) -class CoverCreateTest(_BaseTestCase): +class CoverCreatedTest(_BaseTestCase): def test_cover_created(self): cover = utils.create_cover() @@ -187,7 +187,7 @@ class CoverCreateTest(_BaseTestCase): self.assertEventFields(events[0]) -class SeriesCreateTest(_BaseTestCase): +class SeriesCreatedTest(_BaseTestCase): def test_series_created(self): series = utils.create_series() @@ -196,3 +196,28 @@ class SeriesCreateTest(_BaseTestCase): self.assertEqual(events[0].category, Event.CATEGORY_SERIES_CREATED) self.assertEqual(events[0].project, series.project) self.assertEventFields(events[0]) + + +class SeriesChangedTest(_BaseTestCase): + + def test_series_completed(self): + """Validate 'series-completed' events.""" + series = utils.create_series(total=2) + + # the series has no patches associated with it so it's not yet complete + events = _get_events(series=series) + self.assertNotIn(Event.CATEGORY_SERIES_COMPLETED, + [x.category for x in events]) + + # create the second of two patches in the series; series is still not + # complete + utils.create_patch(series=series, number=2) + events = _get_events(series=series) + self.assertNotIn(Event.CATEGORY_SERIES_COMPLETED, + [x.category for x in events]) + + # now create the first patch, which will "complete" the series + utils.create_patch(series=series, number=1) + events = _get_events(series=series) + self.assertIn(Event.CATEGORY_SERIES_COMPLETED, + [x.category for x in events])