]> git.ozlabs.org Git - patchwork/blobdiff - apps/patchwork/tests/bundles.py
bundles: Don't display 'Change order' button on public bundle view
[patchwork] / apps / patchwork / tests / bundles.py
index d9c0bedd1bf2629ea997e5d82ef92eed4c621847..5e8b95ba92f7fafd3dcda2f04698b0c731133275 100644 (file)
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 import unittest
+import datetime
 from django.test import TestCase
 from django.test.client import Client
+from django.utils.http import urlencode
+from django.conf import settings
 from patchwork.models import Patch, Bundle, BundlePatch, Person
 from patchwork.tests.utils import defaults, create_user, find_in_context
 
+def bundle_url(bundle):
+    return '/bundle/%s/%s/' % (bundle.owner.username, bundle.name)
+
 class BundleListTest(TestCase):
     def setUp(self):
         self.user = create_user()
@@ -48,8 +54,8 @@ class BundleListTest(TestCase):
         self.user.delete()
 
 class BundleTestBase(TestCase):
-    def setUp(self):
-        patch_names = ['testpatch1', 'testpatch2', 'testpatch3']
+    def setUp(self, patch_count=3):
+        patch_names = ['testpatch%d' % (i) for i in range(1, patch_count+1)]
         self.user = create_user()
         self.client.login(username = self.user.username,
                 password = self.user.username)
@@ -76,7 +82,7 @@ class BundleTestBase(TestCase):
 class BundleViewTest(BundleTestBase):
 
     def testEmptyBundle(self):
-        response = self.client.get('/user/bundle/%d/' % self.bundle.id)
+        response = self.client.get(bundle_url(self.bundle))
         self.failUnlessEqual(response.status_code, 200)
         page = find_in_context(response.context, 'page')
         self.failUnlessEqual(len(page.object_list), 0)
@@ -84,7 +90,7 @@ class BundleViewTest(BundleTestBase):
     def testNonEmptyBundle(self):
         self.bundle.append_patch(self.patches[0])
 
-        response = self.client.get('/user/bundle/%d/' % self.bundle.id)
+        response = self.client.get(bundle_url(self.bundle))
         self.failUnlessEqual(response.status_code, 200)
         page = find_in_context(response.context, 'page')
         self.failUnlessEqual(len(page.object_list), 1)
@@ -93,7 +99,7 @@ class BundleViewTest(BundleTestBase):
         for patch in self.patches:
             self.bundle.append_patch(patch)
 
-        response = self.client.get('/user/bundle/%d/' % self.bundle.id)
+        response = self.client.get(bundle_url(self.bundle))
 
         pos = 0
         for patch in self.patches:
@@ -111,7 +117,7 @@ class BundleViewTest(BundleTestBase):
             bundlepatch.save()
             i += 1
 
-        response = self.client.get('/user/bundle/%d/' % self.bundle.id)
+        response = self.client.get(bundle_url(self.bundle))
         pos = len(response.content)
         for patch in self.patches:
             next_pos = response.content.find(patch.name)
@@ -119,6 +125,150 @@ class BundleViewTest(BundleTestBase):
             self.failUnless(next_pos < pos)
             pos = next_pos
 
