def test_sort_title_reversed(self):
        """ Test sorting by title field reversed.

        If the request has a GET variable of 'sort' with the value of
        'title', and a variable 'reverse' with the value of 'true',
        then the threads should be ordered by title in reverse
        alphabetical order.
        """
        thread1 = create_thread(
            topic=self.topic,
            title='cats')
        thread2 = create_thread(
            topic=self.topic,
            title='animals')
        thread3 = create_thread(
            topic=self.topic,
            title='bats')

        url = thread_list_url(topic=self.topic, sort='title', rev=True)
        response = self.client.get(url)

        expected = [
            '<Thread: %s>' % thread1,
            '<Thread: %s>' % thread3,
            '<Thread: %s>' % thread2,
        ]

        self.assertEqual(200, response.status_code)
        self.assertQuerysetEqual(
            response.context['thread_list'],
            expected)
    def test_full_title_search(self):
        """ Test searching for the title of a thread.

        Searching for the title of a thread should return only that
        thread.
        """
        thread = create_thread(title='cat')
        create_thread(title='dog')

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

        self.assertEqual([thread], results)
    def test_full_title_search(self):
        """ Test searching for the title of a thread.

        Searching for the title of a thread should return only that
        thread.
        """
        create_thread(title='cat')
        create_thread(title='dog')

        self.assertQuerysetEqual(
            self.backend.search('cat'),
            ['<Thread: cat>'])
    def test_partial_title_match(self):
        """ Test search that matches multiple threads.

        If a search matches multiple threads, all matching threads
        should be returned.
        """
        t1 = create_thread(title='cat escapes prison')
        t2 = create_thread(title='woman finds cat playing with yarn')
        create_thread(title='stray dog wins lottery')

        results = [t for t, _ in self.backend.search('cat')]
        expected = [t1, t2]

        self.assertEqual(expected, results)
    def test_partial_title_match(self):
        """ Test search that matches multiple threads.

        If a search matches multiple threads, all matching threads
        should be returned.
        """
        t1 = create_thread(title='cat escapes prison')
        t2 = create_thread(title='woman finds cat playing with yarn')
        create_thread(title='stray dog wins lottery')

        self.assertQuerysetEqual(
            self.backend.search('cat'),
            ['<Thread: %s>' % t1, '<Thread: %s>' % t2],
            ordered=False)
    def test_blank_search(self):
        """ Test result of searching for a blank string.

        Searching for a blank string should return all threads.
        """
        create_thread(title='thread 1')
        create_thread(title='thread 2')

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

        expected = list()
        for thread in models.Thread.objects.all():
            expected.append(thread)

        self.assertEqual(expected, results)
    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_blank_search(self):
        """ Test result of searching for a blank string.

        Searching for a blank string should return all threads.
        """
        create_thread(title='thread 1')
        create_thread(title='thread 2')

        expected = list()
        for thread in models.Thread.objects.all():
            expected.append('<Thread: %s>' % thread)

        self.assertQuerysetEqual(
            self.backend.search(''),
            expected,
            ordered=False)
    def test_thread_for_different_topic(self):
        """ Test view when there is a thread for a different topic.

        If a thread is associated with a different topic, it should not
        be displayed.
        """
        thread = create_thread(topic=self.topic)
        create_thread(title="I shouldn't be included")

        url = thread_list_url(topic=self.topic)
        response = self.client.get(url)

        self.assertEqual(200, response.status_code)
        self.assertQuerysetEqual(
            response.context['thread_list'],
            ['<Thread: %s>' % thread])
    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_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_get_title(self):
        """ Test getting the thread's title.

        This method should return the title field of the thread.
        """
        thread = create_thread()

        self.assertEqual(thread.title, thread.get_title())
    def test_sticky_default(self):
        """ Test default 'sticky' value.

        Threads should not be sticky by default.
        """
        thread = create_thread()

        self.assertFalse(thread.sticky)
    def test_slug_generation(self):
        """ Test the automatic generation of a url slug.

        When creating a thread instance, the instance should generate a
        url slug based on its title.
        """
        thread = create_thread(title='test title')

        self.assertEqual('test-title', thread.slug)
    def test_num_replies_with_no_replies(self):
        """ Test retrieving the number of replies for a thread.

        If there are no messages associated with the thread, the number
        of replies should be 0.
        """
        thread = create_thread()

        self.assertEqual(0, thread.num_replies)
    def test_time_last_activity_no_replies(self):
        """ Test the 'time_last_activity' property with no replies.

        If there are no replies, this property should return the time
        that the thread was created.
        """
        thread = create_thread()

        self.assertEqual(thread.time_created, thread.time_last_activity)
    def test_slug_generation_for_long_title(self):
        """ Test generating a slug when the title is really long.

        If the title is longer than 50 characters, the slug should be
        truncated to 50 chars.
        """
        thread = create_thread(title='a' * 51)

        self.assertEqual('a' * 50, thread.slug)
    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_get_search_description_no_message(self):
        """ Test getting the search description with no message.

        If there is no message assocated with the thread, the search
        description should display a message that there are no replies.
        """
        thread = create_thread()
        expected = 'There are no replies to this thread.'

        self.assertEqual(expected, thread.get_search_description())
    def test_add(self):
        """ Test adding a result to the result set.

        Adding a thread to the result set with it's score should save
        the thread in the result set.
        """
        thread = create_thread()
        result_set = SearchResultSet()
        result_set.add(thread, 0)

        self.assertEqual((thread, 0), result_set[0])
    def test_default_time_created(self):
        """ Test the default for the 'time_created' field.

        If no parameter is passed to 'time_created', it should default
        to the current time.
        """
        start_time = timezone.now()
        thread = create_thread()
        end_time = timezone.now()

        self.assertTrue(start_time <= thread.time_created <= end_time)
    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_add_default_score(self):
        """ Test adding a result to the result set with no score.

        Adding a thread to the result set and not specifying a score
        should set the result's score to 0.
        """
        thread = create_thread()
        result_set = SearchResultSet()
        result_set.add(thread)

        self.assertEqual((thread, 0), result_set[0])
    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_reply_form_unauthenticated(self):
        """ Test presence of reply form for unauthenticated users.

        If a user isn't logged in, there should be no reply form.
        """
        thread = create_thread()

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

        self.assertEqual(200, response.status_code)
        self.assertTrue('reply_form' not in response.context)
    def test_post_unauthenticated(self):
        """ Test sending an unauthenticated POST request.

        A POST request from an unauthenticated user should result in a
        403 status code.
        """
        thread = create_thread()

        url = reverse('simple-forums:follow-thread', kwargs={'pk': thread.pk})
        response = self.client.post(url, {})

        self.assertEqual(403, response.status_code)
    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_remove_invalid_pk(self):
        """ Test removing an object that is not in the index.

        Removing an object that is not in the index should raise a
        NotFoundError
        """
        thread = create_thread()
        self.backend.add(thread)
        self.backend.remove(thread)
        # try removing it after it's been removed
        with self.assertRaises(NotFoundError):
            self.backend.remove(thread)
    def test_threads(self):
        """ Test view with multiple threads.

        If there are multiple threads, they should be ordered by the
        date of their last activity.
        """
        past = timezone.now() - timedelta(days=1)
        thread1 = create_thread(
            topic=self.topic,
            title='Test Thread 1',
            time_created=past)
        thread2 = create_thread(
            topic=self.topic,
            title='Test Thread 2')

        url = thread_list_url(topic=self.topic)
        response = self.client.get(url)

        self.assertEqual(200, response.status_code)
        self.assertQuerysetEqual(
            response.context['thread_list'],
            ['<Thread: %s>' % thread2, '<Thread: %s>' % thread1])
    def test_sticky_thread(self):
        """ Test view when there is a sticky thread.

        If there is a sticky thread, it should be in a context variable
        for the sticky threads, and not in the one for normal threads.
        """
        thread = create_thread(topic=self.topic)
        sticky_thread = create_thread(
            topic=self.topic,
            title='Sticky Thread',
            sticky=True)

        url = thread_list_url(topic=self.topic)
        response = self.client.get(url)

        self.assertEqual(200, response.status_code)
        self.assertQuerysetEqual(
            response.context['thread_list'],
            ['<Thread: %s>' % thread])
        self.assertQuerysetEqual(
            response.context['sticky_thread_list'],
            ['<Thread: %s>' % sticky_thread])