Exemple #1
0
def load_staff(cli, size, fake, root, *_):
    """
    Load staff
    """
    nb_staffs = size * 3
    cli.stdout.write(f"Nombres de staffs à créer : {nb_staffs}")
    tps1 = time.time()
    cpt = 1
    for i in range(0, nb_staffs):
        while Profile.objects.filter(user__username=f"{root}staff{cpt}").count() > 0:
            cpt += 1
        profile = StaffProfileFactory(user__username=f"{root}staff{cpt}")
        profile.user.first_name = fake.first_name()
        profile.user.last_name = fake.last_name()
        profile.user.email = fake.free_email()
        profile.user.save()
        profile.site = fake.url()
        profile.biography = fake.paragraph()
        profile.sign = fake.text(max_nb_chars=80)
        profile.last_ip_address = fake.ipv6()
        profile.save()
        cpt += 1
        sys.stdout.write(f" Staff {i + 1}/{nb_staffs}  \r")
        sys.stdout.flush()
    tps2 = time.time()
    cli.stdout.write(f"\nFait en {tps2 - tps1} sec")
class ViewsTests(TutorialTestMixin, TestCase):
    def setUp(self):

        settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
        self.mas = ProfileFactory().user
        settings.ZDS_APP["member"]["bot_account"] = self.mas.username

        self.category, self.forum = create_category_and_forum()

        self.user = ProfileFactory().user
        self.staff = StaffProfileFactory().user

        self.manager = ESIndexManager(**settings.ES_SEARCH_INDEX)
        self.indexable = [FakeChapter, PublishedContent, Topic, Post]

        self.manager.reset_es_index(self.indexable)
        self.manager.setup_custom_analyzer()
        self.manager.refresh_index()

    def test_basic_search(self):
        """Basic search and filtering"""

        if not self.manager.connected_to_es:
            return

        # 1. Index and test search:
        text = "test"

        topic_1 = TopicFactory(forum=self.forum, author=self.user, title=text)
        post_1 = PostFactory(topic=topic_1, author=self.user, position=1)
        post_1.text = post_1.text_html = text
        post_1.save()

        # create a middle-size content and publish it
        tuto = PublishableContentFactory(type="TUTORIAL")
        tuto_draft = tuto.load_version()

        tuto.title = text
        tuto.authors.add(self.user)
        tuto.save()

        tuto_draft.repo_update_top_container(
            text, tuto.slug, text,
            text)  # change title to be sure it will match

        chapter1 = ContainerFactory(parent=tuto_draft, db_object=tuto)
        extract = ExtractFactory(container=chapter1, db_object=tuto)
        extract.repo_update(text, text)

        published = publish_content(tuto, tuto_draft, is_major_update=True)

        tuto.sha_public = tuto_draft.current_version
        tuto.sha_draft = tuto_draft.current_version
        tuto.public_version = published
        tuto.save()

        # nothing has been indexed yet:
        self.assertEqual(
            len(
                self.manager.setup_search(Search().query(
                    MatchAll())).execute()), 0)

        # index
        for model in self.indexable:
            if model is FakeChapter:
                continue
            self.manager.es_bulk_indexing_of_model(model)
        self.manager.refresh_index()

        result = self.client.get(reverse("search:query") + "?q=" + text,
                                 follow=False)
        self.assertEqual(result.status_code, 200)

        response = result.context["object_list"].execute()

        self.assertEqual(response.hits.total, 4)  # get 4 results

        # 2. Test filtering:
        topic_1 = Topic.objects.get(pk=topic_1.pk)
        post_1 = Post.objects.get(pk=post_1.pk)
        published = PublishedContent.objects.get(pk=published.pk)

        ids = {
            "topic": [topic_1.es_id],
            "post": [post_1.es_id],
            "content": [
                published.es_id,
                published.content_public_slug + "__" + chapter1.slug
            ],
        }

        search_groups = [
            k for k, v in settings.ZDS_APP["search"]["search_groups"].items()
        ]
        group_to_model = {
            k: v[1]
            for k, v in settings.ZDS_APP["search"]["search_groups"].items()
        }

        for doc_type in search_groups:
            result = self.client.get(reverse("search:query") + "?q=" + text +
                                     "&models=" + doc_type,
                                     follow=False)
            self.assertEqual(result.status_code, 200)

            response = result.context["object_list"].execute()

            self.assertEqual(response.hits.total,
                             len(ids[doc_type]))  # get 1 result of each …
            for i, r in enumerate(response):
                self.assertIn(
                    r.meta.doc_type,
                    group_to_model[doc_type])  # … and only of the right type …
                self.assertEqual(r.meta.id,
                                 ids[doc_type][i])  # … with the right id !

    def test_get_similar_topics(self):
        """Get similar topics lists"""

        if not self.manager.connected_to_es:
            return

        text = "Clem ne se mange pas"

        topic_1 = TopicFactory(forum=self.forum, author=self.user, title=text)
        post_1 = PostFactory(topic=topic_1, author=self.user, position=1)
        post_1.text = post_1.text_html = text
        post_1.save()

        text = "Clem est la meilleure mascotte"

        topic_2 = TopicFactory(forum=self.forum, author=self.user, title=text)
        post_2 = PostFactory(topic=topic_2, author=self.user, position=1)
        post_2.text = post_1.text_html = text
        post_2.save()

        # 1. Should not get any result
        result = self.client.get(reverse("search:similar") + "?q=est",
                                 follow=False)
        self.assertEqual(result.status_code, 200)
        content = json_handler.loads(result.content.decode("utf-8"))
        self.assertEqual(len(content["results"]), 0)

        # index
        for model in self.indexable:
            if model is FakeChapter:
                continue
            self.manager.es_bulk_indexing_of_model(model)
        self.manager.refresh_index()

        # 2. Should get exactly one result
        result = self.client.get(reverse("search:similar") + "?q=mange",
                                 follow=False)
        self.assertEqual(result.status_code, 200)
        content = json_handler.loads(result.content.decode("utf-8"))
        self.assertEqual(len(content["results"]), 1)

        # 2. Should get exactly two results
        result = self.client.get(reverse("search:similar") + "?q=Clem",
                                 follow=False)
        self.assertEqual(result.status_code, 200)
        content = json_handler.loads(result.content.decode("utf-8"))
        self.assertEqual(len(content["results"]), 2)

    def test_hidden_post_are_not_result(self):
        """Hidden posts should not show up in the search results"""

        if not self.manager.connected_to_es:
            return

        # 1. Index and test search:
        text = "test"

        topic_1 = TopicFactory(forum=self.forum, author=self.user, title=text)
        post_1 = PostFactory(topic=topic_1, author=self.user, position=1)
        post_1.text = post_1.text_html = text
        post_1.save()

        self.manager.es_bulk_indexing_of_model(Topic)
        self.manager.es_bulk_indexing_of_model(Post)
        self.manager.refresh_index()

        self.assertEqual(
            len(
                self.manager.setup_search(Search().query(
                    MatchAll())).execute()), 2)  # indexing ok

        post_1 = Post.objects.get(pk=post_1.pk)

        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=" + Post.get_es_document_type(),
                                 follow=False)

        self.assertEqual(result.status_code, 200)

        response = result.context["object_list"].execute()

        self.assertEqual(response.hits.total, 1)
        self.assertEqual(response[0].meta.id, post_1.es_id)

        # 2. Hide, reindex and search again:
        post_1.hide_comment_by_user(self.staff,
                                    "Un abus de pouvoir comme un autre ;)")
        self.manager.refresh_index()

        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=" + Post.get_es_document_type(),
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 0)  # nothing in the results

    def test_hidden_forums_give_no_results_if_user_not_allowed(self):
        """Long name, isn't ?"""

        if not self.manager.connected_to_es:
            return

        # 1. Create a hidden forum belonging to a hidden staff group.
        text = "test"

        group = Group.objects.create(name="Les illuminatis anonymes de ZdS")
        _, hidden_forum = create_category_and_forum(group)

        self.staff.groups.add(group)
        self.staff.save()

        topic_1 = TopicFactory(forum=hidden_forum,
                               author=self.staff,
                               title=text)
        post_1 = PostFactory(topic=topic_1, author=self.user, position=1)
        post_1.text = post_1.text_html = text
        post_1.save()

        self.manager.es_bulk_indexing_of_model(Topic)
        self.manager.es_bulk_indexing_of_model(Post)
        self.manager.refresh_index()

        self.assertEqual(
            len(
                self.manager.setup_search(Search().query(
                    MatchAll())).execute()), 2)  # indexing ok

        # 2. search without connection and get not result
        result = self.client.get(reverse("search:query") + "?q=" + text,
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 0)

        # 3. Connect with user (not a member of the group), search, and get no result
        self.client.force_login(self.user)

        result = self.client.get(reverse("search:query") + "?q=" + text,
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 0)

        # 4. Connect with staff, search, and get the topic and the post
        self.client.logout()
        self.client.force_login(self.staff)

        result = self.client.get(reverse("search:query") + "?q=" + text,
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 2)  # ok !

    def test_boosts(self):
        """Check if boosts are doing their job"""

        if not self.manager.connected_to_es:
            return

        # 1. Create topics (with identical titles), posts (with identical texts), an article and a tuto
        text = "test"

        topic_1_solved_sticky = TopicFactory(forum=self.forum,
                                             author=self.user)
        topic_1_solved_sticky.title = text
        topic_1_solved_sticky.subtitle = ""
        topic_1_solved_sticky.solved_by = self.user
        topic_1_solved_sticky.is_sticky = True
        topic_1_solved_sticky.save()

        post_1 = PostFactory(topic=topic_1_solved_sticky,
                             author=self.user,
                             position=1)
        post_1.text = post_1.text_html = text
        post_1.save()

        post_2_useful = PostFactory(topic=topic_1_solved_sticky,
                                    author=self.user,
                                    position=2)
        post_2_useful.text = post_2_useful.text_html = text
        post_2_useful.is_useful = True
        post_2_useful.like = 5
        post_2_useful.dislike = 2  # l/d ratio above 1
        post_2_useful.save()

        topic_2_locked = TopicFactory(forum=self.forum,
                                      author=self.user,
                                      title=text)
        topic_2_locked.title = text
        topic_2_locked.subtitle = ""
        topic_2_locked.is_locked = True
        topic_2_locked.save()

        post_3_ld_below_1 = PostFactory(topic=topic_2_locked,
                                        author=self.user,
                                        position=1)
        post_3_ld_below_1.text = post_3_ld_below_1.text_html = text
        post_3_ld_below_1.like = 2
        post_3_ld_below_1.dislike = 5  # l/d ratio below 1
        post_3_ld_below_1.save()

        tuto = PublishableContentFactory(type="TUTORIAL")
        tuto_draft = tuto.load_version()

        tuto.title = text
        tuto.authors.add(self.user)
        tuto.save()

        tuto_draft.repo_update_top_container(text, tuto.slug, text, text)

        chapter1 = ContainerFactory(parent=tuto_draft, db_object=tuto)
        chapter1.repo_update(text, "Who cares ?", "Same here")
        ExtractFactory(container=chapter1, db_object=tuto)

        published_tuto = publish_content(tuto,
                                         tuto_draft,
                                         is_major_update=True)

        tuto.sha_public = tuto_draft.current_version
        tuto.sha_draft = tuto_draft.current_version
        tuto.public_version = published_tuto
        tuto.save()

        article = PublishedContentFactory(type="ARTICLE", title=text)
        published_article = PublishedContent.objects.get(content_pk=article.pk)

        opinion_not_picked = PublishedContentFactory(type="OPINION",
                                                     title=text)
        published_opinion_not_picked = PublishedContent.objects.get(
            content_pk=opinion_not_picked.pk)

        opinion_picked = PublishedContentFactory(type="OPINION", title=text)
        opinion_picked.sha_picked = opinion_picked.sha_draft
        opinion_picked.date_picked = datetime.datetime.now()
        opinion_picked.save()

        published_opinion_picked = PublishedContent.objects.get(
            content_pk=opinion_picked.pk)

        for model in self.indexable:
            if model is FakeChapter:
                continue
            self.manager.es_bulk_indexing_of_model(model)
        self.manager.refresh_index()

        self.assertEqual(
            len(
                self.manager.setup_search(Search().query(
                    MatchAll())).execute()), 10)

        # 2. Reset all boosts to 1
        for doc_type in settings.ZDS_APP["search"]["boosts"]:
            for key in settings.ZDS_APP["search"]["boosts"][doc_type]:
                settings.ZDS_APP["search"]["boosts"][doc_type][key] = 1.0

        # 3. Test posts
        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=" + Post.get_es_document_type(),
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 3)

        # score are equals without boost:
        self.assertTrue(response[0].meta.score == response[1].meta.score ==
                        response[2].meta.score)

        settings.ZDS_APP["search"]["boosts"]["post"]["if_first"] = 2.0

        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=" + Post.get_es_document_type(),
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 3)

        self.assertTrue(response[0].meta.score == response[1].meta.score >
                        response[2].meta.score)
        self.assertEqual(response[2].meta.id, str(
            post_2_useful.pk))  # post 2 is the only one not first

        settings.ZDS_APP["search"]["boosts"]["post"]["if_first"] = 1.0
        settings.ZDS_APP["search"]["boosts"]["post"]["if_useful"] = 2.0

        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=" + Post.get_es_document_type(),
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 3)

        self.assertTrue(response[0].meta.score > response[1].meta.score ==
                        response[2].meta.score)
        self.assertEqual(response[0].meta.id,
                         str(post_2_useful.pk))  # post 2 is useful

        settings.ZDS_APP["search"]["boosts"]["post"]["if_useful"] = 1.0
        settings.ZDS_APP["search"]["boosts"]["post"]["ld_ratio_above_1"] = 2.0

        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=" + Post.get_es_document_type(),
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 3)

        self.assertTrue(response[0].meta.score > response[1].meta.score ==
                        response[2].meta.score)
        self.assertEqual(response[0].meta.id, str(
            post_2_useful.pk))  # post 2 have a l/d ratio of 5/2

        settings.ZDS_APP["search"]["boosts"]["post"]["ld_ratio_above_1"] = 1.0
        settings.ZDS_APP["search"]["boosts"]["post"][
            "ld_ratio_below_1"] = 2.0  # no one would do that in real life

        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=" + Post.get_es_document_type(),
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 3)

        self.assertTrue(response[0].meta.score > response[1].meta.score ==
                        response[2].meta.score)
        self.assertEqual(response[0].meta.id, str(
            post_3_ld_below_1.pk))  # post 3 have a l/d ratio of 2/5

        settings.ZDS_APP["search"]["boosts"]["post"]["ld_ratio_below_1"] = 1.0

        # 4. Test topics
        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=" + Topic.get_es_document_type(),
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 2)

        # score are equals without boost:
        self.assertTrue(response[0].meta.score == response[1].meta.score)

        settings.ZDS_APP["search"]["boosts"]["topic"]["if_sticky"] = 2.0

        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=" + Topic.get_es_document_type(),
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 2)

        self.assertTrue(response[0].meta.score > response[1].meta.score)
        self.assertEqual(response[0].meta.id,
                         str(topic_1_solved_sticky.pk))  # topic 1 is sticky

        settings.ZDS_APP["search"]["boosts"]["topic"]["if_sticky"] = 1.0
        settings.ZDS_APP["search"]["boosts"]["topic"]["if_solved"] = 2.0

        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=" + Topic.get_es_document_type(),
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 2)

        self.assertTrue(response[0].meta.score > response[1].meta.score)
        self.assertEqual(response[0].meta.id,
                         str(topic_1_solved_sticky.pk))  # topic 1 is solved

        settings.ZDS_APP["search"]["boosts"]["topic"]["if_solved"] = 1.0
        settings.ZDS_APP["search"]["boosts"]["topic"][
            "if_locked"] = 2.0  # no one would do that in real life

        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=" + Topic.get_es_document_type(),
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 2)

        self.assertTrue(response[0].meta.score > response[1].meta.score)
        self.assertEqual(response[0].meta.id,
                         str(topic_2_locked.pk))  # topic 2 is locked

        settings.ZDS_APP["search"]["boosts"]["topic"][
            "if_locked"] = 1.0  # no one would do that in real life

        # 5. Test published contents
        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=content",
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 5)

        # score are equals without boost:
        self.assertTrue(
            response[0].meta.score == response[1].meta.score == response[2].
            meta.score == response[3].meta.score == response[4].meta.score)

        settings.ZDS_APP["search"]["boosts"]["publishedcontent"][
            "if_article"] = 2.0

        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=content",
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 5)

        self.assertTrue(response[0].meta.score > response[1].meta.score)
        self.assertEqual(response[0].meta.id,
                         str(published_article.pk))  # obvious

        settings.ZDS_APP["search"]["boosts"]["publishedcontent"][
            "if_article"] = 1.0
        settings.ZDS_APP["search"]["boosts"]["publishedcontent"][
            "if_tutorial"] = 2.0

        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=content",
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 5)

        self.assertTrue(response[0].meta.score > response[1].meta.score)
        self.assertEqual(response[0].meta.id,
                         str(published_tuto.pk))  # obvious

        settings.ZDS_APP["search"]["boosts"]["publishedcontent"][
            "if_tutorial"] = 1.0
        settings.ZDS_APP["search"]["boosts"]["publishedcontent"][
            "if_opinion"] = 2.0
        settings.ZDS_APP["search"]["boosts"]["publishedcontent"][
            "if_opinion_not_picked"] = 4.0
        # Note: in "real life", unpicked opinion would get a boost < 1.

        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=content",
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 5)

        self.assertTrue(response[0].meta.score > response[1].meta.score >
                        response[2].meta.score)
        self.assertEqual(
            response[0].meta.id,
            str(published_opinion_not_picked.pk))  # unpicked opinion got first
        self.assertEqual(response[1].meta.id, str(published_opinion_picked.pk))

        settings.ZDS_APP["search"]["boosts"]["publishedcontent"][
            "if_opinion"] = 1.0
        settings.ZDS_APP["search"]["boosts"]["publishedcontent"][
            "if_opinion_not_picked"] = 1.0
        settings.ZDS_APP["search"]["boosts"]["publishedcontent"][
            "if_medium_or_big_tutorial"] = 2.0

        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=content",
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 5)

        self.assertTrue(response[0].meta.score > response[1].meta.score)
        self.assertEqual(response[0].meta.id,
                         str(published_tuto.pk))  # obvious

        settings.ZDS_APP["search"]["boosts"]["publishedcontent"][
            "if_medium_or_big_tutorial"] = 1.0

        # 6. Test global boosts
        # NOTE: score are NOT the same for all documents, no matter how hard it tries to, small differences exists

        for model in self.indexable:

            # set a huge number to overcome the small differences:
            settings.ZDS_APP["search"]["boosts"][
                model.get_es_document_type()]["global"] = 10.0

            result = self.client.get(reverse("search:query") + "?q=" + text,
                                     follow=False)

            self.assertEqual(result.status_code, 200)
            response = result.context["object_list"].execute()
            self.assertEqual(response.hits.total, 10)

            self.assertEqual(response[0].meta.doc_type,
                             model.get_es_document_type())  # obvious

            settings.ZDS_APP["search"]["boosts"][
                model.get_es_document_type()]["global"] = 1.0

    def test_change_topic_impacts_posts(self):

        if not self.manager.connected_to_es:
            return

        # 1. Create a hidden forum belonging to a hidden group and add staff in it.
        text = "test"

        group = Group.objects.create(name="Les illuminatis anonymes de ZdS")
        _, hidden_forum = create_category_and_forum(group)

        self.staff.groups.add(group)
        self.staff.save()

        # 2. Create a normal topic and index it
        topic_1 = TopicFactory(forum=self.forum, author=self.user, title=text)
        post_1 = PostFactory(topic=topic_1, author=self.user, position=1)
        post_1.text = post_1.text_html = text
        post_1.save()

        self.manager.es_bulk_indexing_of_model(Topic)
        self.manager.es_bulk_indexing_of_model(Post)
        self.manager.refresh_index()

        self.assertEqual(
            len(
                self.manager.setup_search(Search().query(
                    MatchAll())).execute()), 2)  # indexing ok

        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=" + Post.get_es_document_type(),
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 1)  # ok
        self.assertEqual(response[0].meta.doc_type,
                         Post.get_es_document_type())
        self.assertEqual(response[0].forum_pk, self.forum.pk)
        self.assertEqual(response[0].topic_pk, topic_1.pk)
        self.assertEqual(response[0].topic_title, topic_1.title)

        # 3. Change topic title and reindex
        topic_1.title = "new title"
        topic_1.save()

        self.manager.es_bulk_indexing_of_model(Topic)
        self.manager.es_bulk_indexing_of_model(Post)
        self.manager.refresh_index()

        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=" + Post.get_es_document_type(),
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 1)  # ok

        self.assertEqual(response[0].topic_title,
                         topic_1.title)  # title was changed

        # 4. connect with staff and move topic
        self.client.force_login(self.staff)

        data = {"move": "", "forum": hidden_forum.pk, "topic": topic_1.pk}
        response = self.client.post(reverse("topic-edit"), data, follow=False)

        self.assertEqual(302, response.status_code)

        self.manager.es_bulk_indexing_of_model(Topic)
        self.manager.es_bulk_indexing_of_model(Post)
        self.manager.refresh_index()

        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=" + Post.get_es_document_type(),
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(
            response.hits.total,
            1)  # Note: without staff, would not get any results (see below)

        self.assertEqual(response[0].forum_pk,
                         hidden_forum.pk)  # post was updated with new forum

        # 5. Topic is now hidden
        self.client.logout()

        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=" + Post.get_es_document_type(),
                                 follow=False)

        self.assertEqual(result.status_code, 200)
        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 0)  # ok

    def test_change_publishedcontents_impacts_chapter(self):

        if not self.manager.connected_to_es:
            return

        # 1. Create middle-size content and index it
        text = "test"

        tuto = PublishableContentFactory(type="TUTORIAL")
        tuto_draft = tuto.load_version()

        tuto.title = text
        tuto.authors.add(self.user)
        tuto.save()

        tuto_draft.repo_update_top_container(
            text, tuto.slug, text,
            text)  # change title to be sure it will match

        chapter1 = ContainerFactory(parent=tuto_draft, db_object=tuto)
        chapter1.repo_update(text, text, text)
        extract = ExtractFactory(container=chapter1, db_object=tuto)
        extract.repo_update(text, text)

        published = publish_content(tuto, tuto_draft, is_major_update=True)

        tuto.sha_public = tuto_draft.current_version
        tuto.sha_draft = tuto_draft.current_version
        tuto.public_version = published
        tuto.save()

        self.manager.es_bulk_indexing_of_model(PublishedContent)
        self.manager.refresh_index()

        self.assertEqual(
            len(
                self.manager.setup_search(Search().query(
                    MatchAll())).execute()), 2)  # indexing ok

        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=content",
                                 follow=False)
        self.assertEqual(result.status_code, 200)

        response = result.context["object_list"].execute()

        self.assertEqual(response.hits.total, 2)

        chapters = [r for r in response if r.meta.doc_type == "chapter"]
        self.assertEqual(chapters[0].meta.doc_type,
                         FakeChapter.get_es_document_type())
        self.assertEqual(chapters[0].meta.id,
                         published.content_public_slug + "__" + chapter1.slug)

        # 2. Change tuto: delete chapter and insert new one !
        tuto = PublishableContent.objects.get(pk=tuto.pk)
        tuto_draft = tuto.load_version()

        tuto_draft.children[0].repo_delete()  # chapter 1 is gone !

        another_text = "another thing"
        self.assertTrue(
            text not in another_text
        )  # to prevent a future modification from breaking this test

        chapter2 = ContainerFactory(parent=tuto_draft, db_object=tuto)
        chapter2.repo_update(another_text, another_text, another_text)
        extract2 = ExtractFactory(container=chapter2, db_object=tuto)
        extract2.repo_update(another_text, another_text)

        published = publish_content(tuto, tuto_draft, is_major_update=False)

        tuto.sha_public = tuto_draft.current_version
        tuto.sha_draft = tuto_draft.current_version
        tuto.public_version = published
        tuto.save()

        self.manager.es_bulk_indexing_of_model(PublishedContent)
        self.manager.refresh_index()

        self.assertEqual(
            len(
                self.manager.setup_search(Search().query(
                    MatchAll())).execute()), 2)  # 2 objects, not 3 !

        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&models=content",
                                 follow=False)
        self.assertEqual(result.status_code, 200)

        response = result.context["object_list"].execute()

        contents = [r for r in response if r.meta.doc_type != "chapter"]
        self.assertEqual(response.hits.total,
                         len(contents))  # no chapter found anymore

        result = self.client.get(reverse("search:query") + "?q=" +
                                 another_text + "&models=content",
                                 follow=False)

        self.assertEqual(result.status_code, 200)

        response = result.context["object_list"].execute()
        chapters = [r for r in response if r.meta.doc_type == "chapter"]
        self.assertEqual(response.hits.total, 1)
        self.assertEqual(chapters[0].meta.doc_type,
                         FakeChapter.get_es_document_type())
        self.assertEqual(chapters[0].meta.id, published.content_public_slug +
                         "__" + chapter2.slug)  # got new chapter

    def test_opensearch(self):

        result = self.client.get(reverse("search:opensearch"), follow=False)

        self.assertEqual(result.status_code, 200)

        self.assertContains(result, reverse("search:query"))
        self.assertContains(result, reverse("search:opensearch"))

    def test_upercase_and_lowercase_search_give_same_results(self):
        """Pretty self-explanatory function name, isn't it ?"""

        if not self.manager.connected_to_es:
            return

        # 1. Index lowercase stuffs
        text_lc = "test"

        topic_1_lc = TopicFactory(forum=self.forum,
                                  author=self.user,
                                  title=text_lc)

        tag_lc = TagFactory(title=text_lc)
        topic_1_lc.tags.add(tag_lc)
        topic_1_lc.subtitle = text_lc
        topic_1_lc.save()

        post_1_lc = PostFactory(topic=topic_1_lc, author=self.user, position=1)
        post_1_lc.text = post_1_lc.text_html = text_lc
        post_1_lc.save()

        tuto_lc = PublishableContentFactory(type="TUTORIAL")
        tuto_draft_lc = tuto_lc.load_version()

        tuto_lc.title = text_lc
        tuto_lc.authors.add(self.user)
        subcategory_lc = SubCategoryFactory(title=text_lc)
        tuto_lc.subcategory.add(subcategory_lc)
        tuto_lc.tags.add(tag_lc)
        tuto_lc.save()

        tuto_draft_lc.description = text_lc
        tuto_draft_lc.repo_update_top_container(text_lc, tuto_lc.slug, text_lc,
                                                text_lc)

        chapter1_lc = ContainerFactory(parent=tuto_draft_lc, db_object=tuto_lc)
        extract_lc = ExtractFactory(container=chapter1_lc, db_object=tuto_lc)
        extract_lc.repo_update(text_lc, text_lc)

        published_lc = publish_content(tuto_lc,
                                       tuto_draft_lc,
                                       is_major_update=True)

        tuto_lc.sha_public = tuto_draft_lc.current_version
        tuto_lc.sha_draft = tuto_draft_lc.current_version
        tuto_lc.public_version = published_lc
        tuto_lc.save()

        # 2. Index uppercase stuffs
        text_uc = "TEST"

        topic_1_uc = TopicFactory(forum=self.forum,
                                  author=self.user,
                                  title=text_uc)

        topic_1_uc.tags.add(
            tag_lc)  # Note: a constraint forces tags title to be unique
        topic_1_uc.subtitle = text_uc
        topic_1_uc.save()

        post_1_uc = PostFactory(topic=topic_1_uc, author=self.user, position=1)
        post_1_uc.text = post_1_uc.text_html = text_uc
        post_1_uc.save()

        tuto_uc = PublishableContentFactory(type="TUTORIAL")
        tuto_draft_uc = tuto_uc.load_version()

        tuto_uc.title = text_uc
        tuto_uc.authors.add(self.user)
        tuto_uc.subcategory.add(subcategory_lc)
        tuto_uc.tags.add(tag_lc)
        tuto_uc.save()

        tuto_draft_uc.description = text_uc
        tuto_draft_uc.repo_update_top_container(text_uc, tuto_uc.slug, text_uc,
                                                text_uc)

        chapter1_uc = ContainerFactory(parent=tuto_draft_uc, db_object=tuto_uc)
        extract_uc = ExtractFactory(container=chapter1_uc, db_object=tuto_uc)
        extract_uc.repo_update(text_uc, text_uc)

        published_uc = publish_content(tuto_uc,
                                       tuto_draft_uc,
                                       is_major_update=True)

        tuto_uc.sha_public = tuto_draft_uc.current_version
        tuto_uc.sha_draft = tuto_draft_uc.current_version
        tuto_uc.public_version = published_uc
        tuto_uc.save()

        # 3. Index and search:
        self.assertEqual(
            len(
                self.manager.setup_search(Search().query(
                    MatchAll())).execute()), 0)

        # index
        for model in self.indexable:
            if model is FakeChapter:
                continue
            self.manager.es_bulk_indexing_of_model(model)
        self.manager.refresh_index()

        result = self.client.get(reverse("search:query") + "?q=" + text_lc,
                                 follow=False)
        self.assertEqual(result.status_code, 200)

        response_lc = result.context["object_list"].execute()
        self.assertEqual(response_lc.hits.total, 8)

        result = self.client.get(reverse("search:query") + "?q=" + text_uc,
                                 follow=False)
        self.assertEqual(result.status_code, 200)

        response_uc = result.context["object_list"].execute()
        self.assertEqual(response_uc.hits.total, 8)

        for responses in zip(
                response_lc,
                response_uc):  # we should get results in the same order!
            self.assertEqual(responses[0].meta.id, responses[1].meta.id)

    def test_category_and_subcategory_impact_search(self):
        """If two contents do not belong to the same (sub)category"""

        if not self.manager.connected_to_es:
            return

        text = "Did you ever hear the tragedy of Darth Plagueis The Wise?"

        # 1. Create two contents with different subcategories
        category_1 = "category 1"
        subcategory_1 = SubCategoryFactory(title=category_1)
        category_2 = "category 2"
        subcategory_2 = SubCategoryFactory(title=category_2)

        tuto_1 = PublishableContentFactory(type="TUTORIAL")
        tuto_1_draft = tuto_1.load_version()

        tuto_1.title = text
        tuto_1.authors.add(self.user)
        tuto_1.subcategory.add(subcategory_1)
        tuto_1.save()

        tuto_1_draft.description = text
        tuto_1_draft.repo_update_top_container(text, tuto_1.slug, text, text)

        chapter_1 = ContainerFactory(parent=tuto_1_draft, db_object=tuto_1)
        extract_1 = ExtractFactory(container=chapter_1, db_object=tuto_1)
        extract_1.repo_update(text, text)

        published_1 = publish_content(tuto_1,
                                      tuto_1_draft,
                                      is_major_update=True)

        tuto_1.sha_public = tuto_1_draft.current_version
        tuto_1.sha_draft = tuto_1_draft.current_version
        tuto_1.public_version = published_1
        tuto_1.save()

        tuto_2 = PublishableContentFactory(type="TUTORIAL")
        tuto_2_draft = tuto_2.load_version()

        tuto_2.title = text
        tuto_2.authors.add(self.user)
        tuto_2.subcategory.add(subcategory_2)
        tuto_2.save()

        tuto_2_draft.description = text
        tuto_2_draft.repo_update_top_container(text, tuto_2.slug, text, text)

        chapter_2 = ContainerFactory(parent=tuto_2_draft, db_object=tuto_2)
        extract_2 = ExtractFactory(container=chapter_2, db_object=tuto_2)
        extract_2.repo_update(text, text)

        published_2 = publish_content(tuto_2,
                                      tuto_2_draft,
                                      is_major_update=True)

        tuto_2.sha_public = tuto_2_draft.current_version
        tuto_2.sha_draft = tuto_2_draft.current_version
        tuto_2.public_version = published_2
        tuto_2.save()

        # 2. Index:
        self.assertEqual(
            len(
                self.manager.setup_search(Search().query(
                    MatchAll())).execute()), 0)

        # index
        for model in self.indexable:
            if model is FakeChapter:
                continue
            self.manager.es_bulk_indexing_of_model(model)
        self.manager.refresh_index()

        result = self.client.get(reverse("search:query") + "?q=" + text,
                                 follow=False)
        self.assertEqual(result.status_code, 200)

        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 4)  # Ok

        # 3. Test
        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&model=content&subcategory=" +
                                 subcategory_1.slug,
                                 follow=False)

        self.assertEqual(result.status_code, 200)

        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 2)

        self.assertEqual([
            int(r.meta.id) for r in response
            if r.meta.doc_type == "publishedcontent"
        ][0], published_1.pk)
        self.assertEqual([
            r.meta.id for r in response if r.meta.doc_type == "chapter"
        ][0], tuto_1.slug + "__" + chapter_1.slug)

        result = self.client.get(reverse("search:query") + "?q=" + text +
                                 "&model=content&subcategory=" +
                                 subcategory_2.slug,
                                 follow=False)

        self.assertEqual(result.status_code, 200)

        response = result.context["object_list"].execute()
        self.assertEqual(response.hits.total, 2)

        self.assertEqual([
            int(r.meta.id) for r in response
            if r.meta.doc_type == "publishedcontent"
        ][0], published_2.pk)
        self.assertEqual([
            r.meta.id for r in response if r.meta.doc_type == "chapter"
        ][0], tuto_2.slug + "__" + chapter_2.slug)

    def tearDown(self):
        super().tearDown()

        # delete index:
        self.manager.clear_es_index()
