Merge lp:~ricardokirkner/locolander/celery-tasks into lp:locolander

Proposed by Ricardo Kirkner
Status: Merged
Approved by: Ricardo Kirkner
Approved revision: 24
Merged at revision: 21
Proposed branch: lp:~ricardokirkner/locolander/celery-tasks
Merge into: lp:locolander
Prerequisite: lp:~ricardokirkner/locolander/add-author
Diff against target: 302 lines (+215/-33)
6 files modified
locolander/locolanderweb/models.py (+0/-4)
locolander/locolanderweb/tasks.py (+52/-0)
locolander/locolanderweb/tests/__init__.py (+5/-4)
locolander/locolanderweb/tests/test_base.py (+1/-1)
locolander/locolanderweb/tests/test_models.py (+10/-24)
locolander/locolanderweb/tests/test_tasks.py (+147/-0)
To merge this branch: bzr merge lp:~ricardokirkner/locolander/celery-tasks
Reviewer Review Type Date Requested Status
Natalia Bidart Approve
Review via email: mp+174619@code.launchpad.net

Commit message

added celery-based tasks

- run_project: processes all outstanding merge requests for a project
- run_request: runs tests and merges a merge requests
- scan_project: finds approved requests for a project

To post a comment you must log in.
24. By Ricardo Kirkner

merged add-author

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

Looks good!

One note: you should not import os.path, but import os. From the help(os.path):

"Instead of importing this module directly, import os and refer to this module as os.path."

Any reason to change the prefix on l.94-95?

The import from l.158 should have a blank line separating import from stdlib from imports from third-party.

When needing the same patch in more than one test, and specially when the same patch is needed in every test inside the test case, I advice to do the patching in the setUp method, so we can have the patch in a single place and don't change the signature of every method (but if you disagree, you can leave as is.

Approving since none of the above is a blocker.

review: Approve
25. By Ricardo Kirkner

fixes per review

Revision history for this message
Ricardo Kirkner (ricardokirkner) wrote :

> Looks good!
>
> One note: you should not import os.path, but import os. From the
> help(os.path):
>
> "Instead of importing this module directly, import os and refer to this module
> as os.path."

fixed

>
> Any reason to change the prefix on l.94-95?

because the extra '-' is unnecessary; it will be added by make_random_string, so having it in the prefix will result in a double '-' in the final string

>
> The import from l.158 should have a blank line separating import from stdlib
> from imports from third-party.

fixed

>
> When needing the same patch in more than one test, and specially when the same
> patch is needed in every test inside the test case, I advice to do the
> patching in the setUp method, so we can have the patch in a single place and
> don't change the signature of every method (but if you disagree, you can leave
> as is.
>

having them in the methods is a bit nicer sometimes as it keeps context local. have changed as you suggested anyway, as it's applied to all tests.

