Skip to content

Commit 34092ee

Browse files
committed
added tests for mkdocs yml
1 parent 44714b7 commit 34092ee

File tree

3 files changed

+72
-13
lines changed

3 files changed

+72
-13
lines changed

readthedocs/builds/models.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,12 @@ def get_conf_py_path(self):
540540
conf_py_path = os.path.relpath(conf_py_path, checkout_prefix)
541541
return conf_py_path
542542

543+
def get_mkdocs_yml_path(self):
544+
mkdocs_yml_path = self.project.mkdocs_dir(self.slug)
545+
checkout_prefix = self.project.checkout_path(self.slug)
546+
mkdocs_yml_path = os.path.relpath(mkdocs_yml_path, checkout_prefix)
547+
return mkdocs_yml_path
548+
543549
def get_storage_paths(self, version_slug=None):
544550
"""
545551
Return a list of all build artifact storage paths for this version.

readthedocs/projects/models.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Project models."""
2+
23
import fnmatch
34
import hashlib
45
import hmac
@@ -34,6 +35,7 @@
3435
from readthedocs.core.resolver import Resolver
3536
from readthedocs.core.utils import extract_valid_attributes_for_model, slugify
3637
from readthedocs.core.utils.url import unsafe_join_url_path
38+
from readthedocs.doc_builder.exceptions import MkDocsYAMLParseError
3739
from readthedocs.domains.querysets import DomainQueryset
3840
from readthedocs.domains.validators import check_domains_limit
3941
from readthedocs.notifications.models import Notification as NewNotification
@@ -80,7 +82,6 @@ def default_privacy_level():
8082

8183

8284
class ProjectRelationship(models.Model):
83-
8485
"""
8586
Project to project relationship.
8687
@@ -135,7 +136,6 @@ def subproject_prefix(self):
135136

136137

137138
class AddonsConfig(TimeStampedModel):
138-
139139
"""
140140
Addons project configuration.
141141
@@ -266,7 +266,6 @@ class AddonsConfig(TimeStampedModel):
266266

267267

268268
class AddonSearchFilter(TimeStampedModel):
269-
270269
"""
271270
Addon search user defined filter.
272271
@@ -279,7 +278,6 @@ class AddonSearchFilter(TimeStampedModel):
279278

280279

281280
class Project(models.Model):
282-
283281
"""Project model."""
284282

285283
# Auto fields
@@ -962,7 +960,6 @@ def conf_file(self, version=LATEST):
962960
# contains the `doc` word in its path and return this one
963961
if filename.find("doc", 70) != -1:
964962
return filename
965-
966963
# If the project has more than one conf.py file but none of them have
967964
# the `doc` word in the path, we raise an error informing this to the user
968965
if len(files) > 1:
@@ -972,11 +969,27 @@ def conf_file(self, version=LATEST):
972969

973970
raise ProjectConfigurationError(ProjectConfigurationError.NOT_FOUND)
974971

972+
def yml_file(self, version=LATEST):
973+
"""Find a Mkdocs ``mkdocs.yml`` file in the project checkout."""
974+
files = self.find("mkdocs.yml", version)
975+
if not files:
976+
files = self.full_find("mkdocs.yml", version)
977+
if len(files) == 1:
978+
return files[0]
979+
# TODO: handle for multiple mkdocs.yml files
980+
981+
raise MkDocsYAMLParseError(MkDocsYAMLParseError.NOT_FOUND)
982+
975983
def conf_dir(self, version=LATEST):
976984
conf_file = self.conf_file(version)
977985
if conf_file:
978986
return os.path.dirname(conf_file)
979987

988+
def mkdocs_dir(self, version=LATEST):
989+
yml_file = self.yml_file(version)
990+
if yml_file:
991+
return os.path.dirname(yml_file)
992+
980993
@property
981994
def has_good_build(self):
982995
# Check if there is `_good_build` annotation in the Queryset.
@@ -1432,7 +1445,6 @@ def organization(self):
14321445

14331446

14341447
class APIProject(Project):
1435-
14361448
"""
14371449
Project proxy model for API data deserialization.
14381450
@@ -1506,7 +1518,6 @@ def environment_variables(self, *, public_only=True):
15061518

15071519

15081520
class ImportedFile(models.Model):
1509-
15101521
"""
15111522
Imported files model.
15121523
@@ -1558,7 +1569,6 @@ def get_absolute_url(self):
15581569

15591570

15601571
class HTMLFile(ImportedFile):
1561-
15621572
"""
15631573
Imported HTML file Proxy model.
15641574
@@ -1580,7 +1590,6 @@ def processed_json(self):
15801590