+class BundleUpdateTest(BundleTestBase):
+
+    def setUp(self):
+        super(BundleUpdateTest, self).setUp()
+        self.newname = 'newbundlename'
+
+    def checkPatchformErrors(self, response):
+        formname = 'patchform'
+        if not formname in response.context:
+            return
+        form = response.context[formname]
+        if not form:
+            return
+        self.assertEquals(form.errors, {})
+
+    def publicString(self, public):
+        if public:
+            return 'on'
+        return ''
+
+    def testNoAction(self):
+        data = {
+            'form': 'bundle',
+            'name': self.newname,
+            'public': self.publicString(not self.bundle.public)
+        }
+        response = self.client.post(bundle_url(self.bundle), data)
+        self.assertEqual(response.status_code, 200)
+
+        bundle = Bundle.objects.get(pk = self.bundle.pk)
+        self.assertEqual(bundle.name, self.bundle.name)
+        self.assertEqual(bundle.public, self.bundle.public)
+
+    def testUpdateName(self):
+        newname = 'newbundlename'
+        data = {
+            'form': 'bundle',
+            'action': 'update',
+            'name': newname,
+            'public': self.publicString(self.bundle.public)
+        }
+        response = self.client.post(bundle_url(self.bundle), data)
+        bundle = Bundle.objects.get(pk = self.bundle.pk)
+        self.assertRedirects(response, bundle_url(bundle))
+        self.assertEqual(bundle.name, newname)
+        self.assertEqual(bundle.public, self.bundle.public)
+
+    def testUpdatePublic(self):
+        newname = 'newbundlename'
+        data = {
+            'form': 'bundle',
+            'action': 'update',
+            'name': self.bundle.name,
+            'public': self.publicString(not self.bundle.public)
+        }
+        response = self.client.post(bundle_url(self.bundle), data)
+        self.assertEqual(response.status_code, 200)
+        bundle = Bundle.objects.get(pk = self.bundle.pk)
+        self.assertEqual(bundle.name, self.bundle.name)
+        self.assertEqual(bundle.public, not self.bundle.public)
+
+        # check other forms for errors
+        self.checkPatchformErrors(response)
+
+class BundleMaintainerUpdateTest(BundleUpdateTest):
+
+    def setUp(self):
+        super(BundleMaintainerUpdateTest, self).setUp()
+        profile = self.user.get_profile()
+        profile.maintainer_projects.add(defaults.project)
+        profile.save()
+
+class BundlePublicViewTest(BundleTestBase):
+
+    def setUp(self):
+        super(BundlePublicViewTest, self).setUp()
+        self.client.logout()
+        self.bundle.append_patch(self.patches[0])
+        self.url = bundle_url(self.bundle)
+
+    def testPublicBundle(self):
+        self.bundle.public = True
+        self.bundle.save()
+        response = self.client.get(self.url)
+        self.assertEqual(response.status_code, 200)
+        self.assertContains(response, self.patches[0].name)
+
+    def testPrivateBundle(self):
+        self.bundle.public = False
+        self.bundle.save()
+        response = self.client.get(self.url)
+        self.assertEqual(response.status_code, 404)
+
+class BundlePublicViewMboxTest(BundlePublicViewTest):
+    def setUp(self):
+        super(BundlePublicViewMboxTest, self).setUp()
+        self.url = bundle_url(self.bundle) + "mbox/"
+
+class BundlePublicModifyTest(BundleTestBase):
+    """Ensure that non-owners can't modify bundles"""
+
+    def setUp(self):
+        super(BundlePublicModifyTest, self).setUp()
+        self.bundle.public = True
+        self.bundle.save()
+        self.other_user = create_user()
+
+    def testBundleFormPresence(self):
+        """Check for presence of the modify form on the bundle"""
+        self.client.login(username = self.other_user.username,
+                password = self.other_user.username)
+        response = self.client.get(bundle_url(self.bundle))
+        self.assertNotContains(response, 'name="form" value="bundle"')
+        self.assertNotContains(response, 'Change order')
+
+    def testBundleFormSubmission(self):
+        oldname = 'oldbundlename'
+        newname = 'newbundlename'
+        data = {
+            'form': 'bundle',
+            'action': 'update',
+            'name': newname,
+        }
+        self.bundle.name = oldname
+        self.bundle.save()
+
+        # first, check that we can modify with the owner
+        self.client.login(username = self.user.username,
+                password = self.user.username)
+        response = self.client.post(bundle_url(self.bundle), data)
+        self.bundle = Bundle.objects.get(pk = self.bundle.pk)
+        self.assertEqual(self.bundle.name, newname)
+
+        # reset bundle name
+        self.bundle.name = oldname
+        self.bundle.save()
+
+        # log in with a different user, and check that we can no longer modify
+        self.client.login(username = self.other_user.username,
+                password = self.other_user.username)
+        response = self.client.post(bundle_url(self.bundle), data)
+        self.bundle = Bundle.objects.get(pk = self.bundle.pk)
+        self.assertNotEqual(self.bundle.name, newname)
+
 class BundleCreateFromListTest(BundleTestBase):
     def testCreateEmptyBundle(self):
         newbundlename = 'testbundle-new'
@@ -177,6 +327,38 @@ class BundleCreateFromListTest(BundleTestBase):
         # test that no new bundles are present
         self.failUnlessEqual(n_bundles, Bundle.objects.count())
 
+    def testCreateDuplicateName(self):
+        newbundlename = 'testbundle-dup'
+        patch = self.patches[0]
+
+        params = {'form': 'patchlistform',
+                  'bundle_name': newbundlename,
+                  'action': 'Create',
+                  'project': defaults.project.id,
+                  'patch_id:%d' % patch.id: 'checked'}
+
+        response = self.client.post(
+                '/project/%s/list/' % defaults.project.linkname,
+                params)
+
+        n_bundles = Bundle.objects.count()
+        self.assertContains(response, 'Bundle %s created' % newbundlename)
+        self.assertContains(response, 'added to bundle %s' % newbundlename,
+            count = 1)
+
+        bundle = Bundle.objects.get(name = newbundlename)
+        self.failUnlessEqual(bundle.patches.count(), 1)
+        self.failUnlessEqual(bundle.patches.all()[0], patch)
+
+        response = self.client.post(
+                '/project/%s/list/' % defaults.project.linkname,
+                params)
+
+        self.assertNotContains(response, 'Bundle %s created' % newbundlename)
+        self.assertContains(response, 'You already have a bundle called')
+        self.assertEqual(Bundle.objects.count(), n_bundles)
+        self.assertEqual(bundle.patches.count(), 1)
+
 class BundleCreateFromPatchTest(BundleTestBase):
     def testCreateNonEmptyBundle(self):
         newbundlename = 'testbundle-new'