> Approving since none of the above is a blocker.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'locolander/locolanderweb/models.py'
--- locolander/locolanderweb/models.py 2013-07-13 17:25:25 +0000
+++ locolander/locolanderweb/models.py 2013-07-20 21:10:30 +0000
@@ -39,10 +39,6 @@
3939
40 def approved_requests(self):40 def approved_requests(self):
41 """Return approved merge proposals."""41 """Return approved merge proposals."""
42 mrs = self.service.approved_requests_for_project_url(self.url)
43 for mr in mrs:
44 MergeRequest.objects.get_or_create(project=self, **mr)
45
46 return MergeRequest.objects.filter(project=self, status=STATUS_PENDING)42 return MergeRequest.objects.filter(project=self, status=STATUS_PENDING)
4743
4844
4945
=== added file 'locolander/locolanderweb/tasks.py'
--- locolander/locolanderweb/tasks.py 1970-01-01 00:00:00 +0000
+++ locolander/locolanderweb/tasks.py 2013-07-20 21:10:30 +0000
@@ -0,0 +1,52 @@
1import os
2import subprocess
3
4from celery import task
5
6from locolanderweb.models import MergeRequest, RunLog
7
8
9@task
10def run_project(project):
11 for request in project.approved_requests():
12 run_request(request)
13
14
15@task
16def run_request(request):
17 project = request.project
18 source = request.source
19 target = request.target
20 author = request.author
21 message = request.commit_message
22
23 cmd = [
24 os.path.realpath(os.path.join(
25 os.path.abspath('.'), 'docker/scripts/locolander')),
26 project.name,
27 source,
28 target,
29 message,
30 author,
31 ]
32 try:
33 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
34 return_code = 0
35 error = False
36 except subprocess.CalledProcessError as err:
37 output = err.output
38 return_code = err.returncode
39 error = True
40
41 RunLog.objects.create(merge_request=request, raw=output,
42 return_code=return_code)
43
44 # update status on merge request
45 request.complete(error=error)
46
47
48@task
49def scan_project(project):
50 mrs = project.service.approved_requests_for_project_url(project.url)
51 for mr in mrs:
52 MergeRequest.objects.get_or_create(project=project, **mr)
053
=== modified file 'locolander/locolanderweb/tests/__init__.py'
--- locolander/locolanderweb/tests/__init__.py 2013-06-21 15:34:22 +0000
+++ locolander/locolanderweb/tests/__init__.py 2013-07-20 21:10:30 +0000
@@ -1,4 +1,5 @@
1from locolanderweb.tests.test_code_style import *1from .test_code_style import *
2from locolanderweb.tests.test_forms import *2from .test_forms import *
3from locolanderweb.tests.test_models import *3from .test_models import *
4from locolanderweb.tests.test_views import *4from .test_tasks import *
5from .test_views import *
56
=== modified file 'locolander/locolanderweb/tests/test_base.py'
--- locolander/locolanderweb/tests/test_base.py 2013-07-13 17:25:25 +0000
+++ locolander/locolanderweb/tests/test_base.py 2013-07-20 21:10:30 +0000
@@ -36,7 +36,7 @@
3636
37 def make_project(self, name=None, user=None, url=None):37 def make_project(self, name=None, user=None, url=None):
38 if name is None:38 if name is None:
39 name = self.make_random_string(prefix='project-')39 name = self.make_random_string(prefix='project')
40 if user is None:40 if user is None:
41 user = self.make_user()41 user = self.make_user()
42 if url is None:42 if url is None:
4343
=== modified file 'locolander/locolanderweb/tests/test_models.py'
--- locolander/locolanderweb/tests/test_models.py 2013-07-20 18:14:16 +0000
+++ locolander/locolanderweb/tests/test_models.py 2013-07-20 21:10:30 +0000
@@ -15,20 +15,6 @@
15)15)
16from locolanderweb.tests.test_base import BaseTestCase16from locolanderweb.tests.test_base import BaseTestCase
1717
18MERGE_REQUESTS = [
19 dict(
20 source='source1', target='target1', reviewers='a,b,c',
21 date_created=datetime(2013, 4, 23), commit_message='something 1',
22 ),
23 dict(
24 source='source2', target='target2', reviewers='pepe',
25 date_created=datetime(2012, 2, 29), commit_message='something else',
26 ),
27 dict(
28 source='source3', target='target3', reviewers='pepe y pepa',
29 date_created=datetime.utcnow(), commit_message='',
30 ),
31]
3218
33INVALID_URLS = (19INVALID_URLS = (
34 'locolander',20 'locolander',
@@ -99,16 +85,16 @@
99 self.get_service_from_url.assert_called_once_with(self.obj.url)85 self.get_service_from_url.assert_called_once_with(self.obj.url)
10086
101 def test_approved_requests(self):87 def test_approved_requests(self):
102 assert MergeRequest.objects.all().count() == 088 assert MergeRequest.objects.filter(project=self.obj).count() == 0
10389
104 service = self.get_service_from_url.return_value90 result = self.obj.approved_requests()
105 service.approved_requests_for_project_url.return_value = MERGE_REQUESTS91 self.assertEqual(len(result), 0)
10692
107 result = self.obj.approved_requests()93 # Create an approved merge request
108 self.assertEqual(MergeRequest.objects.all().count(), len(result))94 self.factory.make_merge_request(project=self.obj)
109 for mr in MERGE_REQUESTS:95
110 self.assertEqual(96 result = self.obj.approved_requests()
111 MergeRequest.objects.filter(**mr).count(), 1)97 self.assertEqual(len(result), 1)
11298
11399
114class MergeRequestTestCase(BaseTestCase):100class MergeRequestTestCase(BaseTestCase):
115101
=== added file 'locolander/locolanderweb/tests/test_tasks.py'
--- locolander/locolanderweb/tests/test_tasks.py 1970-01-01 00:00:00 +0000
+++ locolander/locolanderweb/tests/test_tasks.py 2013-07-20 21:10:30 +0000
@@ -0,0 +1,147 @@
1import subprocess
2from datetime import datetime
3
4from mock import ANY, patch
5
6from locolanderweb.models import (
7 STATUS_ERROR,
8 STATUS_MERGED,
9 MergeRequest,
10 RunLog,
11)
12from locolanderweb.tasks import run_project, run_request, scan_project
13from locolanderweb.tests.test_base import BaseTestCase
14
15
16MERGE_REQUESTS = [
17 dict(
18 source='source1', target='target1', reviewers='a,b,c',
19 date_created=datetime(2013, 4, 23), commit_message='something 1',
20 ),
21 dict(
22 source='source2', target='target2', reviewers='pepe',
23 date_created=datetime(2012, 2, 29), commit_message='something else',
24 ),
25 dict(
26 source='source3', target='target3', reviewers='pepe y pepa',
27 date_created=datetime.utcnow(), commit_message='',
28 ),
29]
30
31
32class RunProjectTestCase(BaseTestCase):
33
34 def setUp(self):
35 super(RunProjectTestCase, self).setUp()
36 patcher = patch('locolanderweb.tasks.run_request')
37 self.mock_run_request = patcher.start()
38 self.addCleanup(patcher.stop)
39
40 def test_run_project(self):
41 project = self.factory.make_project()
42 merge_request = self.factory.make_merge_request(project=project)
43
44 run_project(project)
45 self.mock_run_request.assert_called_once_with(merge_request)
46
47 def test_run_project_with_no_approved_requests(self):
48 project = self.factory.make_project()
49
50 run_project(project)
51 self.assertFalse(self.mock_run_request.called)
52
53
54class RunRequestTestCase(BaseTestCase):
55
56 def setUp(self):
57 super(RunRequestTestCase, self).setUp()
58 patcher = patch('locolanderweb.tasks.subprocess.check_output')
59 self.mock_check_output = patcher.start()
60 self.addCleanup(patcher.stop)
61
62 def test_run_request(self):
63 self.mock_check_output.return_value = 'SUCCESS'
64
65 merge_request = self.factory.make_merge_request()
66
67 run_request(merge_request)
68
69 cmd = [
70 ANY,
71 merge_request.project.name,
72 merge_request.source,
73 merge_request.target,
74 merge_request.commit_message,
75 merge_request.author,
76 ]
77 self.mock_check_output.assert_called_once_with(
78 cmd, stderr=subprocess.STDOUT)
79 real_cmd = self.mock_check_output.call_args[0][0][0]
80 self.assertTrue(real_cmd.endswith('docker/scripts/locolander'))
81
82 self.assertEqual(RunLog.objects.all().count(), 1)
83 run_log = RunLog.objects.get(merge_request=merge_request)
84 self.assertEqual(run_log.raw, 'SUCCESS')
85 self.assertEqual(run_log.return_code, 0)
86
87 # refresh MR object
88 mr = MergeRequest.objects.get(pk=merge_request.pk)
89 self.assertEqual(mr.status, STATUS_MERGED)
90
91 def test_run_request_with_error(self):
92 merge_request = self.factory.make_merge_request()
93
94 cmd = [
95 ANY,
96 merge_request.project.name,
97 merge_request.source,
98 merge_request.target,
99 merge_request.commit_message,
100 merge_request.author,
101 ]
102 error = subprocess.CalledProcessError(1, cmd, output='FAILURE')
103 self.mock_check_output.side_effect = error
104
105 run_request(merge_request)
106
107 self.mock_check_output.assert_called_once_with(
108 cmd, stderr=subprocess.STDOUT)
109 real_cmd = self.mock_check_output.call_args[0][0][0]
110 self.assertTrue(real_cmd.endswith('docker/scripts/locolander'))
111
112 self.assertEqual(RunLog.objects.all().count(), 1)
113 run_log = RunLog.objects.get(merge_request=merge_request)
114 self.assertEqual(run_log.raw, 'FAILURE')
115 self.assertEqual(run_log.return_code, 1)
116
117 # refresh MR object
118 mr = MergeRequest.objects.get(pk=merge_request.pk)
119 self.assertEqual(mr.status, STATUS_ERROR)
120
121
122class ScanProjectTestCase(BaseTestCase):
123
124 def test_scan_project(self):
125 project = self.factory.make_project()
126
127 service = self.get_service_from_url.return_value
128 service.approved_requests_for_project_url.return_value = MERGE_REQUESTS
129
130 scan_project(project)
131
132 self.assertEqual(MergeRequest.objects.filter(project=project).count(),
133 len(MERGE_REQUESTS))
134 for mr in MERGE_REQUESTS:
135 self.assertEqual(
136 MergeRequest.objects.filter(**mr).count(), 1)
137
138 def test_scan_project_with_no_approved_request(self):
139 project = self.factory.make_project()
140
141 service = self.get_service_from_url.return_value
142 service.approved_requests_for_project_url.return_value = []
143
144 scan_project(project)
145
146 self.assertEqual(MergeRequest.objects.filter(project=project).count(),
147 0)

Subscribers

People subscribed via source and target branches

to all changes: