Merge lp:~ricardokirkner/locolander/merge-request-views into lp:locolander

Proposed by Ricardo Kirkner
Status: Merged
Approved by: Natalia Bidart
Approved revision: 27
Merged at revision: 24
Proposed branch: lp:~ricardokirkner/locolander/merge-request-views
Merge into: lp:locolander
Prerequisite: lp:~ricardokirkner/locolander/celery-tasks
Diff against target: 209 lines (+124/-12)
4 files modified
locolander/locolanderweb/templates/locolanderweb/project/detail.html (+24/-9)
locolander/locolanderweb/tests/test_views.py (+77/-0)
locolander/locolanderweb/urls.py (+2/-0)
locolander/locolanderweb/views.py (+21/-3)
To merge this branch: bzr merge lp:~ricardokirkner/locolander/merge-request-views
Reviewer Review Type Date Requested Status
Natalia Bidart Approve
Review via email: mp+174625@code.launchpad.net

Commit message

added view for triggering a merge request run

To post a comment you must log in.
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Branch looks great. There is only one blocker, and is in the merge_request_run view: the get_object_or_404 should ensure that the user currently logged is the owner of the project that holds the given merge request (otherwise anyone logged in could visit this url by knowing the MR id).

review: Needs Fixing
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Looks good!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'locolander/locolanderweb/templates/locolanderweb/project/detail.html'
2--- locolander/locolanderweb/templates/locolanderweb/project/detail.html 2013-07-06 23:19:29 +0000
3+++ locolander/locolanderweb/templates/locolanderweb/project/detail.html 2013-07-20 21:25:25 +0000
4@@ -4,6 +4,12 @@
5 <div class="project-detail">
6 {% if project %}
7 <h1>Project {{ project.name }}</h1>
8+ <form action="." method="POST">
9+ {% csrf_token %}
10+ <div class="actions">
11+ <input type="submit" value="Scan this project" />
12+ </div>
13+ </form>
14
15 {% if project.mergerequest_set.count %}
16 {% for mr in project.mergerequest_set.all %}
17@@ -12,14 +18,29 @@
18 <ul>
19 <li>Source: {{ mr.source }}</li>
20 <li>Target: {{ mr.target }}</li>
21+ <li>Author: {{ mr.author }}</li>
22 <li>Reviewers: {{ mr.reviewers }}</li>
23 <li>Created: {{ mr.date_created }}</li>
24 <li>Status: {{ mr.status }}</li>
25 </ul>
26+ <form action="{% url 'request-run' mr.id %}" method="POST">
27+ {% csrf_token %}
28+ <div class="actions">
29+ <input type="submit" value="Land now!" />
30+ </div>
31+ </form>
32+
33 {% if mr.runlog_set.count %}
34- <pre style="background: #eeeeee;">
35- {{ mr.runlog_set.latest.timestamp }}
36- </pre>
37+ {% for entry in mr.runlog_set.all reversed %}
38+ <div class="runlog-entry">
39+ <span>
40+ {{ entry.timestamp }}
41+ </span>
42+ <pre style="background: #eeeeee;">
43+ {{ entry.raw|wordwrap:120 }}
44+ </pre>
45+ </div>
46+ {% endfor %}
47 {% endif %}
48 </div>
49 {% endfor %}
50@@ -28,12 +49,6 @@
51 processed merge requests.</h2>
52 {% endif %}
53
54- <form action="." method="POST">
55- {% csrf_token %}
56- <div class="actions">
57- <input type="submit" value="Scan this project" />
58- </div>
59- </form>
60 </div>
61 {% endif %}
62 {% endblock content %}
63
64=== modified file 'locolander/locolanderweb/tests/test_views.py'
65--- locolander/locolanderweb/tests/test_views.py 2013-07-06 22:26:19 +0000
66+++ locolander/locolanderweb/tests/test_views.py 2013-07-20 21:25:25 +0000
67@@ -1,4 +1,6 @@
68 from django.core.urlresolvers import reverse
69+from django.utils import html
70+from mock import ANY, patch
71 from pyquery import PyQuery
72
73 from locolanderweb.forms import ProjectForm
74@@ -77,3 +79,78 @@
75 response = self.client.get(self.url)
76
77 self.assertContains(response, 'Project %s' % self.project.name)
78+
79+ def test_detail_does_not_include_runlog(self):
80+ response = self.client.get(self.url)
81+
82+ self.assertNotContains(response, '<div class="runlog-entry">')
83+
84+ def test_detail_includes_runlog(self):
85+ mr = self.factory.make_merge_request(project=self.project)
86+ self.factory.make_run_log(merge_request=mr)
87+ self.factory.make_run_log(merge_request=mr)
88+ response = self.client.get(self.url)
89+
90+ self.assertContains(response, '<div class="runlog-entry">', count=2)
91+
92+ def test_detail_includes_land_now_button(self):
93+ merge_request = self.factory.make_merge_request(project=self.project)
94+ response = self.client.get(self.url)
95+
96+ url = reverse('request-run', args=[merge_request.id])
97+ self.assertContains(response, '<form action="%s" method="POST">' % url)
98+
99+ def test_details_includes_author(self):
100+ mr = self.factory.make_merge_request(project=self.project)
101+
102+ response = self.client.get(self.url)
103+
104+ author_html = "Author: %s" % html.escape(mr.author)
105+ self.assertContains(response, author_html)
106+
107+
108+class MergeRequestRunTestCase(BaseTestCase):
109+
110+ def setUp(self):
111+ super(MergeRequestRunTestCase, self).setUp()
112+ self.user = self.factory.make_user(password=self.password)
113+ assert self.client.login(username=self.user.username,
114+ password=self.password)
115+ self.project = self.factory.make_project(user=self.user)
116+ self.merge_request = self.factory.make_merge_request(
117+ project=self.project)
118+ self.url = reverse('request-run',
119+ kwargs=dict(merge_request_id=self.merge_request.id))
120+
121+ def test_get(self):
122+ response = self.client.get(self.url)
123+ self.assertTemplateUsed(response, 'locolanderweb/project/detail.html')
124+ self.assertEqual(response.context['project'],
125+ self.project)
126+
127+ def test_get_not_existing(self):
128+ url = reverse('request-run', kwargs=dict(merge_request_id=99))
129+ response = self.client.get(url)
130+ self.assertEqual(response.status_code, 404)
131+
132+ def test_get_non_owner(self):
133+ # make new user
134+ user = self.factory.make_user(password=self.password)
135+ # login using non-owner user
136+ self.client.login(username=user.username, password=self.password)
137+
138+ response = self.client.get(self.url)
139+ self.assertEqual(response.status_code, 404)
140+
141+ @patch('locolanderweb.views.messages.info')
142+ @patch('locolanderweb.tasks.run_request.delay')
143+ def test_post(self, mock_delay, mock_info):
144+ response = self.client.post(self.url)
145+ mock_delay.assert_called_once_with(self.merge_request)
146+ msg = 'Merge of {mr.source} into {mr.target} requested.'
147+ mock_info.assert_called_once_with(ANY,
148+ msg.format(mr=self.merge_request))
149+ self.assertRedirects(
150+ response,
151+ reverse('project-detail',
152+ kwargs=dict(project_id=self.project.id)))
153
154=== modified file 'locolander/locolanderweb/urls.py'
155--- locolander/locolanderweb/urls.py 2013-07-06 23:19:29 +0000
156+++ locolander/locolanderweb/urls.py 2013-07-20 21:25:25 +0000
157@@ -13,4 +13,6 @@
158 url(r'^projects/add/$', 'project_add', name='project-add'),
159 url(r'^projects/(?P<project_id>\d+)/$', 'project_detail',
160 name='project-detail'),
161+ url(r'requests/(?P<merge_request_id>\d+)/run/$', 'merge_request_run',
162+ name='request-run'),
163 )
164
165=== modified file 'locolander/locolanderweb/views.py'
166--- locolander/locolanderweb/views.py 2013-07-06 23:19:29 +0000
167+++ locolander/locolanderweb/views.py 2013-07-20 21:25:25 +0000
168@@ -5,8 +5,9 @@
169 from django.shortcuts import get_object_or_404
170 from django.template.response import TemplateResponse
171
172-from locolanderweb.models import Project
173+from locolanderweb import tasks
174 from locolanderweb.forms import ProjectForm
175+from locolanderweb.models import MergeRequest, Project
176
177
178 @login_required
179@@ -48,11 +49,28 @@
180
181 context = dict(project=project)
182 if request.method == 'POST':
183- # scan, this is a fake now
184- project.approved_requests()
185+ tasks.scan_project.delay(project)
186 messages.info(request, 'Scan for %s requested.' % project.name)
187 return HttpResponseRedirect(reverse('project-detail',
188 kwargs=dict(project_id=project.id)))
189
190 return TemplateResponse(
191 request, 'locolanderweb/project/detail.html', context=context)
192+
193+
194+@login_required
195+def merge_request_run(request, merge_request_id):
196+ merge_request = get_object_or_404(
197+ MergeRequest, id=merge_request_id, project__owner=request.user)
198+ project = merge_request.project
199+
200+ context = dict(project=project)
201+ if request.method == 'POST':
202+ tasks.run_request.delay(merge_request)
203+ msg = 'Merge of {mr.source} into {mr.target} requested.'
204+ messages.info(request, msg.format(mr=merge_request))
205+ return HttpResponseRedirect(
206+ reverse('project-detail', kwargs=dict(project_id=project.id)))
207+
208+ return TemplateResponse(
209+ request, 'locolanderweb/project/detail.html', context=context)

Subscribers

People subscribed via source and target branches

to all changes: