← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~andrey-fedoseev/launchpad:edit-feature-rules into launchpad:master

 

Andrey Fedoseev has proposed merging ~andrey-fedoseev/launchpad:edit-feature-rules into launchpad:master.

Commit message:
Add redirects from old feature rules URLs

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~andrey-fedoseev/launchpad/+git/launchpad/+merge/433363

/+feature-info -> /+feature-rules/info
/+feature-changelog -> /+feature-rules/changelog


Add `responseStatusCode` property to test browser classes

Update the existing tests to use the new property
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~andrey-fedoseev/launchpad:edit-feature-rules into launchpad:master.
diff --git a/lib/lp/charms/browser/tests/test_charmrecipe.py b/lib/lp/charms/browser/tests/test_charmrecipe.py
index 7db9c4c..f3ca37e 100644
--- a/lib/lp/charms/browser/tests/test_charmrecipe.py
+++ b/lib/lp/charms/browser/tests/test_charmrecipe.py
@@ -430,7 +430,7 @@ class TestCharmRecipeAddView(BaseTestCharmRecipeView):
                 ]
             ),
         )
-        self.assertEqual(303, int(browser.headers["Status"].split(" ", 1)[0]))
+        self.assertEqual(303, browser.responseStatusCode)
         return_to_matcher = AfterPreprocessing(
             urlsplit,
             MatchesStructure(
@@ -716,7 +716,7 @@ class TestCharmRecipeEditView(BaseTestCharmRecipeView):
 
         browser.getControl("Update charm recipe").click()
 
-        self.assertEqual(303, int(browser.headers["Status"].split(" ", 1)[0]))
+        self.assertEqual(303, browser.responseStatusCode)
 
         browser = self.getViewBrowser(recipe, user=self.person)
         content = find_main_content(browser.contents)
@@ -792,7 +792,7 @@ class TestCharmRecipeEditView(BaseTestCharmRecipeView):
 
         browser.getControl("Update charm recipe").click()
 
-        self.assertEqual(303, int(browser.headers["Status"].split(" ", 1)[0]))
+        self.assertEqual(303, browser.responseStatusCode)
 
         browser = self.getViewBrowser(recipe, user=self.person)
         content = find_main_content(browser.contents)
@@ -1101,7 +1101,7 @@ class TestCharmRecipeAuthorizeView(BaseTestCharmRecipeView):
             self.assertEqual(
                 {"root": root_macaroon_raw}, self.recipe.store_secrets
             )
-        self.assertEqual(303, int(browser.headers["Status"].split(" ", 1)[0]))
+        self.assertEqual(303, browser.responseStatusCode)
         return_to_matcher = AfterPreprocessing(
             urlsplit,
             MatchesStructure(
diff --git a/lib/lp/charms/tests/test_charmrecipebuild.py b/lib/lp/charms/tests/test_charmrecipebuild.py
index 1e7b069..6a949db 100644
--- a/lib/lp/charms/tests/test_charmrecipebuild.py
+++ b/lib/lp/charms/tests/test_charmrecipebuild.py
@@ -985,7 +985,7 @@ class TestCharmRecipeBuildWebservice(TestCaseWithFactory):
 
     def assertCanOpenRedirectedUrl(self, browser, url):
         browser.open(url)
-        self.assertEqual(303, int(browser.headers["Status"].split(" ", 1)[0]))
+        self.assertEqual(303, browser.responseStatusCode)
         urlopen(browser.headers["Location"]).close()
 
     def test_logs(self):
diff --git a/lib/lp/code/browser/tests/test_branch.py b/lib/lp/code/browser/tests/test_branch.py
index e8d914e..8d49a15 100644
--- a/lib/lp/code/browser/tests/test_branch.py
+++ b/lib/lp/code/browser/tests/test_branch.py
@@ -1413,7 +1413,7 @@ class TestBranchDiffView(BrowserTestCase):
         browser = self.getUserBrowser()
         browser.raiseHttpErrors = False
         browser.open(branch_url + "/+diff/2/1")
-        self.assertEqual(401, int(browser.headers["Status"].split(" ", 1)[0]))
+        self.assertEqual(401, browser.responseStatusCode)
         self.assertEqual(
             "Proxying of branch diffs is disabled.\n", browser.contents
         )
diff --git a/lib/lp/code/model/tests/test_cibuild.py b/lib/lp/code/model/tests/test_cibuild.py
index 8f555a1..755eb96 100644
--- a/lib/lp/code/model/tests/test_cibuild.py
+++ b/lib/lp/code/model/tests/test_cibuild.py
@@ -1457,7 +1457,7 @@ class TestCIBuildWebservice(TestCaseWithFactory):
 
     def assertCanOpenRedirectedUrl(self, browser, url):
         browser.open(url)
-        self.assertEqual(303, int(browser.headers["Status"].split(" ", 1)[0]))
+        self.assertEqual(303, browser.responseStatusCode)
         urlopen(browser.headers["Location"]).close()
 
     def test_logs(self):
diff --git a/lib/lp/code/model/tests/test_revisionstatus.py b/lib/lp/code/model/tests/test_revisionstatus.py
index afc7ff2..1ab7618 100644
--- a/lib/lp/code/model/tests/test_revisionstatus.py
+++ b/lib/lp/code/model/tests/test_revisionstatus.py
@@ -452,9 +452,7 @@ class TestRevisionStatusReportWebservice(TestCaseWithFactory):
             # ensure the url works
             browser = self.getNonRedirectingBrowser()
             browser.open(log_url)
-            self.assertEqual(
-                303, int(browser.headers["Status"].split(" ", 1)[0])
-            )
+            self.assertEqual(303, browser.responseStatusCode)
             self.assertEqual(
                 b"log_data", requests.get(browser.headers["Location"]).content
             )
@@ -517,9 +515,7 @@ class TestRevisionStatusReportWebservice(TestCaseWithFactory):
             # we should be redirected to librarian with authentication
             browser = self.getNonRedirectingBrowser(user=requester)
             browser.open(log_url)
-            self.assertEqual(
-                303, int(browser.headers["Status"].split(" ", 1)[0])
-            )
+            self.assertEqual(303, browser.responseStatusCode)
             # Actually requesting files from the restricted librarian is
             # cumbersome, but at least test that we're redirected to the
             # restricted librarian with a suitable token.
diff --git a/lib/lp/oci/tests/test_ocirecipebuild.py b/lib/lp/oci/tests/test_ocirecipebuild.py
index 9dbc708..da54f9c 100644
--- a/lib/lp/oci/tests/test_ocirecipebuild.py
+++ b/lib/lp/oci/tests/test_ocirecipebuild.py
@@ -942,7 +942,7 @@ class TestOCIRecipeBuildWebservice(OCIConfigHelperMixin, TestCaseWithFactory):
 
     def assertCanOpenRedirectedUrl(self, browser, url):
         browser.open(url)
-        self.assertEqual(303, int(browser.headers["Status"].split(" ", 1)[0]))
+        self.assertEqual(303, browser.responseStatusCode)
         urlopen(browser.headers["Location"]).close()
 
     def test_logs(self):
diff --git a/lib/lp/services/features/browser/configure.zcml b/lib/lp/services/features/browser/configure.zcml
index 344dbf8..268166b 100644
--- a/lib/lp/services/features/browser/configure.zcml
+++ b/lib/lp/services/features/browser/configure.zcml
@@ -35,10 +35,26 @@
       template="../templates/feature-info.pt"/>
 
   <browser:page
+      for="lp.services.webapp.interfaces.ILaunchpadRoot"
+      class="lp.services.features.browser.redirect.FeatureRulesRedirectView"
+      name="+feature-info"
+      permission="launchpad.Edit"
+      attribute="redirect_to_info"
+      />
+
+  <browser:page
       for="lp.services.features.interfaces.IFeatureRules"
       class="lp.services.features.browser.changelog.FeatureChangeLogView"
       name="changelog"
       permission="launchpad.View"
       template="../templates/feature-changelog.pt"/>
 
+  <browser:page
+      for="lp.services.webapp.interfaces.ILaunchpadRoot"
+      class="lp.services.features.browser.redirect.FeatureRulesRedirectView"
+      name="+feature-changelog"
+      permission="launchpad.Edit"
+      attribute="redirect_to_changelog"
+      />
+
 </configure>
diff --git a/lib/lp/services/features/browser/redirect.py b/lib/lp/services/features/browser/redirect.py
new file mode 100644
index 0000000..e4053eb
--- /dev/null
+++ b/lib/lp/services/features/browser/redirect.py
@@ -0,0 +1,35 @@
+# Copyright 2022 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Redirect to new feature rule URLs."""
+
+__all__ = [
+    "FeatureRulesRedirectView",
+]
+
+from zope.component import getUtility
+
+from lp.services.features.interfaces import IFeatureRules
+from lp.services.webapp.publisher import canonical_url
+
+
+class FeatureRulesRedirectView:
+    """Redirect to new feature rule URLs."""
+
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request
+        self.feature_rules = getUtility(IFeatureRules)
+
+    def redirect_to_info(self):
+        """/+feature-info -> /+feature-rules/info"""
+        return self.request.response.redirect(
+            canonical_url(self.feature_rules, view_name="info"), status=301
+        )
+
+    def redirect_to_changelog(self):
+        """/+feature-changelog -> /+feature-rules/changelog"""
+        return self.request.response.redirect(
+            canonical_url(self.feature_rules, view_name="changelog"),
+            status=301,
+        )
diff --git a/lib/lp/services/features/browser/tests/test_redirect.py b/lib/lp/services/features/browser/tests/test_redirect.py
new file mode 100644
index 0000000..fa4cd9e
--- /dev/null
+++ b/lib/lp/services/features/browser/tests/test_redirect.py
@@ -0,0 +1,48 @@
+# Copyright 2011 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Tests for feature flag change log views."""
+from http.client import MOVED_PERMANENTLY
+
+from zope.component import getUtility
+
+from lp.app.interfaces.launchpad import ILaunchpadCelebrities
+from lp.services.webapp import canonical_url
+from lp.services.webapp.interfaces import ILaunchpadRoot
+from lp.testing import TestCaseWithFactory
+from lp.testing.layers import DatabaseFunctionalLayer
+
+
+class TestRedirectView(TestCaseWithFactory):
+    """Test the feature flag ChangeLog view."""
+
+    layer = DatabaseFunctionalLayer
+
+    def setUp(self, **kwargs):
+        super().setUp(**kwargs)
+        self.root = getUtility(ILaunchpadRoot)
+        admin = self.factory.makePerson(
+            member_of=[getUtility(ILaunchpadCelebrities).admin]
+        )
+        self.admin_browser = self.getNonRedirectingBrowser(user=admin)
+
+    def test_redirect_to_info(self):
+        self.admin_browser.open(canonical_url(self.root) + "+feature-info")
+        self.assertEqual(
+            MOVED_PERMANENTLY,
+            self.admin_browser.responseStatusCode,
+        )
+        self.assertEndsWith(
+            self.admin_browser.headers["Location"], "/+feature-rules/info"
+        )
+
+    def test_redirect_changelog(self):
+        self.admin_browser.open(
+            canonical_url(self.root) + "+feature-changelog"
+        )
+        self.assertEqual(
+            self.admin_browser.responseStatusCode, MOVED_PERMANENTLY
+        )
+        self.assertEndsWith(
+            self.admin_browser.headers["Location"], "/+feature-rules/changelog"
+        )
diff --git a/lib/lp/services/webapp/tests/test_login.py b/lib/lp/services/webapp/tests/test_login.py
index e2f5f49..3647d12 100644
--- a/lib/lp/services/webapp/tests/test_login.py
+++ b/lib/lp/services/webapp/tests/test_login.py
@@ -765,15 +765,12 @@ class TestOpenIDReplayAttack(TestCaseWithFactory):
 
         self.assertEqual("Login", browser.title)
         fill_login_form_and_submit(browser, "test@xxxxxxxxxxxxx")
-        self.assertEqual(
-            http.client.FOUND, int(browser.headers["Status"].split(" ", 1)[0])
-        )
+        self.assertEqual(http.client.FOUND, browser.responseStatusCode)
         callback_url = browser.headers["Location"]
         self.assertIn("+openid-callback", callback_url)
         browser.open(callback_url)
         self.assertEqual(
-            http.client.TEMPORARY_REDIRECT,
-            int(browser.headers["Status"].split(" ", 1)[0]),
+            http.client.TEMPORARY_REDIRECT, browser.responseStatusCode
         )
         browser.open(browser.headers["Location"])
         login_status = extract_text(
diff --git a/lib/lp/snappy/browser/tests/test_snap.py b/lib/lp/snappy/browser/tests/test_snap.py
index 4089efc..c9eb67c 100644
--- a/lib/lp/snappy/browser/tests/test_snap.py
+++ b/lib/lp/snappy/browser/tests/test_snap.py
@@ -682,7 +682,7 @@ class TestSnapAddView(BaseTestSnapView):
         self.assertEqual(
             expected_body, json.loads(call.request.body.decode("UTF-8"))
         )
-        self.assertEqual(303, int(browser.headers["Status"].split(" ", 1)[0]))
+        self.assertEqual(303, browser.responseStatusCode)
         parsed_location = urlsplit(browser.headers["Location"])
         self.assertEqual(
             urlsplit(canonical_url(snap) + "/+authorize/+login")[:3],
@@ -1729,7 +1729,7 @@ class TestSnapEditView(BaseTestSnapView):
         self.assertEqual(
             expected_body, json.loads(call.request.body.decode("UTF-8"))
         )
-        self.assertEqual(303, int(browser.headers["Status"].split(" ", 1)[0]))
+        self.assertEqual(303, browser.responseStatusCode)
         parsed_location = urlsplit(browser.headers["Location"])
         self.assertEqual(
             urlsplit(canonical_url(snap) + "/+authorize/+login")[:3],
@@ -1815,7 +1815,7 @@ class TestSnapAuthorizeView(BaseTestSnapView):
             self.assertEqual(
                 {"root": root_macaroon_raw}, self.snap.store_secrets
             )
-        self.assertEqual(303, int(browser.headers["Status"].split(" ", 1)[0]))
+        self.assertEqual(303, browser.responseStatusCode)
         self.assertEqual(
             snap_url + "/+authorize/+login?macaroon_caveat_id=dummy&"
             "discharge_macaroon_action=field.actions.complete&"
diff --git a/lib/lp/snappy/tests/test_snapbuild.py b/lib/lp/snappy/tests/test_snapbuild.py
index c1e0378..33a1d1d 100644
--- a/lib/lp/snappy/tests/test_snapbuild.py
+++ b/lib/lp/snappy/tests/test_snapbuild.py
@@ -983,7 +983,7 @@ class TestSnapBuildWebservice(TestCaseWithFactory):
 
     def assertCanOpenRedirectedUrl(self, browser, url):
         browser.open(url)
-        self.assertEqual(303, int(browser.headers["Status"].split(" ", 1)[0]))
+        self.assertEqual(303, browser.responseStatusCode)
         urlopen(browser.headers["Location"]).close()
 
     def test_logs(self):
diff --git a/lib/lp/soyuz/tests/test_livefsbuild.py b/lib/lp/soyuz/tests/test_livefsbuild.py
index b8f33b0..83110e0 100644
--- a/lib/lp/soyuz/tests/test_livefsbuild.py
+++ b/lib/lp/soyuz/tests/test_livefsbuild.py
@@ -610,7 +610,7 @@ class TestLiveFSBuildWebservice(TestCaseWithFactory):
 
     def assertCanOpenRedirectedUrl(self, browser, url):
         browser.open(url)
-        self.assertEqual(303, int(browser.headers["Status"].split(" ", 1)[0]))
+        self.assertEqual(303, browser.responseStatusCode)
         urlopen(browser.headers["Location"]).close()
 
     def test_logs(self):
diff --git a/lib/lp/soyuz/tests/test_packageupload.py b/lib/lp/soyuz/tests/test_packageupload.py
index 654a0d6..7e56c8b 100644
--- a/lib/lp/soyuz/tests/test_packageupload.py
+++ b/lib/lp/soyuz/tests/test_packageupload.py
@@ -1235,7 +1235,7 @@ class TestPackageUploadWebservice(TestCaseWithFactory):
 
     def assertCanOpenRedirectedUrl(self, browser, url):
         browser.open(url)
-        self.assertEqual(303, int(browser.headers["Status"].split(" ", 1)[0]))
+        self.assertEqual(303, browser.responseStatusCode)
         urlopen(browser.headers["Location"]).close()
 
     def assertRequiresEdit(self, method_name, **kwargs):
diff --git a/lib/lp/testing/browser.py b/lib/lp/testing/browser.py
index 6a4a665..be59f2b 100644
--- a/lib/lp/testing/browser.py
+++ b/lib/lp/testing/browser.py
@@ -14,6 +14,7 @@ __all__ = [
 ]
 
 import ssl
+from typing import Optional
 
 import six
 from lazr.uri import URI
@@ -63,6 +64,13 @@ class Browser(_Browser):
         uri = URI(self.url)
         return uri.path
 
+    @property
+    def responseStatusCode(self) -> Optional[int]:
+        try:
+            return int(self.headers["Status"].split(" ", 1)[0])
+        except (KeyError, IndexError, ValueError):
+            return None
+
 
 def setUp(test):
     """Set up appserver tests."""
diff --git a/lib/lp/testing/pages.py b/lib/lp/testing/pages.py
index 632d4ec..0a032f4 100644
--- a/lib/lp/testing/pages.py
+++ b/lib/lp/testing/pages.py
@@ -23,6 +23,7 @@ from contextlib import contextmanager
 from datetime import datetime
 from io import BytesIO
 from itertools import chain
+from typing import Optional
 from urllib.parse import urljoin
 
 import six
@@ -816,6 +817,13 @@ class Browser(_Browser):
 
         return Link(elem, self, baseurl)
 
+    @property
+    def responseStatusCode(self) -> Optional[int]:
+        try:
+            return int(self.headers["Status"].split(" ", 1)[0])
+        except (KeyError, IndexError, ValueError):
+            return None
+
 
 def setupBrowser(auth=None):
     """Create a testbrowser object for use in pagetests.