Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 0 additions & 52 deletions lms/djangoapps/discussion/django_comment_client/base/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,22 +210,6 @@ def test_flag(self, mock_is_forum_v2_enabled, mock_request):
response = self.call_view("un_flag_abuse_for_thread", mock_is_forum_v2_enabled, mock_request)
self._assert_json_response_contains_group_info(response)

def test_pin(self, mock_is_forum_v2_enabled, mock_request):
response = self.call_view(
"pin_thread",
mock_is_forum_v2_enabled,
mock_request,
user=self.moderator
)
self._assert_json_response_contains_group_info(response)
response = self.call_view(
"un_pin_thread",
mock_is_forum_v2_enabled,
mock_request,
user=self.moderator
)
self._assert_json_response_contains_group_info(response)

def test_openclose(self, mock_is_forum_v2_enabled, mock_request):
response = self.call_view(
"openclose_thread",
Expand Down Expand Up @@ -1191,42 +1175,6 @@ def setUp(self):
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)

def test_pin_thread_as_student(self, mock_is_forum_v2_enabled, mock_request):
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {})
self.client.login(username=self.student.username, password=self.password)
response = self.client.post(
reverse("pin_thread", kwargs={"course_id": str(self.course.id), "thread_id": "dummy"})
)
assert response.status_code == 401

def test_pin_thread_as_moderator(self, mock_is_forum_v2_enabled, mock_request):
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {})
self.client.login(username=self.moderator.username, password=self.password)
response = self.client.post(
reverse("pin_thread", kwargs={"course_id": str(self.course.id), "thread_id": "dummy"})
)
assert response.status_code == 200

def test_un_pin_thread_as_student(self, mock_is_forum_v2_enabled, mock_request):
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {})
self.client.login(username=self.student.username, password=self.password)
response = self.client.post(
reverse("un_pin_thread", kwargs={"course_id": str(self.course.id), "thread_id": "dummy"})
)
assert response.status_code == 401

def test_un_pin_thread_as_moderator(self, mock_is_forum_v2_enabled, mock_request):
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {})
self.client.login(username=self.moderator.username, password=self.password)
response = self.client.post(
reverse("un_pin_thread", kwargs={"course_id": str(self.course.id), "thread_id": "dummy"})
)
assert response.status_code == 200

def _set_mock_request_thread_and_comment(self, mock_is_forum_v2_enabled, mock_request, thread_data, comment_data):
def handle_request(*args, **kwargs):
url = args[1]
Expand Down
244 changes: 244 additions & 0 deletions lms/djangoapps/discussion/django_comment_client/base/tests_v2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
# pylint: skip-file
"""Tests for django comment client views."""

import json
import logging
from contextlib import contextmanager
from unittest import mock
from unittest.mock import ANY, Mock, patch

import ddt
from django.contrib.auth.models import User
from django.core.management import call_command
from django.test.client import RequestFactory
from django.urls import reverse
from eventtracking.processors.exceptions import EventEmissionExit
from opaque_keys.edx.keys import CourseKey
from openedx_events.learning.signals import (
FORUM_THREAD_CREATED,
FORUM_THREAD_RESPONSE_CREATED,
FORUM_RESPONSE_COMMENT_CREATED,
)

