Files
edx-platform/lms/djangoapps/user_tours/v1/tests/test_views.py
Agrendalath 21f2e30354 feat: allow disabling user tours
A similar waffle flag was dropped in 20386337.
2024-02-07 17:14:52 +01:00

237 lines
10 KiB
Python

""" Tests for v1 User Tour views. """
import ddt
from django.contrib.auth import get_user_model
from django.db.models.signals import post_save
from django.test import TestCase, override_settings
from django.urls import reverse
from edx_toggles.toggles.testutils import override_waffle_flag
from rest_framework import status
from common.djangoapps.student.tests.factories import UserFactory
from lms.djangoapps.user_tours.handlers import init_user_tour
from lms.djangoapps.user_tours.models import UserTour, UserDiscussionsTours
from lms.djangoapps.user_tours.toggles import USER_TOURS_DISABLED
from openedx.core.djangoapps.oauth_dispatch.jwt import create_jwt_for_user
User = get_user_model()
def build_jwt_headers(user):
""" Helper function for creating headers for the JWT authentication. """
token = create_jwt_for_user(user)
headers = {'HTTP_AUTHORIZATION': f'JWT {token}'}
return headers
@ddt.ddt
class TestUserTourView(TestCase):
""" Tests for the v1 User Tour views. """
def setUp(self):
""" Test set up. """
super().setUp()
self.user = UserFactory()
self.existing_user_tour = self.user.tour
self.existing_user_tour.course_home_tour_status = UserTour.CourseHomeChoices.EXISTING_USER_TOUR
self.existing_user_tour.show_courseware_tour = False
self.existing_user_tour.save()
self.staff_user = UserFactory(is_staff=True)
self.new_user_tour = self.staff_user.tour
def send_request(self, jwt_user, request_user, method, data=None):
""" Helper function to call API. """
headers = build_jwt_headers(jwt_user)
url = reverse('user-tours', args=[request_user.username])
if method == 'GET':
return self.client.get(url, **headers)
elif method == 'PATCH':
return self.client.patch(url, data, content_type='application/json', **headers)
@ddt.data('GET', 'PATCH')
@override_waffle_flag(USER_TOURS_DISABLED, active=True)
def test_tours_disabled(self, method):
""" Test that the tours can be turned off with a waffle flag. """
response = self.send_request(self.staff_user, self.user, method)
assert response.status_code == status.HTTP_403_FORBIDDEN
@ddt.data('GET', 'PATCH')
def test_unauthorized_user(self, method):
""" Test all endpoints if request does not have jwt auth. """
url = reverse('user-tours', args=[self.user.username])
if method == 'GET':
response = self.client.get(url)
elif method == 'PATCH':
response = self.client.patch(url, data={})
assert response.status_code == status.HTTP_401_UNAUTHORIZED
def test_get_success(self):
""" Test GET request for a user. """
response = self.send_request(self.staff_user, self.staff_user, 'GET')
assert response.status_code == status.HTTP_200_OK
assert response.data == {
'course_home_tour_status': self.new_user_tour.course_home_tour_status,
'show_courseware_tour': self.new_user_tour.show_courseware_tour
}
def test_get_staff_user_for_other_user(self):
""" Test GET request for a staff user requesting info for another user. """
response = self.send_request(self.staff_user, self.user, 'GET')
assert response.status_code == status.HTTP_200_OK
assert response.data == {
'course_home_tour_status': self.existing_user_tour.course_home_tour_status,
'show_courseware_tour': self.existing_user_tour.show_courseware_tour
}
def test_get_user_for_other_user(self):
""" Test GET request for a regular user requesting info for another user. """
response = self.send_request(self.user, self.staff_user, 'GET')
assert response.status_code == status.HTTP_400_BAD_REQUEST
def test_get_nonexistent_user_tour(self):
"""
Test GET request for a non-existing user tour.
Note: In reality, this should never happen, but better safe than sorry
"""
# We need to disable the UserTour handler for this test since it will be automatically
# created otherwise.
post_save.disconnect(init_user_tour, sender=User)
response = self.send_request(self.staff_user, UserFactory(), 'GET')
assert response.status_code == status.HTTP_404_NOT_FOUND
post_save.connect(init_user_tour, sender=User)
def test_patch_success(self):
""" Test PATCH request for a user. """
tour = UserTour.objects.get(user=self.user)
assert tour.course_home_tour_status == UserTour.CourseHomeChoices.EXISTING_USER_TOUR
data = {'course_home_tour_status': UserTour.CourseHomeChoices.NO_TOUR}
response = self.send_request(self.user, self.user, 'PATCH', data=data)
assert response.status_code == status.HTTP_200_OK
tour.refresh_from_db()
assert tour.course_home_tour_status == UserTour.CourseHomeChoices.NO_TOUR
def test_patch_update_multiple_fields(self):
""" Test PATCH request for a user changing multiple fields. """
tour = UserTour.objects.get(user=self.staff_user)
assert tour.course_home_tour_status == UserTour.CourseHomeChoices.NEW_USER_TOUR
assert tour.show_courseware_tour is True
data = {
'course_home_tour_status': UserTour.CourseHomeChoices.NO_TOUR,
'show_courseware_tour': False
}
response = self.send_request(self.staff_user, self.staff_user, 'PATCH', data=data)
assert response.status_code == status.HTTP_200_OK
tour.refresh_from_db()
assert tour.course_home_tour_status == UserTour.CourseHomeChoices.NO_TOUR
assert tour.show_courseware_tour is False
def test_patch_user_for_other_user(self):
""" Test PATCH request for a user trying to change UserTour status for another user. """
data = {'course_home_tour_status': UserTour.CourseHomeChoices.NO_TOUR}
response = self.send_request(self.staff_user, self.user, 'PATCH', data=data)
assert response.status_code == status.HTTP_400_BAD_REQUEST
def test_patch_bad_data(self):
""" Test PATCH request for a request with bad data. """
# Invalid value
data = {'course_home_tour_status': 'blah'}
response = self.send_request(self.user, self.user, 'PATCH', data=data)
assert response.status_code == status.HTTP_400_BAD_REQUEST
assert response.data['course_home_tour_status'][0] == '"blah" is not a valid choice.'
# Invalid param, dropped from validated data so no update happens
data = {'user': 7}
response = self.send_request(self.user, self.user, 'PATCH', data=data)
assert response.status_code == status.HTTP_400_BAD_REQUEST
# Param that doesn't even exist on model, dropped from validated data so no update happens
data = {'foo': 'bar'}
response = self.send_request(self.user, self.user, 'PATCH', data=data)
assert response.status_code == status.HTTP_400_BAD_REQUEST
def test_put_not_supported(self):
""" Test PUT request returns method not supported. """
headers = build_jwt_headers(self.staff_user)
url = reverse('user-tours', args=[self.staff_user.username])
response = self.client.put(url, data={}, content_type='application/json', **headers)
assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED
@override_settings(AVAILABLE_DISCUSSION_TOURS=['not_responded_filter'])
class UserDiscussionsToursViewTestCase(TestCase):
"""
Tests for the UserDiscussionsToursView view.
"""
def setUp(self):
# Create a user and a tour for testing
self.user = User.objects.create_user(
username='testuser',
password='testpass'
)
# create a UserDiscussionsTour for the user
self.tour = UserDiscussionsTours.objects.create(
tour_name='Test Tour',
show_tour=False,
user=self.user
)
self.url = reverse('discussion-tours')
def test_get_tours(self):
"""
Test GET request for a user's discussion tours.
"""
# create a header with our user's credentials
headers = build_jwt_headers(self.user)
# Send a GET request to the view
response = self.client.get(self.url, **headers)
# Check that the response status code is correct
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Check that the returned data is correct
self.assertEqual(len(response.data), 2)
self.assertEqual(response.data[0]['tour_name'], 'Test Tour')
self.assertFalse(response.data[0]['show_tour'])
self.assertEqual(response.data[1]['tour_name'], 'not_responded_filter')
self.assertTrue(response.data[1]['show_tour'])
# Test that the view can be disabled by a waffle flag.
with override_waffle_flag(USER_TOURS_DISABLED, active=True):
response = self.client.get(self.url, **headers)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
def test_get_tours_unauthenticated(self):
"""
Test that an unauthenticated user cannot access the discussion tours endpoint.
"""
# Send a GET request to the view without logging in
response = self.client.get(self.url)
# Check that the response status code is correct
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test_update_tour(self):
"""
Test that a user can update their discussion tour status.
"""
headers = build_jwt_headers(self.user)
# Send a PUT request to the view with the updated tour data
updated_data = {'show_tour': False}
url = reverse('discussion-tours', args=[self.tour.id])
response = self.client.put(url, updated_data, content_type='application/json', **headers)
# Check that the response status code is correct
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Check that the tour was updated in the database
updated_tour = UserDiscussionsTours.objects.get(id=self.tour.id)
self.assertEqual(updated_tour.show_tour, False)
# Test that the view can be disabled by a waffle flag.
with override_waffle_flag(USER_TOURS_DISABLED, active=True):
response = self.client.put(url, updated_data, content_type='application/json', **headers)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)