]> git.ozlabs.org Git - patchwork/blob - apps/patchwork/tests/bundles.py
tests/bundle: Add tests for public bundle views
[patchwork] / apps / patchwork / tests / bundles.py
1 # Patchwork - automated patch tracking system
2 # Copyright (C) 2009 Jeremy Kerr <jk@ozlabs.org>
3 #
4 # This file is part of the Patchwork package.
5 #
6 # Patchwork is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # Patchwork is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with Patchwork; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20 import unittest
21 import datetime
22 from django.test import TestCase
23 from django.test.client import Client
24 from django.utils.http import urlencode
25 from patchwork.models import Patch, Bundle, BundlePatch, Person
26 from patchwork.tests.utils import defaults, create_user, find_in_context
27
28 class BundleListTest(TestCase):
29     def setUp(self):
30         self.user = create_user()
31         self.client.login(username = self.user.username,
32                 password = self.user.username)
33
34     def testNoBundles(self):
35         response = self.client.get('/user/bundles/')
36         self.failUnlessEqual(response.status_code, 200)
37         self.failUnlessEqual(
38                 len(find_in_context(response.context, 'bundles')), 0)
39
40     def testSingleBundle(self):
41         defaults.project.save()
42         bundle = Bundle(owner = self.user, project = defaults.project)
43         bundle.save()
44         response = self.client.get('/user/bundles/')
45         self.failUnlessEqual(response.status_code, 200)
46         self.failUnlessEqual(
47                 len(find_in_context(response.context, 'bundles')), 1)
48
49     def tearDown(self):
50         self.user.delete()
51
52 class BundleTestBase(TestCase):
53     def setUp(self, patch_count=3):
54         patch_names = ['testpatch%d' % (i) for i in range(1, patch_count+1)]
55         self.user = create_user()
56         self.client.login(username = self.user.username,
57                 password = self.user.username)
58         defaults.project.save()
59         self.bundle = Bundle(owner = self.user, project = defaults.project,
60                 name = 'testbundle')
61         self.bundle.save()
62         self.patches = []
63
64         for patch_name in patch_names:
65             patch = Patch(project = defaults.project,
66                                msgid = patch_name, name = patch_name,
67                                submitter = Person.objects.get(user = self.user),
68                                content = '')
69             patch.save()
70             self.patches.append(patch)
71
72     def tearDown(self):
73         for patch in self.patches:
74             patch.delete()
75         self.bundle.delete()
76         self.user.delete()
77
78 class BundleViewTest(BundleTestBase):
79
80     def testEmptyBundle(self):
81         response = self.client.get('/user/bundle/%d/' % self.bundle.id)
82         self.failUnlessEqual(response.status_code, 200)
83         page = find_in_context(response.context, 'page')
84         self.failUnlessEqual(len(page.object_list), 0)
85
86     def testNonEmptyBundle(self):
87         self.bundle.append_patch(self.patches[0])
88
89         response = self.client.get('/user/bundle/%d/' % self.bundle.id)
90         self.failUnlessEqual(response.status_code, 200)
91         page = find_in_context(response.context, 'page')
92         self.failUnlessEqual(len(page.object_list), 1)
93
94     def testBundleOrder(self):
95         for patch in self.patches:
96             self.bundle.append_patch(patch)
97
98         response = self.client.get('/user/bundle/%d/' % self.bundle.id)
99
100         pos = 0
101         for patch in self.patches:
102             next_pos = response.content.find(patch.name)
103             # ensure that this patch is after the previous
104             self.failUnless(next_pos > pos)
105             pos = next_pos
106
107         # reorder and recheck
108         i = 0
109         for patch in self.patches.__reversed__():
110             bundlepatch = BundlePatch.objects.get(bundle = self.bundle,
111                     patch = patch)
112             bundlepatch.order = i
113             bundlepatch.save()
114             i += 1
115
116         response = self.client.get('/user/bundle/%d/' % self.bundle.id)
117         pos = len(response.content)
118         for patch in self.patches:
119             next_pos = response.content.find(patch.name)
120             # ensure that this patch is now *before* the previous
121             self.failUnless(next_pos < pos)
122             pos = next_pos
123
124 class BundlePublicViewTest(BundleTestBase):
125
126     def setUp(self):
127         super(BundlePublicViewTest, self).setUp()
128         self.client.logout()
129         self.bundle.append_patch(self.patches[0])
130         self.url = '/bundle/%s/%s/' % (self.user.username, self.bundle.name)
131
132     def testPublicBundle(self):
133         self.bundle.public = True
134         self.bundle.save()
135         response = self.client.get(self.url)
136         self.assertEqual(response.status_code, 200)
137         self.assertContains(response, self.patches[0].name)
138
139     def testPrivateBundle(self):
140         self.bundle.public = False
141         self.bundle.save()
142         response = self.client.get(self.url)
143         self.assertEqual(response.status_code, 404)
144
145 class BundlePublicViewMboxTest(BundlePublicViewTest):
146     def setUp(self):
147         super(BundlePublicViewMboxTest, self).setUp()
148         self.url = '/bundle/%s/%s/mbox/' % (self.user.username,
149                                             self.bundle.name)
150
151 class BundleCreateFromListTest(BundleTestBase):
152     def testCreateEmptyBundle(self):
153         newbundlename = 'testbundle-new'
154         params = {'form': 'patchlistform',
155                   'bundle_name': newbundlename,
156                   'action': 'Create',
157                   'project': defaults.project.id}
158
159         response = self.client.post(
160                 '/project/%s/list/' % defaults.project.linkname,
161                 params)
162
163         self.assertContains(response, 'Bundle %s created' % newbundlename)
164
165     def testCreateNonEmptyBundle(self):
166         newbundlename = 'testbundle-new'
167         patch = self.patches[0]
168
169         params = {'form': 'patchlistform',
170                   'bundle_name': newbundlename,
171                   'action': 'Create',
172                   'project': defaults.project.id,
173                   'patch_id:%d' % patch.id: 'checked'}
174
175         response = self.client.post(
176                 '/project/%s/list/' % defaults.project.linkname,
177                 params)
178
179         self.assertContains(response, 'Bundle %s created' % newbundlename)
180         self.assertContains(response, 'added to bundle %s' % newbundlename,
181             count = 1)
182
183         bundle = Bundle.objects.get(name = newbundlename)
184         self.failUnlessEqual(bundle.patches.count(), 1)
185         self.failUnlessEqual(bundle.patches.all()[0], patch)
186
187     def testCreateNonEmptyBundleEmptyName(self):
188         newbundlename = 'testbundle-new'
189         patch = self.patches[0]
190
191         n_bundles = Bundle.objects.count()
192
193         params = {'form': 'patchlistform',
194                   'bundle_name': '',
195                   'action': 'Create',
196                   'project': defaults.project.id,
197                   'patch_id:%d' % patch.id: 'checked'}
198
199         response = self.client.post(
200                 '/project/%s/list/' % defaults.project.linkname,
201                 params)
202
203         self.assertContains(response, 'No bundle name was specified',
204                 status_code = 200)
205
206         # test that no new bundles are present
207         self.failUnlessEqual(n_bundles, Bundle.objects.count())
208
209     def testCreateDuplicateName(self):
210         newbundlename = 'testbundle-dup'
211         patch = self.patches[0]
212
213         params = {'form': 'patchlistform',
214                   'bundle_name': newbundlename,
215                   'action': 'Create',
216                   'project': defaults.project.id,
217                   'patch_id:%d' % patch.id: 'checked'}
218
219         response = self.client.post(
220                 '/project/%s/list/' % defaults.project.linkname,
221                 params)
222
223         n_bundles = Bundle.objects.count()
224         self.assertContains(response, 'Bundle %s created' % newbundlename)
225         self.assertContains(response, 'added to bundle %s' % newbundlename,
226             count = 1)
227
228         bundle = Bundle.objects.get(name = newbundlename)
229         self.failUnlessEqual(bundle.patches.count(), 1)
230         self.failUnlessEqual(bundle.patches.all()[0], patch)
231
232         response = self.client.post(
233                 '/project/%s/list/' % defaults.project.linkname,
234                 params)
235
236         self.assertNotContains(response, 'Bundle %s created' % newbundlename)
237         self.assertContains(response, 'You already have a bundle called')
238         self.assertEqual(Bundle.objects.count(), n_bundles)
239         self.assertEqual(bundle.patches.count(), 1)
240
241 class BundleCreateFromPatchTest(BundleTestBase):
242     def testCreateNonEmptyBundle(self):
243         newbundlename = 'testbundle-new'
244         patch = self.patches[0]
245
246         params = {'name': newbundlename,
247                   'action': 'createbundle'}
248
249         response = self.client.post('/patch/%d/' % patch.id, params)
250
251         self.assertContains(response,
252                 'Bundle %s created' % newbundlename)
253
254         bundle = Bundle.objects.get(name = newbundlename)
255         self.failUnlessEqual(bundle.patches.count(), 1)
256         self.failUnlessEqual(bundle.patches.all()[0], patch)
257
258     def testCreateWithExistingName(self):
259         newbundlename = self.bundle.name
260         patch = self.patches[0]
261
262         params = {'name': newbundlename,
263                   'action': 'createbundle'}
264
265         response = self.client.post('/patch/%d/' % patch.id, params)
266
267         self.assertContains(response,
268                 'A bundle called %s already exists' % newbundlename)
269
270         count = Bundle.objects.count()
271         self.failUnlessEqual(Bundle.objects.count(), 1)
272
273 class BundleAddFromListTest(BundleTestBase):
274     def testAddToEmptyBundle(self):
275         patch = self.patches[0]
276         params = {'form': 'patchlistform',
277                   'action': 'Add',
278                   'project': defaults.project.id,
279                   'bundle_id': self.bundle.id,
280                   'patch_id:%d' % patch.id: 'checked'}
281
282         response = self.client.post(
283                 '/project/%s/list/' % defaults.project.linkname,
284                 params)
285
286         self.assertContains(response, 'added to bundle %s' % self.bundle.name,
287             count = 1)
288
289         self.failUnlessEqual(self.bundle.patches.count(), 1)
290         self.failUnlessEqual(self.bundle.patches.all()[0], patch)
291
292     def testAddToNonEmptyBundle(self):
293         self.bundle.append_patch(self.patches[0])
294         patch = self.patches[1]
295         params = {'form': 'patchlistform',
296                   'action': 'Add',
297                   'project': defaults.project.id,
298                   'bundle_id': self.bundle.id,
299                   'patch_id:%d' % patch.id: 'checked'}
300
301         response = self.client.post(
302                 '/project/%s/list/' % defaults.project.linkname,
303                 params)
304
305         self.assertContains(response, 'added to bundle %s' % self.bundle.name,
306             count = 1)
307
308         self.failUnlessEqual(self.bundle.patches.count(), 2)
309         self.failUnless(self.patches[0] in self.bundle.patches.all())
310         self.failUnless(self.patches[1] in self.bundle.patches.all())
311
312         # check order
313         bps = [ BundlePatch.objects.get(bundle = self.bundle,
314                                         patch = self.patches[i]) \
315                 for i in [0, 1] ]
316         self.failUnless(bps[0].order < bps[1].order)
317
318     def testAddDuplicate(self):
319         self.bundle.append_patch(self.patches[0])
320         count = self.bundle.patches.count()
321         patch = self.patches[0]
322
323         params = {'form': 'patchlistform',
324                   'action': 'Add',
325                   'project': defaults.project.id,
326                   'bundle_id': self.bundle.id,
327                   'patch_id:%d' % patch.id: 'checked'}
328
329         response = self.client.post(
330                 '/project/%s/list/' % defaults.project.linkname,
331                 params)
332
333         self.assertContains(response, 'Patch &#39;%s&#39; already in bundle' \
334                             % patch.name, count = 1, status_code = 200)
335
336         self.assertEquals(count, self.bundle.patches.count())
337
338     def testAddNewAndDuplicate(self):
339         self.bundle.append_patch(self.patches[0])
340         count = self.bundle.patches.count()
341         patch = self.patches[0]
342
343         params = {'form': 'patchlistform',
344                   'action': 'Add',
345                   'project': defaults.project.id,
346                   'bundle_id': self.bundle.id,
347                   'patch_id:%d' % patch.id: 'checked',
348                   'patch_id:%d' % self.patches[1].id: 'checked'}
349
350         response = self.client.post(
351                 '/project/%s/list/' % defaults.project.linkname,
352                 params)
353
354         self.assertContains(response, 'Patch &#39;%s&#39; already in bundle' \
355                             % patch.name, count = 1, status_code = 200)
356         self.assertContains(response, 'Patch &#39;%s&#39; added to bundle' \
357                             % self.patches[1].name, count = 1,
358                             status_code = 200)
359         self.assertEquals(count + 1, self.bundle.patches.count())
360
361 class BundleAddFromPatchTest(BundleTestBase):
362     def testAddToEmptyBundle(self):
363         patch = self.patches[0]
364         params = {'action': 'addtobundle',
365                   'bundle_id': self.bundle.id}
366
367         response = self.client.post('/patch/%d/' % patch.id, params)
368
369         self.assertContains(response,
370                 'added to bundle &quot;%s&quot;' % self.bundle.name,
371                 count = 1)
372
373         self.failUnlessEqual(self.bundle.patches.count(), 1)
374         self.failUnlessEqual(self.bundle.patches.all()[0], patch)
375
376     def testAddToNonEmptyBundle(self):
377         self.bundle.append_patch(self.patches[0])
378         patch = self.patches[1]
379         params = {'action': 'addtobundle',
380                   'bundle_id': self.bundle.id}
381
382         response = self.client.post('/patch/%d/' % patch.id, params)
383
384         self.assertContains(response,
385                 'added to bundle &quot;%s&quot;' % self.bundle.name,
386                 count = 1)
387
388         self.failUnlessEqual(self.bundle.patches.count(), 2)
389         self.failUnless(self.patches[0] in self.bundle.patches.all())
390         self.failUnless(self.patches[1] in self.bundle.patches.all())
391
392         # check order
393         bps = [ BundlePatch.objects.get(bundle = self.bundle,
394                                         patch = self.patches[i]) \
395                 for i in [0, 1] ]
396         self.failUnless(bps[0].order < bps[1].order)
397
398 class BundleInitialOrderTest(BundleTestBase):
399     """When creating bundles from a patch list, ensure that the patches in the
400        bundle are ordered by date"""
401
402     def setUp(self):
403         super(BundleInitialOrderTest, self).setUp(5)
404
405         # put patches in an arbitrary order
406         idxs = [2, 4, 3, 1, 0]
407         self.patches = [ self.patches[i] for i in idxs ]
408
409         # set dates to be sequential
410         last_patch = self.patches[0]
411         for patch in self.patches[1:]:
412             patch.date = last_patch.date + datetime.timedelta(0, 1)
413             patch.save()
414             last_patch = patch
415
416     def _testOrder(self, ids, expected_order):
417         newbundlename = 'testbundle-new'
418
419         # need to define our querystring explicity to enforce ordering
420         params = {'form': 'patchlistform',
421                   'bundle_name': newbundlename,
422                   'action': 'Create',
423                   'project': defaults.project.id,
424         }
425
426         data = urlencode(params) + \
427                ''.join([ '&patch_id:%d=checked' % i for i in ids ])
428
429         response = self.client.post(
430                 '/project/%s/list/' % defaults.project.linkname,
431                 data = data,
432                 content_type = 'application/x-www-form-urlencoded',
433                 )
434
435         self.assertContains(response, 'Bundle %s created' % newbundlename)
436         self.assertContains(response, 'added to bundle %s' % newbundlename,
437             count = 5)
438
439         bundle = Bundle.objects.get(name = newbundlename)
440
441         # BundlePatches should be sorted by .order by default
442         bps = BundlePatch.objects.filter(bundle = bundle)
443
444         for (bp, p) in zip(bps, expected_order):
445             self.assertEqual(bp.patch.pk, p.pk)
446
447         bundle.delete()
448
449     def testBundleForwardOrder(self):
450         ids = map(lambda p: p.id, self.patches)
451         self._testOrder(ids, self.patches)
452
453     def testBundleReverseOrder(self):
454         ids = map(lambda p: p.id, self.patches)
455         ids.reverse()
456         self._testOrder(ids, self.patches)
457
458 class BundleReorderTest(BundleTestBase):
459     def setUp(self):
460         super(BundleReorderTest, self).setUp(5)
461         for i in range(5):
462             self.bundle.append_patch(self.patches[i])
463
464     def checkReordering(self, neworder, start, end):
465         neworder_ids = [ self.patches[i].id for i in neworder ]
466
467         firstpatch = BundlePatch.objects.get(bundle = self.bundle,
468                 patch = self.patches[start]).patch
469
470         slice_ids = neworder_ids[start:end]
471         params = {'form': 'reorderform',
472                   'order_start': firstpatch.id,
473                   'neworder': slice_ids}
474
475         response = self.client.post('/user/bundle/%d/' % self.bundle.id,
476                                     params)
477
478         self.failUnlessEqual(response.status_code, 200)
479
480         bps = BundlePatch.objects.filter(bundle = self.bundle) \
481                         .order_by('order')
482
483         # check if patch IDs are in the expected order:
484         bundle_ids = [ bp.patch.id for bp in bps ]
485         self.failUnlessEqual(neworder_ids, bundle_ids)
486
487         # check if order field is still sequential:
488         order_numbers = [ bp.order for bp in bps ]
489         expected_order = range(1, len(neworder)+1) # [1 ... len(neworder)]
490         self.failUnlessEqual(order_numbers, expected_order)
491
492     def testBundleReorderAll(self):
493         # reorder all patches:
494         self.checkReordering([2,1,4,0,3], 0, 5)
495
496     def testBundleReorderEnd(self):
497         # reorder only the last three patches
498         self.checkReordering([0,1,3,2,4], 2, 5)
499
500     def testBundleReorderBegin(self):
501         # reorder only the first three patches
502         self.checkReordering([2,0,1,3,4], 0, 3)
503
504     def testBundleReorderMiddle(self):
505         # reorder only 2nd, 3rd, and 4th patches
506         self.checkReordering([0,2,3,1,4], 1, 4)