Repository: readthedocs/readthedocs.org
Issue: "Default branch: latest" does not exist

* Read the Docs project: https://readthedocs.org/projects/test-builds/

Going to `Advanced settings` the `Default branch` selector shows `latest` as an option. I think this is invalid since the repo has no `latest` branch: https://github.com/rtfd/test-builds/branches/all?utf8=%E2%9C%93&query=latest

In this particular project seems that this is not a real problem, since the build that was triggered after saving it to this branch [picked the `origin/master` anyway](https://readthedocs.org/projects/test-builds/builds/8694899/) but I saw other projects failing for this (in the corporate site).

Anyway, `latest` should not be an option since it's not a valid branch on the repository.

Changed files:
readthedocs/projects/forms.py, readthedocs/rtd_tests/tests/test_project_forms.py

diff --git a/readthedocs/projects/forms.py b/readthedocs/projects/forms.py
index d8e3e9d32..631633e6a 100644
--- a/readthedocs/projects/forms.py
+++ b/readthedocs/projects/forms.py
@@ -242,14 +242,13 @@ class ProjectAdvancedForm(ProjectTriggerBuildMixin, ProjectForm):
         self.helper.add_input(Submit('save', _('Save')))
 
         default_choice = (None, '-' * 9)
-        # commit_name is used as the id because it handles
-        # the special cases of LATEST and STABLE.
-        all_versions_choices = [
-            (v.commit_name, v.verbose_name)
-            for v in self.instance.versions.all()
-        ]
+        versions_choices = self.instance.versions.filter(
+            machine=False).values_list('verbose_name', flat=True)
+
         self.fields['default_branch'].widget = forms.Select(
-            choices=[default_choice] + all_versions_choices,
+            choices=[default_choice] + list(
+                zip(versions_choices, versions_choices)
+            ),
         )
 
         active_versions = self.get_all_active_versions()
diff --git a/readthedocs/rtd_tests/tests/test_project_forms.py b/readthedocs/rtd_tests/tests/test_project_forms.py
index f8603ef81..50d9815d0 100644
--- a/readthedocs/rtd_tests/tests/test_project_forms.py
+++ b/readthedocs/rtd_tests/tests/test_project_forms.py
@@ -5,7 +5,7 @@ from django.test.utils import override_settings
 from django_dynamic_fixture import get
 from textclassifier.validators import ClassifierValidator
 
-from readthedocs.builds.constants import LATEST
+from readthedocs.builds.constants import LATEST, STABLE
 from readthedocs.builds.models import Version
 from readthedocs.projects.constants import (
     PRIVATE,
@@ -261,31 +261,127 @@ class TestProjectAdvancedForm(TestCase):
             {'latest', 'public-1', 'public-2', 'private', 'protected'},
         )
 
-    def test_list_all_versions_on_default_branch(self):
+    def test_default_version_field_if_no_active_version(self):
+        project_1 = get(Project)
+        project_1.versions.filter(active=True).update(active=False)
+
+        # No active versions of project exists
+        self.assertFalse(project_1.versions.filter(active=True).exists())
+
+        form = ProjectAdvancedForm(instance=project_1)
+        self.assertTrue(form.fields['default_version'].widget.attrs['readonly'])
+        self.assertEqual(form.fields['default_version'].initial, 'latest')
+
+
+class TestProjectAdvancedFormDefaultBranch(TestCase):
+
+    def setUp(self):
+        self.project = get(Project)
+        user_created_stable_version = get(
+            Version,
+            project=self.project,
+            slug='stable',
+            active=True,
+            privacy_level=PUBLIC,
+            identifier='ab96cbff71a8f40a4340aaf9d12e6c10',
+            verbose_name='stable',
+        )
+        get(
+            Version,
+            project=self.project,
+            slug='public-1',
+            active=True,
+            privacy_level=PUBLIC,
+            identifier='public-1',
+            verbose_name='public-1',
+        )
+        get(
+            Version,
+            project=self.project,
+            slug='private',
+            active=True,
+            privacy_level=PRIVATE,
+            identifier='private',
+            verbose_name='private',
+        )
+        get(
+            Version,
+            project=self.project,
+            slug='protected',
+            active=True,
+            privacy_level=PROTECTED,
+            identifier='protected',
+            verbose_name='protected',
+        )
+
+    def test_list_only_non_auto_generated_versions_on_default_branch(self):
         form = ProjectAdvancedForm(instance=self.project)
         # This version is created automatically by the project on save
-        self.assertTrue(self.project.versions.filter(slug=LATEST).exists())
+        latest = self.project.versions.filter(slug=LATEST)
+        self.assertTrue(latest.exists())
+        # show only the versions that are not auto generated as choices
         self.assertEqual(
             {
                 identifier
                 for identifier, _ in form.fields['default_branch'].widget.choices
             },
             {
-                None, 'master', 'public-1', 'public-2',
-                'public-3', 'public/4', 'protected', 'private',
+                None, 'stable', 'public-1', 'protected', 'private',
             },
         )
+        # Auto generated version `latest` should not be among the choices
+        self.assertNotIn(
+            latest.first().verbose_name,
+            [identifier for identifier, _ in form.fields[
+                'default_branch'].widget.choices],
+        )
 
-    def test_default_version_field_if_no_active_version(self):
-        project_1 = get(Project)
-        project_1.versions.filter(active=True).update(active=False)
+    def test_list_user_created_latest_and_stable_versions_on_default_branch(self):
+        self.project.versions.filter(slug=LATEST).first().delete()
+        user_created_latest_version = get(
+            Version,
+            project=self.project,
+            slug='latest',
+            active=True,
+            privacy_level=PUBLIC,
+            identifier='ab96cbff71a8f40a4240aaf9d12e6c10',
+            verbose_name='latest',
+        )
+        form = ProjectAdvancedForm(instance=self.project)
+        # This version is created by the user
+        latest = self.project.versions.filter(slug=LATEST)
+        # This version is created by the user
+        stable = self.project.versions.filter(slug=STABLE)
 
-        # No active versions of project exists
-        self.assertFalse(project_1.versions.filter(active=True).exists())
+        self.assertIn(
+            latest.first().verbose_name,
+            [identifier for identifier, _ in form.fields[
+                'default_branch'].widget.choices],
+        )
+        self.assertIn(
+            stable.first().verbose_name,
+            [identifier for identifier, _ in form.fields[
+                'default_branch'].widget.choices],
+        )
 
-        form = ProjectAdvancedForm(instance=project_1)
-        self.assertTrue(form.fields['default_version'].widget.attrs['readonly'])
-        self.assertEqual(form.fields['default_version'].initial, 'latest')
+    def test_commit_name_not_in_default_branch_choices(self):
+        form = ProjectAdvancedForm(instance=self.project)
+        # This version is created by the user
+        latest = self.project.versions.filter(slug=LATEST)
+        # This version is created by the user
+        stable = self.project.versions.filter(slug=STABLE)
+
+        # `commit_name` can not be used as the value for the choices
+        self.assertNotIn(
+            latest.first().commit_name,
+            [identifier for identifier, _ in form.fields[
+                'default_branch'].widget.choices],
+        )
+        self.assertNotIn(
+            stable.first().commit_name,
+            [identifier for identifier, _ in form.fields[
+                'default_branch'].widget.choices],
+        )
 
 
 class TestTranslationForms(TestCase):