from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
from common.djangoapps.student.roles import CourseStaffRole, UserBasedRole
from common.djangoapps.student.tests.factories import (
CourseAccessRoleFactory,
CourseEnrollmentFactory,
UserFactory,
)
from common.djangoapps.track.middleware import TrackMiddleware
from common.djangoapps.track.views import segmentio
from common.djangoapps.track.views.tests.base import (
SEGMENTIO_TEST_USER_ID,
SegmentIOTrackingTestCaseBase,
)
from common.djangoapps.util.testing import UrlResetMixin
from common.test.utils import MockSignalHandlerMixin, disable_signal
from lms.djangoapps.discussion.django_comment_client.base import views
from lms.djangoapps.discussion.django_comment_client.tests.group_id import (
CohortedTopicGroupIdTestMixin,
GroupIdAssertionMixin,
NonCohortedTopicGroupIdTestMixin,
)
from lms.djangoapps.discussion.django_comment_client.tests.unicode import (
UnicodeTestMixin,
)
from lms.djangoapps.discussion.django_comment_client.tests.utils import (
CohortedTestCase,
ForumsEnableMixin,
)
from lms.djangoapps.teams.tests.factories import (
CourseTeamFactory,
CourseTeamMembershipFactory,
)
from openedx.core.djangoapps.course_groups.cohorts import set_course_cohorted
from openedx.core.djangoapps.course_groups.tests.helpers import CohortFactory
from openedx.core.djangoapps.django_comment_common.comment_client import Thread
from openedx.core.djangoapps.django_comment_common.models import (
FORUM_ROLE_STUDENT,
CourseDiscussionSettings,
Role,
assign_role,
)
from openedx.core.djangoapps.django_comment_common.utils import (
ThreadContext,
seed_permissions_roles,
)
from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES
from openedx.core.lib.teams_config import TeamsConfig
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import (
TEST_DATA_SPLIT_MODULESTORE,
ModuleStoreTestCase,
SharedModuleStoreTestCase,
)
from xmodule.modulestore.tests.factories import (
CourseFactory,
BlockFactory,
check_mongo_calls,
)

from .event_transformers import ForumThreadViewedEventTransformer
from lms.djangoapps.discussion.django_comment_client.tests.mixins import (
MockForumApiMixin,
)


@disable_signal(views, "thread_edited")
@disable_signal(views, "thread_voted")
@disable_signal(views, "thread_deleted")
class ThreadActionGroupIdTestCase(
CohortedTestCase, GroupIdAssertionMixin, MockForumApiMixin
):
"""Test case for thread actions with group ID assertions."""

@classmethod
def setUpClass(cls):
"""Set up class and forum mock."""
super().setUpClass()
super().setUpClassAndForumMock()

@classmethod
def tearDownClass(cls):
"""Stop patches after tests complete."""
super().tearDownClass()
super().disposeForumMocks()

def call_view(
self, view_name, mock_function, user=None, post_params=None, view_args=None
):
"""Call a view with the given parameters."""
thread_response = {
"user_id": str(self.student.id),
"group_id": self.student_cohort.id,
"closed": False,
"type": "thread",
"commentable_id": "non_team_dummy_id",
"body": "test body",
}

self.set_mock_return_value("get_course_id_by_thread", self.course.id)
self.set_mock_return_value("get_thread", thread_response)
self.set_mock_return_value(mock_function, thread_response)

request = RequestFactory().post("dummy_url", post_params or {})
request.user = user or self.student
request.view_name = view_name

return getattr(views, view_name)(
request,
course_id=str(self.course.id),
thread_id="dummy",
**(view_args or {})
)

def test_pin_thread(self):
"""Test pinning a thread."""
response = self.call_view("pin_thread", "pin_thread", user=self.moderator)
assert response.status_code == 200
self._assert_json_response_contains_group_info(response)

response = self.call_view("un_pin_thread", "unpin_thread", user=self.moderator)
assert response.status_code == 200
self._assert_json_response_contains_group_info(response)


@disable_signal(views, "comment_endorsed")
class ViewPermissionsTestCase(
ForumsEnableMixin,
UrlResetMixin,
SharedModuleStoreTestCase,
MockForumApiMixin,
):
"""Test case for view permissions."""

@classmethod
def setUpClass(cls): # pylint: disable=super-method-not-called
"""Set up class and forum mock."""
super().setUpClassAndForumMock()

with super().setUpClassAndTestData():
cls.course = CourseFactory.create()

@classmethod
def tearDownClass(cls):
"""Stop patches after tests complete."""
super().tearDownClass()
super().disposeForumMocks()

@classmethod
def setUpTestData(cls):
"""Set up test data."""
super().setUpTestData()

seed_permissions_roles(cls.course.id)

cls.password = "test password"
cls.student = UserFactory.create(password=cls.password)
cls.moderator = UserFactory.create(password=cls.password)