@@ -194,6 +376,20 @@ class BundleCreateFromPatchTest(BundleTestBase):
         self.failUnlessEqual(bundle.patches.count(), 1)
         self.failUnlessEqual(bundle.patches.all()[0], patch)
 
+    def testCreateWithExistingName(self):
+        newbundlename = self.bundle.name
+        patch = self.patches[0]
+
+        params = {'name': newbundlename,
+                  'action': 'createbundle'}
+
+        response = self.client.post('/patch/%d/' % patch.id, params)
+
+        self.assertContains(response,
+                'A bundle called %s already exists' % newbundlename)
+
+        count = Bundle.objects.count()
+        self.failUnlessEqual(Bundle.objects.count(), 1)
 
 class BundleAddFromListTest(BundleTestBase):
     def testAddToEmptyBundle(self):
@@ -240,6 +436,49 @@ class BundleAddFromListTest(BundleTestBase):
                 for i in [0, 1] ]
         self.failUnless(bps[0].order < bps[1].order)
 
+    def testAddDuplicate(self):
+        self.bundle.append_patch(self.patches[0])
+        count = self.bundle.patches.count()
+        patch = self.patches[0]
+
+        params = {'form': 'patchlistform',
+                  'action': 'Add',
+                  'project': defaults.project.id,
+                  'bundle_id': self.bundle.id,
+                  'patch_id:%d' % patch.id: 'checked'}
+
+        response = self.client.post(
+                '/project/%s/list/' % defaults.project.linkname,
+                params)
+
+        self.assertContains(response, 'Patch &#39;%s&#39; already in bundle' \
+                            % patch.name, count = 1, status_code = 200)
+
+        self.assertEquals(count, self.bundle.patches.count())
+
+    def testAddNewAndDuplicate(self):
+        self.bundle.append_patch(self.patches[0])
+        count = self.bundle.patches.count()
+        patch = self.patches[0]
+
+        params = {'form': 'patchlistform',
+                  'action': 'Add',
+                  'project': defaults.project.id,
+                  'bundle_id': self.bundle.id,
+                  'patch_id:%d' % patch.id: 'checked',
+                  'patch_id:%d' % self.patches[1].id: 'checked'}
+
+        response = self.client.post(
+                '/project/%s/list/' % defaults.project.linkname,
+                params)
+
+        self.assertContains(response, 'Patch &#39;%s&#39; already in bundle' \
+                            % patch.name, count = 1, status_code = 200)
+        self.assertContains(response, 'Patch &#39;%s&#39; added to bundle' \
+                            % self.patches[1].name, count = 1,
+                            status_code = 200)
+        self.assertEquals(count + 1, self.bundle.patches.count())
+
 class BundleAddFromPatchTest(BundleTestBase):
     def testAddToEmptyBundle(self):
         patch = self.patches[0]
@@ -277,33 +516,131 @@ class BundleAddFromPatchTest(BundleTestBase):
                 for i in [0, 1] ]
         self.failUnless(bps[0].order < bps[1].order)
 
