Browse Source

Code cleanup, tests for participants views.

Rafał Pitoń 10 years ago
parent
commit
43fba72d0a

+ 1 - 3
misago/threads/models/threadparticipant.py

@@ -1,4 +1,4 @@
-from django.db import models, transaction
+from django.db import models
 
 from misago.conf import settings
 
@@ -7,7 +7,6 @@ class ThreadParticipantManager(models.Manager):
     def remove_participant(self, thread, user):
         ThreadParticipant.objects.filter(thread=thread, user=user).delete()
 
-    @transaction.atomic
     def set_owner(self, thread, user):
         thread_owner = ThreadParticipant.objects.filter(
             thread=thread, is_owner=True)
@@ -19,7 +18,6 @@ class ThreadParticipantManager(models.Manager):
             user=user,
             is_owner=True)
 
-    @transaction.atomic
     def add_participant(self, thread, user, is_owner=False):
         ThreadParticipant.objects.create(
             thread=thread,

+ 17 - 8
misago/threads/participants.py

@@ -1,6 +1,10 @@
 from misago.threads.models import ThreadParticipant
 
 
+def thread_has_participants(thread):
+    return thread.threadparticipant_set.exists()
+
+
 def make_thread_participants_aware(user, thread):
     thread.participants_list = []
     thread.participant = None
@@ -15,15 +19,11 @@ def make_thread_participants_aware(user, thread):
     return thread.participants_list
 
 
-def thread_has_participants(thread):
-    return thread.threadparticipant_set.exists()
-
-
 def set_thread_owner(thread, user):
-    ThreadParticipant.objects.set_thread_owner(thread, user)
+    ThreadParticipant.objects.set_owner(thread, user)
 
 
-def sync_user_unread_private_threads(user):
+def set_user_unread_private_threads_sync(user):
     user.sync_unread_private_threads = True
     user.save(update_fields=['sync_unread_private_threads'])
 
@@ -34,7 +34,16 @@ def add_participant(request, thread, user, is_owner=False):
     notify user about being added to thread and mail him about it
     """
     ThreadParticipant.objects.add_participant(thread, user, is_owner)
-    sync_user_unread_private_threads(user)
+    set_user_unread_private_threads_sync(user)
+
+
+def add_owner(thread, user):
+    """
+    Add owner to thread, set "recound private threads" flag on user,
+    notify user about being added to thread
+    """
+    ThreadParticipant.objects.add_participant(thread, user, True)
+    set_user_unread_private_threads_sync(user)
 
 
 def remove_participant(thread, user):
@@ -42,4 +51,4 @@ def remove_participant(thread, user):
     Remove thread participant, set "recound private threads" flag on user
     """
     thread.threadparticipant_set.filter(user=user).delete()
-    sync_user_unread_private_threads(user)
+    set_user_unread_private_threads_sync(user)

+ 2 - 2
misago/threads/posting/participants.py

@@ -1,6 +1,6 @@
 from misago.threads.forms.posting import ThreadParticipantsForm
 from misago.threads.posting import PostingMiddleware, START
-from misago.threads.participants import add_participant
+from misago.threads.participants import add_participant, add_owner
 
 
 class ThreadParticipantsFormMiddleware(PostingMiddleware):
@@ -15,6 +15,6 @@ class ThreadParticipantsFormMiddleware(PostingMiddleware):
             return ThreadParticipantsForm(prefix=self.prefix)
 
     def save(self, form):
-        add_participant(self.request, self.thread, self.user, True)
+        add_owner(self.thread, self.user)
         for user in form.users_cache:
             add_participant(self.request, self.thread, user)

+ 158 - 0
misago/threads/tests/test_participants.py

@@ -0,0 +1,158 @@
+from django.contrib.auth import get_user_model
+from django.test import TestCase
+from django.utils import timezone
+
+from misago.forums.models import Forum
+
+from misago.threads.models import Thread, ThreadParticipant, Post
+from misago.threads.participants import (thread_has_participants,
+                                         make_thread_participants_aware,
+                                         set_thread_owner,
+                                         set_user_unread_private_threads_sync,
+                                         add_owner,
+                                         remove_participant
+                                         )
+
+
+class ParticipantsTests(TestCase):
+    def setUp(self):
+        datetime = timezone.now()
+
+        self.forum = Forum.objects.filter(role="forum")[:1][0]
+        self.thread = Thread(
+            forum=self.forum,
+            started_on=datetime,
+            starter_name='Tester',
+            starter_slug='tester',
+            last_post_on=datetime,
+            last_poster_name='Tester',
+            last_poster_slug='tester')
+
+        self.thread.set_title("Test thread")
+        self.thread.save()
+
+        post = Post.objects.create(
+            forum=self.forum,
+            thread=self.thread,
+            poster_name='Tester',
+            poster_ip='127.0.0.1',
+            original="Hello! I am test message!",
+            parsed="<p>Hello! I am test message!</p>",
+            checksum="nope",
+            posted_on=datetime,
+            updated_on=datetime)
+
+        self.thread.first_post = post
+        self.thread.last_post = post
+        self.thread.save()
+
+    def test_thread_has_participants(self):
+        """thread_has_participants returns true if thread has participants"""
+        User = get_user_model()
+        user = User.objects.create_user(
+            "Bob", "bob@boberson.com", "Pass.123")
+        other_user = User.objects.create_user(
+            "Bob2", "bob2@boberson.com", "Pass.123")
+
+        self.assertFalse(thread_has_participants(self.thread))
+
+        ThreadParticipant.objects.add_participant(self.thread, user)
+        self.assertTrue(thread_has_participants(self.thread))
+
+        ThreadParticipant.objects.add_participant(self.thread, other_user)
+        self.assertTrue(thread_has_participants(self.thread))
+
+        self.thread.threadparticipant_set.all().delete()
+        self.assertFalse(thread_has_participants(self.thread))
+
+    def test_make_thread_participants_aware(self):
+        """
+        make_thread_participants_aware sets participants_list and participant
+        adnotations on thread model
+        """
+        User = get_user_model()
+        user = User.objects.create_user(
+            "Bob", "bob@boberson.com", "Pass.123")
+        other_user = User.objects.create_user(
+            "Bob2", "bob2@boberson.com", "Pass.123")
+
+        self.assertFalse(hasattr(self.thread, 'participants_list'))
+        self.assertFalse(hasattr(self.thread, 'participant'))
+
+        make_thread_participants_aware(user, self.thread)
+
+        self.assertTrue(hasattr(self.thread, 'participants_list'))
+        self.assertTrue(hasattr(self.thread, 'participant'))
+
+        self.assertEqual(self.thread.participants_list, [])
+        self.assertIsNone(self.thread.participant)
+
+        ThreadParticipant.objects.add_participant(self.thread, user, True)
+        ThreadParticipant.objects.add_participant(self.thread, other_user)
+
+        make_thread_participants_aware(user, self.thread)
+
+        self.assertEqual(self.thread.participant.user, user)
+        for participant in self.thread.participants_list:
+            if participant.user == user:
+                break
+        else:
+            self.fail("thread.participants_list didn't contain user")
+
+    def test_set_thread_owner(self):
+        """set_thread_owner sets user as thread owner"""
+        User = get_user_model()
+        user = User.objects.create_user(
+            "Bob", "bob@boberson.com", "Pass.123")
+
+        set_thread_owner(self.thread, user)
+
+        owner = self.thread.threadparticipant_set.get(is_owner=True)
+        self.assertEqual(user, owner.user)
+
+    def test_set_user_unread_private_threads_sync(self):
+        """
+        set_user_unread_private_threads_sync sets sync_unread_private_threads
+        flag on user model to true
+        """
+        User = get_user_model()
+        user = User.objects.create_user(
+            "Bob", "bob@boberson.com", "Pass.123")
+
+        self.assertFalse(user.sync_unread_private_threads)
+
+        set_user_unread_private_threads_sync(user)
+        self.assertTrue(user.sync_unread_private_threads)
+
+        db_user = User.objects.get(pk=user.pk)
+        self.assertTrue(db_user.sync_unread_private_threads)
+
+    def test_add_owner(self):
+        """add_owner adds user as thread owner"""
+        User = get_user_model()
+        user = User.objects.create_user(
+            "Bob", "bob@boberson.com", "Pass.123")
+
+        add_owner(self.thread, user)
+        self.assertTrue(user.sync_unread_private_threads)
+
+        owner = self.thread.threadparticipant_set.get(is_owner=True)
+        self.assertEqual(user, owner.user)
+
+    def test_remove_participant(self):
+        """remove_participant removes user from thread"""
+        User = get_user_model()
+        user = User.objects.create_user(
+            "Bob", "bob@boberson.com", "Pass.123")
+
+        add_owner(self.thread, user)
+        remove_participant(self.thread, user)
+
+        with self.assertRaises(ThreadParticipant.DoesNotExist):
+            self.thread.threadparticipant_set.get(user=user)
+
+        set_user_unread_private_threads_sync(user)
+        self.assertTrue(user.sync_unread_private_threads)
+
+        db_user = User.objects.get(pk=user.pk)
+        self.assertTrue(db_user.sync_unread_private_threads)

+ 1 - 7
misago/threads/tests/test_threadparticipant_model.py

@@ -1,5 +1,3 @@
-from datetime import timedelta
-
 from django.contrib.auth import get_user_model
 from django.test import TestCase
 from django.utils import timezone
@@ -74,11 +72,7 @@ class ThreadParticipantTests(TestCase):
         self.assertFalse(participation.is_owner)
 
         ThreadParticipant.objects.add_participant(self.thread, user)
-        self.assertEqual(self.thread.participants.count(), 1)
-
-        participation = ThreadParticipant.objects.get(
-            thread=self.thread, user=user)
-        self.assertFalse(participation.is_owner)
+        self.assertEqual(self.thread.participants.count(), 2)
 
     def test_set_owner(self):
         """set_owner makes user thread owner"""

+ 0 - 50
misago/threads/tests/test_threadparticipants_view.py

@@ -1,50 +0,0 @@
-from django.contrib.auth import get_user_model
-from django.core.urlresolvers import reverse
-from django.utils import timezone
-
-from misago.acl.testutils import override_acl
-from misago.forums.models import Forum
-from misago.users.testutils import AuthenticatedUserTestCase
-
-from misago.threads import testutils
-from misago.threads.models import ThreadParticipant
-
-
-class ThreadParticipantsTests(AuthenticatedUserTestCase):
-    ajax_header = {'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'}
-
-    def setUp(self):
-        super(ThreadParticipantsTests, self).setUp()
-
-        self.forum = Forum.objects.private_threads()
-        self.thread = testutils.post_thread(self.forum)
-
-    def test_participants_list(self):
-        """participants list displays thread participants"""
-        User = get_user_model()
-        users = (
-            User.objects.create_user("Bob", "bob@bob.com", "pass123"),
-            User.objects.create_user("Dam", "dam@bob.com", "pass123")
-        )
-
-        ThreadParticipant.objects.set_owner(self.thread, self.user)
-        ThreadParticipant.objects.add_participant(self.thread, users[0])
-        ThreadParticipant.objects.add_participant(self.thread, users[1])
-
-        override_acl(self.user, {
-            'can_use_private_threads': True,
-            'can_moderate_private_threads': True
-        })
-
-        link = reverse('misago:private_thread_participants', kwargs={
-            'thread_id': self.thread.id,
-            'thread_slug': self.thread.slug
-        })
-
-        response = self.client.get(link, **self.ajax_header)
-        self.assertEqual(response.status_code, 200)
-
-        owner_pos = response.content.find(self.user.get_absolute_url())
-        for user in users:
-            participant_pos = response.content.find(user.get_absolute_url())
-            self.assertTrue(owner_pos < participant_pos)

+ 328 - 0
misago/threads/tests/test_threadparticipants_views.py

@@ -0,0 +1,328 @@
+from django.contrib.auth import get_user_model
+from django.core.urlresolvers import reverse
+from django.utils import timezone
+
+from misago.acl.testutils import override_acl
+from misago.forums.models import Forum
+from misago.users.testutils import AuthenticatedUserTestCase
+
+from misago.threads import testutils
+from misago.threads.models import Thread, ThreadParticipant
+
+
+class ThreadParticipantsTests(AuthenticatedUserTestCase):
+    ajax_header = {'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'}
+
+    def setUp(self):
+        super(ThreadParticipantsTests, self).setUp()
+
+        self.forum = Forum.objects.private_threads()
+        self.thread = testutils.post_thread(self.forum)
+
+    def test_participants_list(self):
+        """participants list displays thread participants"""
+        User = get_user_model()
+        users = (
+            User.objects.create_user("Bob", "bob@bob.com", "pass123"),
+            User.objects.create_user("Dam", "dam@bob.com", "pass123")
+        )
+
+        ThreadParticipant.objects.set_owner(self.thread, self.user)
+        ThreadParticipant.objects.add_participant(self.thread, users[0])
+        ThreadParticipant.objects.add_participant(self.thread, users[1])
+
+        override_acl(self.user, {
+            'can_use_private_threads': True,
+            'can_moderate_private_threads': True
+        })
+
+        link = reverse('misago:private_thread_participants', kwargs={
+            'thread_id': self.thread.id,
+            'thread_slug': self.thread.slug
+        })
+
+        response = self.client.get(link, **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+
+        owner_pos = response.content.find(self.user.get_absolute_url())
+        for user in users:
+            participant_pos = response.content.find(user.get_absolute_url())
+            self.assertTrue(owner_pos < participant_pos)
+
+    def test_edit_participants(self):
+        """edit participants view displays thread participants"""
+        User = get_user_model()
+        users = (
+            User.objects.create_user("Bob", "bob@bob.com", "pass123"),
+            User.objects.create_user("Dam", "dam@bob.com", "pass123")
+        )
+
+        ThreadParticipant.objects.set_owner(self.thread, self.user)
+        ThreadParticipant.objects.add_participant(self.thread, users[0])
+        ThreadParticipant.objects.add_participant(self.thread, users[1])
+
+        override_acl(self.user, {
+            'can_use_private_threads': True,
+            'can_moderate_private_threads': True
+        })
+
+        link = reverse('misago:private_thread_edit_participants', kwargs={
+            'thread_id': self.thread.id,
+            'thread_slug': self.thread.slug
+        })
+
+        response = self.client.get(link, **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+
+        owner_pos = response.content.find(self.user.get_absolute_url())
+        for user in users:
+            participant_pos = response.content.find(user.get_absolute_url())
+            self.assertTrue(owner_pos < participant_pos)
+
+    def test_owner_remove_participant(self):
+        """remove participant allows owner to remove participant"""
+        User = get_user_model()
+        other_user = User.objects.create_user("Bob", "bob@bob.com", "pass123")
+
+        ThreadParticipant.objects.set_owner(self.thread, self.user)
+        ThreadParticipant.objects.add_participant(self.thread, other_user)
+
+        override_acl(self.user, {
+            'can_use_private_threads': True,
+            'can_moderate_private_threads': True
+        })
+
+        link = reverse('misago:private_thread_remove_participant', kwargs={
+            'thread_id': self.thread.id,
+            'thread_slug': self.thread.slug,
+            'user_id': other_user.id,
+        })
+
+        response = self.client.post(link, **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+
+        self.assertEqual(self.thread.threadparticipant_set.count(), 1)
+        owner = self.thread.threadparticipant_set.get(is_owner=True)
+        self.assertEqual(owner.user, self.user)
+
+        Thread.objects.get(pk=self.thread.pk)
+        self.thread.threadparticipant_set.get(user=self.user)
+
+    def test_owner_remove_non_participant(self):
+        """remove participant handles attempt to remove invalid participant"""
+        User = get_user_model()
+        other_user = User.objects.create_user("Bob", "bob@bob.com", "pass123")
+
+        ThreadParticipant.objects.set_owner(self.thread, self.user)
+        ThreadParticipant.objects.add_participant(self.thread, other_user)
+
+        override_acl(self.user, {
+            'can_use_private_threads': True,
+            'can_moderate_private_threads': True
+        })
+
+        link = reverse('misago:private_thread_remove_participant', kwargs={
+            'thread_id': self.thread.id,
+            'thread_slug': self.thread.slug,
+            'user_id': 123456,
+        })
+
+        response = self.client.post(link, **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+
+        self.assertEqual(self.thread.threadparticipant_set.count(), 2)
+        owner = self.thread.threadparticipant_set.get(is_owner=True)
+        self.assertEqual(owner.user, self.user)
+
+        Thread.objects.get(pk=self.thread.pk)
+        self.thread.threadparticipant_set.get(user=self.user)
+
+    def test_non_owner_remove_participant(self):
+        """non-owner cant remove participant"""
+        User = get_user_model()
+        other_user = User.objects.create_user("Bob", "bob@bob.com", "pass123")
+
+        ThreadParticipant.objects.set_owner(self.thread, other_user)
+        ThreadParticipant.objects.add_participant(self.thread, self.user)
+
+        override_acl(self.user, {
+            'can_use_private_threads': True,
+            'can_moderate_private_threads': True
+        })
+
+        link = reverse('misago:private_thread_remove_participant', kwargs={
+            'thread_id': self.thread.id,
+            'thread_slug': self.thread.slug,
+            'user_id': other_user.pk,
+        })
+
+        response = self.client.post(link, **self.ajax_header)
+        self.assertEqual(response.status_code, 406)
+
+        self.assertEqual(self.thread.threadparticipant_set.count(), 2)
+        owner = self.thread.threadparticipant_set.get(is_owner=True)
+        self.assertEqual(owner.user, other_user)
+
+        Thread.objects.get(pk=self.thread.pk)
+        self.thread.threadparticipant_set.get(user=self.user)
+
+    def test_owner_add_participant(self):
+        """owner can add participants"""
+        User = get_user_model()
+        users = (
+            User.objects.create_user("Bob", "bob@bob.com", "pass123"),
+            User.objects.create_user("Dam", "dam@bob.com", "pass123")
+        )
+
+        ThreadParticipant.objects.set_owner(self.thread, self.user)
+        ThreadParticipant.objects.add_participant(self.thread, users[0])
+
+        override_acl(self.user, {
+            'can_use_private_threads': True,
+            'can_moderate_private_threads': True
+        })
+
+        link = reverse('misago:private_thread_add_participants', kwargs={
+            'thread_id': self.thread.id,
+            'thread_slug': self.thread.slug,
+        })
+
+        response = self.client.post(link, data={
+            'users': 'Bob, Dam'
+        }, **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+
+        self.assertEqual(self.thread.threadparticipant_set.count(), 3)
+        for participant in self.thread.threadparticipant_set.all():
+            if participant.is_owner:
+                self.assertEqual(participant.user, self.user)
+            else:
+                self.assertIn(participant.user, users)
+
+        Thread.objects.get(pk=self.thread.pk)
+        self.thread.threadparticipant_set.get(user=self.user)
+
+    def test_non_owner_add_participant(self):
+        """non-owner cant add participants"""
+        User = get_user_model()
+        users = (
+            User.objects.create_user("Bob", "bob@bob.com", "pass123"),
+            User.objects.create_user("Dam", "dam@bob.com", "pass123")
+        )
+
+        ThreadParticipant.objects.set_owner(self.thread, users[0])
+        ThreadParticipant.objects.add_participant(self.thread, self.user)
+
+        override_acl(self.user, {
+            'can_use_private_threads': True,
+            'can_moderate_private_threads': True
+        })
+
+        link = reverse('misago:private_thread_add_participants', kwargs={
+            'thread_id': self.thread.id,
+            'thread_slug': self.thread.slug,
+        })
+
+        response = self.client.post(link, data={
+            'users': 'Bob, Dam'
+        }, **self.ajax_header)
+        self.assertEqual(response.status_code, 406)
+
+    def test_owner_leave_thread_new_owner(self):
+        """
+        leave thread view makes owner leave thread and makes new user owner
+        """
+        User = get_user_model()
+        users = (
+            User.objects.create_user("Bob", "bob@bob.com", "pass123"),
+            User.objects.create_user("Dam", "dam@bob.com", "pass123")
+        )
+
+        ThreadParticipant.objects.set_owner(self.thread, self.user)
+        ThreadParticipant.objects.add_participant(self.thread, users[0])
+        ThreadParticipant.objects.add_participant(self.thread, users[1])
+
+        override_acl(self.user, {
+            'can_use_private_threads': True,
+            'can_moderate_private_threads': True
+        })
+
+        link = reverse('misago:private_thread_leave', kwargs={
+            'thread_id': self.thread.id,
+            'thread_slug': self.thread.slug
+        })
+
+        response = self.client.post(link, **self.ajax_header)
+        self.assertEqual(response.status_code, 302)
+
+        self.assertEqual(self.thread.threadparticipant_set.count(), 2)
+        new_owner = self.thread.threadparticipant_set.get(is_owner=True)
+        self.assertNotEqual(new_owner.user, self.user)
+        self.assertIn(new_owner.user, users)
+
+        Thread.objects.get(pk=self.thread.pk)
+        with self.assertRaises(ThreadParticipant.DoesNotExist):
+            self.thread.threadparticipant_set.get(user=self.user)
+
+    def test_owner_leave_thread_delete(self):
+        """
+        leave thread view makes owner leave thread and deletes abadoned thread
+        """
+        ThreadParticipant.objects.set_owner(self.thread, self.user)
+
+        override_acl(self.user, {
+            'can_use_private_threads': True,
+            'can_moderate_private_threads': True
+        })
+
+        link = reverse('misago:private_thread_leave', kwargs={
+            'thread_id': self.thread.id,
+            'thread_slug': self.thread.slug
+        })
+
+        response = self.client.post(link, **self.ajax_header)
+        self.assertEqual(response.status_code, 302)
+
+        with self.assertRaises(Thread.DoesNotExist):
+            Thread.objects.get(pk=self.thread.pk)
+
+        with self.assertRaises(ThreadParticipant.DoesNotExist):
+            self.thread.threadparticipant_set.get(user=self.user)
+
+    def test_participant_leave_thread(self):
+        """
+        leave thread view makes user leave thread
+        """
+        User = get_user_model()
+        users = (
+            User.objects.create_user("Bob", "bob@bob.com", "pass123"),
+            User.objects.create_user("Dam", "dam@bob.com", "pass123")
+        )
+
+        ThreadParticipant.objects.set_owner(self.thread, users[0])
+        ThreadParticipant.objects.add_participant(self.thread, users[1])
+        ThreadParticipant.objects.add_participant(self.thread, self.user)
+
+        override_acl(self.user, {
+            'can_use_private_threads': True,
+            'can_moderate_private_threads': True
+        })
+
+        link = reverse('misago:private_thread_leave', kwargs={
+            'thread_id': self.thread.id,
+            'thread_slug': self.thread.slug
+        })
+
+        response = self.client.post(link, **self.ajax_header)
+        self.assertEqual(response.status_code, 302)
+
+        self.assertEqual(self.thread.threadparticipant_set.count(), 2)
+        owner = self.thread.threadparticipant_set.get(is_owner=True)
+        self.assertEqual(owner.user, users[0])
+
+        for participants in self.thread.threadparticipant_set.all():
+            self.assertIn(participants.user, users)
+
+        Thread.objects.get(pk=self.thread.pk)
+        with self.assertRaises(ThreadParticipant.DoesNotExist):
+            self.thread.threadparticipant_set.get(user=self.user)

+ 2 - 3
misago/threads/views/privatethreads.py

@@ -346,11 +346,12 @@ class LeaveThreadView(BaseEditThreadParticipantView):
             messages.error(request, unicode(e))
             return redirect(thread.get_absolute_url())
 
-        participants.remove_participant(thread, user)
+        participants.remove_participant(thread, request.user)
         if not thread.threadparticipant_set.exists():
             thread.delete()
         elif thread.participant.is_owner:
             new_owner = user_qs.order_by('id')[:1][0].user
+            participants.set_thread_owner(thread, new_owner)
 
             message = _("%(user)s left this thread. "
                         "%(new_owner)s is now thread owner.")
@@ -359,8 +360,6 @@ class LeaveThreadView(BaseEditThreadParticipantView):
                 'new_owner': new_owner
             })
             thread.save(update_fields=['has_events'])
-
-            participants.set_thread_owner(thread, request.user)
         else:
             message = _("%(user)s left this thread.")
             record_event(request.user, thread, 'user', message, {