From dcc4b761a179165c8b86c7e567c4ddfe11685417 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Tue, 19 Sep 2023 17:44:50 +0200 Subject: [PATCH 1/4] Addons: add test cases for the endpoint Closes #10685 --- readthedocs/proxito/tests/test_headers.py | 12 +- readthedocs/proxito/tests/test_hosting.py | 290 +++++++++++++++++++++- readthedocs/proxito/views/hosting.py | 5 +- 3 files changed, 301 insertions(+), 6 deletions(-) diff --git a/readthedocs/proxito/tests/test_headers.py b/readthedocs/proxito/tests/test_headers.py index 9f619c5655a..9daf27d368b 100644 --- a/readthedocs/proxito/tests/test_headers.py +++ b/readthedocs/proxito/tests/test_headers.py @@ -4,7 +4,7 @@ from readthedocs.builds.constants import LATEST from readthedocs.projects.constants import PRIVATE, PUBLIC -from readthedocs.projects.models import Domain, HTTPHeader +from readthedocs.projects.models import AddonsConfig, Domain, HTTPHeader from .base import BaseDocServing @@ -159,6 +159,16 @@ def test_hosting_integrations_header(self): self.assertIsNotNone(r.get("X-RTD-Hosting-Integrations")) self.assertEqual(r["X-RTD-Hosting-Integrations"], "true") + def test_force_addons_header(self): + fixture.get(AddonsConfig, project=self.project, enabled=True) + + r = self.client.get( + "/en/latest/", secure=True, headers={"host": "project.dev.readthedocs.io"} + ) + self.assertEqual(r.status_code, 200) + self.assertIsNotNone(r.get("X-RTD-Force-Addons")) + self.assertEqual(r["X-RTD-Force-Addons"], "true") + def test_cors_headers_private_version(self): version = self.project.versions.get(slug=LATEST) version.privacy_level = PRIVATE diff --git a/readthedocs/proxito/tests/test_hosting.py b/readthedocs/proxito/tests/test_hosting.py index 01e78212691..cd7132bbfd9 100644 --- a/readthedocs/proxito/tests/test_hosting.py +++ b/readthedocs/proxito/tests/test_hosting.py @@ -10,9 +10,9 @@ from django.urls import reverse from readthedocs.builds.constants import LATEST -from readthedocs.builds.models import Build -from readthedocs.projects.constants import PUBLIC -from readthedocs.projects.models import Project +from readthedocs.builds.models import Build, Version +from readthedocs.projects.constants import PRIVATE, PUBLIC +from readthedocs.projects.models import Feature, Project @override_settings( @@ -117,3 +117,287 @@ def test_get_config_unsupported_version(self): ) assert r.status_code == 400 assert r.json() == self._get_response_dict("v2") + + def test_disabled_addons_via_feature_flags(self): + fixture.get( + Feature, + projects=[self.project], + feature_id=Feature.ADDONS_ANALYTICS_DISABLED, + ) + fixture.get( + Feature, + projects=[self.project], + feature_id=Feature.ADDONS_EXTERNAL_VERSION_WARNING_DISABLED, + ) + fixture.get( + Feature, + projects=[self.project], + feature_id=Feature.ADDONS_NON_LATEST_VERSION_WARNING_DISABLED, + ) + fixture.get( + Feature, + projects=[self.project], + feature_id=Feature.ADDONS_DOC_DIFF_DISABLED, + ) + fixture.get( + Feature, + projects=[self.project], + feature_id=Feature.ADDONS_FLYOUT_DISABLED, + ) + fixture.get( + Feature, + projects=[self.project], + feature_id=Feature.ADDONS_SEARCH_DISABLED, + ) + fixture.get( + Feature, + projects=[self.project], + feature_id=Feature.ADDONS_HOTKEYS_DISABLED, + ) + + r = self.client.get( + reverse("proxito_readthedocs_docs_addons"), + {"url": "https://project.dev.readthedocs.io/en/latest/"}, + secure=True, + headers={ + "host": "project.dev.readthedocs.io", + "x-rtd-hosting-integrations-version": "0.1.0", + }, + ) + assert r.status_code == 200 + assert r.json()["addons"]["analytics"]["enabled"] is False + assert r.json()["addons"]["external_version_warning"]["enabled"] is False + assert r.json()["addons"]["non_latest_version_warning"]["enabled"] is False + assert r.json()["addons"]["doc_diff"]["enabled"] is False + assert r.json()["addons"]["flyout"]["enabled"] is False + assert r.json()["addons"]["search"]["enabled"] is False + assert r.json()["addons"]["hotkeys"]["enabled"] is False + + def test_non_latest_version_warning_versions(self): + fixture.get( + Version, + project=self.project, + privacy_level=PRIVATE, + slug="private", + verbose_name="private", + built=True, + active=True, + ) + fixture.get( + Version, + project=self.project, + privacy_level=PUBLIC, + slug="public-built", + verbose_name="public-built", + built=True, + active=True, + ) + fixture.get( + Version, + project=self.project, + privacy_level=PUBLIC, + slug="public-not-built", + verbose_name="public-not-built", + built=False, + active=True, + ) + + r = self.client.get( + reverse("proxito_readthedocs_docs_addons"), + {"url": "https://project.dev.readthedocs.io/en/latest/"}, + secure=True, + headers={ + "host": "project.dev.readthedocs.io", + "x-rtd-hosting-integrations-version": "0.1.0", + }, + ) + assert r.status_code == 200 + + expected = ["latest", "public-built"] + assert r.json()["addons"]["non_latest_version_warning"]["versions"] == expected + + def test_flyout_versions(self): + fixture.get( + Version, + project=self.project, + privacy_level=PRIVATE, + slug="private", + verbose_name="private", + built=True, + active=True, + ) + fixture.get( + Version, + project=self.project, + privacy_level=PUBLIC, + slug="public-built", + verbose_name="public-built", + built=True, + active=True, + ) + fixture.get( + Version, + project=self.project, + privacy_level=PUBLIC, + slug="public-not-built", + verbose_name="public-not-built", + built=False, + active=True, + ) + fixture.get( + Version, + project=self.project, + privacy_level=PUBLIC, + slug="hidden", + verbose_name="hidden", + built=False, + hidden=True, + active=True, + ) + + r = self.client.get( + reverse("proxito_readthedocs_docs_addons"), + {"url": "https://project.dev.readthedocs.io/en/latest/"}, + secure=True, + headers={ + "host": "project.dev.readthedocs.io", + "x-rtd-hosting-integrations-version": "0.1.0", + }, + ) + assert r.status_code == 200 + + expected = [ + {"slug": "latest", "url": "/en/latest/"}, + {"slug": "public-built", "url": "/en/public-built/"}, + ] + assert r.json()["addons"]["flyout"]["versions"] == expected + + def test_flyout_translations(self): + fixture.get( + Project, + slug="translation", + main_language_project=self.project, + language="ja", + ) + + r = self.client.get( + reverse("proxito_readthedocs_docs_addons"), + {"url": "https://project.dev.readthedocs.io/en/latest/"}, + secure=True, + headers={ + "host": "project.dev.readthedocs.io", + "x-rtd-hosting-integrations-version": "0.1.0", + }, + ) + assert r.status_code == 200 + + expected = [ + {"slug": "ja", "url": "/ja/"}, + ] + assert r.json()["addons"]["flyout"]["translations"] == expected + + def test_flyout_downloads(self): + fixture.get( + Version, + project=self.project, + privacy_level=PUBLIC, + slug="offline", + verbose_name="offline", + built=True, + has_pdf=True, + has_epub=True, + has_htmlzip=True, + active=True, + ) + + r = self.client.get( + reverse("proxito_readthedocs_docs_addons"), + {"url": "https://project.dev.readthedocs.io/en/offline/"}, + secure=True, + headers={ + "host": "project.dev.readthedocs.io", + "x-rtd-hosting-integrations-version": "0.1.0", + }, + ) + assert r.status_code == 200 + + expected = [ + { + "name": "PDF", + "url": "//project.dev.readthedocs.io/_/downloads/en/offline/pdf/", + }, + { + "name": "HTML", + "url": "//project.dev.readthedocs.io/_/downloads/en/offline/htmlzip/", + }, + { + "name": "Epub", + "url": "//project.dev.readthedocs.io/_/downloads/en/offline/epub/", + }, + ] + assert r.json()["addons"]["flyout"]["downloads"] == expected + + def test_flyout_single_version_project(self): + self.version.has_pdf = True + self.version.has_epub = True + self.version.has_htmlzip = True + self.version.save() + + self.project.single_version = True + self.project.save() + + r = self.client.get( + reverse("proxito_readthedocs_docs_addons"), + {"url": "https://project.dev.readthedocs.io/"}, + secure=True, + headers={ + "host": "project.dev.readthedocs.io", + "x-rtd-hosting-integrations-version": "0.1.0", + }, + ) + assert r.status_code == 200 + + expected = [] + assert r.json()["addons"]["flyout"]["versions"] == expected + + expected = [ + { + "name": "PDF", + "url": "//project.dev.readthedocs.io/_/downloads/en/latest/pdf/", + }, + { + "name": "HTML", + "url": "//project.dev.readthedocs.io/_/downloads/en/latest/htmlzip/", + }, + { + "name": "Epub", + "url": "//project.dev.readthedocs.io/_/downloads/en/latest/epub/", + }, + ] + assert r.json()["addons"]["flyout"]["downloads"] == expected + + def test_project_subproject(self): + subproject = fixture.get( + Project, slug="subproject", repo="https://github.com/readthedocs/subproject" + ) + self.project.add_subproject(subproject) + + r = self.client.get( + reverse("proxito_readthedocs_docs_addons"), + { + "url": "https://project.dev.readthedocs.io/projects/subproject/en/latest/" + }, + secure=True, + headers={ + "host": "project.dev.readthedocs.io", + "x-rtd-hosting-integrations-version": "0.1.0", + }, + ) + assert r.status_code == 200 + + assert r.json()["projects"]["current"]["id"] == subproject.pk + assert r.json()["projects"]["current"]["slug"] == subproject.slug + assert ( + r.json()["projects"]["current"]["repository"]["url"] + == "https://github.com/readthedocs/subproject" + ) diff --git a/readthedocs/proxito/views/hosting.py b/readthedocs/proxito/views/hosting.py index 98096c92d50..adc46fc2a64 100644 --- a/readthedocs/proxito/views/hosting.py +++ b/readthedocs/proxito/views/hosting.py @@ -237,8 +237,9 @@ def _v0(self, project, version, build, filename, user): .only("slug") .order_by("slug") ) - if version: - version_downloads = version.get_downloads(pretty=True).items() + + if version: + version_downloads = version.get_downloads(pretty=True).items() project_translations = ( project.translations.all().only("language").order_by("language") From 3b1fdb88e68f497df566e3f0fed56b02358e4bdf Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Wed, 20 Sep 2023 16:43:26 +0200 Subject: [PATCH 2/4] Missing field on JSON response for test --- .../api/v3/tests/responses/remoterepositories-list.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/readthedocs/api/v3/tests/responses/remoterepositories-list.json b/readthedocs/api/v3/tests/responses/remoterepositories-list.json index cf3f9ba7b04..55762d7f782 100644 --- a/readthedocs/api/v3/tests/responses/remoterepositories-list.json +++ b/readthedocs/api/v3/tests/responses/remoterepositories-list.json @@ -49,7 +49,8 @@ }, "users": [{ "username": "testuser" }], "privacy_level": "public", - "external_builds_privacy_level": "public" + "external_builds_privacy_level": "public", + "single_version": false }], "remote_organization": { "avatar_url": "https://avatars.githubusercontent.com/u/366329?v=4", From a5c2f11082ff8b2862297fb80b8c3d095d10a266 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Wed, 20 Sep 2023 16:46:26 +0200 Subject: [PATCH 3/4] Revert "Missing field on JSON response for test" This reverts commit 3b1fdb88e68f497df566e3f0fed56b02358e4bdf. --- .../api/v3/tests/responses/remoterepositories-list.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/readthedocs/api/v3/tests/responses/remoterepositories-list.json b/readthedocs/api/v3/tests/responses/remoterepositories-list.json index 55762d7f782..cf3f9ba7b04 100644 --- a/readthedocs/api/v3/tests/responses/remoterepositories-list.json +++ b/readthedocs/api/v3/tests/responses/remoterepositories-list.json @@ -49,8 +49,7 @@ }, "users": [{ "username": "testuser" }], "privacy_level": "public", - "external_builds_privacy_level": "public", - "single_version": false + "external_builds_privacy_level": "public" }], "remote_organization": { "avatar_url": "https://avatars.githubusercontent.com/u/366329?v=4", From 1721320be799e1fe3828f9ae4d866c8194c82dcc Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Mon, 16 Oct 2023 14:16:13 +0200 Subject: [PATCH 4/4] Tests: send api-version and client-version to addons endpoint --- readthedocs/proxito/tests/test_hosting.py | 47 ++++++++++++++++------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/readthedocs/proxito/tests/test_hosting.py b/readthedocs/proxito/tests/test_hosting.py index 62607c26f14..d55734a72f0 100644 --- a/readthedocs/proxito/tests/test_hosting.py +++ b/readthedocs/proxito/tests/test_hosting.py @@ -163,11 +163,14 @@ def test_disabled_addons_via_feature_flags(self): r = self.client.get( reverse("proxito_readthedocs_docs_addons"), - {"url": "https://project.dev.readthedocs.io/en/latest/"}, + { + "url": "https://project.dev.readthedocs.io/en/latest/", + "client-version": "0.6.0", + "api-version": "0.1.0", + }, secure=True, headers={ "host": "project.dev.readthedocs.io", - "x-rtd-hosting-integrations-version": "0.1.0", }, ) assert r.status_code == 200 @@ -210,11 +213,14 @@ def test_non_latest_version_warning_versions(self): r = self.client.get( reverse("proxito_readthedocs_docs_addons"), - {"url": "https://project.dev.readthedocs.io/en/latest/"}, + { + "url": "https://project.dev.readthedocs.io/en/latest/", + "client-version": "0.6.0", + "api-version": "0.1.0", + }, secure=True, headers={ "host": "project.dev.readthedocs.io", - "x-rtd-hosting-integrations-version": "0.1.0", }, ) assert r.status_code == 200 @@ -263,11 +269,14 @@ def test_flyout_versions(self): r = self.client.get( reverse("proxito_readthedocs_docs_addons"), - {"url": "https://project.dev.readthedocs.io/en/latest/"}, + { + "url": "https://project.dev.readthedocs.io/en/latest/", + "client-version": "0.6.0", + "api-version": "0.1.0", + }, secure=True, headers={ "host": "project.dev.readthedocs.io", - "x-rtd-hosting-integrations-version": "0.1.0", }, ) assert r.status_code == 200 @@ -288,11 +297,14 @@ def test_flyout_translations(self): r = self.client.get( reverse("proxito_readthedocs_docs_addons"), - {"url": "https://project.dev.readthedocs.io/en/latest/"}, + { + "url": "https://project.dev.readthedocs.io/en/latest/", + "client-version": "0.6.0", + "api-version": "0.1.0", + }, secure=True, headers={ "host": "project.dev.readthedocs.io", - "x-rtd-hosting-integrations-version": "0.1.0", }, ) assert r.status_code == 200 @@ -318,11 +330,14 @@ def test_flyout_downloads(self): r = self.client.get( reverse("proxito_readthedocs_docs_addons"), - {"url": "https://project.dev.readthedocs.io/en/offline/"}, + { + "url": "https://project.dev.readthedocs.io/en/offline/", + "client-version": "0.6.0", + "api-version": "0.1.0", + }, secure=True, headers={ "host": "project.dev.readthedocs.io", - "x-rtd-hosting-integrations-version": "0.1.0", }, ) assert r.status_code == 200 @@ -354,11 +369,14 @@ def test_flyout_single_version_project(self): r = self.client.get( reverse("proxito_readthedocs_docs_addons"), - {"url": "https://project.dev.readthedocs.io/"}, + { + "url": "https://project.dev.readthedocs.io/", + "client-version": "0.6.0", + "api-version": "0.1.0", + }, secure=True, headers={ "host": "project.dev.readthedocs.io", - "x-rtd-hosting-integrations-version": "0.1.0", }, ) assert r.status_code == 200 @@ -391,12 +409,13 @@ def test_project_subproject(self): r = self.client.get( reverse("proxito_readthedocs_docs_addons"), { - "url": "https://project.dev.readthedocs.io/projects/subproject/en/latest/" + "url": "https://project.dev.readthedocs.io/projects/subproject/en/latest/", + "client-version": "0.6.0", + "api-version": "0.1.0", }, secure=True, headers={ "host": "project.dev.readthedocs.io", - "x-rtd-hosting-integrations-version": "0.1.0", }, ) assert r.status_code == 200