Exemple #3
0
class ForumNotification(TestCase):
    def setUp(self):
        self.user1 = ProfileFactory().user
        self.user2 = ProfileFactory().user
        self.to_be_changed_staff = StaffProfileFactory().user
        self.staff = StaffProfileFactory().user
        self.assertTrue(self.staff.has_perm("forum.change_topic"))
        self.category1 = ForumCategoryFactory(position=1)
        self.forum11 = ForumFactory(category=self.category1,
                                    position_in_category=1)
        self.forum12 = ForumFactory(category=self.category1,
                                    position_in_category=2)
        for group in self.staff.groups.all():
            self.forum12.groups.add(group)
        self.forum12.save()

    def test_ping_unknown(self):
        self.client.force_login(self.user2)
        result = self.client.post(
            reverse("topic-new") + f"?forum={self.forum11.pk}",
            {
                "title": "Super sujet",
                "subtitle": "Pour tester les notifs",
                "text":
                "@anUnExistingUser is pinged, also a special chars user @{}",
                "tags": "",
            },
            follow=False,
        )
        self.assertEqual(result.status_code, 302, "Request must succeed")
        self.assertEqual(
            0, PingSubscription.objects.count(),
            "As one user is pinged, only one subscription is created.")

    def test_no_auto_ping(self):
        self.client.force_login(self.user2)
        result = self.client.post(
            reverse("topic-new") + f"?forum={self.forum11.pk}",
            {
                "title": "Super sujet",
                "subtitle": "Pour tester les notifs",
                "text":
                f"@{self.user1.username} is pinged, not @{self.user2.username}",
                "tags": "",
            },
            follow=False,
        )
        self.assertEqual(result.status_code, 302)
        self.assertEqual(
            1, PingSubscription.objects.count(),
            "As one user is pinged, only one subscription is created.")

    def test_edit_with_more_than_max_ping(self):
        overridden_zds_app["comment"]["max_pings"] = 2
        pinged_users = [
            ProfileFactory(),
            ProfileFactory(),
            ProfileFactory(),
            ProfileFactory()
        ]
        self.client.force_login(self.user2)
        self.client.post(
            reverse("topic-new") + f"?forum={self.forum11.pk}",
            {
                "title":
                "Super sujet",
                "subtitle":
                "Pour tester les notifs",
                "text":
                "@{} @{} are pinged, not @{} @{}".format(
                    *[a.user.username for a in pinged_users]),
                "tags":
                "",
            },
            follow=False,
        )
        topic = Topic.objects.last()
        post = topic.last_message
        self.assertEqual(2, PingSubscription.objects.count())
        self.assertTrue(
            PingSubscription.objects.get_existing(pinged_users[0].user, post,
                                                  True))
        self.assertTrue(
            PingSubscription.objects.get_existing(pinged_users[1].user, post,
                                                  True))
        self.assertFalse(
            PingSubscription.objects.get_existing(pinged_users[2].user, post,
                                                  True))
        self.assertFalse(
            PingSubscription.objects.get_existing(pinged_users[3].user, post,
                                                  True))
        self.client.post(
            reverse("topic-edit") + f"?topic={topic.pk}",
            {
                "title": "Super sujet",
                "subtitle": "Pour tester les notifs",
                "text":
                f"@{pinged_users[1].user.username} @{pinged_users[3].user.username} are pinged",
                "tags": "",
            },
            follow=False,
        )
        self.assertTrue(
            PingSubscription.objects.get_existing(pinged_users[3].user, post,
                                                  True))
        self.assertTrue(
            PingSubscription.objects.get_existing(pinged_users[1].user, post,
                                                  True))
        self.assertFalse(
            PingSubscription.objects.get_existing(pinged_users[0].user, post,
                                                  True))

    def test_no_reping_on_edition(self):
        """
        to be more accurate : on edition, only ping **new** members
        """
        self.client.force_login(self.user2)
        result = self.client.post(
            reverse("topic-new") + f"?forum={self.forum11.pk}",
            {
                "title": "Super sujet",
                "subtitle": "Pour tester les notifs",
                "text": f"@{self.user1.username} is pinged",
                "tags": "",
            },
            follow=False,
        )
        self.assertEqual(result.status_code, 302)
        self.assertEqual(
            1, PingSubscription.objects.count(),
            "As one user is pinged, only one subscription is created.")
        self.assertEqual(1, Notification.objects.count())
        user3 = ProfileFactory().user
        post = Topic.objects.last().last_message
        result = self.client.post(
            reverse("post-edit") + f"?message={post.pk}",
            {
                "title": "Super sujet",
                "subtitle": "Pour tester les notifs",
                "text":
                f"@{self.user1.username} is pinged even twice @{self.user1.username}",
                "tags": "",
            },
            follow=False,
        )
        self.assertEqual(result.status_code, 302)
        self.assertEqual(1, PingSubscription.objects.count(),
                         "No added subscription.")
        self.assertEqual(1, Notification.objects.count())
        result = self.client.post(
            reverse("post-edit") + f"?message={post.pk}",
            {
                "title":
                "Super sujet",
                "subtitle":
                "Pour tester les notifs",
                "text":
                "@{} is pinged even twice @{} and add @{}".format(
                    self.user1.username, self.user1.username, user3.username),
                "tags":
                "",
            },
            follow=False,
        )
        self.assertEqual(result.status_code, 302)
        self.assertEqual(2, PingSubscription.objects.count())
        self.assertEqual(2, Notification.objects.count())

    def test_loose_ping_on_edition(self):
        """
        to be more accurate : on edition, only ping **new** members
        """
        self.client.force_login(self.user2)
        result = self.client.post(
            reverse("topic-new") + f"?forum={self.forum11.pk}",
            {
                "title": "Super sujet",
                "subtitle": "Pour tester les notifs",
                "text": f"@{self.user1.username} is pinged",
                "tags": "",
            },
            follow=False,
        )
        self.assertEqual(result.status_code, 302)
        self.assertEqual(
            1, PingSubscription.objects.count(),
            "As one user is pinged, only one subscription is created.")
        self.assertEqual(1, Notification.objects.count())
        post = Topic.objects.last().last_message
        result = self.client.post(
            reverse("post-edit") + f"?message={post.pk}",
            {
                "title": "Super sujet",
                "subtitle": "Pour tester les notifs",
                "text": f"@ {self.user1.username} is no more pinged ",
                "tags": "",
            },
            follow=False,
        )
        self.assertEqual(result.status_code, 302)
        self.assertEqual(1, PingSubscription.objects.count(),
                         "No added subscription.")
        self.assertFalse(PingSubscription.objects.first().is_active)
        self.assertEqual(1, Notification.objects.count())
        self.assertTrue(Notification.objects.first().is_read)

    def test_no_dead_notif_on_moving(self):
        NewTopicSubscription.objects.get_or_create_active(
            self.user1, self.forum11)
        self.client.force_login(self.user2)
        result = self.client.post(
            reverse("topic-new") + f"?forum={self.forum11.pk}",
            {
                "title": "Super sujet",
                "subtitle": "Pour tester les notifs",
                "text": "En tout cas l'un abonnement",
                "tags": "",
            },
            follow=False,
        )
        self.assertEqual(result.status_code, 302)

        topic = Topic.objects.filter(title="Super sujet").first()
        subscription = NewTopicSubscription.objects.get_existing(
            self.user1, self.forum11, True)
        self.assertIsNotNone(subscription,
                             "There must be an active subscription for now")
        self.assertIsNotNone(subscription.last_notification,
                             "There must be a notification for now")
        self.assertFalse(subscription.last_notification.is_read)
        self.client.logout()
        self.client.force_login(self.staff)
        data = {"move": "", "forum": self.forum12.pk, "topic": topic.pk}
        response = self.client.post(reverse("topic-edit"), data, follow=False)
        self.assertEqual(302, response.status_code)
        subscription = NewTopicSubscription.objects.get_existing(
            self.user1, self.forum11, True)
        self.assertIsNotNone(subscription,
                             "There must still be an active subscription")
        self.assertIsNotNone(
            subscription.last_notification,
            "There must still be a notification as object is not removed.")
        self.assertEqual(
            subscription.last_notification,
            Notification.objects.filter(sender=self.user2).first())
        self.assertTrue(subscription.last_notification.is_read,
                        "As forum is not reachable, notification is read")

    def test_no_ping_on_private_forum(self):
        self.client.force_login(self.staff)
        result = self.client.post(
            reverse("topic-new") + f"?forum={self.forum12.pk}",
            {
                "title": "Super sujet",
                "subtitle": "Pour tester les notifs",
                "text": f"ping @{self.user1.username}",
                "tags": "",
            },
            follow=False,
        )
        self.assertEqual(result.status_code, 302)

        topic = Topic.objects.filter(title="Super sujet").first()
        subscription = PingSubscription.objects.get_existing(
            self.user1, topic.last_message, True)
        self.assertIsNone(subscription,
                          "There must be no active subscription for now")

    def test_no_dead_ping_notif_on_moving_to_private_forum(self):
        self.client.force_login(self.user2)
        result = self.client.post(
            reverse("topic-new") + f"?forum={self.forum11.pk}",
            {
                "title": "Super sujet",
                "subtitle": "Pour tester les notifs",
                "text": f"ping @{self.user1.username}",
                "tags": "",
            },
            follow=False,
        )
        self.assertEqual(result.status_code, 302)

        topic = Topic.objects.filter(title="Super sujet").first()
        subscription = PingSubscription.objects.get_existing(
            self.user1, topic.last_message, True)
        self.assertIsNotNone(subscription,
                             "There must be an active subscription for now")
        self.assertIsNotNone(subscription.last_notification,
                             "There must be a notification for now")
        self.assertFalse(subscription.last_notification.is_read)
        self.client.logout()
        self.client.force_login(self.staff)
        data = {"move": "", "forum": self.forum12.pk, "topic": topic.pk}
        response = self.client.post(reverse("topic-edit"), data, follow=False)
        self.assertEqual(302, response.status_code)
        subscription = PingSubscription.objects.get_existing(
            self.user1, topic.last_message, True)
        self.assertIsNotNone(subscription,
                             "There must still be an active subscription")
        self.assertIsNotNone(
            subscription.last_notification,
            "There must still be a notification as object is not removed.")
        self.assertEqual(
            subscription.last_notification,
            Notification.objects.filter(sender=self.user2).first())
        self.assertTrue(subscription.last_notification.is_read,
                        "As forum is not reachable, notification is read")

    def test_no_more_new_topic_notif_on_losing_all_groups(self):
        NewTopicSubscription.objects.get_or_create_active(
            self.to_be_changed_staff, self.forum12)
        self.client.force_login(self.staff)
        self.client.post(
            reverse("topic-new") + f"?forum={self.forum12.pk}",
            {
                "title": "Super sujet",
                "subtitle": "Pour tester les notifs",
                "text": "En tout cas l'un abonnement",
                "tags": "",
            },
            follow=False,
        )
        subscription = NewTopicSubscription.objects.get_existing(
            self.to_be_changed_staff, self.forum12, True)
        self.assertIsNotNone(subscription,
                             "There must be an active subscription for now")
        self.assertIsNotNone(subscription.last_notification,
                             "There must be a notification.")
        self.assertFalse(subscription.last_notification.is_read,
                         "The notification has not been read yet")

        self.to_be_changed_staff.groups.clear()
        self.to_be_changed_staff.save()

        subscription = NewTopicSubscription.objects.get_existing(
            self.to_be_changed_staff, self.forum12, False)
        self.assertIsNotNone(subscription,
                             "The subscription should now be inactive")
        self.assertFalse(subscription.is_active)
        self.assertTrue(subscription.last_notification.is_read,
                        "As forum is not reachable, notification is read")

    def test_no_more_new_topic_notif_on_losing_one_group(self):
        NewTopicSubscription.objects.get_or_create_active(
            self.to_be_changed_staff, self.forum12)
        self.client.force_login(self.staff)
        self.client.post(
            reverse("topic-new") + f"?forum={self.forum12.pk}",
            {
                "title": "Super sujet",
                "subtitle": "Pour tester les notifs",
                "text": "En tout cas l'un abonnement",
                "tags": "",
            },
            follow=False,
        )
        subscription = NewTopicSubscription.objects.get_existing(
            self.to_be_changed_staff, self.forum12, True)
        self.assertIsNotNone(subscription,
                             "There must be an active subscription for now")
        self.assertIsNotNone(subscription.last_notification,
                             "There must be a notification.")
        self.assertFalse(subscription.last_notification.is_read,
                         "The notification has not been read yet")

        self.to_be_changed_staff.groups.remove(
            list(self.to_be_changed_staff.groups.all())[0])
        self.to_be_changed_staff.save()

        subscription = NewTopicSubscription.objects.get_existing(
            self.to_be_changed_staff, self.forum12, False)
        self.assertIsNotNone(subscription,
                             "There must be an inactive subscription now")
        self.assertFalse(subscription.is_active)
        self.assertTrue(subscription.last_notification.is_read,
                        "As forum is not reachable, notification is read")

    def test_no_more_topic_answer_notif_on_losing_all_groups(self):
        self.client.force_login(self.to_be_changed_staff)
        self.client.post(
            reverse("topic-new") + f"?forum={self.forum12.pk}",
            {
                "title": "Super sujet",
                "subtitle": "Pour tester les notifs",
                "text": "En tout cas l'un abonnement",
                "tags": "",
            },
            follow=False,
        )
        topic = Topic.objects.filter(title="Super sujet").first()

        self.client.force_login(self.staff)
        self.client.post(
            reverse("post-new") + f"?sujet={topic.pk}",
            {
                "last_post":
                topic.last_message.pk,
                "text":
                "C'est tout simplement l'histoire de la ville de Paris que je voudrais vous conter ",
            },
            follow=False,
        )

        subscription = TopicAnswerSubscription.objects.get_existing(
            content_object=topic,
            user=self.to_be_changed_staff,
            is_active=True)
        self.assertIsNotNone(subscription,
                             "There must be an active subscription for now")
        self.assertIsNotNone(subscription.last_notification,
                             "There must be a notification.")
        self.assertFalse(subscription.last_notification.is_read,
                         "The notification has not been read yet")

        self.to_be_changed_staff.groups.clear()
        self.to_be_changed_staff.save()

        subscription = TopicAnswerSubscription.objects.get_existing(
            content_object=topic,
            user=self.to_be_changed_staff,
            is_active=False)
        self.assertIsNotNone(subscription,
                             "The subscription must now be inactive")
        self.assertFalse(subscription.is_active)
        self.assertTrue(subscription.last_notification.is_read,
                        "As forum is not reachable, notification is read")

    def test_no_more_topic_answer_notif_on_losing_one_group(self):
        self.client.force_login(self.to_be_changed_staff)
        self.client.post(
            reverse("topic-new") + f"?forum={self.forum12.pk}",
            {
                "title": "Super sujet",
                "subtitle": "Pour tester les notifs",
                "text": "En tout cas l'un abonnement",
                "tags": "",
            },
            follow=False,
        )
        topic = Topic.objects.filter(title="Super sujet").first()

        self.client.force_login(self.staff)
        self.client.post(
            reverse("post-new") + f"?sujet={topic.pk}",
            {
                "last_post":
                topic.last_message.pk,
                "text":
                "C'est tout simplement l'histoire de la ville de Paris que je voudrais vous conter ",
            },
            follow=False,
        )

        subscription = TopicAnswerSubscription.objects.get_existing(
            content_object=topic,
            user=self.to_be_changed_staff,
            is_active=True)
        self.assertIsNotNone(subscription,
                             "There must be an active subscription for now")
        self.assertIsNotNone(subscription.last_notification,
                             "There must be a notification.")
        self.assertFalse(subscription.last_notification.is_read,
                         "The notification has not been read yet")

        self.to_be_changed_staff.groups.remove(
            list(self.to_be_changed_staff.groups.all())[0])
        self.to_be_changed_staff.save()

        subscription = TopicAnswerSubscription.objects.get_existing(
            content_object=topic,
            user=self.to_be_changed_staff,
            is_active=False)
        self.assertIsNotNone(subscription,
                             "The subscription must now be inactive")
        self.assertFalse(subscription.is_active)
        self.assertTrue(subscription.last_notification.is_read,
                        "As forum is not reachable, notification is read")