diff --git a/eox_core/api/v1/tests/integration/data/fake_users.py b/eox_core/api/v1/tests/integration/data/fake_users.py index 1329137f..88dbd02b 100644 --- a/eox_core/api/v1/tests/integration/data/fake_users.py +++ b/eox_core/api/v1/tests/integration/data/fake_users.py @@ -1,4 +1,5 @@ """Fake user data for testing purposes.""" +# pylint: disable=too-many-lines FAKE_USER_DATA = iter( [ @@ -678,5 +679,343 @@ "city": "Sacramento", "goals": "Praesent vestibulum dapibus nibh.", }, + { + "username": "ksmith45", + "email": "ksmith45@outlook.com", + "fullname": "Kathy Smith", + "password": "rB3@9Pq!vL$", + "activate_user": True, + "mailing_address": "192 Maple Drive", + "year_of_birth": 1983, + "gender": "f", + "level_of_education": "b", + "city": "Seattle", + "goals": "Curabitur ullamcorper ultricies nisi.", + }, + { + "username": "mdavis32", + "email": "mdavis32@yahoo.com", + "fullname": "Mark Davis", + "password": "eT1&4Xm!hK%", + "activate_user": False, + "mailing_address": "74 Oak Avenue", + "year_of_birth": 1990, + "gender": "m", + "level_of_education": "m", + "city": "Chicago", + "goals": "Etiam sit amet orci eget eros faucibus tincidunt.", + }, + { + "username": "llee27", + "email": "llee27@gmail.com", + "fullname": "Linda Lee", + "password": "gD5@8Fk!rP#", + "activate_user": True, + "mailing_address": "10 Cedar Street", + "year_of_birth": 1979, + "gender": "f", + "level_of_education": "p", + "city": "San Francisco", + "goals": "Nullam dictum felis eu pede mollis pretium.", + }, + { + "username": "rjohnson29", + "email": "rjohnson29@protonmail.com", + "fullname": "Robert Johnson", + "password": "uF2@6Kl!sY@", + "activate_user": False, + "mailing_address": "58 Elm Street", + "year_of_birth": 1985, + "gender": "m", + "level_of_education": "b", + "city": "Houston", + "goals": "Sed cursus turpis vitae tortor.", + }, + { + "username": "phall33", + "email": "phall33@icloud.com", + "fullname": "Patricia Hall", + "password": "iR6&9Gv!cB%", + "activate_user": True, + "mailing_address": "43 Willow Court", + "year_of_birth": 1992, + "gender": "f", + "level_of_education": "hs", + "city": "Denver", + "goals": "Cras ultricies mi eu turpis.", + }, + { + "username": "bmoore38", + "email": "bmoore38@live.com", + "fullname": "Brian Moore", + "password": "hL3@7Op!qV#", + "activate_user": False, + "mailing_address": "71 Ash Street", + "year_of_birth": 1987, + "gender": "m", + "level_of_education": "m", + "city": "Phoenix", + "goals": "Pellentesque habitant morbi tristique senectus.", + }, + { + "username": "cdavis41", + "email": "cdavis41@gmail.com", + "fullname": "Cindy Davis", + "password": "dB1&5Np!kM$", + "activate_user": True, + "mailing_address": "12 Poplar Lane", + "year_of_birth": 1981, + "gender": "f", + "level_of_education": "b", + "city": "San Diego", + "goals": "In dui magna, posuere eget, vestibulum et, tempor auctor.", + }, + { + "username": "jrobinson25", + "email": "jrobinson25@protonmail.com", + "fullname": "John Robinson", + "password": "qT4@8Sv!wD%", + "activate_user": False, + "mailing_address": "6 Maple Lane", + "year_of_birth": 1997, + "gender": "m", + "level_of_education": "hs", + "city": "Los Angeles", + "goals": "Vestibulum ante ipsum primis in faucibus.", + }, + { + "username": "sclark37", + "email": "sclark37@hotmail.com", + "fullname": "Susan Clark", + "password": "oX5@2Yv!bJ#", + "activate_user": True, + "mailing_address": "82 Pine Avenue", + "year_of_birth": 1984, + "gender": "f", + "level_of_education": "hs", + "city": "Miami", + "goals": "Aliquam lorem ante, dapibus in, viverra quis.", + }, + { + "username": "wwilson26", + "email": "wwilson26@outlook.com", + "fullname": "William Wilson", + "password": "vN8@7Lp!tC@", + "activate_user": False, + "mailing_address": "27 Oak Circle", + "year_of_birth": 1993, + "gender": "m", + "level_of_education": "b", + "city": "Boston", + "goals": "Phasellus viverra nulla ut metus varius laoreet.", + }, + { + "username": "jmartinez30", + "email": "jmartinez30@gmail.com", + "fullname": "Jennifer Martinez", + "password": "wK3@9Xn!dF$", + "activate_user": True, + "mailing_address": "95 Birch Boulevard", + "year_of_birth": 1991, + "gender": "f", + "level_of_education": "hs", + "city": "Dallas", + "goals": "Etiam rhoncus. Maecenas tempus, tellus eget.", + }, + { + "username": "tharris31", + "email": "tharris31@live.com", + "fullname": "Thomas Harris", + "password": "eQ5@2Gm!pX%", + "activate_user": False, + "mailing_address": "39 Spruce Avenue", + "year_of_birth": 1994, + "gender": "m", + "level_of_education": "m", + "city": "Las Vegas", + "goals": "Fusce vulputate eleifend sapien.", + }, + { + "username": "jjones24", + "email": "jjones24@protonmail.com", + "fullname": "Jessica Jones", + "password": "uN4@7Dz!cR@", + "activate_user": True, + "mailing_address": "51 Willow Drive", + "year_of_birth": 1989, + "gender": "f", + "level_of_education": "b", + "city": "Austin", + "goals": "Etiam sit amet orci eget eros faucibus.", + }, + { + "username": "rthomas28", + "email": "rthomas28@gmail.com", + "fullname": "Richard Thomas", + "password": "iM7&5Lq!vB$", + "activate_user": False, + "mailing_address": "36 Poplar Road", + "year_of_birth": 1992, + "gender": "m", + "level_of_education": "hs", + "city": "Orlando", + "goals": "Quisque rutrum. Aenean imperdiet.", + }, + { + "username": "lwilliams40", + "email": "lwilliams40@yahoo.com", + "fullname": "Laura Williams", + "password": "dB9@2Km!eC@", + "activate_user": True, + "mailing_address": "78 Cedar Court", + "year_of_birth": 1986, + "gender": "f", + "level_of_education": "m", + "city": "Atlanta", + "goals": "Maecenas tempus, tellus eget.", + }, + { + "username": "pdavis36", + "email": "pdavis36@live.com", + "fullname": "Paul Davis", + "password": "oX4@7Zn!rY%", + "activate_user": False, + "mailing_address": "15 Ash Road", + "year_of_birth": 1990, + "gender": "m", + "level_of_education": "b", + "city": "Detroit", + "goals": "Aenean commodo ligula eget dolor.", + }, + { + "username": "jmartin39", + "email": "jmartin39@protonmail.com", + "fullname": "Joan Martin", + "password": "eQ1@5Lv!kW#", + "activate_user": True, + "mailing_address": "55 Pine Drive", + "year_of_birth": 1983, + "gender": "f", + "level_of_education": "p", + "city": "San Antonio", + "goals": "Vestibulum dapibus nunc ac augue.", + }, + { + "username": "msmith25", + "email": "msmith25@outlook.com", + "fullname": "Michael Smith", + "password": "hL2@8Mz!kN%", + "activate_user": True, + "mailing_address": "22 Maple Street", + "year_of_birth": 1995, + "gender": "m", + "level_of_education": "b", + "city": "New York", + "goals": "Cras ultricies mi eu turpis hendrerit fringilla.", + }, + { + "username": "tclark27", + "email": "tclark27@gmail.com", + "fullname": "Tina Clark", + "password": "nJ4@6Pc!vQ#", + "activate_user": False, + "mailing_address": "47 Elm Lane", + "year_of_birth": 1997, + "gender": "f", + "level_of_education": "hs", + "city": "Los Angeles", + "goals": "Aliquam lorem ante, dapibus in, viverra quis.", + }, + { + "username": "kwright29", + "email": "kwright29@yahoo.com", + "fullname": "Kevin Wright", + "password": "pC5@7Df!yT%", + "activate_user": True, + "mailing_address": "34 Cypress Way", + "year_of_birth": 1990, + "gender": "m", + "level_of_education": "m", + "city": "Chicago", + "goals": "Sed cursus turpis vitae tortor.", + }, + { + "username": "alee35", + "email": "alee35@protonmail.com", + "fullname": "Alice Lee", + "password": "mK3@4Gv!uH@", + "activate_user": False, + "mailing_address": "67 Fir Street", + "year_of_birth": 1992, + "gender": "f", + "level_of_education": "b", + "city": "Houston", + "goals": "Nulla facilisi. Integer lacinia sollicitudin massa.", + }, + { + "username": "jmiller38", + "email": "jmiller38@live.com", + "fullname": "James Miller", + "password": "qL6@9Tw!xV%", + "activate_user": True, + "mailing_address": "89 Oakwood Avenue", + "year_of_birth": 1987, + "gender": "m", + "level_of_education": "hs", + "city": "Philadelphia", + "goals": "Vestibulum ante ipsum primis in faucibus orci luctus.", + }, + { + "username": "klopez32", + "email": "klopez32@gmail.com", + "fullname": "Karen Lopez", + "password": "oX8@3Vp!sR#", + "activate_user": False, + "mailing_address": "90 Pine Crescent", + "year_of_birth": 1989, + "gender": "f", + "level_of_education": "m", + "city": "Phoenix", + "goals": "Donec pede justo, fringilla vel, aliquet nec.", + }, + { + "username": "djohnson23", + "email": "djohnson23@outlook.com", + "fullname": "Daniel Johnson", + "password": "yR5@7Qz!vE%", + "activate_user": True, + "mailing_address": "12 Magnolia Road", + "year_of_birth": 1985, + "gender": "m", + "level_of_education": "b", + "city": "San Francisco", + "goals": "In enim justo, rhoncus ut, imperdiet a.", + }, + { + "username": "hsanchez28", + "email": "hsanchez28@live.com", + "fullname": "Helen Sanchez", + "password": "bD6@9Pw!fC%", + "activate_user": False, + "mailing_address": "56 Palm Boulevard", + "year_of_birth": 1991, + "gender": "f", + "level_of_education": "hs", + "city": "Seattle", + "goals": "Nam quam nunc, blandit vel, luctus pulvinar.", + }, + { + "username": "cwilliams34", + "email": "cwilliams34@protonmail.com", + "fullname": "Charles Williams", + "password": "cM4@2Xf!gY#", + "activate_user": True, + "mailing_address": "101 Birch Lane", + "year_of_birth": 1988, + "gender": "m", + "level_of_education": "p", + "city": "Denver", + "goals": "Maecenas nec odio et ante tincidunt tempus.", + }, ] ) diff --git a/eox_core/api/v1/tests/integration/test_views.py b/eox_core/api/v1/tests/integration/test_views.py index 61699628..1cbd2479 100644 --- a/eox_core/api/v1/tests/integration/test_views.py +++ b/eox_core/api/v1/tests/integration/test_views.py @@ -119,7 +119,7 @@ def get_tenant_data(self, prefix: str = "") -> dict: class UsersAPIRequestMixin: """ - Mixin class for the API request methods. + Mixin class for the Users API request methods. """ USER_URL = f"{settings['EOX_CORE_API_BASE']}{reverse('eox-api:eox-api:edxapp-user')}" @@ -166,35 +166,35 @@ def update_user(self, tenant: dict, data: dict) -> requests.Response: class EnrollmentAPIRequestMixin: - """Mixin class for the API request methods.""" + """Mixin class for the Enrollment API request methods.""" ENROLLMENT_URL = f"{settings['EOX_CORE_API_BASE']}{reverse('eox-api:eox-api:edxapp-enrollment')}" def create_enrollment(self, tenant: dict, data: dict) -> requests.Response: """ - Create a new user in a tenant. + Create a new enrollment in a tenant. Args: tenant (dict): The tenant data. - data (dict): The user data. + data (dict): The enrollment data. Returns: requests.Response: The response object. """ return make_request(tenant, "POST", url=self.ENROLLMENT_URL, data=data) - def get_enrollment(self, tenant: dict, data: dict | None = None) -> requests.Response: + def get_enrollment(self, tenant: dict, params: dict | None = None) -> requests.Response: """ - Get a user in a tenant by username or email. + Get an enrollment in a tenant. Args: tenant (dict): The tenant data. - data (dict, optional): The body data for the request. + params (dict, optional): The query parameters for the request. Returns: requests.Response: The response object. """ - return make_request(tenant, "GET", url=self.ENROLLMENT_URL, data=data) + return make_request(tenant, "GET", url=self.ENROLLMENT_URL, params=params) def update_enrollment(self, tenant: dict, data: dict | None = None) -> requests.Response: """ @@ -223,6 +223,64 @@ def delete_enrollment(self, tenant: dict, data: dict | None = None) -> requests. return make_request(tenant, "DELETE", url=self.ENROLLMENT_URL, data=data) +class PreEnrollmentAPIRequestMixin: + """Mixin class for the Pre Enrollments API request methods.""" + + PRE_ENROLLMENT_URL = f"{settings['EOX_CORE_API_BASE']}{reverse('eox-api:eox-api:edxapp-pre-enrollment')}" + + def create_pre_enrollment(self, tenant: dict, data: dict) -> requests.Response: + """ + Create a new pre-enrollment in a tenant. + + Args: + tenant (dict): The tenant data. + data (dict): The pre-enrollment data. + + Returns: + requests.Response: The response object. + """ + return make_request(tenant, "POST", url=self.PRE_ENROLLMENT_URL, data=data) + + def get_pre_enrollment(self, tenant: dict, params: dict | None = None) -> requests.Response: + """ + Get a pre-enrollment in a tenant by username or email. + + Args: + tenant (dict): The tenant data. + params (dict, optional): The query parameters for the request. + + Returns: + requests.Response: The response object. + """ + return make_request(tenant, "GET", url=self.PRE_ENROLLMENT_URL, params=params) + + def update_pre_enrollment(self, tenant: dict, data: dict | None = None) -> requests.Response: + """ + Update an pre-enrollment in a tenant. + + Args: + tenant (dict): The tenant data. + data (dict, optional): The body data for the request. + + Returns: + requests.Response: The response object. + """ + return make_request(tenant, "PUT", url=self.PRE_ENROLLMENT_URL, data=data) + + def delete_pre_enrollment(self, tenant: dict, data: dict | None = None) -> requests.Response: + """ + Delete an pre-enrollment in a tenant. + + Args: + tenant (dict): The tenant data. + data (dict, optional): The body data for the request. + + Returns: + requests.Response: The response object. + """ + return make_request(tenant, "DELETE", url=self.PRE_ENROLLMENT_URL, data=data) + + @ddt.ddt class TestUsersAPIIntegration(BaseAPIIntegrationTest, UsersAPIRequestMixin): """Integration test suite for the Users API""" @@ -483,7 +541,7 @@ def test_create_enrollment_success(self, param: str, force_value: bool) -> None: self.assertEqual(response_data["course_id"], enrollment_data["course_id"]) self.assertTrue(response_data["is_active"]) self.assertIn("created", response_data) - enrollment_response = self.get_enrollment(self.tenant_x, data=enrollment_data) + enrollment_response = self.get_enrollment(self.tenant_x, enrollment_data) self.assertEqual(enrollment_response.status_code, status.HTTP_200_OK) @ddt.data( @@ -517,7 +575,7 @@ def test_create_enrollment_missing_required_fields(self, param: str, error: list response_data = response.json() self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(response_data, error) - enrollment_response = self.get_enrollment(self.tenant_x, data=enrollment_data_copy) + enrollment_response = self.get_enrollment(self.tenant_x, enrollment_data_copy) self.assertEqual(enrollment_response.status_code, status.HTTP_404_NOT_FOUND) @ddt.data( @@ -549,7 +607,7 @@ def test_create_valid_course_mode_invalid_user(self, param: str, value: str) -> self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertIn("non_field_errors", response_data) self.assertEqual(response_data["non_field_errors"], ["User not found"]) - enrollment_response = self.get_enrollment(self.tenant_x, data=enrollment_data) + enrollment_response = self.get_enrollment(self.tenant_x, enrollment_data) self.assertEqual(enrollment_response.status_code, status.HTTP_404_NOT_FOUND) @ddt.data("email", "username") @@ -581,7 +639,7 @@ def test_create_valid_course_mode_invalid_user_for_tenant(self, param: str) -> N response_data["error"]["detail"], f"No user found by {{'{param}': '{enrollment_data[param]}'}} on site {self.tenant_x['domain']}.", ) - enrollment_response = self.get_enrollment(self.tenant_x, data=enrollment_data) + enrollment_response = self.get_enrollment(self.tenant_x, enrollment_data) self.assertEqual(enrollment_response.status_code, status.HTTP_404_NOT_FOUND) @ddt.data("email", "username") @@ -609,7 +667,7 @@ def test_create_valid_user_mode_invalid_course(self, param: str) -> None: self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertIn("non_field_errors", response_data) self.assertEqual(response_data["non_field_errors"], ["Course not found"]) - enrollment_response = self.get_enrollment(self.tenant_x, data=enrollment_data) + enrollment_response = self.get_enrollment(self.tenant_x, enrollment_data) self.assertEqual(enrollment_response.status_code, status.HTTP_404_NOT_FOUND) @ddt.data("email", "username") @@ -638,7 +696,7 @@ def test_create_valid_user_mode_invalid_course_for_tenant(self, param: str) -> N self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertIn("course_id", response_data) self.assertEqual(response_data["course_id"], [f"Invalid course_id {self.demo_course_id}"]) - enrollment_response = self.get_enrollment(self.tenant_x, data=enrollment_data) + enrollment_response = self.get_enrollment(self.tenant_x, enrollment_data) self.assertEqual(enrollment_response.status_code, status.HTTP_404_NOT_FOUND) @ddt.data("email", "username") @@ -667,7 +725,7 @@ def test_create_valid_user_course_unavailable_mode(self, param: str) -> None: self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertIn("non_field_errors", response_data) self.assertEqual(response_data["non_field_errors"], ["Mode not found"]) - enrollment_response = self.get_enrollment(self.tenant_x, data=enrollment_data) + enrollment_response = self.get_enrollment(self.tenant_x, enrollment_data) self.assertEqual(enrollment_response.status_code, status.HTTP_404_NOT_FOUND) @ddt.data("email", "username") @@ -703,7 +761,7 @@ def test_force_create_valid_user_course_unavailable_mode(self, param: str) -> No self.assertEqual(response_data["course_id"], enrollment_data["course_id"]) self.assertTrue(response_data["is_active"]) self.assertIn("created", response_data) - enrollment_response = self.get_enrollment(self.tenant_x, data=enrollment_data) + enrollment_response = self.get_enrollment(self.tenant_x, enrollment_data) self.assertEqual(enrollment_response.status_code, status.HTTP_200_OK) @ddt.data("email", "username") @@ -726,7 +784,7 @@ def test_get_enrollment_success(self, param: str) -> None: } self.create_enrollment(self.tenant_x, enrollment_data) - response = self.get_enrollment(self.tenant_x, data=enrollment_data) + response = self.get_enrollment(self.tenant_x, enrollment_data) response_data = response.json() self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -754,7 +812,7 @@ def test_get_enrollment_does_not_exist(self, param: str) -> None: "course_id": self.demo_course_id, } - response = self.get_enrollment(self.tenant_x, data=enrollment_data) + response = self.get_enrollment(self.tenant_x, enrollment_data) response_data = response.json() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) @@ -779,7 +837,7 @@ def test_get_enrollment_not_found_in_tenant(self, param: str) -> None: } self.create_enrollment(self.tenant_x, enrollment_data) - response = self.get_enrollment(self.tenant_y, data=enrollment_data) + response = self.get_enrollment(self.tenant_y, enrollment_data) response_data = response.json() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) @@ -809,10 +867,10 @@ def test_delete_enrollment_success(self, param: str) -> None: } self.create_enrollment(self.tenant_x, enrollment_data) - response = self.delete_enrollment(self.tenant_x, data=enrollment_data) + response = self.delete_enrollment(self.tenant_x, enrollment_data) self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) - enrollment_response = self.get_enrollment(self.tenant_x, data=enrollment_data) + enrollment_response = self.get_enrollment(self.tenant_x, enrollment_data) self.assertEqual(enrollment_response.status_code, status.HTTP_404_NOT_FOUND) @ddt.data("email", "username") @@ -833,7 +891,7 @@ def test_delete_enrollment_does_not_exist(self, param: str) -> None: "course_id": self.demo_course_id, } - response = self.delete_enrollment(self.tenant_x, data=enrollment_data) + response = self.delete_enrollment(self.tenant_x, enrollment_data) response_data = response.json() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) @@ -864,7 +922,7 @@ def test_delete_invalid_enrollment_for_tenant(self, param: str) -> None: } self.create_enrollment(self.tenant_x, enrollment_data) - response = self.delete_enrollment(self.tenant_y, data=enrollment_data) + response = self.delete_enrollment(self.tenant_y, enrollment_data) response_data = response.json() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) @@ -873,7 +931,7 @@ def test_delete_invalid_enrollment_for_tenant(self, param: str) -> None: response_data["detail"], f"No user found by {{'{param}': '{self.user[param]}'}} on site {self.tenant_y['domain']}.", ) - enrollment_response = self.get_enrollment(self.tenant_x, data=enrollment_data) + enrollment_response = self.get_enrollment(self.tenant_x, enrollment_data) self.assertEqual(enrollment_response.status_code, status.HTTP_200_OK) @ddt.data("email", "username") @@ -899,7 +957,7 @@ def test_update_valid_enrollment_change_is_active_mode_field(self, param: str) - enrollment_data["is_active"] = True enrollment_data["mode"] = "honor" - response = self.update_enrollment(self.tenant_x, data=enrollment_data) + response = self.update_enrollment(self.tenant_x, enrollment_data) response_data = response.json() self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -907,7 +965,7 @@ def test_update_valid_enrollment_change_is_active_mode_field(self, param: str) - self.assertEqual(response_data["course_id"], enrollment_data["course_id"]) self.assertEqual(response_data["mode"], enrollment_data["mode"]) self.assertTrue(response_data["is_active"]) - enrollment_response = self.get_enrollment(self.tenant_x, data=enrollment_data) + enrollment_response = self.get_enrollment(self.tenant_x, enrollment_data) enrollment_response_data = enrollment_response.json() self.assertEqual(enrollment_response.status_code, status.HTTP_200_OK) self.assertEqual(enrollment_response_data["mode"], enrollment_data["mode"]) @@ -937,13 +995,13 @@ def test_update_valid_enrollment_update_unavailable_mode(self, param: str) -> No self.create_enrollment(self.tenant_x, enrollment_data) enrollment_data["mode"] = "masters" - response = self.update_enrollment(self.tenant_x, data=enrollment_data) + response = self.update_enrollment(self.tenant_x, enrollment_data) response_data = response.json() self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertIn("non_field_errors", response_data) self.assertEqual(response_data["non_field_errors"], ["Mode not found"]) - enrollment_response = self.get_enrollment(self.tenant_x, data=enrollment_data_copy) + enrollment_response = self.get_enrollment(self.tenant_x, enrollment_data_copy) enrollment_response_data = enrollment_response.json() self.assertEqual(enrollment_response.status_code, status.HTTP_200_OK) self.assertEqual(enrollment_response_data["mode"], self.mode) @@ -968,12 +1026,12 @@ def test_update_enrollment_does_not_exist(self, param: str) -> None: "mode": self.mode, } - response = self.update_enrollment(self.tenant_x, data=enrollment_data) + response = self.update_enrollment(self.tenant_x, enrollment_data) response_data = response.json() self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) self.assertEqual(response_data["error"]["detail"], f"No enrollment found for {self.user['username']}") - enrollment_response = self.get_enrollment(self.tenant_x, data=enrollment_data) + enrollment_response = self.get_enrollment(self.tenant_x, enrollment_data) self.assertEqual(enrollment_response.status_code, status.HTTP_404_NOT_FOUND) @ddt.data("email", "username") @@ -1011,13 +1069,454 @@ def test_update_valid_enrollment_using_force_flag(self, param: str) -> None: self.assertEqual(response_data["course_id"], enrollment_data["course_id"]) self.assertEqual(response_data["mode"], enrollment_data["mode"]) self.assertTrue(response_data["is_active"]) - enrollment_response = self.get_enrollment(self.tenant_x, data=enrollment_data) + enrollment_response = self.get_enrollment(self.tenant_x, enrollment_data) enrollment_response_data = enrollment_response.json() self.assertEqual(enrollment_response.status_code, status.HTTP_200_OK) self.assertEqual(enrollment_response_data["mode"], enrollment_data["mode"]) self.assertTrue(enrollment_response_data["is_active"]) +@ddt.ddt +class TestPreEnrollmentAPIIntegration( + BaseAPIIntegrationTest, + UsersAPIRequestMixin, + PreEnrollmentAPIRequestMixin, +): + """Integration test suite for the Pre-Enrollment API""" + + def setUp(self) -> None: + """Set up the test suite""" + super().setUp() + self.user = next(FAKE_USER_DATA) + self.create_user(self.tenant_x, self.user) + + @ddt.data( + {"auto_enroll": True}, + {"auto_enroll": False}, + {"email": "user-not-found1@mail.com", "auto_enroll": True}, + {"email": "user-not-found2@mail.com", "auto_enroll": False}, + ) + def test_create_pre_enrollment_success(self, data: dict) -> None: + """ + Test creating an pre-enrollment with a valid course and a user in a tenant. + + The pre-enrollment is created even if the user does not exist in the tenant. + + Open edX definitions tested: + - `validate_org` + - `get_valid_course_key` + - `CourseEnrollmentAllowed` + - `get_courseware_courses` + + Expected result: + - The status code is 200. + - The response contains the pre-enrollment data. + - The pre-enrollment is created in the tenant. + """ + pre_enrollment_data = { + "course_id": self.demo_course_id, + "email": data.get("email") or self.user["email"], + "auto_enroll": data.get("auto_enroll"), + } + + response = self.create_pre_enrollment(self.tenant_x, pre_enrollment_data) + response_data = response.json() + pre_enrollment_response = self.get_pre_enrollment(self.tenant_x, pre_enrollment_data) + pre_enrollment_response_data = pre_enrollment_response.json() + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertNotIn("warning", response_data) + self.assertEqual(response_data["email"], pre_enrollment_data["email"]) + self.assertEqual(response_data["course_id"], pre_enrollment_data["course_id"]) + self.assertEqual(response_data["auto_enroll"], pre_enrollment_data["auto_enroll"]) + self.assertEqual(pre_enrollment_response.status_code, status.HTTP_200_OK) + self.assertEqual(pre_enrollment_response_data["email"], pre_enrollment_data["email"]) + self.assertEqual(pre_enrollment_response_data["course_id"], pre_enrollment_data["course_id"]) + self.assertEqual(pre_enrollment_response_data["auto_enroll"], pre_enrollment_data["auto_enroll"]) + + @ddt.data( + {"course_id": ["This field is required."]}, + {"email": ["This field is required."]}, + {"course_id": ["This field is required."], "email": ["This field is required."]}, + ) + def test_create_pre_enrollment_missing_required_fields(self, errors: dict) -> None: + """ + Test creating an pre-enrollment with missing required fields. + + Open edX definitions tested: + - `validate_org` + - `get_valid_course_key` + + Expected result: + - The status code is 400. + - The response contains a message about the missing fields. + - The pre-enrollment is not created in the tenant. + """ + complete_pre_enrollment_data = { + "email": self.user["email"], + "course_id": self.demo_course_id, + } + incomplete_pre_enrollment_data = { + key: value for key, value in complete_pre_enrollment_data.items() if key not in errors + } + + response = self.create_pre_enrollment(self.tenant_x, incomplete_pre_enrollment_data) + response_data = response.json() + pre_enrollment_response = self.get_pre_enrollment(self.tenant_x, complete_pre_enrollment_data) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response_data, errors) + self.assertEqual(pre_enrollment_response.status_code, status.HTTP_404_NOT_FOUND) + + def test_create_pre_enrollmet_already_exists(self) -> None: + """ + Test creating an pre-enrollment that already exists in a tenant. + + Open edX definitions tested: + - `validate_org` + - `get_valid_course_key` + - `CourseEnrollmentAllowed` + - `get_courseware_courses` + + Expected result: + - The status code is 404. + - The response contains an error message about the pre-enrollment already existing. + - The pre-enrollment is not created in the tenant. + """ + pre_enrollment_data = { + "email": self.user["email"], + "course_id": self.demo_course_id, + } + self.create_pre_enrollment(self.tenant_x, pre_enrollment_data) + + response = self.create_pre_enrollment(self.tenant_x, pre_enrollment_data) + response_data = response.json() + + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertEqual( + response_data["detail"], + f"Pre-enrollment already exists for email: {self.user['email']} course_id: {self.demo_course_id}", + ) + + @ddt.data( + {"auto_enroll": True}, + {"auto_enroll": False}, + {"email": "user-not-found1@mail.com", "auto_enroll": True}, + {"email": "user-not-found2@mail.com", "auto_enroll": False}, + ) + def test_create_pre_enrollment_non_existent_course(self, data: dict) -> None: + """ + Test creating an pre-enrollment with a non-existent course in a tenant. + + Open edX definitions tested: + - `validate_org` + - `get_valid_course_key` + - `CourseEnrollmentAllowed` + - `get_courseware_courses` + + Expected result: + - The status code is 200. + - The response contains a warning message about the course not being found. + - The pre-enrollment is created in the tenant. + """ + course_id = "course-v1:OpenedX+DemoX+NonExistentCourse" + pre_enrollment_data = { + "course_id": course_id, + "email": data.get("email") or self.user["email"], + "auto_enroll": data.get("auto_enroll"), + } + + response = self.create_pre_enrollment(self.tenant_x, pre_enrollment_data) + response_data = response.json() + pre_enrollment_response = self.get_pre_enrollment(self.tenant_x, pre_enrollment_data) + pre_enrollment_response_data = pre_enrollment_response.json() + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response_data["warning"], [f"Course with course_id:{course_id} does not exist"]) + self.assertEqual(response_data["email"], pre_enrollment_data["email"]) + self.assertEqual(response_data["course_id"], pre_enrollment_data["course_id"]) + self.assertEqual(response_data["auto_enroll"], pre_enrollment_data["auto_enroll"]) + self.assertEqual(pre_enrollment_response.status_code, status.HTTP_200_OK) + self.assertEqual(pre_enrollment_response_data["email"], pre_enrollment_data["email"]) + self.assertEqual(pre_enrollment_response_data["course_id"], pre_enrollment_data["course_id"]) + self.assertEqual(pre_enrollment_response_data["auto_enroll"], pre_enrollment_data["auto_enroll"]) + + @ddt.data(True, False) + def test_get_pre_enrollment_success(self, auto_enroll: bool) -> None: + """ + Test getting an pre-enrollment in a tenant. + + Open edX definitions tested: + - `validate_org` + - `get_valid_course_key` + - `CourseEnrollmentAllowed` + + Expected result: + - The status code is 200. + - The response contains the pre-enrollment data. + """ + pre_enrollment_data = { + "course_id": self.demo_course_id, + "email": self.user["email"], + "auto_enroll": auto_enroll, + } + self.create_pre_enrollment(self.tenant_x, pre_enrollment_data) + + response = self.get_pre_enrollment(self.tenant_x, pre_enrollment_data) + response_data = response.json() + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response_data["course_id"], pre_enrollment_data["course_id"]) + self.assertEqual(response_data["email"], pre_enrollment_data["email"]) + self.assertEqual(response_data["auto_enroll"], pre_enrollment_data["auto_enroll"]) + + def test_get_pre_enrollment_does_not_exist(self) -> None: + """ + Test getting an pre-enrollment that does not exist. + + Open edX definitions tested: + - `validate_org` + - `get_valid_course_key` + - `CourseEnrollmentAllowed` + + Expected result: + - The status code is 404. + - The response contains an error message about the pre-enrollment not found. + """ + pre_enrollment_data = { + "course_id": self.demo_course_id, + "email": self.user["email"], + } + + response = self.get_pre_enrollment(self.tenant_x, pre_enrollment_data) + response_data = response.json() + + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertEqual( + response_data["detail"], + f"Pre-enrollment not found for email: {self.user['email']} course_id: {self.demo_course_id}", + ) + + def test_get_pre_enrollment_course_with_not_valid_org(self) -> None: + """ + Test getting an pre-enrollment with a course that does not belong to the tenant. + + Open edX definitions tested: + - `validate_org` + - `get_valid_course_key` + - `CourseEnrollmentAllowed` + + Expected result: + - The status code is 400 + - The response contains an error message about the course is not valid for the tenant. + """ + course_id = "course-v1:AnotherOrg+DemoX+Demo_Course" + pre_enrollment_data = { + "course_id": course_id, + "email": self.user["email"], + } + + response = self.get_pre_enrollment(self.tenant_x, pre_enrollment_data) + response_data = response.json() + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response_data["course_id"], [f"Invalid course_id {course_id}"]) + + def test_update_pre_enrollment_success(self) -> None: + """ + Test updating an existing pre-enrollment. The endpoint only allows updating the `auto_enroll` field. + + Open edX definitions tested: + - `validate_org` + - `get_valid_course_key` + - `CourseEnrollmentAllowed` + - `get_courseware_courses` + + Expected result: + - The status code is 200. + - The pre-enrollment is updated successfully in the tenant with the provided data. + """ + pre_enrollment_data = { + "course_id": self.demo_course_id, + "email": self.user["email"], + "auto_enroll": True, + } + self.create_pre_enrollment(self.tenant_x, pre_enrollment_data) + pre_enrollment_data["auto_enroll"] = False + + response = self.update_pre_enrollment(self.tenant_x, pre_enrollment_data) + response_data = response.json() + pre_enrollment_response = self.get_pre_enrollment(self.tenant_x, pre_enrollment_data) + pre_enrollment_response_data = pre_enrollment_response.json() + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response_data["course_id"], pre_enrollment_data["course_id"]) + self.assertEqual(response_data["email"], pre_enrollment_data["email"]) + self.assertEqual(response_data["auto_enroll"], pre_enrollment_data["auto_enroll"]) + self.assertEqual(pre_enrollment_response.status_code, status.HTTP_200_OK) + self.assertEqual(pre_enrollment_response_data["course_id"], pre_enrollment_data["course_id"]) + self.assertEqual(pre_enrollment_response_data["email"], pre_enrollment_data["email"]) + self.assertEqual(pre_enrollment_response_data["auto_enroll"], pre_enrollment_data["auto_enroll"]) + + def test_update_pre_enrollment_does_not_exist(self) -> None: + """ + Test updating an pre-enrollment that does not exist. + + Open edX definitions tested: + - `validate_org` + - `get_valid_course_key` + - `CourseEnrollmentAllowed` + + Expected result: + - The status code is 404. + - The response contains an error message about the pre-enrollment not found. + """ + pre_enrollment_data = { + "course_id": self.demo_course_id, + "email": self.user["email"], + } + + response = self.update_pre_enrollment(self.tenant_x, pre_enrollment_data) + response_data = response.json() + pre_enrollment_response = self.get_pre_enrollment(self.tenant_x, pre_enrollment_data) + + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertEqual( + response_data["detail"], + f"Pre-enrollment not found for email: {self.user['email']} course_id: {self.demo_course_id}", + ) + self.assertEqual(pre_enrollment_response.status_code, status.HTTP_404_NOT_FOUND) + + @ddt.data( + {"course_id": ["This field is required."]}, + {"email": ["This field is required."]}, + {"course_id": ["This field is required."], "email": ["This field is required."]}, + ) + def test_update_pre_enrollment_missing_required_fields(self, errors: dict) -> None: + """ + Test updating an pre-enrollment with missing required fields. + + Open edX definitions tested: + - `validate_org` + - `get_valid_course_key` + + Expected result: + - The status code is 400. + - The response contains a message about the missing fields. + - The pre-enrollment is not updated in the tenant. + """ + complete_pre_enrollment_data = { + "email": self.user["email"], + "course_id": self.demo_course_id, + } + self.create_pre_enrollment(self.tenant_x, complete_pre_enrollment_data) + incomplete_pre_enrollment_data = { + key: value for key, value in complete_pre_enrollment_data.items() if key not in errors + } + + response = self.update_pre_enrollment(self.tenant_x, incomplete_pre_enrollment_data) + response_data = response.json() + pre_enrollment_response = self.get_pre_enrollment(self.tenant_x, complete_pre_enrollment_data) + pre_enrollment_response_data = pre_enrollment_response.json() + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response_data, errors) + self.assertEqual(pre_enrollment_response.status_code, status.HTTP_200_OK) + self.assertEqual(pre_enrollment_response_data["course_id"], complete_pre_enrollment_data["course_id"]) + self.assertEqual(pre_enrollment_response_data["email"], complete_pre_enrollment_data["email"]) + self.assertEqual(pre_enrollment_response_data["auto_enroll"], True) + + def test_delete_pre_enrollment_success(self) -> None: + """ + Test deleting an existing pre-enrollment. + + Open edX definitions tested: + - `validate_org` + - `get_valid_course_key` + - `CourseEnrollmentAllowed` + - `get_courseware_courses` + + Expected result: + - The status code is 204. + - The pre-enrollment is deleted successfully in the tenant. + """ + pre_enrollment_data = { + "course_id": self.demo_course_id, + "email": self.user["email"], + "auto_enroll": True, + } + self.create_pre_enrollment(self.tenant_x, pre_enrollment_data) + pre_enrollment_data["auto_enroll"] = False + + response = self.delete_pre_enrollment(self.tenant_x, pre_enrollment_data) + pre_enrollment_response = self.get_pre_enrollment(self.tenant_x, pre_enrollment_data) + + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + self.assertEqual(pre_enrollment_response.status_code, status.HTTP_404_NOT_FOUND) + + def test_delete_pre_enrollment_does_not_exist(self) -> None: + """ + Test deleting an pre-enrollment that does not exist. + + Open edX definitions tested: + - `validate_org` + - `get_valid_course_key` + - `CourseEnrollmentAllowed` + + Expected result: + - The status code is 404. + - The response contains an error message about the pre-enrollment not found. + """ + pre_enrollment_data = { + "course_id": self.demo_course_id, + "email": self.user["email"], + } + + response = self.delete_pre_enrollment(self.tenant_x, pre_enrollment_data) + response_data = response.json() + + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertEqual( + response_data["detail"], + f"Pre-enrollment not found for email: {self.user['email']} course_id: {self.demo_course_id}", + ) + + @ddt.data( + {"course_id": ["This field is required."]}, + {"email": ["This field is required."]}, + {"course_id": ["This field is required."], "email": ["This field is required."]}, + ) + def test_delete_pre_enrollment_missing_required_fields(self, errors: dict) -> None: + """ + Test deleting an pre-enrollment with missing required fields. + + Open edX definitions tested: + - `validate_org` + - `get_valid_course_key` + + Expected result: + - The status code is 400. + - The response contains a message about the missing fields. + - The pre-enrollment is not deleted in the tenant. + """ + complete_pre_enrollment_data = { + "email": self.user["email"], + "course_id": self.demo_course_id, + } + self.create_pre_enrollment(self.tenant_x, complete_pre_enrollment_data) + incomplete_pre_enrollment_data = { + key: value for key, value in complete_pre_enrollment_data.items() if key not in errors + } + + response = self.delete_pre_enrollment(self.tenant_x, incomplete_pre_enrollment_data) + response_data = response.json() + pre_enrollment_response = self.get_pre_enrollment(self.tenant_x, complete_pre_enrollment_data) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response_data, errors) + self.assertEqual(pre_enrollment_response.status_code, status.HTTP_200_OK) + + class TestInfoView(BaseAPIIntegrationTest): """ Integration test suite for the info view. @@ -1038,8 +1537,8 @@ def test_info_view_success(self) -> None: - The response contains the version, name and git commit hash. """ response = make_request(self.default_site, "GET", url=self.url, with_auth=False) - response_data = response.json() + self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertIn("version", response_data) self.assertIn("name", response_data) diff --git a/fixtures/initial_data.json b/fixtures/initial_data.json index 16f2bf8b..e01b949e 100644 --- a/fixtures/initial_data.json +++ b/fixtures/initial_data.json @@ -90,10 +90,19 @@ "pk": 1, "fields": { "external_key": "tenant-x-key", - "lms_configs": "{\n \"EDNX_USE_SIGNAL\": true,\n \"PLATFORM_NAME\": \"Tenant X\",\n \"SITE_NAME\": \"tenant-x.local.edly.io\",\n \"course_org_filter\": [\n \"TenantX\",\n \"OpenedX\",\n \"edX\"\n ]\n}", - "studio_configs": "{}", - "theming_configs": "{}", - "meta": "{}", + "lms_configs": { + "EDNX_USE_SIGNAL": true, + "PLATFORM_NAME": "Tenant X", + "SITE_NAME": "tenant-x.local.edly.io", + "course_org_filter": [ + "TenantX", + "OpenedX", + "edX" + ] + }, + "studio_configs": {}, + "theming_configs": {}, + "meta": {}, "organizations": [ 1 ] @@ -104,10 +113,17 @@ "pk": 2, "fields": { "external_key": "tenant-y-key", - "lms_configs": "{\n \"EDNX_USE_SIGNAL\": true,\n \"PLATFORM_NAME\": \"Tenant Y\",\n \"SITE_NAME\": \"tenant-y.local.edly.io\",\n \"course_org_filter\": [\n \"TenantY\"\n ]\n}", - "studio_configs": "{}", - "theming_configs": "{}", - "meta": "{}", + "lms_configs": { + "EDNX_USE_SIGNAL": true, + "PLATFORM_NAME": "Tenant Y", + "SITE_NAME": "tenant-y.local.edly.io", + "course_org_filter": [ + "TenantY" + ] + }, + "studio_configs": {}, + "theming_configs": {}, + "meta": {}, "organizations": [ 2 ]