15811591

15821592
class Notification(TimeStampedModel):
1583-
15841593
"""WebHook / Email notification attached to a Project."""
15851594

15861595
# TODO: Overridden from TimeStampedModel just to allow null values,
@@ -1745,7 +1754,6 @@ def sign_payload(self, payload):
17451754

17461755

17471756
class Domain(TimeStampedModel):
1748-
17491757
"""A custom domain name for a project."""
17501758

17511759
# TODO: Overridden from TimeStampedModel just to allow null values,
@@ -1869,7 +1877,6 @@ def save(self, *args, **kwargs):
18691877

18701878

18711879
class HTTPHeader(TimeStampedModel, models.Model):
1872-
18731880
"""
18741881
Define a HTTP header for a user Domain.
18751882
@@ -1912,7 +1919,6 @@ def __str__(self):
19121919

19131920

19141921
class Feature(models.Model):
1915-
19161922
"""
19171923
Project feature flags.
19181924

readthedocs/rtd_tests/tests/test_doc_builder.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@
88
from django.test.utils import override_settings
99

1010
from readthedocs.config.tests.test_config import get_build_config
11+
from readthedocs.doc_builder.backends.mkdocs import BaseMkdocs
1112
from readthedocs.doc_builder.backends.sphinx import BaseSphinx
13+
from readthedocs.doc_builder.exceptions import MkDocsYAMLParseError
1214
from readthedocs.doc_builder.python_environments import Virtualenv
1315
from readthedocs.projects.exceptions import ProjectConfigurationError
1416
from readthedocs.projects.models import Project
1517

1618

1719
@override_settings(PRODUCTION_DOMAIN="readthedocs.org")
18-
class SphinxBuilderTest(TestCase):
20+
class BuilderTest(TestCase):
1921
fixtures = ["test_data", "eric"]
2022

2123
def setUp(self):
@@ -33,6 +35,9 @@ def setUp(self):
3335
BaseSphinx.type = "base"
3436
BaseSphinx.sphinx_build_dir = tempfile.mkdtemp()
3537
BaseSphinx.relative_output_dir = "_readthedocs/"
38+
BaseMkdocs.type = "base"
39+
BaseMkdocs.sphinx_build_dir = tempfile.mkdtemp()
40+
BaseMkdocs.relative_output_dir = "_readthedocs/"
3641

3742
@patch("readthedocs.doc_builder.backends.sphinx.BaseSphinx.docs_dir")
3843
@patch("readthedocs.doc_builder.backends.sphinx.BaseSphinx.run")
@@ -112,3 +117,45 @@ def test_multiple_conf_py(
112117
with pytest.raises(ProjectConfigurationError):
113118
with override_settings(DOCROOT=tmp_docs_dir):
114119
base_sphinx.show_conf()
120+
121+
@patch("readthedocs.doc_builder.backends.mkdocs.BaseMkdocs.docs_dir")
122+
@patch("readthedocs.doc_builder.backends.mkdocs.BaseMkdocs.run")
123+
@patch("readthedocs.builds.models.Version.get_mkdocs_yml_path")
124+
@patch("readthedocs.projects.models.Project.checkout_path")
125+
@patch("readthedocs.doc_builder.python_environments.load_yaml_config")
126+
def test_project_without_conf_py(
127+
self,
128+
load_yaml_config,
129+
checkout_path,
130+
get_mkdocs_yml_path,
131+
_,
132+
docs_dir,
133+
):
134+
"""
135+
Test for a project without ``mkdocs.yml`` file.
136+
137+
When this happen, the ``get_mkdocs_yml_path`` raises a
138+
``MkDocsYAMLParseError`` which is captured by our own code.
139+
"""
140+
tmp_dir = tempfile.mkdtemp()
141+
checkout_path.return_value = tmp_dir
142+
docs_dir.return_value = tmp_dir
143+
get_mkdocs_yml_path.side_effect = MkDocsYAMLParseError
144+
python_env = Virtualenv(
145+
version=self.version,
146+
build_env=self.build_env,
147+
config=get_build_config(
148+
{"mkdocs": {"configuration": "mkdocs.yml"}}, validate=True
149+
),
150+
)
151+
base_mkdocs = BaseMkdocs(
152+
build_env=self.build_env,
153+
python_env=python_env,
154+
)
155+
with self.assertRaises(MkDocsYAMLParseError) as e:
156+
base_mkdocs.show_conf()
157+
158+
self.assertEqual(
159+
e.exception.message_id,
160+
MkDocsYAMLParseError.NOT_FOUND,
161+
)

0 commit comments

Comments
 (0)