Repository: readthedocs/readthedocs.org

Issue:
Link to PDF in RTD popup menu shows even though PDF build is turned off
See https://github.com/rtfd/readthedocs.org/pull/1344#issuecomment-120510729

The link is in the RTD popup menu
![RTD popup menu](http://i.imgur.com/9E5C1Sl.png)

An example is in [Pyramid docs](http://docs.pylonsproject.org/projects/pyramid/en/latest/).



Discussion:

Resolving patch:
diff --git a/readthedocs/projects/models.py b/readthedocs/projects/models.py
--- a/readthedocs/projects/models.py
+++ b/readthedocs/projects/models.py
@@ -607,9 +607,13 @@ def has_aliases(self):
         return self.aliases.exists()
 
     def has_pdf(self, version_slug=LATEST):
+        if not self.enable_pdf_build:
+            return False
         return os.path.exists(self.get_production_media_path(type='pdf', version_slug=version_slug))
 
     def has_epub(self, version_slug=LATEST):
+        if not self.enable_epub_build:
+            return False
         return os.path.exists(self.get_production_media_path(type='epub', version_slug=version_slug))
 
     def has_htmlzip(self, version_slug=LATEST):


Test patch:
diff --git a/readthedocs/rtd_tests/mocks/paths.py b/readthedocs/rtd_tests/mocks/paths.py
new file mode 100644
--- /dev/null
+++ b/readthedocs/rtd_tests/mocks/paths.py
@@ -0,0 +1,53 @@
+import os
+import re
+import mock
+
+
+def fake_paths(check):
+    """
+    >>> with fake_paths(lambda path: True if path.endswith('.pdf') else None):
+    ...     assert os.path.exists('my.pdf')
+    ...     assert os.path.exists('Nopdf.txt')
+
+    The first assertion will be ok, the second assertion is not faked as the
+    ``check`` returned ``None`` for this path.
+    """
+
+    original_exists = os.path.exists
+
+    def patched_exists(path):
+        result = check(path)
+        if result is None:
+            return original_exists(path)
+        return result
+
+    return mock.patch.object(os.path, 'exists', patched_exists)
+
+
+def fake_paths_lookup(path_dict):
+    """
+    >>> paths = {'my.txt': True, 'no.txt': False}
+    >>> with fake_paths_lookup(paths):
+    ...     assert os.path.exists('my.txt') == True
+    ...     assert os.path.exists('no.txt') == False
+    """
+    def check(path):
+        return path_dict.get(path, None)
+    return fake_paths(check)
+
+
+def fake_paths_by_regex(pattern, exists=True):
+    """
+    >>> with fake_paths_by_regex('\.pdf$'):
+    ...     assert os.path.exists('my.pdf') == True
+    >>> with fake_paths_by_regex('\.pdf$', exists=False):
+    ...     assert os.path.exists('my.pdf') == False
+    """
+    regex = re.compile(pattern)
+
+    def check(path):
+        if regex.search(path):
+            return exists
+        return None
+
+    return fake_paths(check)
diff --git a/readthedocs/rtd_tests/tests/test_builds.py b/readthedocs/rtd_tests/tests/test_builds.py
--- a/readthedocs/rtd_tests/tests/test_builds.py
+++ b/readthedocs/rtd_tests/tests/test_builds.py
@@ -6,6 +6,7 @@
 
 from projects.tasks import build_docs
 from rtd_tests.factories.projects_factories import ProjectFactory
+from rtd_tests.mocks.paths import fake_paths_lookup
 from doc_builder.loader import get_builder_class
 
 
@@ -29,22 +30,6 @@ def build_subprocess_side_effect(*args, **kwargs):
         return subprocess.Popen(*args, **kwargs)
 
 
-def fake_paths(*paths):
-    """
-    Returns a context manager that patches ``os.path.exists`` to return
-    ``True`` for the given ``paths``.
-    """
-
-    original_exists = os.path.exists
-
-    def patched_exists(path):
-        if path in paths:
-            return True
-        return original_exists(path)
-
-    return mock.patch.object(os.path, 'exists', patched_exists)
-
-
 class BuildTests(TestCase):
 
     @mock.patch('slumber.Resource')
@@ -68,11 +53,13 @@ def test_build(self, mock_Popen, mock_api_versions, mock_chdir, mock_apiv2_downl
 
         mock_apiv2_downloads.get.return_value = {'downloads': "no_url_here"}
 
-        conf_path = os.path.join(project.checkout_path(version.slug), project.conf_py_file)
+        conf_path = os.path.join(
+            project.checkout_path(version.slug),
+            project.conf_py_file)
 
         # Mock open to simulate existing conf.py file
         with mock.patch('codecs.open', mock.mock_open(), create=True):
-            with fake_paths(conf_path):
+            with fake_paths_lookup({conf_path: True}):
                 built_docs = build_docs(version,
                                         False,
                                         False,
@@ -136,7 +123,7 @@ def test_build_respects_pdf_flag(self,
 
         # Mock open to simulate existing conf.py file
         with mock.patch('codecs.open', mock.mock_open(), create=True):
-            with fake_paths(conf_path):
+            with fake_paths_lookup({conf_path: True}):
                 built_docs = build_docs(version,
                                         False,
                                         False,
@@ -180,7 +167,7 @@ def test_build_respects_epub_flag(self,
 
         # Mock open to simulate existing conf.py file
         with mock.patch('codecs.open', mock.mock_open(), create=True):
-            with fake_paths(conf_path):
+            with fake_paths_lookup({conf_path: True}):
                 built_docs = build_docs(version,
                                         False,
                                         False,
diff --git a/readthedocs/rtd_tests/tests/test_footer.py b/readthedocs/rtd_tests/tests/test_footer.py
--- a/readthedocs/rtd_tests/tests/test_footer.py
+++ b/readthedocs/rtd_tests/tests/test_footer.py
@@ -2,6 +2,7 @@
 
 from django.test import TestCase
 
+from rtd_tests.mocks.paths import fake_paths_by_regex
 from projects.models import Project
 
 
@@ -28,3 +29,30 @@ def test_footer(self):
         self.assertEqual(resp['version_active'], False)
         self.assertEqual(r.status_code, 200)
 
+    def test_pdf_build_mentioned_in_footer(self):
+        with fake_paths_by_regex('\.pdf$'):
+            response = self.client.get(
+                '/api/v2/footer_html/?project=pip&version=latest&page=index', {})
+        self.assertContains(response, 'pdf')
+
+    def test_pdf_not_mentioned_in_footer_when_build_is_disabled(self):
+        self.pip.enable_pdf_build = False
+        self.pip.save()
+        with fake_paths_by_regex('\.pdf$'):
+            response = self.client.get(
+                '/api/v2/footer_html/?project=pip&version=latest&page=index', {})
+        self.assertNotContains(response, 'pdf')
+
+    def test_epub_build_mentioned_in_footer(self):
+        with fake_paths_by_regex('\.epub$'):
+            response = self.client.get(
+                '/api/v2/footer_html/?project=pip&version=latest&page=index', {})
+        self.assertContains(response, 'epub')
+
+    def test_epub_not_mentioned_in_footer_when_build_is_disabled(self):
+        self.pip.enable_epub_build = False
+        self.pip.save()
+        with fake_paths_by_regex('\.epub$'):
+            response = self.client.get(
+                '/api/v2/footer_html/?project=pip&version=latest&page=index', {})
+        self.assertNotContains(response, 'epub')
diff --git a/readthedocs/rtd_tests/tests/test_project.py b/readthedocs/rtd_tests/tests/test_project.py
--- a/readthedocs/rtd_tests/tests/test_project.py
+++ b/readthedocs/rtd_tests/tests/test_project.py
@@ -1,11 +1,13 @@
 from bamboo_boy.utils import with_canopy
 import json
 from django.test import TestCase
+from builds.constants import LATEST
 from projects.models import Project
 from rtd_tests.factories.projects_factories import OneProjectWithTranslationsOneWithout,\
     ProjectFactory
 from rest_framework.reverse import reverse
 from restapi.serializers import ProjectSerializer
+from rtd_tests.mocks.paths import fake_paths_by_regex
 
 
 @with_canopy(OneProjectWithTranslationsOneWithout)
@@ -50,3 +52,33 @@ def test_token(self):
         resp = json.loads(r.content)
         self.assertEqual(r.status_code, 200)
         self.assertEqual(resp['token'], None)
+
+    def test_has_pdf(self):
+        # The project has a pdf if the PDF file exists on disk.
+        with fake_paths_by_regex('\.pdf$'):
+            self.assertTrue(self.pip.has_pdf(LATEST))
+
+        # The project has no pdf if there is no file on disk.
+        with fake_paths_by_regex('\.pdf$', exists=False):
+            self.assertFalse(self.pip.has_pdf(LATEST))
+
+    def test_has_pdf_with_pdf_build_disabled(self):
+        # The project has NO pdf if pdf builds are disabled
+        self.pip.enable_pdf_build = False
+        with fake_paths_by_regex('\.pdf$'):
+            self.assertFalse(self.pip.has_pdf(LATEST))
+
+    def test_has_epub(self):
+        # The project has a epub if the PDF file exists on disk.
+        with fake_paths_by_regex('\.epub$'):
+            self.assertTrue(self.pip.has_epub(LATEST))
+
+        # The project has no epub if there is no file on disk.
+        with fake_paths_by_regex('\.epub$', exists=False):
+            self.assertFalse(self.pip.has_epub(LATEST))
+
+    def test_has_epub_with_epub_build_disabled(self):
+        # The project has NO epub if epub builds are disabled
+        self.pip.enable_epub_build = False
+        with fake_paths_by_regex('\.epub$'):
+            self.assertFalse(self.pip.has_epub(LATEST))