+class BundleInitialOrderTest(BundleTestBase):
+    """When creating bundles from a patch list, ensure that the patches in the
+       bundle are ordered by date"""
+
+    def setUp(self):
+        super(BundleInitialOrderTest, self).setUp(5)
+
+        # put patches in an arbitrary order
+        idxs = [2, 4, 3, 1, 0]
+        self.patches = [ self.patches[i] for i in idxs ]
+
+        # set dates to be sequential
+        last_patch = self.patches[0]
+        for patch in self.patches[1:]:
+            patch.date = last_patch.date + datetime.timedelta(0, 1)
+            patch.save()
+            last_patch = patch
+
+    def _testOrder(self, ids, expected_order):
+        newbundlename = 'testbundle-new'
+
+        # need to define our querystring explicity to enforce ordering
+        params = {'form': 'patchlistform',
+                  'bundle_name': newbundlename,
+                  'action': 'Create',
+                  'project': defaults.project.id,
+        }
+
+        data = urlencode(params) + \
+               ''.join([ '&patch_id:%d=checked' % i for i in ids ])
+
+        response = self.client.post(
+                '/project/%s/list/' % defaults.project.linkname,
+                data = data,
+                content_type = 'application/x-www-form-urlencoded',
+                )
+
+        self.assertContains(response, 'Bundle %s created' % newbundlename)
+        self.assertContains(response, 'added to bundle %s' % newbundlename,
+            count = 5)
+
+        bundle = Bundle.objects.get(name = newbundlename)
+
+        # BundlePatches should be sorted by .order by default
+        bps = BundlePatch.objects.filter(bundle = bundle)
+
+        for (bp, p) in zip(bps, expected_order):
+            self.assertEqual(bp.patch.pk, p.pk)
+
+        bundle.delete()
+
+    def testBundleForwardOrder(self):
+        ids = map(lambda p: p.id, self.patches)
+        self._testOrder(ids, self.patches)
+
+    def testBundleReverseOrder(self):
+        ids = map(lambda p: p.id, self.patches)
+        ids.reverse()
+        self._testOrder(ids, self.patches)
+
 class BundleReorderTest(BundleTestBase):
     def setUp(self):
-        super(BundleReorderTest, self).setUp()
-        self.bundle.append_patch(self.patches[0])
-        self.bundle.append_patch(self.patches[1])
-        self.bundle.append_patch(self.patches[2])
+        super(BundleReorderTest, self).setUp(5)
+        for i in range(5):
+            self.bundle.append_patch(self.patches[i])
 
-    def testBundleReorder(self):
-        bundlepatch = BundlePatch.objects.get(bundle = self.bundle,
-                patch = self.patches[0])
+    def checkReordering(self, neworder, start, end):
+        neworder_ids = [ self.patches[i].id for i in neworder ]
 
-        neworder = [self.patches[2], self.patches[0], self.patches[1]]
-        neworder_ids = [ p.id for p in neworder ]
+        firstpatch = BundlePatch.objects.get(bundle = self.bundle,
+                patch = self.patches[start]).patch
 
+        slice_ids = neworder_ids[start:end]
         params = {'form': 'reorderform',
-                  'order_start': bundlepatch.order,
-                  'neworder': neworder_ids}
+                  'order_start': firstpatch.id,
+                  'neworder': slice_ids}
 
-        response = self.client.post('/user/bundle/%d/' % self.bundle.id,
-                                    params)
+        response = self.client.post(bundle_url(self.bundle), params)
 
         self.failUnlessEqual(response.status_code, 200)
 
-        bundle_ids = [ bp.patch.id for bp in \
-                        BundlePatch.objects.filter(bundle = self.bundle) \
-                        .order_by('order') ]
+        bps = BundlePatch.objects.filter(bundle = self.bundle) \
+                        .order_by('order')
 
+        # check if patch IDs are in the expected order:
+        bundle_ids = [ bp.patch.id for bp in bps ]
         self.failUnlessEqual(neworder_ids, bundle_ids)
 
+        # check if order field is still sequential:
+        order_numbers = [ bp.order for bp in bps ]
+        expected_order = range(1, len(neworder)+1) # [1 ... len(neworder)]
+        self.failUnlessEqual(order_numbers, expected_order)
+
+    def testBundleReorderAll(self):
+        # reorder all patches:
+        self.checkReordering([2,1,4,0,3], 0, 5)
+
+    def testBundleReorderEnd(self):
+        # reorder only the last three patches
+        self.checkReordering([0,1,3,2,4], 2, 5)
+
+    def testBundleReorderBegin(self):
+        # reorder only the first three patches
+        self.checkReordering([2,0,1,3,4], 0, 3)
 
+    def testBundleReorderMiddle(self):
+        # reorder only 2nd, 3rd, and 4th patches
+        self.checkReordering([0,2,3,1,4], 1, 4)
+
+class BundleRedirTest(BundleTestBase):
+    # old URL: private bundles used to be under /user/bundle/<id>
+
+    def setUp(self):
+        super(BundleRedirTest, self).setUp()
+
+    @unittest.skipIf(not settings.COMPAT_REDIR, "compat redirections disabled")
+    def testBundleRedir(self):
+        url = '/user/bundle/%d/' % self.bundle.id
+        response = self.client.get(url)
+        self.assertRedirects(response, bundle_url(self.bundle))
+
+    @unittest.skipIf(not settings.COMPAT_REDIR, "compat redirections disabled")
+    def testMboxRedir(self):
+        url = '/user/bundle/%d/mbox/' % self.bundle.id
+        response = self.client.get(url)
+        self.assertRedirects(response,'/bundle/%s/%s/mbox/' %
+                                        (self.bundle.owner.username,
+                                         self.bundle.name))