CourseEnrollmentFactory(user=cls.student, course_id=cls.course.id)
CourseEnrollmentFactory(user=cls.moderator, course_id=cls.course.id)

cls.moderator.roles.add(
Role.objects.get(name="Moderator", course_id=cls.course.id)
)

@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
"""Set up the test case."""
super().setUp()

# Set return values dynamically using the mixin method
self.set_mock_return_value("get_course_id_by_comment", self.course.id)
self.set_mock_return_value("get_course_id_by_thread", self.course.id)
self.set_mock_return_value("get_thread", {})
self.set_mock_return_value("pin_thread", {})
self.set_mock_return_value("unpin_thread", {})

def test_pin_thread_as_student(self):
"""Test pinning a thread as a student."""
self.client.login(username=self.student.username, password=self.password)
response = self.client.post(
reverse(
"pin_thread",
kwargs={"course_id": str(self.course.id), "thread_id": "dummy"},
)
)
assert response.status_code == 401

def test_pin_thread_as_moderator(self):
"""Test pinning a thread as a moderator."""
self.client.login(username=self.moderator.username, password=self.password)
response = self.client.post(
reverse(
"pin_thread",
kwargs={"course_id": str(self.course.id), "thread_id": "dummy"},
)
)
assert response.status_code == 200

def test_un_pin_thread_as_student(self):
"""Test unpinning a thread as a student."""
self.client.login(username=self.student.username, password=self.password)
response = self.client.post(
reverse(
"un_pin_thread",
kwargs={"course_id": str(self.course.id), "thread_id": "dummy"},
)
)
assert response.status_code == 401

def test_un_pin_thread_as_moderator(self):
"""Test unpinning a thread as a moderator."""
self.client.login(username=self.moderator.username, password=self.password)
response = self.client.post(
reverse(
"un_pin_thread",
kwargs={"course_id": str(self.course.id), "thread_id": "dummy"},
)
)
assert response.status_code == 200
67 changes: 67 additions & 0 deletions lms/djangoapps/discussion/django_comment_client/tests/mixins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""
Mixin for django_comment_client tests.
"""

from unittest import mock


class MockForumApiMixin:
"""Mixin to mock forum_api across different test cases with a single mock instance."""

@classmethod
def setUpClass(cls):
"""Apply a single forum_api mock at the class level."""
cls.setUpClassAndForumMock()

@classmethod
def setUpClassAndForumMock(cls):
"""
Set up the class and apply the forum_api mock.
"""
cls.mock_forum_api = mock.Mock()

# TODO: Remove this after moving all APIs
cls.flag_v2_patcher = mock.patch(
"openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled"
)
cls.mock_enable_forum_v2 = cls.flag_v2_patcher.start()
cls.mock_enable_forum_v2.return_value = True

patch_targets = [
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api",
"openedx.core.djangoapps.django_comment_common.comment_client.comment.forum_api",
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api",
"openedx.core.djangoapps.django_comment_common.comment_client.course.forum_api",
"openedx.core.djangoapps.django_comment_common.comment_client.subscriptions.forum_api",
"openedx.core.djangoapps.django_comment_common.comment_client.user.forum_api",
]
cls.forum_api_patchers = [
mock.patch(target, cls.mock_forum_api) for target in patch_targets
]
for patcher in cls.forum_api_patchers:
patcher.start()

@classmethod
def disposeForumMocks(cls):
"""Stop patches after tests complete."""
cls.flag_v2_patcher.stop()

for patcher in cls.forum_api_patchers:
patcher.stop()

@classmethod
def tearDownClass(cls):
"""Stop patches after tests complete."""
cls.disposeForumMocks()

def set_mock_return_value(self, function_name, return_value):
"""
Set a return value for a specific method in forum_api mock.

Args:
function_name (str): The method name in the mock to set a return value for.
return_value (Any): The return value for the method.
"""
setattr(
self.mock_forum_api, function_name, mock.Mock(return_value=return_value)
)
Loading
Loading