|
@@ -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)
|