def test_num_replies_with_initial_reply(self):
        """ Test retrieving the number of replies for a thread.

        If the only message associated with a thread is the initial
        message, then the property should return 0 replies.
        """
        thread = create_thread()
        create_message(thread=thread)

        self.assertEqual(0, thread.num_replies)
    def test_num_replies_with_more_replies(self):
        """ Test retrieving the number of replies for a thread.

        If the thread has a message that is not the initial message,
        then the property should return the number of additional
        messages.
        """
        thread = create_thread()
        # simulate inital message
        create_message(thread=thread)

        # create additional message
        create_message(thread=thread)

        self.assertEqual(1, thread.num_replies)
    def test_create_new_message(self):
        """ Test creating a new message.

        Creating a new message on a thread that has a notification
        associated with it should send an email notification.
        """
        user = get_test_user()
        thread = create_thread()
        notification = create_thread_notification(
            user=user, thread=thread)

        message = create_message(
            user=get_test_user(username="******"),
            thread=thread)

        self.assertEqual(1, len(mail.outbox))

        result = mail.outbox[0]

        mail.outbox = []

        notification.send_notification(message)
        expected = mail.outbox[0]

        self.assertMailEqual(expected, result)
    def test_self_notification(self):
        """ Test user replying to watched thread.

        If a user is receiving notifications for a thread, they should
        not receive a notification if they were the one who posted a
        reply.
        """
        user = get_test_user()
        thread = create_thread()

        create_thread_notification(
            user=user, thread=thread)

        create_message(user=user, thread=thread)

        self.assertEqual(0, len(mail.outbox))
    def test_add_messages(self):
        """ Test adding a thread that has messages associated with it.

        Adding a message that has messages associated with it should
        also add those messages to the search index.
        """
        thread = create_thread()
        message = create_message(thread=thread)

        self.backend.add(thread)

        es = Elasticsearch([{'host': 'localhost', 'port': 9200}])

        source = es.get_source(
            index=self.backend.index,
            doc_type='message',
            id=message.pk)
        source_json = json.dumps(source)

        expected = {
            'body': message.body,
        }
        expected_json = json.dumps(expected)

        self.assertJSONEqual(expected_json, source_json)
    def test_send_notification(self):
        """ Test sending a notification email.

        Calling this method should send an email to the user assocated
        with the notification.
        """
        user = get_test_user(email='*****@*****.**')
        thread = create_thread()
        message = create_message(thread=thread)

        notification = models.ThreadNotification.objects.create(
            user=user,
            thread=thread)
        notification.send_notification(message)

        expected = 'Thread #%d was updated' % (thread.pk)

        self.assertEqual(1, len(mail.outbox))

        m = mail.outbox[0]

        self.assertEqual('Thread Updated', m.subject)
        self.assertEqual(expected, m.body)
        self.assertEqual('*****@*****.**', m.from_email)
        self.assertEqual([user.email], m.to)
    def test_get_title(self):
        """ Test getting the message's title.

        The message's title should be its parent thread's title.
        """
        message = create_message()

        self.assertEqual(message.thread.get_title(), message.get_title())
    def test_default_time_created(self):
        """ Test the default for the 'time_created' field.

        The field should default to the current time.
        """
        start_time = timezone.now()
        message = create_message()
        end_time = timezone.now()

        self.assertTrue(start_time <= message.time_created <= end_time)
    def test_get_anchor(self):
        """ Test getting the anchor for the message.

        The anchor should be in the format 'm-<pk>'.
        """
        message = create_message()

        expected = 'm-%d' % message.pk

        self.assertEqual(expected, message.get_anchor())
    def test_get_search_description(self):
        """ Test getting the search description.

        The search description should return text used to describe the
        message for search results.
        """
        message = create_message()

        expected = '%s said: %s' % (message.user, message.body)

        self.assertEqual(expected, message.get_search_description())
    def test_time_last_activity_with_reply(self):
        """ Test the 'time_last_activity' property with a reply.

        If there is a reply, this property should return the time that
        the most recent reply was posted.
        """
        past = timezone.now() - timedelta(days=1)
        thread = create_thread(time_created=past)
        message = create_message(thread=thread)

        self.assertEqual(message.time_created, thread.time_last_activity)
    def test_update_last_activity_time(self):
        """ Test if saving a message updates its parent thread.

        Saving a message should update the 'time_last_activity' field
        on its parent thread instance.
        """
        past = timezone.now() - timedelta(days=1)
        thread = create_thread(time_created=past)
        message = create_message(thread=thread)

        self.assertEqual(message.time_created, thread.time_last_activity)
    def test_get_search_description(self):
        """ Test getting the search description.

        The search description should return text used to describe the
        thread for search results.
        """
        thread = create_thread()
        message = create_message(thread=thread)

        expected = message.body

        self.assertEqual(expected, thread.get_search_description())
    def test_get_absolute_url(self):
        """ Test getting the message's url.

        The url should be the url of the message's parent thread with
        a named anchor of the message's pk.
        """
        message = create_message()

        expected = '%s#%s' % (message.thread.get_absolute_url(),
                              message.get_anchor())

        self.assertEqual(expected, message.get_absolute_url())
    def test_with_message(self):
        """ Test the view when the given thread has a message.

        If the thread has a message, it should be displayed following
        the thread's title.
        """
        thread = create_thread()
        message = create_message(user=self.user, thread=thread)

        url = thread_detail_url(thread=thread)
        response = self.client.get(url)

        self.assertEqual(200, response.status_code)
        self.assertContains(response, message.body)
    def test_update_message(self):
        """ Test updating an existing message.

        Updating an existing message should not trigger a new
        notification.
        """
        user = get_test_user()
        thread = create_thread()
        message = create_message(thread=thread)
        create_thread_notification(
            user=user, thread=thread)

        message.body = "New body text"
        message.save()

        self.assertEqual(0, len(mail.outbox))
    def test_search_messages(self):
        """ Test searching with messages.

        Indexed messages should be included in search results along with
        the indexed threads.
        """
        thread = create_thread(title='Darth Maul')
        message = create_message(thread=thread, body='Darth Vader')

        self.backend.add(thread)

        self.backend.es.indices.refresh()

        raw_results = self.backend.search('Darth Vader')
        results = [t for t, _ in raw_results]

        expected = [message, thread]

        self.assertEqual(expected, results)
    def test_remove_message(self):
        """ Test removing a thread with messages.

        If a thread has messages assocated with it, those messages
        should be removed from the search backend when the thread
        instance is removed.
        """
        thread = create_thread()
        message = create_message(thread=thread)

        self.backend.add(thread)
        self.backend.remove(thread)

        es = Elasticsearch([{'host': 'localhost', 'port': 9200}])

        with self.assertRaises(NotFoundError):
            es.get_source(
                index=self.backend.index,
                doc_type='message',
                id=message.pk)