def setUp(self): super().setUp() # Setup # Create consumer, context and user self.lti_consumer = LTIConsumerFactory() self.context = LTIContextFactory(lti_consumer=self.lti_consumer) # Create forum and add context self.forum = ForumFactory(name="Forum") self.forum.lti_contexts.add(self.context)
def test_can_tell_if_the_user_is_instructor_of_this_forum(): """ Given two users. User1 is student of the forum, User2 is instructor. We control that is_user_instructor can detect if a user is an instructor. Then a forum can be part of multiple contexts. If a user is instructor in one context, he is considered intructor of this forum in all contexts. We add a new context to the forum where user is instructor and test that user is now considered as instructor """ # load template def get_rendered(topic, user): template = Template( "{% load custom_tags %}" + "{% if topic|is_user_instructor:user %}YES{% else %}NO{% endif %}" ) context = Context({"topic": topic, "user": user}) rendered = template.render(context) return rendered lti_consumer = LTIConsumerFactory() # Create two LTI Context context1 = LTIContextFactory(lti_consumer=lti_consumer) context2 = LTIContextFactory(lti_consumer=lti_consumer) # Create two users user1 = UserFactory(lti_consumer=lti_consumer) user2 = UserFactory(lti_consumer=lti_consumer) # Sync user1 groups in context1 with role "student" context1.sync_user_groups(user1, ["student"]) # Sync user1 groups in context2 with role "instructor" context2.sync_user_groups(user1, ["instructor"]) # Sync user2 groups in context1 with role "instructor" context1.sync_user_groups(user2, ["instructor"]) # Create forum and add context1 forum = ForumFactory(name="Initial forum name") forum.lti_contexts.add(context1) # Set up topic topic = TopicFactory(forum=forum, poster=user1, subject="topic création") # Chek that user1 is not instructor assert get_rendered(topic, user1) == "NO" # Chek that user2 is instructor assert get_rendered(topic, user2) == "YES" # Add forum to context2 where user1 has role "instructor" forum.lti_contexts.add(context2) # Check that user1 is now instructor as well assert get_rendered(topic, user1) == "YES"
def test_get_user_roles_other_context(self): """get_user_roles should return the list of the roles names without the specific patterns cg:{context.id}:role: only the label of the roles and only for the current context""" lti_consumer = LTIConsumerFactory() context = LTIContextFactory(lti_consumer=lti_consumer) context2 = LTIContextFactory(lti_consumer=lti_consumer) # Create user user = UserFactory(lti_consumer=lti_consumer) # Sync user groups in context with multiple roles context.sync_user_groups(user, ["role1", "role2"]) # Sync user groups in context2 with other roles) context2.sync_user_groups(user, ["newgroup"]) # check that the list of roles is returned self.assertCountEqual( ["role1", "role2"], context.get_user_roles(user), ) self.assertCountEqual( ["newgroup"], context2.get_user_roles(user), )
def test_get_user_roles(self): """get_user_roles should return the list of the roles names without the specific patterns cg:{context.id}:role: only the label of the roles """ lti_consumer = LTIConsumerFactory() context = LTIContextFactory(lti_consumer=lti_consumer) # Create two users user = UserFactory(lti_consumer=lti_consumer) user2 = UserFactory(lti_consumer=lti_consumer) # Sync user groups in context with multiple roles context.sync_user_groups(user, ["role1", "role2"]) # check that the list of roles is returned self.assertCountEqual( ["role1", "role2"], context.get_user_roles(user), ) # add an extra group instructor_group = Group.objects.create( name=f"{context.base_group_name}:role:instructor") user.groups.add(instructor_group) # check that the list of roles is returned self.assertCountEqual( ["role1", "role2", "instructor"], context.get_user_roles(user), ) # check that group that are not roles get ignored other_group = Group.objects.create( name=f"{context.base_group_name}:other)") user2.groups.add(other_group) self.assertCountEqual( [], context.get_user_roles(user2), )
def test_sync_user_groups(self): """Test group synchronization""" # Create a LTI Consumer lti_consumer = LTIConsumerFactory() # Create 2 LTI Contexts for lti_consumer context1 = LTIContextFactory(lti_consumer=lti_consumer) context2 = LTIContextFactory(lti_consumer=lti_consumer) # Create an unrelated django group unrelated_group = Group.objects.create(name="unrelated_django_group") # Initialize a user with no group user = UserFactory(lti_consumer=lti_consumer) self.assertEqual(0, user.groups.count()) # Sync user groups in context1 with role "student" context1.sync_user_groups(user, ["student"]) self.assertCountEqual( [ context1.base_group_name, f"{context1.base_group_name}:role:student" ], list(user.groups.values_list("name", flat=True)), ) # Add the user to an unrelated django group user.groups.add(unrelated_group) # Sync user groups in context2 with multiple roles context2.sync_user_groups(user, ["role1", "role2"]) self.assertCountEqual( [ unrelated_group.name, context1.base_group_name, f"{context1.base_group_name}:role:student", context2.base_group_name, f"{context2.base_group_name}:role:role1", f"{context2.base_group_name}:role:role2", ], list(user.groups.values_list("name", flat=True)), ) # Sync user groups in context 2 with another role context2.sync_user_groups(user, ["instructor"]) self.assertCountEqual( [ unrelated_group.name, context1.base_group_name, f"{context1.base_group_name}:role:student", context2.base_group_name, f"{context2.base_group_name}:role:instructor", ], list(user.groups.values_list("name", flat=True)), ) # Sync user groups in context 1 with no role context1.sync_user_groups(user, []) self.assertCountEqual( [ unrelated_group.name, context1.base_group_name, context2.base_group_name, f"{context2.base_group_name}:role:instructor", ], list(user.groups.values_list("name", flat=True)), ) # Create another LTIConsumer lti_consumer2 = LTIConsumerFactory() # Create a LTI Context for lti_consumer2 context3 = LTIContextFactory(lti_consumer=lti_consumer2) # Create a user for this LTI Context user2 = UserFactory(lti_consumer=lti_consumer2) # Check the PermissionDenied gets called as the user in not part of this LTIContext with self.assertRaises(PermissionDenied): context3.sync_user_groups(user, ["instructor"]) # Check the PermissionDenied gets called as the user in not part of this LTIContext with self.assertRaises(PermissionDenied): context2.sync_user_groups(user2, [])
def test_sync_user_with_internal_moderators_groups(self): """ Test that internal group moderator doesn't get removed using sync_user_groups """ lti_consumer = LTIConsumerFactory() context = LTIContextFactory(lti_consumer=lti_consumer) # Initialize a user with no group user = UserFactory(lti_consumer=lti_consumer) self.assertEqual(0, user.groups.count()) # Sync user groups in context with role "student" context.sync_user_groups(user, ["student"]) self.assertCountEqual( [ context.base_group_name, f"{context.base_group_name}:role:student" ], list(user.groups.values_list("name", flat=True)), ) # Add the user to moderator group group_moderator_name = context.get_group_role_name( _FORUM_ROLE_MODERATOR) group_moderator = Group.objects.create(name=group_moderator_name) user.groups.add(group_moderator) user.save() # confirm group has been added self.assertCountEqual( [ context.base_group_name, f"{context.base_group_name}:role:student", f"{context.base_group_name}:role:moderator", ], list(user.groups.values_list("name", flat=True)), ) # Sync user groups in context with funnyrole group context.sync_user_groups(user, ["funnyrole"]) self.assertCountEqual( ["funnyrole", "moderator"], context.get_user_roles(user), ) # User should still have the moderator group self.assertCountEqual( [ context.base_group_name, f"{context.base_group_name}:role:funnyrole", f"{context.base_group_name}:role:moderator", ], list(user.groups.values_list("name", flat=True)), ) # creates a new context context2 = LTIContextFactory(lti_consumer=lti_consumer) context2.sync_user_groups(user, ["instructor"]) # moderator group should not exist in this context self.assertCountEqual( [ context.base_group_name, f"{context.base_group_name}:role:funnyrole", f"{context.base_group_name}:role:moderator", context2.base_group_name, f"{context2.base_group_name}:role:instructor", ], list(user.groups.values_list("name", flat=True)), )
def test_render_block_is_admin_of_this_forum(self): """ check that the admin icon is present on the different views as expected """ lti_consumer = LTIConsumerFactory() # Create two users user1 = UserFactory(public_username="******", lti_consumer=lti_consumer) user2 = UserFactory(public_username="******", lti_consumer=lti_consumer) # Create an LTI Context context = LTIContextFactory(lti_consumer=lti_consumer) # Sync user1 groups in context with role "administrator" context.sync_user_groups(user1, ["administrator"]) # Sync user1 groups in context with role "student" context.sync_user_groups(user2, ["student"]) # Create forum and add context forum = ForumFactory(name="forum_test") forum.lti_contexts.add(context) # assign permission to the group for this forum self._init_forum(forum, context) # Set up topic topic1 = TopicFactory(forum=forum, poster=user1) # Set up post PostFactory.create( topic=topic1, poster=user1, ) # log user1 self.client.force_login(user1) # accessing forum view response = self.client.get(reverse("forum:index")) html = lxml.html.fromstring(response.content) forum_last_post = str( etree.tostring(html.cssselect(".forum-last-post")[0])) # control that the administrator's icon is present self.assertTrue(( '<i class="icon_writer fas fa-award" aria-hidden="true" title="Administrator">' '<span class="sr-only">Administrator</span>' "</i>") in forum_last_post) # control that it's the right user's profile link self.assertTrue( (f'<a href="/forum/member/profile/{user1.id}/">Valéry</a>' ) in forum_last_post) # accessing forum topic listing view response = self.client.get( reverse("forum:forum", kwargs={ "slug": forum.slug, "pk": forum.pk })) html = lxml.html.fromstring(response.content) # check that the topic creation writer contains the icon for administrator's role topic_created = str(etree.tostring( html.cssselect(".topic-created")[0])) # control that the administrator's icon is present self.assertTrue(( '<i class="icon_writer fas fa-award" aria-hidden="true" title="Administrator">' '<span class="sr-only">Administrator</span>' "</i>") in topic_created) # control that it's the right user's profile link self.assertTrue( (f'<a href="/forum/member/profile/{user1.id}/">Valéry</a>' ) in topic_created) # check that the last post creation writer contains the icon for instructor's role html = lxml.html.fromstring(response.content) topic_last_post = str( etree.tostring(html.cssselect(".topic-last-post")[0])) # control that the administrator's icon is present self.assertTrue(( '<i class="icon_writer fas fa-award" aria-hidden="true" title="Administrator">' '<span class="sr-only">Administrator</span>' "</i>") in topic_last_post) # control that it's the right user's profile link self.assertTrue( f'<a href="/forum/member/profile/{user1.id}/">Valéry</a>' in topic_last_post) # user2 add a post to the topic, last message is now from a student PostFactory.create( topic=topic1, poster=user2, ) # reload the topic list view response = self.client.get( reverse("forum:forum", kwargs={ "slug": forum.slug, "pk": forum.pk })) html = lxml.html.fromstring(response.content) topic_created = str(etree.tostring( html.cssselect(".topic-created")[0])) # control that the administrator's icon is present self.assertTrue(( '<i class="icon_writer fas fa-award" aria-hidden="true" title="Administrator">' '<span class="sr-only">Administrator</span>' "</i>") in topic_created) # control that it's the right user's profile link self.assertTrue( (f'<a href="/forum/member/profile/{user1.id}/">Valéry</a>' ) in topic_created) topic_last_post = str( etree.tostring(html.cssselect(".topic-last-post")[0])) # check that there's no more icon as the post has been created by a student self.assertFalse("Administrator" in topic_last_post) # control that it's the right user's profile link self.assertTrue( (f'<a href="/forum/member/profile/{user2.id}/">François</a>' ) in topic_last_post) # accessing forum view response = self.client.get(reverse("forum:index")) # check that there's no more icon in this view as a new post has been created by a student self.assertNotContains(response, "icon_writer fas fa-award") # access list of posts from the topic response = self.client.get( reverse( "forum_conversation:topic", kwargs={ "forum_slug": forum.slug, "forum_pk": forum.pk, "slug": topic1.slug, "pk": topic1.id, }, )) # access 'post an answer' it should list the other posts of the topic response = self.client.get( f"/forum/forum/{forum.slug}-{forum.pk}/topic/{topic1.slug}-{topic1.pk}/post/create/" ) html = lxml.html.fromstring(response.content) # check that for the posts in this topic we have the one from the instructor with the icon # check that for the message on first position there is no icon for the student html_first_post = str(etree.tostring(html.cssselect(".text-muted")[0])) self.assertFalse("Administrator" in html_first_post) self.assertTrue( (f'<a href="/forum/member/profile/{user2.id}/">François</a>' ) in html_first_post) # check that for the second message we have the instructor's icon html_second_post = str(etree.tostring( html.cssselect(".text-muted")[1])) self.assertTrue(( '<i class="icon_writer fas fa-award" aria-hidden="true" title="Administrator">' '<span class="sr-only">Administrator</span>' "</i>") in html_second_post) self.assertTrue( (f'<a href="/forum/member/profile/{user1.id}/">Valéry</a>' ) in html_second_post)