Skip to content

Commit

Permalink
Add test for build redirect
Browse files Browse the repository at this point in the history
  • Loading branch information
ericholscher committed Nov 2, 2018
1 parent 51196b1 commit c972fb2
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 63 deletions.
31 changes: 22 additions & 9 deletions readthedocs/builds/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-

"""Views for builds app."""

from __future__ import (
Expand Down Expand Up @@ -35,8 +36,13 @@ class BuildBase(object):

def get_queryset(self):
self.project_slug = self.kwargs.get('project_slug', None)
self.project = get_object_or_404(Project.objects.protected(self.request.user),slug=self.project_slug,)
queryset = Build.objects.public(user=self.request.user, project=self.project)
self.project = get_object_or_404(
Project.objects.protected(self.request.user),
slug=self.project_slug,
)
queryset = Build.objects.public(
user=self.request.user, project=self.project
)

return queryset

Expand All @@ -57,22 +63,25 @@ def post(self, request, project_slug):
slug=version_slug,
)

_, signature = trigger_build(project=project, version=version)
build_pk = signature.get('kwargs', {}).get('build_pk')
_, build = trigger_build(project=project, version=version)
return HttpResponseRedirect(
reverse('builds_detail', args=[project.slug, build_pk]))
reverse('builds_detail', args=[project.slug, build.pk]),
)


class BuildList(BuildBase, BuildTriggerMixin, ListView):

def get_context_data(self, **kwargs):
context = super(BuildList, self).get_context_data(**kwargs)

active_builds = self.get_queryset().exclude(state='finished').values('id')
active_builds = self.get_queryset().exclude(state='finished'
).values('id')

context['project'] = self.project
context['active_builds'] = active_builds
context['versions'] = Version.objects.public(user=self.request.user, project=self.project)
context['versions'] = Version.objects.public(
user=self.request.user, project=self.project
)
context['build_qs'] = self.get_queryset()

return context
Expand All @@ -91,8 +100,12 @@ def get_context_data(self, **kwargs):


def builds_redirect_list(request, project_slug): # pylint: disable=unused-argument
return HttpResponsePermanentRedirect(reverse('builds_project_list', args=[project_slug]))
return HttpResponsePermanentRedirect(
reverse('builds_project_list', args=[project_slug])
)


def builds_redirect_detail(request, project_slug, pk): # pylint: disable=unused-argument
return HttpResponsePermanentRedirect(reverse('builds_detail', args=[project_slug, pk]))
return HttpResponsePermanentRedirect(
reverse('builds_detail', args=[project_slug, pk])
)
49 changes: 31 additions & 18 deletions readthedocs/core/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-

"""Common utilty functions."""

from __future__ import absolute_import
Expand All @@ -14,13 +15,11 @@
from django.utils.functional import allow_lazy
from django.utils.safestring import SafeText, mark_safe
from django.utils.text import slugify as slugify_base
from future.backports.urllib.parse import urlparse
from celery import group, chord

from readthedocs.builds.constants import LATEST, BUILD_STATE_TRIGGERED
from readthedocs.doc_builder.constants import DOCKER_LIMITS


log = logging.getLogger(__name__)

SYNC_USER = getattr(settings, 'SYNC_USER', getpass.getuser())
Expand All @@ -40,9 +39,9 @@ def broadcast(type, task, args, kwargs=None, callback=None): # pylint: disable=
kwargs = {}
default_queue = getattr(settings, 'CELERY_DEFAULT_QUEUE', 'celery')
if type in ['web', 'app']:
servers = getattr(settings, "MULTIPLE_APP_SERVERS", [default_queue])
servers = getattr(settings, 'MULTIPLE_APP_SERVERS', [default_queue])
elif type in ['build']:
servers = getattr(settings, "MULTIPLE_BUILD_SERVERS", [default_queue])
servers = getattr(settings, 'MULTIPLE_BUILD_SERVERS', [default_queue])

tasks = []
for server in servers:
Expand Down Expand Up @@ -71,7 +70,12 @@ def cname_to_slug(host):


