feat: Integrate Forum V2 into edx-platform

This commit introduces the new Forum V2 application, allowing users to choose between the legacy Forum V1 and the new Forum V2 at the course level.

Key Changes:
- Added waffle flag `discussions.enable_forum_v2` to enable Forum V2 for selected courses, allowing coexistence with Forum V1.
- Default data storage for Forum V2 is set to MongoDB, with an option to switch to MySQL using the waffle flag `forum_v2.enable_mysql_backend`.
- Introduced management command `forum_migrate_course_from_mongodb_to_mysql` for per-course data migration from MongoDB to MySQL.

Note: This PR does not include all unit tests for the Forum V2 native API due to ongoing migration efforts. Further updates will follow to ensure full test coverage before final release.

Co-authored-by: [Muhammad Faraz Maqsood] <faraz.maqsood@arbisoft.com>
Co-authored-by: [Ali Salman] <ali.salman@arbisoft.com>
This commit is contained in:
Muhammad Faraz Maqsood
2024-11-19 21:00:39 +05:00
committed by David Ormsbee
parent 0baf71ce01
commit 70b60ff256
26 changed files with 1834 additions and 426 deletions

View File

@@ -82,6 +82,7 @@ class MockRequestSetupMixin:
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
class CreateThreadGroupIdTestCase(
MockRequestSetupMixin,
CohortedTestCase,
@@ -90,7 +91,21 @@ class CreateThreadGroupIdTestCase(
):
cs_endpoint = "/threads"
def call_view(self, mock_request, commentable_id, user, group_id, pass_group_id=True):
def setUp(self):
super().setUp()
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
def call_view(self, mock_is_forum_v2_enabled, mock_request, commentable_id, user, group_id, pass_group_id=True):
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {})
request_data = {"body": "body", "title": "title", "thread_type": "discussion"}
if pass_group_id:
@@ -105,8 +120,9 @@ class CreateThreadGroupIdTestCase(
commentable_id=commentable_id
)
def test_group_info_in_response(self, mock_request):
def test_group_info_in_response(self, mock_is_forum_v2_enabled, mock_request):
response = self.call_view(
mock_is_forum_v2_enabled,
mock_request,
"cohorted_topic",
self.student,
@@ -116,6 +132,7 @@ class CreateThreadGroupIdTestCase(
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
@disable_signal(views, 'thread_edited')
@disable_signal(views, 'thread_voted')
@disable_signal(views, 'thread_deleted')
@@ -127,11 +144,18 @@ class ThreadActionGroupIdTestCase(
def call_view(
self,
view_name,
mock_is_forum_v2_enabled,
mock_request,
user=None,
post_params=None,
view_args=None
):
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(
mock_request,
{
@@ -154,53 +178,58 @@ class ThreadActionGroupIdTestCase(
**(view_args or {})
)
def test_update(self, mock_request):
def test_update(self, mock_is_forum_v2_enabled, mock_request):
response = self.call_view(
"update_thread",
mock_is_forum_v2_enabled,
mock_request,
post_params={"body": "body", "title": "title"}
)
self._assert_json_response_contains_group_info(response)
def test_delete(self, mock_request):
response = self.call_view("delete_thread", mock_request)
def test_delete(self, mock_is_forum_v2_enabled, mock_request):
response = self.call_view("delete_thread", mock_is_forum_v2_enabled, mock_request)
self._assert_json_response_contains_group_info(response)
def test_vote(self, mock_request):
def test_vote(self, mock_is_forum_v2_enabled, mock_request):
response = self.call_view(
"vote_for_thread",
mock_is_forum_v2_enabled,
mock_request,
view_args={"value": "up"}
)
self._assert_json_response_contains_group_info(response)
response = self.call_view("undo_vote_for_thread", mock_request)
response = self.call_view("undo_vote_for_thread", mock_is_forum_v2_enabled, mock_request)
self._assert_json_response_contains_group_info(response)
def test_flag(self, mock_request):
def test_flag(self, mock_is_forum_v2_enabled, mock_request):
with mock.patch('openedx.core.djangoapps.django_comment_common.signals.thread_flagged.send') as signal_mock:
response = self.call_view("flag_abuse_for_thread", mock_request)
response = self.call_view("flag_abuse_for_thread", mock_is_forum_v2_enabled, mock_request)
self._assert_json_response_contains_group_info(response)
self.assertEqual(signal_mock.call_count, 1)
response = self.call_view("un_flag_abuse_for_thread", 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_request):
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_request):
def test_openclose(self, mock_is_forum_v2_enabled, mock_request):
response = self.call_view(
"openclose_thread",
mock_is_forum_v2_enabled,
mock_request,
user=self.moderator
)
@@ -280,10 +309,11 @@ class ViewsTestCaseMixin:
data["depth"] = 0
self._set_mock_request_data(mock_request, data)
def create_thread_helper(self, mock_request, extra_request_data=None, extra_response_data=None):
def create_thread_helper(self, mock_is_forum_v2_enabled, mock_request, extra_request_data=None, extra_response_data=None):
"""
Issues a request to create a thread and verifies the result.
"""
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {
"thread_type": "discussion",
"title": "Hello",
@@ -350,10 +380,11 @@ class ViewsTestCaseMixin:
)
assert response.status_code == 200
def update_thread_helper(self, mock_request):
def update_thread_helper(self, mock_is_forum_v2_enabled, mock_request):
"""
Issues a request to update a thread and verifies the result.
"""
mock_is_forum_v2_enabled.return_value = False
self._setup_mock_request(mock_request)
# Mock out saving in order to test that content is correctly
# updated. Otherwise, the call to thread.save() receives the
@@ -376,6 +407,7 @@ class ViewsTestCaseMixin:
@ddt.ddt
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
@disable_signal(views, 'thread_created')
@disable_signal(views, 'thread_edited')
class ViewsQueryCountTestCase(
@@ -393,6 +425,11 @@ class ViewsQueryCountTestCase(
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super().setUp()
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
def count_queries(func): # pylint: disable=no-self-argument
"""
@@ -414,22 +451,23 @@ class ViewsQueryCountTestCase(
)
@ddt.unpack
@count_queries
def test_create_thread(self, mock_request):
self.create_thread_helper(mock_request)
def test_create_thread(self, mock_is_forum_v2_enabled, mock_request):
self.create_thread_helper(mock_is_forum_v2_enabled, mock_request)
@ddt.data(
(ModuleStoreEnum.Type.split, 3, 6, 41),
)
@ddt.unpack
@count_queries
def test_update_thread(self, mock_request):
self.update_thread_helper(mock_request)
def test_update_thread(self, mock_is_forum_v2_enabled, mock_request):
self.update_thread_helper(mock_is_forum_v2_enabled, mock_request)
@ddt.ddt
@disable_signal(views, 'comment_flagged')
@disable_signal(views, 'thread_flagged')
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
class ViewsTestCase(
ForumsEnableMixin,
UrlResetMixin,
@@ -464,7 +502,16 @@ class ViewsTestCase(
# so we need to call super.setUp() which reloads urls.py (because
# of the UrlResetMixin)
super().setUp()
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
# Patch the comment client user save method so it does not try
# to create a new cc user when creating a django user
with patch('common.djangoapps.student.models.user.cc.User.save'):
@@ -497,11 +544,11 @@ class ViewsTestCase(
with self.assert_signal_sent(views, signal, sender=None, user=user, exclude_args=('post',)):
yield
def test_create_thread(self, mock_request):
def test_create_thread(self, mock_is_forum_v2_enabled, mock_request):
with self.assert_discussion_signals('thread_created'):
self.create_thread_helper(mock_request)
self.create_thread_helper(mock_is_forum_v2_enabled, mock_request)
def test_create_thread_standalone(self, mock_request):
def test_create_thread_standalone(self, mock_is_forum_v2_enabled, mock_request):
team = CourseTeamFactory.create(
name="A Team",
course_id=self.course_id,
@@ -513,15 +560,15 @@ class ViewsTestCase(
team.add_user(self.student)
# create_thread_helper verifies that extra data are passed through to the comments service
self.create_thread_helper(mock_request, extra_response_data={'context': ThreadContext.STANDALONE})
self.create_thread_helper(mock_is_forum_v2_enabled, mock_request, extra_response_data={'context': ThreadContext.STANDALONE})
@ddt.data(
('follow_thread', 'thread_followed'),
('unfollow_thread', 'thread_unfollowed'),
)
@ddt.unpack
def test_follow_unfollow_thread_signals(self, view_name, signal, mock_request):
self.create_thread_helper(mock_request)
def test_follow_unfollow_thread_signals(self, view_name, signal, mock_is_forum_v2_enabled, mock_request):
self.create_thread_helper(mock_is_forum_v2_enabled, mock_request)
with self.assert_discussion_signals(signal):
response = self.client.post(
@@ -532,7 +579,8 @@ class ViewsTestCase(
)
assert response.status_code == 200
def test_delete_thread(self, mock_request):
def test_delete_thread(self, mock_is_forum_v2_enabled, mock_request):
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {
"user_id": str(self.student.id),
"closed": False,
@@ -551,7 +599,8 @@ class ViewsTestCase(
assert response.status_code == 200
assert mock_request.called
def test_delete_comment(self, mock_request):
def test_delete_comment(self, mock_is_forum_v2_enabled, mock_request):
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {
"user_id": str(self.student.id),
"closed": False,
@@ -573,12 +622,13 @@ class ViewsTestCase(
assert args[0] == 'delete'
assert args[1].endswith(f"/{test_comment_id}")
def _test_request_error(self, view_name, view_kwargs, data, mock_request):
def _test_request_error(self, view_name, view_kwargs, data, mock_is_forum_v2_enabled, mock_request):
"""
Submit a request against the given view with the given data and ensure
that the result is a 400 error and that no data was posted using
mock_request
"""
mock_is_forum_v2_enabled.return_value = False
self._setup_mock_request(mock_request, include_depth=(view_name == "create_sub_comment"))
response = self.client.post(reverse(view_name, kwargs=view_kwargs), data=data)
@@ -586,87 +636,97 @@ class ViewsTestCase(
for call in mock_request.call_args_list:
assert call[0][0].lower() == 'get'
def test_create_thread_no_title(self, mock_request):
def test_create_thread_no_title(self, mock_is_forum_v2_enabled, mock_request):
self._test_request_error(
"create_thread",
{"commentable_id": "dummy", "course_id": str(self.course_id)},
{"body": "foo"},
mock_is_forum_v2_enabled,
mock_request
)
def test_create_thread_empty_title(self, mock_request):
def test_create_thread_empty_title(self, mock_is_forum_v2_enabled, mock_request):
self._test_request_error(
"create_thread",
{"commentable_id": "dummy", "course_id": str(self.course_id)},
{"body": "foo", "title": " "},
mock_is_forum_v2_enabled,
mock_request
)
def test_create_thread_no_body(self, mock_request):
def test_create_thread_no_body(self, mock_is_forum_v2_enabled, mock_request):
self._test_request_error(
"create_thread",
{"commentable_id": "dummy", "course_id": str(self.course_id)},
{"title": "foo"},
mock_is_forum_v2_enabled,
mock_request
)
def test_create_thread_empty_body(self, mock_request):
def test_create_thread_empty_body(self, mock_is_forum_v2_enabled, mock_request):
self._test_request_error(
"create_thread",
{"commentable_id": "dummy", "course_id": str(self.course_id)},
{"body": " ", "title": "foo"},
mock_is_forum_v2_enabled,
mock_request
)
def test_update_thread_no_title(self, mock_request):
def test_update_thread_no_title(self, mock_is_forum_v2_enabled, mock_request):
self._test_request_error(
"update_thread",
{"thread_id": "dummy", "course_id": str(self.course_id)},
{"body": "foo"},
mock_is_forum_v2_enabled,
mock_request
)
def test_update_thread_empty_title(self, mock_request):
def test_update_thread_empty_title(self, mock_is_forum_v2_enabled, mock_request):
self._test_request_error(
"update_thread",
{"thread_id": "dummy", "course_id": str(self.course_id)},
{"body": "foo", "title": " "},
mock_is_forum_v2_enabled,
mock_request
)
def test_update_thread_no_body(self, mock_request):
def test_update_thread_no_body(self, mock_is_forum_v2_enabled, mock_request):
self._test_request_error(
"update_thread",
{"thread_id": "dummy", "course_id": str(self.course_id)},
{"title": "foo"},
mock_is_forum_v2_enabled,
mock_request
)
def test_update_thread_empty_body(self, mock_request):
def test_update_thread_empty_body(self, mock_is_forum_v2_enabled, mock_request):
self._test_request_error(
"update_thread",
{"thread_id": "dummy", "course_id": str(self.course_id)},
{"body": " ", "title": "foo"},
mock_is_forum_v2_enabled,
mock_request
)
def test_update_thread_course_topic(self, mock_request):
def test_update_thread_course_topic(self, mock_is_forum_v2_enabled, mock_request):
with self.assert_discussion_signals('thread_edited'):
self.update_thread_helper(mock_request)
self.update_thread_helper(mock_is_forum_v2_enabled, mock_request)
@patch(
'lms.djangoapps.discussion.django_comment_client.utils.get_discussion_categories_ids',
return_value=["test_commentable"],
)
def test_update_thread_wrong_commentable_id(self, mock_get_discussion_id_map, mock_request):
def test_update_thread_wrong_commentable_id(self, mock_get_discussion_id_map, mock_is_forum_v2_enabled, mock_request):
self._test_request_error(
"update_thread",
{"thread_id": "dummy", "course_id": str(self.course_id)},
{"body": "foo", "title": "foo", "commentable_id": "wrong_commentable"},
mock_is_forum_v2_enabled,
mock_request
)
def test_create_comment(self, mock_request):
def test_create_comment(self, mock_is_forum_v2_enabled, mock_request):
mock_is_forum_v2_enabled.return_value = False
self._setup_mock_request(mock_request)
with self.assert_discussion_signals('comment_created'):
response = self.client.post(
@@ -678,55 +738,62 @@ class ViewsTestCase(
)
assert response.status_code == 200
def test_create_comment_no_body(self, mock_request):
def test_create_comment_no_body(self, mock_is_forum_v2_enabled, mock_request):
self._test_request_error(
"create_comment",
{"thread_id": "dummy", "course_id": str(self.course_id)},
{},
mock_is_forum_v2_enabled,
mock_request
)
def test_create_comment_empty_body(self, mock_request):
def test_create_comment_empty_body(self, mock_is_forum_v2_enabled, mock_request):
self._test_request_error(
"create_comment",
{"thread_id": "dummy", "course_id": str(self.course_id)},
{"body": " "},
mock_is_forum_v2_enabled,
mock_request
)
def test_create_sub_comment_no_body(self, mock_request):
def test_create_sub_comment_no_body(self, mock_is_forum_v2_enabled, mock_request):
self._test_request_error(
"create_sub_comment",
{"comment_id": "dummy", "course_id": str(self.course_id)},
{},
mock_is_forum_v2_enabled,
mock_request
)
def test_create_sub_comment_empty_body(self, mock_request):
def test_create_sub_comment_empty_body(self, mock_is_forum_v2_enabled, mock_request):
self._test_request_error(
"create_sub_comment",
{"comment_id": "dummy", "course_id": str(self.course_id)},
{"body": " "},
mock_is_forum_v2_enabled,
mock_request
)
def test_update_comment_no_body(self, mock_request):
def test_update_comment_no_body(self, mock_is_forum_v2_enabled, mock_request):
self._test_request_error(
"update_comment",
{"comment_id": "dummy", "course_id": str(self.course_id)},
{},
mock_is_forum_v2_enabled,
mock_request
)
def test_update_comment_empty_body(self, mock_request):
def test_update_comment_empty_body(self, mock_is_forum_v2_enabled, mock_request):
self._test_request_error(
"update_comment",
{"comment_id": "dummy", "course_id": str(self.course_id)},
{"body": " "},
mock_is_forum_v2_enabled,
mock_request
)
def test_update_comment_basic(self, mock_request):
def test_update_comment_basic(self, mock_is_forum_v2_enabled, mock_request):
mock_is_forum_v2_enabled.return_value = False
self._setup_mock_request(mock_request)
comment_id = "test_comment_id"
updated_body = "updated body"
@@ -748,13 +815,14 @@ class ViewsTestCase(
data={"body": updated_body}
)
def test_flag_thread_open(self, mock_request):
self.flag_thread(mock_request, False)
def test_flag_thread_open(self, mock_is_forum_v2_enabled, mock_request):
self.flag_thread(mock_is_forum_v2_enabled, mock_request, False)
def test_flag_thread_close(self, mock_request):
self.flag_thread(mock_request, True)
def test_flag_thread_close(self, mock_is_forum_v2_enabled, mock_request):
self.flag_thread(mock_is_forum_v2_enabled, mock_request, True)
def flag_thread(self, mock_request, is_closed):
def flag_thread(self, mock_is_forum_v2_enabled, mock_request, is_closed):
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {
"title": "Hello",
"body": "this is a post",
@@ -826,13 +894,14 @@ class ViewsTestCase(
assert response.status_code == 200
def test_un_flag_thread_open(self, mock_request):
self.un_flag_thread(mock_request, False)
def test_un_flag_thread_open(self, mock_is_forum_v2_enabled, mock_request):
self.un_flag_thread(mock_is_forum_v2_enabled, mock_request, False)
def test_un_flag_thread_close(self, mock_request):
self.un_flag_thread(mock_request, True)
def test_un_flag_thread_close(self, mock_is_forum_v2_enabled, mock_request):
self.un_flag_thread(mock_is_forum_v2_enabled, mock_request, True)
def un_flag_thread(self, mock_request, is_closed):
def un_flag_thread(self, mock_is_forum_v2_enabled, mock_request, is_closed):
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {
"title": "Hello",
"body": "this is a post",
@@ -905,13 +974,14 @@ class ViewsTestCase(
assert response.status_code == 200
def test_flag_comment_open(self, mock_request):
self.flag_comment(mock_request, False)
def test_flag_comment_open(self, mock_is_forum_v2_enabled, mock_request):
self.flag_comment(mock_is_forum_v2_enabled, mock_request, False)
def test_flag_comment_close(self, mock_request):
self.flag_comment(mock_request, True)
def test_flag_comment_close(self, mock_is_forum_v2_enabled, mock_request):
self.flag_comment(mock_is_forum_v2_enabled, mock_request, True)
def flag_comment(self, mock_request, is_closed):
def flag_comment(self, mock_is_forum_v2_enabled, mock_request, is_closed):
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {
"body": "this is a comment",
"course_id": "MITx/999/Robot_Super_Course",
@@ -976,13 +1046,14 @@ class ViewsTestCase(
assert response.status_code == 200
def test_un_flag_comment_open(self, mock_request):
self.un_flag_comment(mock_request, False)
def test_un_flag_comment_open(self, mock_is_forum_v2_enabled, mock_request):
self.un_flag_comment(mock_is_forum_v2_enabled, mock_request, False)
def test_un_flag_comment_close(self, mock_request):
self.un_flag_comment(mock_request, True)
def test_un_flag_comment_close(self, mock_is_forum_v2_enabled, mock_request):
self.un_flag_comment(mock_is_forum_v2_enabled, mock_request, True)
def un_flag_comment(self, mock_request, is_closed):
def un_flag_comment(self, mock_is_forum_v2_enabled, mock_request, is_closed):
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {
"body": "this is a comment",
"course_id": "MITx/999/Robot_Super_Course",
@@ -1054,7 +1125,8 @@ class ViewsTestCase(
('downvote_comment', 'comment_id', 'comment_voted')
)
@ddt.unpack
def test_voting(self, view_name, item_id, signal, mock_request):
def test_voting(self, view_name, item_id, signal, mock_is_forum_v2_enabled, mock_request):
mock_is_forum_v2_enabled.return_value = False
self._setup_mock_request(mock_request)
with self.assert_discussion_signals(signal):
response = self.client.post(
@@ -1065,7 +1137,8 @@ class ViewsTestCase(
)
assert response.status_code == 200
def test_endorse_comment(self, mock_request):
def test_endorse_comment(self, mock_is_forum_v2_enabled, mock_request):
mock_is_forum_v2_enabled.return_value = False
self._setup_mock_request(mock_request)
self.client.login(username=self.moderator.username, password=self.password)
with self.assert_discussion_signals('comment_endorsed', user=self.moderator):
@@ -1079,6 +1152,7 @@ class ViewsTestCase(
@patch("openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request", autospec=True)
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
@disable_signal(views, 'comment_endorsed')
class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStoreTestCase, MockRequestSetupMixin):
@@ -1106,8 +1180,19 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super().setUp()
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
def test_pin_thread_as_student(self, mock_request):
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(
@@ -1115,7 +1200,8 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
)
assert response.status_code == 401
def test_pin_thread_as_moderator(self, mock_request):
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(
@@ -1123,7 +1209,8 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
)
assert response.status_code == 200
def test_un_pin_thread_as_student(self, mock_request):
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(
@@ -1131,7 +1218,8 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
)
assert response.status_code == 401
def test_un_pin_thread_as_moderator(self, mock_request):
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(
@@ -1139,7 +1227,7 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
)
assert response.status_code == 200
def _set_mock_request_thread_and_comment(self, mock_request, thread_data, comment_data):
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]
if "/threads/" in url:
@@ -1148,10 +1236,12 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
return self._create_response_mock(comment_data)
else:
raise ArgumentError("Bad url to mock request")
mock_is_forum_v2_enabled.return_value = False
mock_request.side_effect = handle_request
def test_endorse_response_as_staff(self, mock_request):
def test_endorse_response_as_staff(self, mock_is_forum_v2_enabled, mock_request):
self._set_mock_request_thread_and_comment(
mock_is_forum_v2_enabled,
mock_request,
{"type": "thread", "thread_type": "question", "user_id": str(self.student.id), "commentable_id": "course"},
{"type": "comment", "thread_id": "dummy"}
@@ -1162,8 +1252,9 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
)
assert response.status_code == 200
def test_endorse_response_as_student(self, mock_request):
def test_endorse_response_as_student(self, mock_is_forum_v2_enabled, mock_request):
self._set_mock_request_thread_and_comment(
mock_is_forum_v2_enabled,
mock_request,
{"type": "thread", "thread_type": "question",
"user_id": str(self.moderator.id), "commentable_id": "course"},
@@ -1175,8 +1266,9 @@ class ViewPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleStor
)
assert response.status_code == 401
def test_endorse_response_as_student_question_author(self, mock_request):
def test_endorse_response_as_student_question_author(self, mock_is_forum_v2_enabled, mock_request):
self._set_mock_request_thread_and_comment(
mock_is_forum_v2_enabled,
mock_request,
{"type": "thread", "thread_type": "question", "user_id": str(self.student.id), "commentable_id": "course"},
{"type": "comment", "thread_id": "dummy"}
@@ -1209,10 +1301,12 @@ class CreateThreadUnicodeTestCase(
CourseEnrollmentFactory(user=cls.student, course_id=cls.course.id)
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
def _test_unicode_data(self, text, mock_request,):
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
def _test_unicode_data(self, text, mock_is_forum_v2_enabled, mock_request,):
"""
Test to make sure unicode data in a thread doesn't break it.
"""
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {})
request = RequestFactory().post("dummy_url", {"thread_type": "discussion", "body": text, "title": text})
request.user = self.student
@@ -1235,6 +1329,13 @@ class UpdateThreadUnicodeTestCase(
UnicodeTestMixin,
MockRequestSetupMixin
):
def setUp(self):
super().setUp()
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
@classmethod
def setUpClass(cls):
@@ -1255,7 +1356,9 @@ class UpdateThreadUnicodeTestCase(
return_value=["test_commentable"],
)
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
def _test_unicode_data(self, text, mock_request, mock_get_discussion_id_map):
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
def _test_unicode_data(self, text, mock_is_forum_v2_enabled, mock_request, mock_get_discussion_id_map):
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {
"user_id": str(self.student.id),
"closed": False,
@@ -1280,6 +1383,13 @@ class CreateCommentUnicodeTestCase(
UnicodeTestMixin,
MockRequestSetupMixin
):
def setUp(self):
super().setUp()
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
@classmethod
def setUpClass(cls):
@@ -1296,7 +1406,9 @@ class CreateCommentUnicodeTestCase(
CourseEnrollmentFactory(user=cls.student, course_id=cls.course.id)
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
def _test_unicode_data(self, text, mock_request):
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
def _test_unicode_data(self, text, mock_is_forum_v2_enabled, mock_request):
mock_is_forum_v2_enabled.return_value = False
commentable_id = "non_team_dummy_id"
self._set_mock_request_data(mock_request, {
"closed": False,
@@ -1327,6 +1439,13 @@ class UpdateCommentUnicodeTestCase(
UnicodeTestMixin,
MockRequestSetupMixin
):
def setUp(self):
super().setUp()
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
@classmethod
def setUpClass(cls):
@@ -1343,7 +1462,9 @@ class UpdateCommentUnicodeTestCase(
CourseEnrollmentFactory(user=cls.student, course_id=cls.course.id)
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
def _test_unicode_data(self, text, mock_request):
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
def _test_unicode_data(self, text, mock_is_forum_v2_enabled, mock_request):
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {
"user_id": str(self.student.id),
"closed": False,
@@ -1359,6 +1480,7 @@ class UpdateCommentUnicodeTestCase(
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
class CommentActionTestCase(
MockRequestSetupMixin,
CohortedTestCase,
@@ -1367,11 +1489,18 @@ class CommentActionTestCase(
def call_view(
self,
view_name,
mock_is_forum_v2_enabled,
mock_request,
user=None,
post_params=None,
view_args=None
):
mock_is_forum_v2_enabled.return_value = False
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
self._set_mock_request_data(
mock_request,
{
@@ -1394,9 +1523,9 @@ class CommentActionTestCase(
**(view_args or {})
)
def test_flag(self, mock_request):
def test_flag(self, mock_is_forum_v2_enabled, mock_request):
with mock.patch('openedx.core.djangoapps.django_comment_common.signals.comment_flagged.send') as signal_mock:
self.call_view("flag_abuse_for_comment", mock_request)
self.call_view("flag_abuse_for_comment", mock_is_forum_v2_enabled, mock_request)
self.assertEqual(signal_mock.call_count, 1)
@@ -1410,6 +1539,14 @@ class CreateSubCommentUnicodeTestCase(
"""
Make sure comments under a response can handle unicode.
"""
def setUp(self):
super().setUp()
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
@classmethod
def setUpClass(cls):
# pylint: disable=super-method-not-called
@@ -1425,10 +1562,12 @@ class CreateSubCommentUnicodeTestCase(
CourseEnrollmentFactory(user=cls.student, course_id=cls.course.id)
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
def _test_unicode_data(self, text, mock_request):
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
def _test_unicode_data(self, text, mock_is_forum_v2_enabled, mock_request):
"""
Create a comment with unicode in it.
"""
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {
"closed": False,
"depth": 1,
@@ -1453,6 +1592,7 @@ class CreateSubCommentUnicodeTestCase(
@ddt.ddt
@patch("openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request", autospec=True)
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
@disable_signal(views, 'thread_voted')
@disable_signal(views, 'thread_edited')
@disable_signal(views, 'comment_created')
@@ -1562,13 +1702,24 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
users=[cls.group_moderator, cls.cohorted]
)
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super().setUp()
def _setup_mock(self, user, mock_request, data):
def _setup_mock(self, user, mock_is_forum_v2_enabled, mock_request, data):
user = getattr(self, user)
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, data)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
self.client.login(username=user.username, password=self.password)
@ddt.data(
@@ -1593,7 +1744,7 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
('group_moderator', 'cohorted', 'course_commentable_id', 401, CourseDiscussionSettings.NONE)
)
@ddt.unpack
def test_update_thread(self, user, thread_author, commentable_id, status_code, division_scheme, mock_request):
def test_update_thread(self, user, thread_author, commentable_id, status_code, division_scheme, mock_is_forum_v2_enabled, mock_request):
"""
Verify that update_thread is limited to thread authors and privileged users (team membership does not matter).
"""
@@ -1603,7 +1754,7 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
thread_author = getattr(self, thread_author)
self._setup_mock(
user, mock_request, # user is the person making the request.
user, mock_is_forum_v2_enabled, mock_request, # user is the person making the request.
{
"user_id": str(thread_author.id),
"closed": False, "commentable_id": commentable_id,
@@ -1643,12 +1794,12 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
('group_moderator', 'cohorted', 'team_commentable_id', 401, CourseDiscussionSettings.NONE)
)
@ddt.unpack
def test_delete_comment(self, user, comment_author, commentable_id, status_code, division_scheme, mock_request):
def test_delete_comment(self, user, comment_author, commentable_id, status_code, division_scheme, mock_is_forum_v2_enabled, mock_request):
commentable_id = getattr(self, commentable_id)
comment_author = getattr(self, comment_author)
self.change_divided_discussion_settings(division_scheme)
self._setup_mock(user, mock_request, {
self._setup_mock(user, mock_is_forum_v2_enabled, mock_request, {
"closed": False,
"commentable_id": commentable_id,
"user_id": str(comment_author.id),
@@ -1671,12 +1822,12 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
@ddt.data(*ddt_permissions_args)
@ddt.unpack
def test_create_comment(self, user, commentable_id, status_code, mock_request):
def test_create_comment(self, user, commentable_id, status_code, mock_is_forum_v2_enabled, mock_request):
"""
Verify that create_comment is limited to members of the team or users with 'edit_content' permission.
"""
commentable_id = getattr(self, commentable_id)
self._setup_mock(user, mock_request, {"closed": False, "commentable_id": commentable_id})
self._setup_mock(user, mock_is_forum_v2_enabled, mock_request, {"closed": False, "commentable_id": commentable_id})
response = self.client.post(
reverse(
@@ -1692,13 +1843,13 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
@ddt.data(*ddt_permissions_args)
@ddt.unpack
def test_create_sub_comment(self, user, commentable_id, status_code, mock_request):
def test_create_sub_comment(self, user, commentable_id, status_code, mock_is_forum_v2_enabled, mock_request):
"""
Verify that create_subcomment is limited to members of the team or users with 'edit_content' permission.
"""
commentable_id = getattr(self, commentable_id)
self._setup_mock(
user, mock_request,
user, mock_is_forum_v2_enabled, mock_request,
{"closed": False, "commentable_id": commentable_id, "thread_id": "dummy_thread"},
)
response = self.client.post(
@@ -1715,14 +1866,14 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
@ddt.data(*ddt_permissions_args)
@ddt.unpack
def test_comment_actions(self, user, commentable_id, status_code, mock_request):
def test_comment_actions(self, user, commentable_id, status_code, mock_is_forum_v2_enabled, mock_request):
"""
Verify that voting and flagging of comments is limited to members of the team or users with
'edit_content' permission.
"""
commentable_id = getattr(self, commentable_id)
self._setup_mock(
user, mock_request,
user, mock_is_forum_v2_enabled, mock_request,
{
"closed": False,
"commentable_id": commentable_id,
@@ -1742,14 +1893,14 @@ class TeamsPermissionsTestCase(ForumsEnableMixin, UrlResetMixin, SharedModuleSto
@ddt.data(*ddt_permissions_args)
@ddt.unpack
def test_threads_actions(self, user, commentable_id, status_code, mock_request):
def test_threads_actions(self, user, commentable_id, status_code, mock_is_forum_v2_enabled, mock_request):
"""
Verify that voting, flagging, and following of threads is limited to members of the team or users with
'edit_content' permission.
"""
commentable_id = getattr(self, commentable_id)
self._setup_mock(
user, mock_request,
user, mock_is_forum_v2_enabled, mock_request,
{"closed": False, "commentable_id": commentable_id, "body": "dummy body", "course_id": str(self.course.id)}
)
for action in ["upvote_thread", "downvote_thread", "un_flag_abuse_for_thread", "flag_abuse_for_thread",
@@ -1772,6 +1923,19 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
"""
Forum actions are expected to launch analytics events. Test these here.
"""
def setUp(self):
super().setUp()
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
@classmethod
def setUpClass(cls):
# pylint: disable=super-method-not-called
@@ -1791,12 +1955,14 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
@patch('eventtracking.tracker.emit')
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
def test_response_event(self, mock_request, mock_emit):
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
def test_response_event(self, mock_is_forum_v2_enabled, mock_request, mock_emit):
"""
Check to make sure an event is fired when a user responds to a thread.
"""
event_receiver = Mock()
FORUM_THREAD_RESPONSE_CREATED.connect(event_receiver)
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {
"closed": False,
"commentable_id": 'test_commentable_id',
@@ -1833,12 +1999,14 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
@patch('eventtracking.tracker.emit')
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
def test_comment_event(self, mock_request, mock_emit):
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
def test_comment_event(self, mock_is_forum_v2_enabled, mock_request, mock_emit):
"""
Ensure an event is fired when someone comments on a response.
"""
event_receiver = Mock()
FORUM_RESPONSE_COMMENT_CREATED.connect(event_receiver)
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {
"closed": False,
"depth": 1,
@@ -1875,6 +2043,7 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
@patch('eventtracking.tracker.emit')
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
@ddt.data((
'create_thread',
'edx.forum.thread.created', {
@@ -1896,7 +2065,7 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
{'comment_id': 'dummy_comment_id'}
))
@ddt.unpack
def test_team_events(self, view_name, event_name, view_data, view_kwargs, mock_request, mock_emit):
def test_team_events(self, view_name, event_name, view_data, view_kwargs, mock_is_forum_v2_enabled, mock_request, mock_emit):
user = self.student
team = CourseTeamFactory.create(discussion_topic_id=TEAM_COMMENTABLE_ID)
CourseTeamMembershipFactory.create(team=team, user=user)
@@ -1905,6 +2074,7 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
forum_event = views.TRACKING_LOG_TO_EVENT_MAPS.get(event_name)
forum_event.connect(event_receiver)
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {
'closed': False,
'commentable_id': TEAM_COMMENTABLE_ID,
@@ -1943,9 +2113,11 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
@ddt.unpack
@patch('eventtracking.tracker.emit')
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
def test_thread_voted_event(self, view_name, obj_id_name, obj_type, mock_request, mock_emit):
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
def test_thread_voted_event(self, view_name, obj_id_name, obj_type, mock_is_forum_v2_enabled, mock_request, mock_emit):
undo = view_name.startswith('undo')
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {
'closed': False,
'commentable_id': 'test_commentable_id',
@@ -1971,11 +2143,13 @@ class ForumEventTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockReque
@ddt.data('follow_thread', 'unfollow_thread',)
@patch('eventtracking.tracker.emit')
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
def test_thread_followed_event(self, view_name, mock_request, mock_emit):
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
def test_thread_followed_event(self, view_name, mock_is_forum_v2_enabled, mock_request, mock_emit):
event_receiver = Mock()
for signal in views.TRACKING_LOG_TO_EVENT_MAPS.values():
signal.connect(event_receiver)
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {
'closed': False,
'commentable_id': 'test_commentable_id',
@@ -2025,10 +2199,11 @@ class UsersEndpointTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockRe
cls.other_user = UserFactory.create(username="other")
CourseEnrollmentFactory(user=cls.other_user, course_id=cls.course.id)
def set_post_counts(self, mock_request, threads_count=1, comments_count=1):
def set_post_counts(self, mock_is_forum_v2_enabled, mock_request, threads_count=1, comments_count=1):
"""
sets up a mock response from the comments service for getting post counts for our other_user
"""
mock_is_forum_v2_enabled.return_value = False
self._set_mock_request_data(mock_request, {
"threads_count": threads_count,
"comments_count": comments_count,
@@ -2042,15 +2217,17 @@ class UsersEndpointTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockRe
return views.users(request, course_id=str(course_id))
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
def test_finds_exact_match(self, mock_request):
self.set_post_counts(mock_request)
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
def test_finds_exact_match(self, mock_is_forum_v2_enabled, mock_request):
self.set_post_counts(mock_is_forum_v2_enabled, mock_request)
response = self.make_request(username="other")
assert response.status_code == 200
assert json.loads(response.content.decode('utf-8'))['users'] == [{'id': self.other_user.id, 'username': self.other_user.username}]
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
def test_finds_no_match(self, mock_request):
self.set_post_counts(mock_request)
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
def test_finds_no_match(self, mock_is_forum_v2_enabled, mock_request):
self.set_post_counts(mock_is_forum_v2_enabled, mock_request)
response = self.make_request(username="othor")
assert response.status_code == 200
assert json.loads(response.content.decode('utf-8'))['users'] == []
@@ -2086,8 +2263,9 @@ class UsersEndpointTestCase(ForumsEnableMixin, SharedModuleStoreTestCase, MockRe
assert 'users' not in content
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
def test_requires_matched_user_has_forum_content(self, mock_request):
self.set_post_counts(mock_request, 0, 0)
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
def test_requires_matched_user_has_forum_content(self, mock_is_forum_v2_enabled, mock_request):
self.set_post_counts(mock_is_forum_v2_enabled, mock_request, 0, 0)
response = self.make_request(username="other")
assert response.status_code == 200
assert json.loads(response.content.decode('utf-8'))['users'] == []

View File

@@ -562,7 +562,6 @@ def create_thread(request, course_id, commentable_id):
params['context'] = ThreadContext.STANDALONE
else:
params['context'] = ThreadContext.COURSE
thread = cc.Thread(**params)
# Divide the thread if required

View File

@@ -60,51 +60,76 @@ class CohortedTopicGroupIdTestMixin(GroupIdAssertionMixin):
Provides test cases to verify that views pass the correct `group_id` to
the comments service when requesting content in cohorted discussions.
"""
def call_view(self, mock_request, commentable_id, user, group_id, pass_group_id=True):
def call_view(self, mock_is_forum_v2_enabled, mock_request, commentable_id, user, group_id, pass_group_id=True):
"""
Call the view for the implementing test class, constructing a request
from the parameters.
"""
pass # lint-amnesty, pylint: disable=unnecessary-pass
def test_cohorted_topic_student_without_group_id(self, mock_request):
self.call_view(mock_request, "cohorted_topic", self.student, '', pass_group_id=False)
def test_cohorted_topic_student_without_group_id(self, mock_is_forum_v2_enabled, mock_request):
self.call_view(mock_is_forum_v2_enabled, mock_request, "cohorted_topic", self.student, '', pass_group_id=False)
self._assert_comments_service_called_with_group_id(mock_request, self.student_cohort.id)
def test_cohorted_topic_student_none_group_id(self, mock_request):
self.call_view(mock_request, "cohorted_topic", self.student, "")
def test_cohorted_topic_student_none_group_id(self, mock_is_forum_v2_enabled, mock_request):
self.call_view(mock_is_forum_v2_enabled, mock_request, "cohorted_topic", self.student, "")
self._assert_comments_service_called_with_group_id(mock_request, self.student_cohort.id)
def test_cohorted_topic_student_with_own_group_id(self, mock_request):
self.call_view(mock_request, "cohorted_topic", self.student, self.student_cohort.id)
def test_cohorted_topic_student_with_own_group_id(self, mock_is_forum_v2_enabled, mock_request):
self.call_view(mock_is_forum_v2_enabled, mock_request, "cohorted_topic", self.student, self.student_cohort.id)
self._assert_comments_service_called_with_group_id(mock_request, self.student_cohort.id)
def test_cohorted_topic_student_with_other_group_id(self, mock_request):
self.call_view(mock_request, "cohorted_topic", self.student, self.moderator_cohort.id)
def test_cohorted_topic_student_with_other_group_id(self, mock_is_forum_v2_enabled, mock_request):
self.call_view(
mock_is_forum_v2_enabled,
mock_request,
"cohorted_topic",
self.student,
self.moderator_cohort.id
)
self._assert_comments_service_called_with_group_id(mock_request, self.student_cohort.id)
def test_cohorted_topic_moderator_without_group_id(self, mock_request):
self.call_view(mock_request, "cohorted_topic", self.moderator, '', pass_group_id=False)
def test_cohorted_topic_moderator_without_group_id(self, mock_is_forum_v2_enabled, mock_request):
self.call_view(
mock_is_forum_v2_enabled,
mock_request,
"cohorted_topic",
self.moderator,
'',
pass_group_id=False
)
self._assert_comments_service_called_without_group_id(mock_request)
def test_cohorted_topic_moderator_none_group_id(self, mock_request):
self.call_view(mock_request, "cohorted_topic", self.moderator, "")
def test_cohorted_topic_moderator_none_group_id(self, mock_is_forum_v2_enabled, mock_request):
self.call_view(mock_is_forum_v2_enabled, mock_request, "cohorted_topic", self.moderator, "")
self._assert_comments_service_called_without_group_id(mock_request)
def test_cohorted_topic_moderator_with_own_group_id(self, mock_request):
self.call_view(mock_request, "cohorted_topic", self.moderator, self.moderator_cohort.id)
def test_cohorted_topic_moderator_with_own_group_id(self, mock_is_forum_v2_enabled, mock_request):
self.call_view(
mock_is_forum_v2_enabled,
mock_request,
"cohorted_topic",
self.moderator,
self.moderator_cohort.id
)
self._assert_comments_service_called_with_group_id(mock_request, self.moderator_cohort.id)
def test_cohorted_topic_moderator_with_other_group_id(self, mock_request):
self.call_view(mock_request, "cohorted_topic", self.moderator, self.student_cohort.id)
def test_cohorted_topic_moderator_with_other_group_id(self, mock_is_forum_v2_enabled, mock_request):
self.call_view(
mock_is_forum_v2_enabled,
mock_request,
"cohorted_topic",
self.moderator,
self.student_cohort.id
)
self._assert_comments_service_called_with_group_id(mock_request, self.student_cohort.id)
def test_cohorted_topic_moderator_with_invalid_group_id(self, mock_request):
def test_cohorted_topic_moderator_with_invalid_group_id(self, mock_is_forum_v2_enabled, mock_request):
invalid_id = self.student_cohort.id + self.moderator_cohort.id
response = self.call_view(mock_request, "cohorted_topic", self.moderator, invalid_id) # lint-amnesty, pylint: disable=assignment-from-no-return
response = self.call_view(mock_is_forum_v2_enabled, mock_request, "cohorted_topic", self.moderator, invalid_id) # lint-amnesty, pylint: disable=assignment-from-no-return
assert response.status_code == 500
def test_cohorted_topic_enrollment_track_invalid_group_id(self, mock_request):
def test_cohorted_topic_enrollment_track_invalid_group_id(self, mock_is_forum_v2_enabled, mock_request):
CourseModeFactory.create(course_id=self.course.id, mode_slug=CourseMode.AUDIT)
CourseModeFactory.create(course_id=self.course.id, mode_slug=CourseMode.VERIFIED)
discussion_settings = CourseDiscussionSettings.get(self.course.id)
@@ -115,7 +140,7 @@ class CohortedTopicGroupIdTestMixin(GroupIdAssertionMixin):
})
invalid_id = -1000
response = self.call_view(mock_request, "cohorted_topic", self.moderator, invalid_id) # lint-amnesty, pylint: disable=assignment-from-no-return
response = self.call_view(mock_is_forum_v2_enabled, mock_request, "cohorted_topic", self.moderator, invalid_id) # lint-amnesty, pylint: disable=assignment-from-no-return
assert response.status_code == 500
@@ -124,57 +149,95 @@ class NonCohortedTopicGroupIdTestMixin(GroupIdAssertionMixin):
Provides test cases to verify that views pass the correct `group_id` to
the comments service when requesting content in non-cohorted discussions.
"""
def call_view(self, mock_request, commentable_id, user, group_id, pass_group_id=True):
def call_view(self, mock_is_forum_v2_enabled, mock_request, commentable_id, user, group_id, pass_group_id=True):
"""
Call the view for the implementing test class, constructing a request
from the parameters.
"""
pass # lint-amnesty, pylint: disable=unnecessary-pass
def test_non_cohorted_topic_student_without_group_id(self, mock_request):
self.call_view(mock_request, "non_cohorted_topic", self.student, '', pass_group_id=False)
def test_non_cohorted_topic_student_without_group_id(self, mock_is_forum_v2_enabled, mock_request):
self.call_view(
mock_is_forum_v2_enabled,
mock_request,
"non_cohorted_topic",
self.student,
'',
pass_group_id=False
)
self._assert_comments_service_called_without_group_id(mock_request)
def test_non_cohorted_topic_student_none_group_id(self, mock_request):
self.call_view(mock_request, "non_cohorted_topic", self.student, '')
def test_non_cohorted_topic_student_none_group_id(self, mock_is_forum_v2_enabled, mock_request):
self.call_view(mock_is_forum_v2_enabled, mock_request, "non_cohorted_topic", self.student, '')
self._assert_comments_service_called_without_group_id(mock_request)
def test_non_cohorted_topic_student_with_own_group_id(self, mock_request):
self.call_view(mock_request, "non_cohorted_topic", self.student, self.student_cohort.id)
def test_non_cohorted_topic_student_with_own_group_id(self, mock_is_forum_v2_enabled, mock_request):
self.call_view(
mock_is_forum_v2_enabled,
mock_request,
"non_cohorted_topic",
self.student,
self.student_cohort.id
)
self._assert_comments_service_called_without_group_id(mock_request)
def test_non_cohorted_topic_student_with_other_group_id(self, mock_request):
self.call_view(mock_request, "non_cohorted_topic", self.student, self.moderator_cohort.id)
def test_non_cohorted_topic_student_with_other_group_id(self, mock_is_forum_v2_enabled, mock_request):
self.call_view(
mock_is_forum_v2_enabled,
mock_request,
"non_cohorted_topic",
self.student,
self.moderator_cohort.id
)
self._assert_comments_service_called_without_group_id(mock_request)
def test_non_cohorted_topic_moderator_without_group_id(self, mock_request):
self.call_view(mock_request, "non_cohorted_topic", self.moderator, '', pass_group_id=False)
def test_non_cohorted_topic_moderator_without_group_id(self, mock_is_forum_v2_enabled, mock_request):
self.call_view(
mock_is_forum_v2_enabled,
mock_request,
"non_cohorted_topic",
self.moderator,
"",
pass_group_id=False,
)
self._assert_comments_service_called_without_group_id(mock_request)
def test_non_cohorted_topic_moderator_none_group_id(self, mock_request):
self.call_view(mock_request, "non_cohorted_topic", self.moderator, '')
def test_non_cohorted_topic_moderator_none_group_id(self, mock_is_forum_v2_enabled, mock_request):
self.call_view(mock_is_forum_v2_enabled, mock_request, "non_cohorted_topic", self.moderator, '')
self._assert_comments_service_called_without_group_id(mock_request)
def test_non_cohorted_topic_moderator_with_own_group_id(self, mock_request):
self.call_view(mock_request, "non_cohorted_topic", self.moderator, self.moderator_cohort.id)
def test_non_cohorted_topic_moderator_with_own_group_id(self, mock_is_forum_v2_enabled, mock_request):
self.call_view(
mock_is_forum_v2_enabled,
mock_request,
"non_cohorted_topic",
self.moderator,
self.moderator_cohort.id,
)
self._assert_comments_service_called_without_group_id(mock_request)
def test_non_cohorted_topic_moderator_with_other_group_id(self, mock_request):
self.call_view(mock_request, "non_cohorted_topic", self.moderator, self.student_cohort.id)
def test_non_cohorted_topic_moderator_with_other_group_id(self, mock_is_forum_v2_enabled, mock_request):
self.call_view(
mock_is_forum_v2_enabled,
mock_request,
"non_cohorted_topic",
self.moderator,
self.student_cohort.id,
)
self._assert_comments_service_called_without_group_id(mock_request)
def test_non_cohorted_topic_moderator_with_invalid_group_id(self, mock_request):
def test_non_cohorted_topic_moderator_with_invalid_group_id(self, mock_is_forum_v2_enabled, mock_request):
invalid_id = self.student_cohort.id + self.moderator_cohort.id
self.call_view(mock_request, "non_cohorted_topic", self.moderator, invalid_id)
self.call_view(mock_is_forum_v2_enabled, mock_request, "non_cohorted_topic", self.moderator, invalid_id)
self._assert_comments_service_called_without_group_id(mock_request)
def test_team_discussion_id_not_cohorted(self, mock_request):
def test_team_discussion_id_not_cohorted(self, mock_is_forum_v2_enabled, mock_request):
team = CourseTeamFactory(
course_id=self.course.id,
topic_id='topic-id'
)
team.add_user(self.student)
self.call_view(mock_request, team.discussion_topic_id, self.student, '')
self.call_view(mock_is_forum_v2_enabled, mock_request, team.discussion_topic_id, self.student, '')
self._assert_comments_service_called_without_group_id(mock_request)

View File

@@ -199,7 +199,7 @@ def _get_course(course_key: CourseKey, user: User, check_tab: bool = True) -> Co
return course
def _get_thread_and_context(request, thread_id, retrieve_kwargs=None):
def _get_thread_and_context(request, thread_id, retrieve_kwargs=None, course_id=None):
"""
Retrieve the given thread and build a serializer context for it, returning
both. This function also enforces access control for the thread (checking
@@ -213,7 +213,7 @@ def _get_thread_and_context(request, thread_id, retrieve_kwargs=None):
retrieve_kwargs["with_responses"] = False
if "mark_as_read" not in retrieve_kwargs:
retrieve_kwargs["mark_as_read"] = False
cc_thread = Thread(id=thread_id).retrieve(**retrieve_kwargs)
cc_thread = Thread(id=thread_id).retrieve(course_id=course_id, **retrieve_kwargs)
course_key = CourseKey.from_string(cc_thread["course_id"])
course = _get_course(course_key, request.user)
context = get_context(course, request, cc_thread)
@@ -1645,7 +1645,8 @@ def get_thread(request, thread_id, requested_fields=None, course_id=None):
retrieve_kwargs={
"with_responses": True,
"user_id": str(request.user.id),
}
},
course_id=course_id,
)
if course_id and course_id != cc_thread.course_id:
raise ThreadNotFoundError("Thread not found.")

View File

@@ -202,7 +202,7 @@ class DiscussionNotificationSender:
while has_more_subscribers:
subscribers = Subscription.fetch(self.thread.id, query_params={'page': page})
subscribers = Subscription.fetch(self.thread.id, self.course.id, query_params={'page': page})
if page <= subscribers.num_pages:
for subscriber in subscribers.collection:
# Check if the subscriber is not the thread creator or response creator

View File

@@ -68,7 +68,7 @@ def get_context(course, request, thread=None):
moderator_user_ids = get_moderator_users_list(course.id)
ta_user_ids = get_course_ta_users_list(course.id)
requester = request.user
cc_requester = CommentClientUser.from_django_user(requester).retrieve()
cc_requester = CommentClientUser.from_django_user(requester).retrieve(course_id=course.id)
cc_requester["course_id"] = course.id
course_discussion_settings = CourseDiscussionSettings.get(course.id)
is_global_staff = GlobalStaff().has_user(requester)

View File

@@ -1248,6 +1248,22 @@ class GetCommentListTest(ForumsEnableMixin, CommentsServiceMockMixin, SharedModu
httpretty.enable()
self.addCleanup(httpretty.reset)
self.addCleanup(httpretty.disable)
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
self.maxDiff = None # pylint: disable=invalid-name
self.user = UserFactory.create()
self.register_get_user_response(self.user)
@@ -1872,6 +1888,12 @@ class CreateThreadTest(
httpretty.enable()
self.addCleanup(httpretty.reset)
self.addCleanup(httpretty.disable)
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
self.user = UserFactory.create()
self.register_get_user_response(self.user)
self.request = RequestFactory().get("/test_path")
@@ -2198,6 +2220,22 @@ class CreateCommentTest(
self.course = CourseFactory.create()
self.addCleanup(httpretty.reset)
self.addCleanup(httpretty.disable)
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
self.user = UserFactory.create()
self.register_get_user_response(self.user)
self.request = RequestFactory().get("/test_path")
@@ -2589,6 +2627,17 @@ class UpdateThreadTest(
httpretty.enable()
self.addCleanup(httpretty.reset)
self.addCleanup(httpretty.disable)
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
self.user = UserFactory.create()
self.register_get_user_response(self.user)
@@ -3153,6 +3202,22 @@ class UpdateCommentTest(
self.addCleanup(httpretty.reset)
self.addCleanup(httpretty.disable)
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
self.user = UserFactory.create()
self.register_get_user_response(self.user)
self.request = RequestFactory().get("/test_path")
@@ -3670,6 +3735,22 @@ class DeleteThreadTest(
httpretty.enable()
self.addCleanup(httpretty.reset)
self.addCleanup(httpretty.disable)
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
self.user = UserFactory.create()
self.register_get_user_response(self.user)
self.request = RequestFactory().get("/test_path")
@@ -3823,6 +3904,22 @@ class DeleteCommentTest(
httpretty.enable()
self.addCleanup(httpretty.reset)
self.addCleanup(httpretty.disable)
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
self.user = UserFactory.create()
self.register_get_user_response(self.user)
self.request = RequestFactory().get("/test_path")
@@ -3991,6 +4088,17 @@ class RetrieveThreadTest(
httpretty.enable()
self.addCleanup(httpretty.reset)
self.addCleanup(httpretty.disable)
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
self.user = UserFactory.create()
self.register_get_user_response(self.user)
self.request = RequestFactory().get("/test_path")

View File

@@ -54,6 +54,12 @@ class SerializerTestMixin(ForumsEnableMixin, CommentsServiceMockMixin, UrlResetM
httpretty.enable()
self.addCleanup(httpretty.reset)
self.addCleanup(httpretty.disable)
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
self.maxDiff = None # pylint: disable=invalid-name
self.user = UserFactory.create()
self.register_get_user_response(self.user)
@@ -571,6 +577,12 @@ class ThreadSerializerDeserializationTest(
httpretty.enable()
self.addCleanup(httpretty.reset)
self.addCleanup(httpretty.disable)
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
self.user = UserFactory.create()
self.register_get_user_response(self.user)
self.request = RequestFactory().get("/dummy")
@@ -802,6 +814,22 @@ class CommentSerializerDeserializationTest(ForumsEnableMixin, CommentsServiceMoc
httpretty.enable()
self.addCleanup(httpretty.reset)
self.addCleanup(httpretty.disable)
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
self.user = UserFactory.create()
self.register_get_user_response(self.user)
self.request = RequestFactory().get("/dummy")

View File

@@ -58,10 +58,27 @@ class TestNewThreadCreatedNotification(DiscussionAPIViewTestMixin, ModuleStoreTe
Setup test case
"""
super().setUp()
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
# Creating a course
self.course = CourseFactory.create()
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread",
return_value=self.course.id
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment",
return_value=self.course.id
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
# Creating relative discussion and cohort settings
CourseCohortsSettings.objects.create(course_id=str(self.course.id))
CourseDiscussionSettings.objects.create(course_id=str(self.course.id), _divided_discussions='[]')
@@ -250,8 +267,26 @@ class TestSendResponseNotifications(DiscussionAPIViewTestMixin, ModuleStoreTestC
super().setUp()
httpretty.reset()
httpretty.enable()
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
self.course = CourseFactory.create()
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread",
return_value=self.course.id
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment",
return_value=self.course.id
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
self.user_1 = UserFactory.create()
CourseEnrollment.enroll(self.user_1, self.course.id)
self.user_2 = UserFactory.create()
@@ -536,8 +571,26 @@ class TestSendCommentNotification(DiscussionAPIViewTestMixin, ModuleStoreTestCas
super().setUp()
httpretty.reset()
httpretty.enable()
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
self.course = CourseFactory.create()
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread",
return_value=self.course.id
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment",
return_value=self.course.id
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
self.user_1 = UserFactory.create()
CourseEnrollment.enroll(self.user_1, self.course.id)
self.user_2 = UserFactory.create()
@@ -603,8 +656,26 @@ class TestResponseEndorsedNotifications(DiscussionAPIViewTestMixin, ModuleStoreT
super().setUp()
httpretty.reset()
httpretty.enable()
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
self.course = CourseFactory.create()
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread",
return_value=self.course.id
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment",
return_value=self.course.id
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
self.user_1 = UserFactory.create()
CourseEnrollment.enroll(self.user_1, self.course.id)
self.user_2 = UserFactory.create()

View File

@@ -171,6 +171,12 @@ class UploadFileViewTest(ForumsEnableMixin, CommentsServiceMockMixin, UrlResetMi
self.user = UserFactory.create(password=self.TEST_PASSWORD)
self.course = CourseFactory.create(org='a', course='b', run='c', start=datetime.now(UTC))
self.url = reverse("upload_file", kwargs={"course_id": str(self.course.id)})
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
def user_login(self):
"""
@@ -301,6 +307,7 @@ class UploadFileViewTest(ForumsEnableMixin, CommentsServiceMockMixin, UrlResetMi
@ddt.ddt
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_FORUM_V2": False})
class CommentViewSetListByUserTest(
ForumsEnableMixin,
CommentsServiceMockMixin,
@@ -319,6 +326,12 @@ class CommentViewSetListByUserTest(
httpretty.enable()
self.addCleanup(httpretty.reset)
self.addCleanup(httpretty.disable)
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
self.user = UserFactory.create(password=self.TEST_PASSWORD)
self.register_get_user_response(self.user)
@@ -500,6 +513,12 @@ class CourseViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
def setUp(self):
super().setUp()
self.url = reverse("discussion_course", kwargs={"course_id": str(self.course.id)})
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
def test_404(self):
response = self.client.get(
@@ -561,6 +580,12 @@ class RetireViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
self.superuser_client = APIClient()
self.retired_username = get_retired_username_by_username(self.user.username)
self.url = reverse("retire_discussion_user")
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
def assert_response_correct(self, response, expected_status, expected_content):
"""
@@ -631,6 +656,12 @@ class ReplaceUsernamesViewTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
self.worker_client = APIClient()
self.new_username = "test_username_replacement"
self.url = reverse("replace_discussion_username")
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
def assert_response_correct(self, response, expected_status, expected_content):
"""
@@ -733,6 +764,12 @@ class CourseTopicsViewTest(DiscussionAPIViewTestMixin, CommentsServiceMockMixin,
"courseware-3": {"discussion": 7, "question": 2},
}
self.register_get_course_commentable_counts_response(self.course.id, self.thread_counts_map)
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
def create_course(self, blocks_count, module_store, topics):
"""
@@ -988,6 +1025,12 @@ class CourseTopicsViewV3Test(DiscussionAPIViewTestMixin, CommentsServiceMockMixi
patcher.start()
self.addCleanup(patcher.stop)
self.url = reverse("course_topics_v3", kwargs={"course_id": str(self.course.id)})
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
def test_basic(self):
response = self.client.get(self.url)
@@ -1024,6 +1067,12 @@ class ThreadViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pro
super().setUp()
self.author = UserFactory.create()
self.url = reverse("thread-list")
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
def create_source_thread(self, overrides=None):
"""
@@ -1365,6 +1414,12 @@ class ThreadViewSetCreateTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
def setUp(self):
super().setUp()
self.url = reverse("thread-list")
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
def test_basic(self):
self.register_get_user_response(self.user)
@@ -1437,6 +1492,17 @@ class ThreadViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTest
self.unsupported_media_type = JSONParser.media_type
super().setUp()
self.url = reverse("thread-detail", kwargs={"thread_id": "test_thread"})
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
def test_basic(self):
self.register_get_user_response(self.user)
@@ -1581,6 +1647,17 @@ class ThreadViewSetDeleteTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
super().setUp()
self.url = reverse("thread-detail", kwargs={"thread_id": "test_thread"})
self.thread_id = "test_thread"
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
def test_basic(self):
self.register_get_user_response(self.user)
@@ -1681,6 +1758,12 @@ class LearnerThreadViewAPITest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
]
self.url = reverse("discussion_learner_threads", kwargs={'course_id': str(self.course.id)})
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
def update_thread(self, thread):
"""
@@ -1923,6 +2006,17 @@ class CommentViewSetListTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase, Pr
self.url = reverse("comment-list")
self.thread_id = "test_thread"
self.storage = get_profile_image_storage()
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
def create_source_comment(self, overrides=None):
"""
@@ -2377,6 +2471,22 @@ class CommentViewSetDeleteTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
super().setUp()
self.url = reverse("comment-detail", kwargs={"comment_id": "test_comment"})
self.comment_id = "test_comment"
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
def test_basic(self):
self.register_get_user_response(self.user)
@@ -2416,6 +2526,23 @@ class CommentViewSetCreateTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
def setUp(self):
super().setUp()
self.url = reverse("comment-list")
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
def test_basic(self):
self.register_get_user_response(self.user)
@@ -2518,6 +2645,22 @@ class CommentViewSetPartialUpdateTest(DiscussionAPIViewTestMixin, ModuleStoreTes
httpretty.enable()
self.addCleanup(httpretty.reset)
self.addCleanup(httpretty.disable)
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
self.register_get_user_response(self.user)
self.url = reverse("comment-detail", kwargs={"comment_id": "test_comment"})
@@ -2640,6 +2783,22 @@ class ThreadViewSetRetrieveTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase,
super().setUp()
self.url = reverse("thread-detail", kwargs={"thread_id": "test_thread"})
self.thread_id = "test_thread"
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
def test_basic(self):
self.register_get_user_response(self.user)
@@ -2693,6 +2852,22 @@ class CommentViewSetRetrieveTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase
self.url = reverse("comment-detail", kwargs={"comment_id": "test_comment"})
self.thread_id = "test_thread"
self.comment_id = "test_comment"
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
def make_comment_data(self, comment_id, parent_id=None, children=[]): # pylint: disable=W0102
"""
@@ -2838,6 +3013,12 @@ class CourseDiscussionSettingsAPIViewTest(APITestCase, UrlResetMixin, ModuleStor
self.path = reverse('discussion_course_settings', kwargs={'course_id': str(self.course.id)})
self.password = self.TEST_PASSWORD
self.user = UserFactory(username='staff', password=self.password, is_staff=True)
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
def _get_oauth_headers(self, user):
"""Return the OAuth headers for testing OAuth authentication"""
@@ -3127,6 +3308,12 @@ class CourseDiscussionRolesAPIViewTest(APITestCase, UrlResetMixin, ModuleStoreTe
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super().setUp()
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
self.course = CourseFactory.create(
org="x",
course="y",
@@ -3318,6 +3505,12 @@ class CourseActivityStatsTest(ForumsEnableMixin, UrlResetMixin, CommentsServiceM
@mock.patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self) -> None:
super().setUp()
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
self.course = CourseFactory.create()
self.course_key = str(self.course.id)
seed_permissions_roles(self.course.id)

View File

@@ -232,6 +232,22 @@ class TaskTestCase(ModuleStoreTestCase): # lint-amnesty, pylint: disable=missin
thread_permalink = '/courses/discussion/dummy_discussion_id'
self.permalink_patcher = mock.patch('lms.djangoapps.discussion.tasks.permalink', return_value=thread_permalink)
self.mock_permalink = self.permalink_patcher.start()
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
def tearDown(self):
super().tearDown()

View File

@@ -4,6 +4,7 @@ Tests the forum notification views.
import json
import logging
from datetime import datetime
from unittest import mock
from unittest.mock import ANY, Mock, call, patch
import ddt
@@ -109,9 +110,20 @@ class ViewsExceptionTestCase(UrlResetMixin, ModuleStoreTestCase): # lint-amnest
config = ForumsConfig.current()
config.enabled = True
config.save()
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
@patch('common.djangoapps.student.models.user.cc.User.from_django_user')
@patch('common.djangoapps.student.models.user.cc.User.active_threads')
@patch('openedx.core.djangoapps.django_comment_common.comment_client.user.User.active_threads')
def test_user_profile_exception(self, mock_threads, mock_from_django_user):
# Mock the code that makes the HTTP requests to the cs_comment_service app
@@ -323,6 +335,17 @@ class SingleThreadTestCase(ForumsEnableMixin, ModuleStoreTestCase): # lint-amne
def setUp(self):
super().setUp()
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
self.course = CourseFactory.create(discussion_topics={'dummy discussion': {'id': 'dummy_discussion_id'}})
self.student = UserFactory.create()
@@ -513,6 +536,20 @@ class SingleThreadQueryCountTestCase(ForumsEnableMixin, ModuleStoreTestCase):
Ensures the number of modulestore queries and number of sql queries are
independent of the number of responses retrieved for a given discussion thread.
"""
def setUp(self):
super().setUp()
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
@ddt.data(
# split mongo: 3 queries, regardless of thread response size.
(False, 1, 2, 2, 21, 8),
@@ -582,6 +619,20 @@ class SingleThreadQueryCountTestCase(ForumsEnableMixin, ModuleStoreTestCase):
@patch('requests.request', autospec=True)
class SingleCohortedThreadTestCase(CohortedTestCase): # lint-amnesty, pylint: disable=missing-class-docstring
def setUp(self):
super().setUp()
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
def _create_mock_cohorted_thread(self, mock_request): # lint-amnesty, pylint: disable=missing-function-docstring
mock_text = "dummy content"
mock_thread_id = "test_thread_id"
@@ -644,6 +695,20 @@ class SingleCohortedThreadTestCase(CohortedTestCase): # lint-amnesty, pylint: d
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
class SingleThreadAccessTestCase(CohortedTestCase): # lint-amnesty, pylint: disable=missing-class-docstring
def setUp(self):
super().setUp()
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
def call_view(self, mock_request, commentable_id, user, group_id, thread_group_id=None, pass_group_id=True): # lint-amnesty, pylint: disable=missing-function-docstring
thread_id = "test_thread_id"
mock_request.side_effect = make_mock_request_impl(
@@ -746,6 +811,20 @@ class SingleThreadAccessTestCase(CohortedTestCase): # lint-amnesty, pylint: dis
class SingleThreadGroupIdTestCase(CohortedTestCase, GroupIdAssertionMixin): # lint-amnesty, pylint: disable=missing-class-docstring
cs_endpoint = "/threads/dummy_thread_id"
def setUp(self):
super().setUp()
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
def call_view(self, mock_request, commentable_id, user, group_id, pass_group_id=True, is_ajax=False): # lint-amnesty, pylint: disable=missing-function-docstring
mock_request.side_effect = make_mock_request_impl(
course=self.course, text="dummy context", group_id=self.student_cohort.id
@@ -881,6 +960,22 @@ class SingleThreadContentGroupTestCase(ForumsEnableMixin, UrlResetMixin, Content
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super().setUp()
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
def assert_can_access(self, user, discussion_id, thread_id, should_have_access):
"""
@@ -1046,6 +1141,7 @@ class InlineDiscussionContextTestCase(ForumsEnableMixin, ModuleStoreTestCase):
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
class InlineDiscussionGroupIdTestCase( # lint-amnesty, pylint: disable=missing-class-docstring
CohortedTestCase,
CohortedTopicGroupIdTestMixin,
@@ -1056,8 +1152,22 @@ class InlineDiscussionGroupIdTestCase( # lint-amnesty, pylint: disable=missing-
def setUp(self):
super().setUp()
self.cohorted_commentable_id = 'cohorted_topic'
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
def call_view(self, mock_request, commentable_id, user, group_id, pass_group_id=True):
def call_view(
self,
mock_is_forum_v2_enabled,
mock_request,
commentable_id,
user,
group_id,
pass_group_id=True
): # pylint: disable=arguments-differ
mock_is_forum_v2_enabled.return_value = False
kwargs = {'commentable_id': self.cohorted_commentable_id}
if group_id:
# avoid causing a server error when the LMS chokes attempting
@@ -1084,8 +1194,9 @@ class InlineDiscussionGroupIdTestCase( # lint-amnesty, pylint: disable=missing-
commentable_id
)
def test_group_info_in_ajax_response(self, mock_request):
def test_group_info_in_ajax_response(self, mock_is_forum_v2_enabled, mock_request):
response = self.call_view(
mock_is_forum_v2_enabled,
mock_request,
self.cohorted_commentable_id,
self.student,
@@ -1097,10 +1208,29 @@ class InlineDiscussionGroupIdTestCase( # lint-amnesty, pylint: disable=missing-
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
class ForumFormDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupIdTestMixin): # lint-amnesty, pylint: disable=missing-class-docstring
cs_endpoint = "/threads"
def call_view(self, mock_request, commentable_id, user, group_id, pass_group_id=True, is_ajax=False): # pylint: disable=arguments-differ
def setUp(self):
super().setUp()
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
def call_view(
self,
mock_is_forum_v2_enabled,
mock_request,
commentable_id,
user,
group_id,
pass_group_id=True,
is_ajax=False
): # pylint: disable=arguments-differ
mock_is_forum_v2_enabled.return_value = False
kwargs = {}
if group_id:
kwargs['group_id'] = group_id
@@ -1120,8 +1250,9 @@ class ForumFormDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupIdT
**headers
)
def test_group_info_in_html_response(self, mock_request):
def test_group_info_in_html_response(self, mock_is_forum_v2_enabled, mock_request):
response = self.call_view(
mock_is_forum_v2_enabled,
mock_request,
"cohorted_topic",
self.student,
@@ -1129,8 +1260,9 @@ class ForumFormDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupIdT
)
self._assert_html_response_contains_group_info(response)
def test_group_info_in_ajax_response(self, mock_request):
def test_group_info_in_ajax_response(self, mock_is_forum_v2_enabled, mock_request):
response = self.call_view(
mock_is_forum_v2_enabled,
mock_request,
"cohorted_topic",
self.student,
@@ -1143,16 +1275,38 @@ class ForumFormDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupIdT
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupIdTestMixin): # lint-amnesty, pylint: disable=missing-class-docstring
cs_endpoint = "/active_threads"
def setUp(self):
super().setUp()
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
def call_view_for_profiled_user(
self, mock_request, requesting_user, profiled_user, group_id, pass_group_id, is_ajax=False
self,
mock_is_forum_v2_enabled,
mock_request,
requesting_user,
profiled_user,
group_id,
pass_group_id,
is_ajax=False
):
"""
Calls "user_profile" view method on behalf of "requesting_user" to get information about
the user "profiled_user".
"""
mock_is_forum_v2_enabled.return_value = False
kwargs = {}
if group_id:
kwargs['group_id'] = group_id
@@ -1172,13 +1326,23 @@ class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupI
**headers
)
def call_view(self, mock_request, _commentable_id, user, group_id, pass_group_id=True, is_ajax=False): # pylint: disable=arguments-differ
def call_view(
self,
mock_is_forum_v2_enabled,
mock_request,
_commentable_id,
user,
group_id,
pass_group_id=True,
is_ajax=False
): # pylint: disable=arguments-differ
return self.call_view_for_profiled_user(
mock_request, user, user, group_id, pass_group_id=pass_group_id, is_ajax=is_ajax
mock_is_forum_v2_enabled, mock_request, user, user, group_id, pass_group_id=pass_group_id, is_ajax=is_ajax
)
def test_group_info_in_html_response(self, mock_request):
def test_group_info_in_html_response(self, mock_is_forum_v2_enabled, mock_request):
response = self.call_view(
mock_is_forum_v2_enabled,
mock_request,
"cohorted_topic",
self.student,
@@ -1187,8 +1351,9 @@ class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupI
)
self._assert_html_response_contains_group_info(response)
def test_group_info_in_ajax_response(self, mock_request):
def test_group_info_in_ajax_response(self, mock_is_forum_v2_enabled, mock_request):
response = self.call_view(
mock_is_forum_v2_enabled,
mock_request,
"cohorted_topic",
self.student,
@@ -1200,7 +1365,14 @@ class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupI
)
def _test_group_id_passed_to_user_profile(
self, mock_request, expect_group_id_in_request, requesting_user, profiled_user, group_id, pass_group_id
self,
mock_is_forum_v2_enabled,
mock_request,
expect_group_id_in_request,
requesting_user,
profiled_user,
group_id,
pass_group_id
):
"""
Helper method for testing whether or not group_id was passed to the user_profile request.
@@ -1221,10 +1393,11 @@ class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupI
has_course_id = "course_id" in params
if (for_specific_course and has_course_id) or (not for_specific_course and not has_course_id):
return params
pytest.fail("Did not find appropriate user_profile call for 'for_specific_course'=" + for_specific_course)
pytest.fail(f"Did not find appropriate user_profile call for 'for_specific_course'={for_specific_course}")
mock_request.reset_mock()
self.call_view_for_profiled_user(
mock_is_forum_v2_enabled,
mock_request,
requesting_user,
profiled_user,
@@ -1243,7 +1416,7 @@ class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupI
else:
assert 'group_id' not in params_with_course_id
def test_group_id_passed_to_user_profile_student(self, mock_request):
def test_group_id_passed_to_user_profile_student(self, mock_is_forum_v2_enabled, mock_request):
"""
Test that the group id is always included when requesting user profile information for a particular
course if the requester does not have discussion moderation privileges.
@@ -1254,7 +1427,13 @@ class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupI
(non-privileged user).
"""
self._test_group_id_passed_to_user_profile(
mock_request, True, self.student, profiled_user, self.student_cohort.id, pass_group_id
mock_is_forum_v2_enabled,
mock_request,
True,
self.student,
profiled_user,
self.student_cohort.id,
pass_group_id
)
# In all these test cases, the requesting_user is the student (non-privileged user).
@@ -1264,7 +1443,7 @@ class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupI
verify_group_id_always_present(profiled_user=self.moderator, pass_group_id=True)
verify_group_id_always_present(profiled_user=self.moderator, pass_group_id=False)
def test_group_id_user_profile_moderator(self, mock_request):
def test_group_id_user_profile_moderator(self, mock_is_forum_v2_enabled, mock_request):
"""
Test that the group id is only included when a privileged user requests user profile information for a
particular course and user if the group_id is explicitly passed in.
@@ -1274,7 +1453,13 @@ class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupI
Helper method to verify that group_id is present.
"""
self._test_group_id_passed_to_user_profile(
mock_request, True, self.moderator, profiled_user, requested_cohort.id, pass_group_id
mock_is_forum_v2_enabled,
mock_request,
True,
self.moderator,
profiled_user,
requested_cohort.id,
pass_group_id
)
def verify_group_id_not_present(profiled_user, pass_group_id, requested_cohort=self.moderator_cohort):
@@ -1282,7 +1467,13 @@ class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupI
Helper method to verify that group_id is not present.
"""
self._test_group_id_passed_to_user_profile(
mock_request, False, self.moderator, profiled_user, requested_cohort.id, pass_group_id
mock_is_forum_v2_enabled,
mock_request,
False,
self.moderator,
profiled_user,
requested_cohort.id,
pass_group_id
)
# In all these test cases, the requesting_user is the moderator (privileged user).
@@ -1301,10 +1492,28 @@ class UserProfileDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupI
@patch('openedx.core.djangoapps.django_comment_common.comment_client.utils.requests.request', autospec=True)
@patch('openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled', autospec=True)
class FollowedThreadsDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGroupIdTestMixin): # lint-amnesty, pylint: disable=missing-class-docstring
cs_endpoint = "/subscribed_threads"
def call_view(self, mock_request, commentable_id, user, group_id, pass_group_id=True):
def setUp(self):
super().setUp()
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
def call_view(
self,
mock_is_forum_v2_enabled,
mock_request,
commentable_id,
user,
group_id,
pass_group_id=True
): # pylint: disable=arguments-differ
mock_is_forum_v2_enabled.return_value = False
kwargs = {}
if group_id:
kwargs['group_id'] = group_id
@@ -1325,8 +1534,9 @@ class FollowedThreadsDiscussionGroupIdTestCase(CohortedTestCase, CohortedTopicGr
user.id
)
def test_group_info_in_ajax_response(self, mock_request):
def test_group_info_in_ajax_response(self, mock_is_forum_v2_enabled, mock_request):
response = self.call_view(
mock_is_forum_v2_enabled,
mock_request,
"cohorted_topic",
self.student,
@@ -1528,6 +1738,22 @@ class CommentsServiceRequestHeadersTestCase(ForumsEnableMixin, UrlResetMixin, Mo
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self):
super().setUp()
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.models.forum_api.get_course_id_by_comment"
)
self.mock_get_course_id_by_comment = patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
username = "foo"
password = "bar"
@@ -1742,6 +1968,20 @@ class SingleThreadUnicodeTestCase(ForumsEnableMixin, SharedModuleStoreTestCase,
with super().setUpClassAndTestData():
cls.course = CourseFactory.create(discussion_topics={'dummy_discussion_id': {'id': 'dummy_discussion_id'}})
def setUp(self):
super().setUp()
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
@classmethod
def setUpTestData(cls):
super().setUpTestData()
@@ -1858,7 +2098,17 @@ class EnterpriseConsentTestCase(EnterpriseTestConsentRequired, ForumsEnableMixin
def setUp(self):
# Invoke UrlResetMixin setUp
super().setUp()
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
username = "foo"
password = "bar"
@@ -2195,6 +2445,17 @@ class ThreadViewedEventTestCase(EventTestMixin, ForumsEnableMixin, UrlResetMixin
def setUp(self): # pylint: disable=arguments-differ
super().setUp('lms.djangoapps.discussion.django_comment_client.base.views.tracker')
patcher = mock.patch(
'openedx.core.djangoapps.discussions.config.waffle.ENABLE_FORUM_V2.is_enabled',
return_value=False
)
patcher.start()
self.addCleanup(patcher.stop)
patcher = mock.patch(
"openedx.core.djangoapps.django_comment_common.comment_client.thread.forum_api.get_course_id_by_thread"
)
self.mock_get_course_id_by_thread = patcher.start()
self.addCleanup(patcher.stop)
self.course = CourseFactory.create(
teams_configuration=TeamsConfig({
'topics': [{

View File

@@ -1,6 +1,7 @@
"""
Discussions feature toggles
"""
from openedx.core.djangoapps.discussions.config.waffle import WAFFLE_FLAG_NAMESPACE
from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
@@ -11,4 +12,6 @@ from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
# .. toggle_use_cases: temporary, open_edx
# .. toggle_creation_date: 2021-11-05
# .. toggle_target_removal_date: 2022-12-05
ENABLE_DISCUSSIONS_MFE = CourseWaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.enable_discussions_mfe', __name__)
ENABLE_DISCUSSIONS_MFE = CourseWaffleFlag(
f"{WAFFLE_FLAG_NAMESPACE}.enable_discussions_mfe", __name__
)