def prepare_build(
project, version=None, record=True, force=False, immutable=True):
project,
version=None,
record=True,
force=False,
immutable=True,
):
"""
Prepare a build in a Celery task for project and version.
Expand Down Expand Up @@ -132,11 +136,14 @@ def prepare_build(
options['soft_time_limit'] = time_limit
options['time_limit'] = int(time_limit * 1.2)

return update_docs_task.signature(
args=(project.pk,),
kwargs=kwargs,
options=options,
immutable=True,
return (
update_docs_task.signature(
args=(project.pk,),
kwargs=kwargs,
options=options,
immutable=True,
),
build,
)


Expand All @@ -153,7 +160,7 @@ def trigger_build(project, version=None, record=True, force=False):
:param force: build the HTML documentation even if the files haven't changed
:returns: A tuple (Celery AsyncResult promise, Task Signature from ``prepare_build``)
"""
update_docs_task = prepare_build(
update_docs_task, build = prepare_build(
project,
version,
record,
Expand All @@ -165,11 +172,13 @@ def trigger_build(project, version=None, record=True, force=False):
# Current project is skipped
return None

return (update_docs_task.apply_async(), update_docs_task)
return (update_docs_task.apply_async(), build)


def send_email(recipient, subject, template, template_html, context=None,
request=None, from_email=None, **kwargs): # pylint: disable=unused-argument
def send_email(
recipient, subject, template, template_html, context=None, request=None,
from_email=None, **kwargs
): # pylint: disable=unused-argument
"""
Alter context passed in and call email send task.
Expand All @@ -183,10 +192,14 @@ def send_email(recipient, subject, template, template_html, context=None,
if context is None:
context = {}
context['uri'] = '{scheme}://{host}'.format(
scheme='https', host=settings.PRODUCTION_DOMAIN)
send_email_task.delay(recipient=recipient, subject=subject, template=template,
template_html=template_html, context=context, from_email=from_email,
**kwargs)
scheme='https',
host=settings.PRODUCTION_DOMAIN,
)
send_email_task.delay(
recipient=recipient, subject=subject, template=template,
template_html=template_html, context=context, from_email=from_email,
**kwargs
)


def slugify(value, *args, **kwargs):
Expand Down
126 changes: 90 additions & 36 deletions readthedocs/rtd_tests/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
from __future__ import absolute_import
# -*- coding: utf-8 -*-
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)

import mock
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.test import TestCase
from django.utils.six.moves.urllib.parse import urlsplit
from django_dynamic_fixture import get
from django_dynamic_fixture import new
from django_dynamic_fixture import get, new

from readthedocs.builds.constants import LATEST
from readthedocs.builds.models import Build
from readthedocs.core.permissions import AdminPermission
from readthedocs.projects.models import ImportedFile
from readthedocs.projects.models import Project
from readthedocs.projects.forms import UpdateProjectForm

from readthedocs.projects.models import ImportedFile, Project

class Testmaker(TestCase):

def setUp(self):
self.eric = User(username='eric')
self.eric.set_password('test')
Expand All @@ -27,21 +34,24 @@ def test_imported_docs(self):
self.assertEqual(r.status_code, 200)
r = self.client.get('/dashboard/import/manual/', {})
self.assertEqual(r.status_code, 200)
form = UpdateProjectForm(data={
'name': 'Django Kong',
'repo': 'https://github.com/ericholscher/django-kong',
'repo_type': 'git',
'description': 'OOHHH AH AH AH KONG SMASH',
'language': 'en',
'default_branch': '',
'project_url': 'http://django-kong.rtfd.org',
'default_version': LATEST,
'privacy_level': 'public',
'version_privacy_level': 'public',
'python_interpreter': 'python',
'documentation_type': 'sphinx',
'csrfmiddlewaretoken': '34af7c8a5ba84b84564403a280d9a9be',
}, user=user)
form = UpdateProjectForm(
data={
'name': 'Django Kong',
'repo': 'https://github.com/ericholscher/django-kong',
'repo_type': 'git',
'description': 'OOHHH AH AH AH KONG SMASH',
'language': 'en',
'default_branch': '',
'project_url': 'http://django-kong.rtfd.org',
'default_version': LATEST,
'privacy_level': 'public',
'version_privacy_level': 'public',
'python_interpreter': 'python',
'documentation_type': 'sphinx',
'csrfmiddlewaretoken': '34af7c8a5ba84b84564403a280d9a9be',
},
user=user,
)
_ = form.save()
_ = Project.objects.get(slug='django-kong')

Expand Down Expand Up @@ -111,11 +121,13 @@ def test_project_delete(self):
def test_subprojects_delete(self):
# This URL doesn't exist anymore, 404
response = self.client.get(
'/dashboard/pip/subprojects/delete/a-subproject/')
'/dashboard/pip/subprojects/delete/a-subproject/',
)
self.assertEqual(response.status_code, 404)
# New URL
response = self.client.get(
'/dashboard/pip/subprojects/a-subproject/delete/')
'/dashboard/pip/subprojects/a-subproject/delete/',
)
self.assertRedirectToLogin(response)

def test_subprojects(self):
Expand Down Expand Up @@ -143,7 +155,9 @@ def test_project_translations(self):
self.assertRedirectToLogin(response)

def test_project_translations_delete(self):
response = self.client.get('/dashboard/pip/translations/delete/a-translation/')
response = self.client.get(
'/dashboard/pip/translations/delete/a-translation/'
)
self.assertRedirectToLogin(response)

def test_project_redirects(self):
Expand All @@ -168,7 +182,8 @@ def setUp(self):
slug='file',
path='file.html',
md5='abcdef',
commit='1234567890abcdef')
commit='1234567890abcdef',
)

def test_random_page_view_redirects(self):
response = self.client.get('/random/')
Expand All @@ -188,7 +203,9 @@ def test_404_for_with_no_imported_files(self):
response = self.client.get('/random/pip/')
self.assertEqual(response.status_code, 404)


class SubprojectViewTests(TestCase):

def setUp(self):
self.user = new(User, username='test')
self.user.set_password('test')
Expand All @@ -201,35 +218,72 @@ def setUp(self):
self.client.login(username='test', password='test')

def test_deny_delete_for_non_project_admins(self):
response = self.client.get('/dashboard/my-mainproject/subprojects/delete/my-subproject/')
response = self.client.get(
'/dashboard/my-mainproject/subprojects/delete/my-subproject/'
)
self.assertEqual(response.status_code, 404)

self.assertTrue(self.subproject in [r.child for r in self.project.subprojects.all()])
self.assertTrue(
self.subproject in
[r.child for r in self.project.subprojects.all()]
)

def test_admins_can_delete_subprojects(self):
self.project.users.add(self.user)
self.subproject.users.add(self.user)

# URL doesn't exist anymore, 404
response = self.client.get(
'/dashboard/my-mainproject/subprojects/delete/my-subproject/')
'/dashboard/my-mainproject/subprojects/delete/my-subproject/',
)
self.assertEqual(response.status_code, 404)
# This URL still doesn't accept GET, 405
response = self.client.get(
'/dashboard/my-mainproject/subprojects/my-subproject/delete/')
'/dashboard/my-mainproject/subprojects/my-subproject/delete/',
)
self.assertEqual(response.status_code, 405)
self.assertTrue(self.subproject in [r.child for r in self.project.subprojects.all()])
self.assertTrue(
self.subproject in
[r.child for r in self.project.subprojects.all()]
)
# Test POST
response = self.client.post(
'/dashboard/my-mainproject/subprojects/my-subproject/delete/')
'/dashboard/my-mainproject/subprojects/my-subproject/delete/',
)
self.assertEqual(response.status_code, 302)
self.assertTrue(self.subproject not in [r.child for r in self.project.subprojects.all()])

def test_project_admins_can_delete_subprojects_that_they_are_not_admin_of(self):
self.assertTrue(
self.subproject not in
[r.child for r in self.project.subprojects.all()]
)

def test_project_admins_can_delete_subprojects_that_they_are_not_admin_of(
self
):
self.project.users.add(self.user)
self.assertFalse(AdminPermission.is_admin(self.user, self.subproject))

response = self.client.post(
'/dashboard/my-mainproject/subprojects/my-subproject/delete/')
'/dashboard/my-mainproject/subprojects/my-subproject/delete/',
)
self.assertEqual(response.status_code, 302)
self.assertTrue(self.subproject not in [r.child for r in self.project.subprojects.all()])
self.assertTrue(
self.subproject not in
[r.child for r in self.project.subprojects.all()]
)


class BuildViewTests(TestCase):
fixtures = ['eric', 'test_data']

def setUp(self):
self.client.login(username='eric', password='test')

@mock.patch('readthedocs.projects.tasks.update_docs_task')
def test_build_redirect(self, mock):
r = self.client.post('/projects/pip/builds/', {'version_slug': '0.8.1'})
build = Build.objects.filter(project__slug='pip').latest()
self.assertEqual(r.status_code, 302)
self.assertEqual(
r._headers['location'][1],
'/projects/pip/builds/%s/' % build.pk,
)

0 comments on commit c972fb2

Please sign in to comment.