From e344fd1d76e6364c4a8189732a1b63032a331c22 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Thu, 19 Sep 2024 16:49:07 -0500 Subject: [PATCH 01/32] feat: add tutor command to import democourse --- scripts/execute_integration_tests.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/execute_integration_tests.sh b/scripts/execute_integration_tests.sh index 3408c29b2..531e4aab9 100644 --- a/scripts/execute_integration_tests.sh +++ b/scripts/execute_integration_tests.sh @@ -1,2 +1,3 @@ #!/bin/bash +tutor local do importdemocourse make run-integration-tests From 7a8fe0590f93ff37fa10b016b6636b174ff77005 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Fri, 20 Sep 2024 17:32:21 -0500 Subject: [PATCH 02/32] chore: add output in tests --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index aafdbd991..1c977e9d4 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ python-quality-test: run-tests: python-test python-quality-test run-integration-tests: install-dev-dependencies - pytest ./eox_core --ignore-glob='**/unit/*' --ignore-glob='**/edxapp_wrapper/*' + pytest -rP ./eox_core --ignore-glob='**/unit/*' --ignore-glob='**/edxapp_wrapper/*' upgrade: export CUSTOM_COMPILE_COMMAND=make upgrade upgrade: ## update the requirements/*.txt files with the latest packages satisfying requirements/*.in From 46774d4f36fae9bc6d3394d670e85cc60f997257 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Fri, 20 Sep 2024 17:32:54 -0500 Subject: [PATCH 03/32] feat: add openedx organization in fixtures and improve the format --- fixtures/initial_data.json | 39 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/fixtures/initial_data.json b/fixtures/initial_data.json index d4d13f606..372e2fe75 100644 --- a/fixtures/initial_data.json +++ b/fixtures/initial_data.json @@ -45,6 +45,13 @@ "name": "TenantY" } }, + { + "model": "eox_tenant.tenantorganization", + "pk": 3, + "fields": { + "name": "OpenedX" + } + }, { "model": "organizations.organization", "pk": 1, @@ -71,12 +78,33 @@ "active": true } }, + { + "model": "organizations.organization", + "pk": 2, + "fields": { + "created": "2024-09-11T17:15:35.694Z", + "modified": "2024-09-11T17:15:35.694Z", + "name": "OpenedX", + "short_name": "openedx", + "description": null, + "logo": "", + "active": true + } + }, { "model": "eox_tenant.tenantconfig", "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 ]\n}", + "lms_configs": { + "EDNX_USE_SIGNAL": true, + "PLATFORM_NAME": "Tenant X", + "SITE_NAME": "tenant-x.local.edly.io", + "course_org_filter": [ + "TenantX", + "OpenedX" + ] + }, "studio_configs": "{}", "theming_configs": "{}", "meta": "{}", @@ -90,7 +118,14 @@ "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}", + "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": "{}", From 2d6833226653a2a029bba896d055b65aa3bbd2e0 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Fri, 20 Sep 2024 17:33:31 -0500 Subject: [PATCH 04/32] test: add enrollment api integration tests --- .../api/v1/tests/integration/test_views.py | 974 +++++++++++++----- 1 file changed, 740 insertions(+), 234 deletions(-) diff --git a/eox_core/api/v1/tests/integration/test_views.py b/eox_core/api/v1/tests/integration/test_views.py index 5a0043856..2df64cd50 100644 --- a/eox_core/api/v1/tests/integration/test_views.py +++ b/eox_core/api/v1/tests/integration/test_views.py @@ -4,9 +4,8 @@ from __future__ import annotations +import ddt import requests -from ddt import data as ddt_data -from ddt import ddt, unpack from django.conf import settings as ds from django.test import TestCase from django.urls import reverse @@ -16,6 +15,76 @@ settings = ds.INTEGRATION_TEST_SETTINGS +USER_URL = f"{settings['EOX_CORE_API_BASE']}{reverse('eox-api:eox-api:edxapp-user')}" +USER_UPDATER_URL = f"{settings['EOX_CORE_API_BASE']}{reverse('eox-api:eox-api:edxapp-user-updater')}" +ENROLLMENT_URL = f"{settings['EOX_CORE_API_BASE']}{reverse('eox-api:eox-api:edxapp-enrollment')}" + + +def get_access_token(tenant_base_url: str) -> str: + """ + Get an access token for a tenant. + + Args: + tenant_base_url (str): The tenant base URL. + + Returns: + str: The access token. + """ + data = { + "client_id": settings["CLIENT_ID"], + "client_secret": settings["CLIENT_SECRET"], + "grant_type": "client_credentials", + } + url = f"{tenant_base_url}/oauth2/access_token/" + response = requests.post(url, data=data, timeout=settings["API_TIMEOUT"]) + return response.json()["access_token"] + + +# pylint: disable=too-many-arguments +def make_request( + tenant: dict, + method: str, + url: str, + json: dict | None = None, + data: dict | None = None, + params: dict | None = None, + with_auth: bool = True, +) -> requests.Response: + """ + Make a request to a tenant. + + Args: + tenant (dict): The tenant data. + method (str): The HTTP method ('GET', 'POST', etc.). + url (str): The URL to make the request to. + json (dict, optional): The JSON data for POST, PATCH and PUT requests. + data (dict, optional): The data for POST, PATCH and PUT requests. + params (dict, optional): The parameters for GET and DELETE requests. + with_auth (bool, optional): Whether to include the access token in the request headers. + + Returns: + requests.Response: The response object. + """ + headers = {"Host": tenant["domain"]} + if with_auth: + access_token = get_access_token(tenant["base_url"]) + headers["Authorization"] = f"Bearer {access_token}" + full_url = f"{tenant['base_url']}/{url}" + + method = method.upper() + if method not in ("GET", "POST", "PATCH", "PUT", "DELETE"): + raise ValueError(f"Unsupported HTTP method: {method}.") + + return requests.request( + method, + full_url, + json=json, + data=data, + params=params, + headers=headers, + timeout=settings["API_TIMEOUT"], + ) + class BaseAPIIntegrationTest(TestCase): """ @@ -48,351 +117,788 @@ def get_tenant_data(self, prefix: str = "") -> dict: "domain": domain, } - def get_access_token(self, tenant_base_url: str) -> str: + +class UsersAPIRequestMixin: + """ + Mixin class for the API request methods. + """ + + def create_user(self, tenant: dict, data: dict) -> requests.Response: """ - Get an access token for a tenant. + Create a new user in a tenant. Args: - tenant_base_url (str): The tenant base URL. + tenant (dict): The tenant data. + data (dict): The user data. Returns: - str: The access token. + requests.Response: The response object. """ - data = { - "client_id": settings["CLIENT_ID"], - "client_secret": settings["CLIENT_SECRET"], - "grant_type": "client_credentials", - } - url = f"{tenant_base_url}/oauth2/access_token/" - response = requests.post(url, data=data, timeout=settings["API_TIMEOUT"]) - return response.json()["access_token"] - - # pylint: disable=too-many-arguments - def make_request( - self, - tenant: dict, - method: str, - url: str, - json: dict | None = None, - data: dict | None = None, - params: dict | None = None, - with_auth: bool = True, - ) -> requests.Response: - """ - Make a request to a tenant. + return make_request(tenant, "POST", url=USER_URL, json=data) + + def get_user(self, tenant: dict, params: dict | None = None) -> requests.Response: + """ + Get a user in a tenant by username or email. Args: tenant (dict): The tenant data. - method (str): The HTTP method ('GET', 'POST', etc.). - url (str): The URL to make the request to. - json (dict, optional): The JSON data for POST, PATCH and PUT requests. - data (dict, optional): The data for POST, PATCH and PUT requests. - params (dict, optional): The parameters for GET and DELETE requests. - with_auth (bool, optional): Whether to include the access token in the request headers. + params (dict, optional): The query parameters for the request. Returns: requests.Response: The response object. """ - headers = {"Host": tenant["domain"]} - if with_auth: - access_token = self.get_access_token(tenant["base_url"]) - headers["Authorization"] = f"Bearer {access_token}" - full_url = f"{tenant['base_url']}/{url}" + return make_request(tenant, "GET", url=USER_URL, params=params) - method = method.upper() - if method not in ("GET", "POST", "PATCH"): - raise ValueError(f"Unsupported HTTP method: {method}") + def update_user(self, tenant: dict, data: dict) -> requests.Response: + """ + Update a user in a tenant. - return requests.request( - method, - full_url, - json=json, - data=data, - params=params, - headers=headers, - timeout=settings["API_TIMEOUT"], - ) + Args: + tenant (dict): The tenant data. + data (dict): The user data. + Returns: + requests.Response: The response object. + """ + return make_request(tenant, "PATCH", url=USER_UPDATER_URL, json=data) -@ddt -class TestUsersAPIIntegration(BaseAPIIntegrationTest): - """Integration test suite for the Users API""" - def setUp(self): - """Set up the test suite""" - self.user_url = f"{settings['EOX_CORE_API_BASE']}{reverse('eox-api:eox-api:edxapp-user')}" - self.user_updater_url = f"{settings['EOX_CORE_API_BASE']}{reverse('eox-api:eox-api:edxapp-user-updater')}" - super().setUp() +class EnrollmentAPIRequestMixin: + """Mixin class for the API request methods.""" - def create_user_in_tenant(self, tenant: dict, user_data: dict) -> requests.Response: + def create_enrollment(self, tenant: dict, data: dict) -> requests.Response: """ Create a new user in a tenant. Args: tenant (dict): The tenant data. - user_data (dict): The user data. + data (dict): The user data. Returns: requests.Response: The response object. """ - return self.make_request(tenant, "POST", url=self.user_url, data=user_data) + return make_request(tenant, "POST", url=ENROLLMENT_URL, data=data) - def get_user_in_tenant(self, tenant: dict, params: dict | None = None) -> requests.Response: + def get_enrollment(self, tenant: dict, data: dict | None = None) -> requests.Response: """ Get a user in a tenant by username or email. Args: tenant (dict): The tenant data. - params (dict, optional): The query parameters for the request. + data (dict, optional): The body data for the request. Returns: requests.Response: The response object. """ - return self.make_request(tenant, "GET", url=self.user_url, params=params) + return make_request(tenant, "GET", url=ENROLLMENT_URL, data=data) - def update_user_in_tenant(self, tenant: dict, user_data: dict) -> requests.Response: + def update_enrollment(self, tenant: dict, data: dict | None = None) -> requests.Response: """ - Update a user in a tenant. + Update an enrollment in a tenant. Args: tenant (dict): The tenant data. - user_data (dict): The user data. + data (dict, optional): The body data for the request. Returns: requests.Response: The response object. """ - return self.make_request(tenant, "PATCH", url=self.user_updater_url, json=user_data) + return make_request(tenant, "PUT", url=ENROLLMENT_URL, data=data) - @ddt_data( - {"is_staff": False, "is_superuser": False}, - {"is_staff": True, "is_superuser": False}, - {"is_staff": False, "is_superuser": True}, - {"is_staff": True, "is_superuser": True}, - ) - def test_create_user_in_tenant_success(self, permissions: dict) -> None: + def delete_enrollment(self, tenant: dict, data: dict | None = None) -> requests.Response: """ - Test creating a user in a tenant. + Delete an enrollment in a tenant. - Open edX definitions tested: - - `create_edxapp_user` - - `check_edxapp_account_conflicts` + Args: + tenant (dict): The tenant data. + data (dict, optional): The body data for the request. - Expected result: - - The status code is 200. - - The user is created successfully in the tenant with the provided data. + Returns: + requests.Response: The response object. """ - data = next(FAKE_USER_DATA) - data.update(permissions) + return make_request(tenant, "DELETE", url=ENROLLMENT_URL, data=data) + + +# @ddt +# class TestUsersAPIIntegration(BaseAPIIntegrationTest, UsersAPIRequestMixin): +# """Integration test suite for the Users API""" + +# def setUp(self): +# """Set up the test suite""" +# super().setUp() + +# @ddt_data( +# {"is_staff": False, "is_superuser": False}, +# {"is_staff": True, "is_superuser": False}, +# {"is_staff": False, "is_superuser": True}, +# {"is_staff": True, "is_superuser": True}, +# ) +# def test_create_user_in_tenant_success(self, permissions: dict) -> None: +# """ +# Test creating a user in a tenant. + +# Open edX definitions tested: +# - `create_edxapp_user` +# - `check_edxapp_account_conflicts` + +# Expected result: +# - The status code is 200. +# - The user is created successfully in the tenant with the provided data. +# """ +# data = next(FAKE_USER_DATA) +# data.update(permissions) + +# response = self.create_user(self.tenant_x, data) + +# response_data = response.json() +# self.assertEqual(response.status_code, status.HTTP_200_OK) +# self.assertEqual(response_data["email"], data["email"]) +# self.assertEqual(response_data["username"], data["username"]) +# self.assertTrue(response_data["is_active"]) +# self.assertFalse(response_data["is_staff"]) +# self.assertFalse(response_data["is_superuser"]) + +# def test_create_user_missing_required_fields(self) -> None: +# """ +# Test creating a user in a tenant with invalid data. + +# Open edX definitions tested: +# - `check_edxapp_account_conflicts` + +# Expected result: +# - The status code is 400. +# - The response contains the missing fields. +# - The user is not created in the tenant. +# """ +# data = next(FAKE_USER_DATA) +# del data["email"] +# del data["username"] + +# response = self.create_user(self.tenant_x, data) + +# response_data = response.json() +# self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) +# self.assertIn("email", response_data) +# self.assertIn("username", response_data) + +# def test_create_user_in_tenant_user_already_exists(self) -> None: +# """ +# Test creating a user in a tenant that already exists. + +# Open edX definitions tested: +# - `check_edxapp_account_conflicts` + +# Expected result: +# - The status code is 400. +# - The response contains an error message. +# - The user is not created in the tenant. +# """ +# data = next(FAKE_USER_DATA) +# self.create_user(self.tenant_x, data) + +# response = self.create_user(self.tenant_x, data) + +# response_data = response.json() +# self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) +# self.assertIn("non_field_errors", response_data) + +# @ddt_data("username", "email") +# def test_get_user_in_tenant_success(self, query_param: str) -> None: +# """ +# Test getting a user in a tenant. + +# Open edX definitions tested: +# - `get_edxapp_user` + +# Expected result: +# - The status code is 200. +# - The response contains the user data. +# """ +# data = next(FAKE_USER_DATA) +# self.create_user(self.tenant_x, data) + +# response = self.get_user(self.tenant_x, {query_param: data[query_param]}) + +# response_data = response.json() +# self.assertEqual(response.status_code, status.HTTP_200_OK) +# self.assertEqual(response_data[query_param], data[query_param]) + +# def test_get_user_of_another_tenant(self) -> None: +# """ +# Test getting a user that belongs to another tenant. + +# Open edX definitions tested: +# - `get_edxapp_user` + +# Expected result: +# - The status code is 404. +# - The response contains an error message. +# """ +# data = next(FAKE_USER_DATA) +# self.create_user(self.tenant_x, data) + +# response = self.get_user(self.tenant_y, {"username": data["username"]}) + +# response_data = response.json() +# self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) +# self.assertIn("detail", response_data) +# self.assertEqual( +# response_data["detail"], +# f"No user found by {{'username': '{data['username']}'}} on site {self.tenant_y['domain']}.", +# ) + +# @ddt_data( +# ("username", "user-not-found"), +# ("email", "user-not-found@mail.com"), +# ) +# @unpack +# def test_get_user_in_tenant_user_not_found(self, param: str, value: str) -> None: +# """ +# Test getting a user in a tenant that does not exist. + +# Open edX definitions tested: +# - `get_edxapp_user` + +# Expected result: +# - The status code is 404. +# - The response contains an error message. +# """ +# response = self.get_user(self.tenant_x, {param: value}) + +# response_data = response.json() +# self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) +# self.assertIn("detail", response_data) +# self.assertEqual( +# response_data["detail"], +# f"No user found by {{'{param}': '{value}'}} on site {self.tenant_x['domain']}.", +# ) + +# def test_update_user_in_tenant_success(self) -> None: +# """ +# Test updating a user in a tenant. + +# Open edX definitions tested: +# - `get_edxapp_user` +# - `get_user_profile` +# - `check_edxapp_account_conflicts` +# - `get_user_read_only_serializer` + +# Expected result: +# - The status code is 200. +# - The user is updated successfully in the tenant with the provided data. +# """ +# data = next(FAKE_USER_DATA) +# self.create_user(self.tenant_x, data) +# updated_data = next(FAKE_USER_DATA) +# updated_data["username"] = data["username"] +# updated_data["email"] = data["email"] + +# response = self.update_user(self.tenant_x, data=updated_data) + +# response_data = response.json() +# self.assertEqual(response.status_code, status.HTTP_200_OK) +# self.assertEqual(response_data["username"], data["username"]) +# self.assertEqual(response_data["email"], data["email"]) +# self.assertEqual(response_data["name"], updated_data["fullname"]) +# self.assertEqual(response_data["mailing_address"], updated_data["mailing_address"]) +# self.assertEqual(response_data["year_of_birth"], updated_data["year_of_birth"]) +# self.assertEqual(response_data["gender"], updated_data["gender"]) +# self.assertEqual(response_data["level_of_education"], updated_data["level_of_education"]) +# self.assertEqual(response_data["goals"], updated_data["goals"]) +# self.assertTrue(response_data["is_active"]) + +# @ddt_data( +# ("username", "user-not-found"), +# ("email", "user-not-found@mail.com"), +# ) +# @unpack +# def test_update_user_in_tenant_user_not_found(self, param: str, value: str) -> None: +# """ +# Test updating a user in a tenant that does not exist. + +# Open edX definitions tested: +# - `get_edxapp_user` + +# Expected result: +# - The status code is 404. +# - The response contains an error message. +# """ +# response = self.update_user(self.tenant_x, {param: value}) + +# response_data = response.json() +# self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) +# self.assertIn("detail", response_data) +# self.assertEqual( +# response_data["detail"], +# f"No user found by {{'{param}': '{value}'}} on site {self.tenant_x['domain']}.", +# ) + + +@ddt.ddt +class TestEnrollmentAPIIntegration(BaseAPIIntegrationTest, UsersAPIRequestMixin, EnrollmentAPIRequestMixin): + """Integration test suite for the Enrollment API""" + + def setUp(self): + """Set up the test suite""" + self.course_id = "course-v1:OpenedX+DemoX+DemoCourse" + self.mode = "audit" + return super().setUp() + + # @ddt.data("email", "username") + # def test_create_enrollment_valid_user_mode_course(self, param: str) -> None: + # """ + # Create enrollment with a valid user, valid course and valid mode + # """ + # user_data = next(FAKE_USER_DATA) + # self.create_user(self.tenant_x, user_data) + # data = { + # param: user_data[param], + # "course_id": self.course_id, + # "mode": self.mode, + # } + + # response = self.create_enrollment(self.tenant_x, data) + + # response_data = response.json() + # self.assertEqual(response.status_code, status.HTTP_200_OK) + # self.assertEqual(response_data["username"], user_data["username"]) + # self.assertEqual(response_data["mode"], data["mode"]) + # self.assertEqual(response_data["course_id"], data["course_id"]) + # self.assertTrue(response_data["is_active"]) + # self.assertIn("created", response_data) + + # @ddt.data("email", "username") + # def test_force_create_enrollment_valid_user_mode_course(self, param: str) -> None: + # """ + # Create enrollment with a valid user, valid course, valid mode using force + # """ + # user_data = next(FAKE_USER_DATA) + # self.create_user(self.tenant_x, user_data) + # data = { + # param: user_data[param], + # "course_id": self.course_id, + # "mode": self.mode, + # "force": True, + # } + + # response = self.create_enrollment(self.tenant_x, data) + + # response_data = response.json() + # self.assertEqual(response.status_code, status.HTTP_200_OK) + # self.assertEqual(response_data["username"], user_data["username"]) + # self.assertEqual(response_data["mode"], data["mode"]) + # self.assertEqual(response_data["course_id"], data["course_id"]) + # self.assertTrue(response_data["is_active"]) + # self.assertIn("created", response_data) + + # @ddt.data("email", "username") + # def test_create_valid_course_mode_invalid_user(self, param: str) -> None: + # """ + # Create enrollment with a valid course, valid mode, and a non-existent user + # """ + # data = { + # param: param, + # "course_id": self.course_id, + # "mode": self.mode, + # } + + # response = self.create_enrollment(self.tenant_x, 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"], ["User not found"]) + + # @ddt.data("email", "username") + # def test_create_valid_course_mode_invalid_user_for_site(self, param: str) -> None: + # """ + # Create enrollment with a valid course, valid mode, and a user from another site + # """ + # user_data = next(FAKE_USER_DATA) + # self.create_user(self.tenant_y, user_data) + # data = { + # param: user_data[param], + # "course_id": self.course_id, + # "mode": self.mode, + # } + + # response = self.create_enrollment(self.tenant_x, data) + + # response_data = response.json() + # self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) + # self.assertEqual( + # response_data["error"]["detail"], + # f"No user found by {{'{param}': '{data[param]}'}} on site {self.tenant_x['domain']}.", + # ) + + # @ddt.data("email", "username") + # def test_create_valid_user_mode_invalid_course(self, param: str) -> None: + # """ + # Create enrollment with a valid user, valid mode, and non-existent course + # """ + # user_data = next(FAKE_USER_DATA) + # self.create_user(self.tenant_x, user_data) + # data = { + # param: user_data[param], + # "course_id": "course-v1:OpenedX+DemoX+NonExistentCourse", + # "mode": self.mode, + # } + + # response = self.create_enrollment(self.tenant_x, 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"], ["Course not found"]) + + @ddt.data("email", "username") + def test_create_valid_user_mode_invalid_course_for_site(self, param: str) -> None: + """ + Create enrollment with a valid user, valid mode, and a course from another site + """ + user_data = next(FAKE_USER_DATA) + self.create_user(self.tenant_y, user_data) + data = { + param: user_data[param], + "course_id": self.course_id, + "mode": self.mode, + } - response = self.create_user_in_tenant(self.tenant_x, data) + response = self.create_enrollment(self.tenant_y, data) response_data = response.json() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response_data["email"], data["email"]) - self.assertEqual(response_data["username"], data["username"]) - self.assertTrue(response_data["is_active"]) - self.assertFalse(response_data["is_staff"]) - self.assertFalse(response_data["is_superuser"]) + print(f"\n\nResponse data: {response_data}\n\n") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - def test_create_user_missing_required_fields(self) -> None: + @ddt.data("email", "username") + def test_force_create_valid_user_course_invalid_mode(self, param: str) -> None: """ - Test creating a user in a tenant with invalid data. - - Open edX definitions tested: - - `check_edxapp_account_conflicts` - - Expected result: - - The status code is 400. - - The response contains the missing fields. - - The user is not created in the tenant. + Create enrollment with a valid user, valid course, and a not available mode """ - data = next(FAKE_USER_DATA) - del data["email"] - del data["username"] + user_data = next(FAKE_USER_DATA) + data = { + param: user_data[param], + "course_id": self.course_id, + "mode": "masters", + "force": True, + } - response = self.create_user_in_tenant(self.tenant_x, data) + response = self.create_enrollment(self.tenant_y, data) response_data = response.json() + print(f"\n\nResponse data: {response_data}\n\n") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertIn("email", response_data) - self.assertIn("username", response_data) - def test_create_user_in_tenant_user_already_exists(self) -> None: + @ddt.data("email", "username") + def test_read_valid_email_course(self, param: str) -> None: """ - Test creating a user in a tenant that already exists. - - Open edX definitions tested: - - `check_edxapp_account_conflicts` - - Expected result: - - The status code is 400. - - The response contains an error message. - - The user is not created in the tenant. + Get a valid enrollment """ - data = next(FAKE_USER_DATA) - self.create_user_in_tenant(self.tenant_x, data) + user_data = next(FAKE_USER_DATA) + enrollment_data = { + param: user_data[param], + "course_id": self.course_id, + } + self.create_user(self.tenant_y, user_data) + self.create_enrollment(self.tenant_x, enrollment_data) - response = self.create_user_in_tenant(self.tenant_x, data) + response = self.get_enrollment(self.tenant_x, data=enrollment_data) response_data = response.json() - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertIn("non_field_errors", response_data) + print(f"\n\nResponse data: {response_data}\n\n") + self.assertEqual(response.status_code, status.HTTP_200_OK) - @ddt_data("username", "email") - def test_get_user_in_tenant_success(self, query_param: str) -> None: + @ddt.data("email", "username") + def test_read_invalid_enrollment(self, param: str) -> None: + """ + Get a invalid enrollment (doesn't exist) """ - Test getting a user in a tenant. + user_data = next(FAKE_USER_DATA) + self.create_user(self.tenant_y, user_data) + data = { + param: user_data[param], + "course_id": self.course_id, + } - Open edX definitions tested: - - `get_edxapp_user` + response = self.get_enrollment(self.tenant_x, data=data) - Expected result: - - The status code is 200. - - The response contains the user data. + response_data = response.json() + print(f"\n\nResponse data: {response_data}\n\n") + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + @ddt.data("email", "username") + def test_read_invalid_enrollment_for_site(self, param: str) -> None: + """ + Get a invalid enrollment (enrollment from other site) """ - data = next(FAKE_USER_DATA) - self.create_user_in_tenant(self.tenant_x, data) + user_data = next(FAKE_USER_DATA) + enrollment_data = { + param: user_data[param], + "course_id": self.course_id, + } + self.create_user(self.tenant_x, user_data) + self.create_enrollment(self.tenant_x, enrollment_data) - response = self.get_user_in_tenant(self.tenant_x, {query_param: data[query_param]}) + response = self.get_enrollment(self.tenant_y, data=enrollment_data) response_data = response.json() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response_data[query_param], data[query_param]) + print(f"\n\nResponse data: {response_data}\n\n") + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - def test_get_user_of_another_tenant(self) -> None: + @ddt.data("email", "username") + def test_delete_valid_enrollment(self, param: str) -> None: """ - Test getting a user that belongs to another tenant. + Delete a valid enrollment + """ + user_data = next(FAKE_USER_DATA) + enrollment_data = { + param: user_data[param], + "course_id": self.course_id, + } + self.create_user(self.tenant_x, user_data) + self.create_enrollment(self.tenant_x, enrollment_data) - Open edX definitions tested: - - `get_edxapp_user` + response = self.delete_enrollment(self.tenant_x, data=enrollment_data) - Expected result: - - The status code is 404. - - The response contains an error message. + response_data = response.json() + print(f"\n\nResponse data: {response_data}\n\n") + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + + def test_delete_invalid_enrollment(self) -> None: """ - data = next(FAKE_USER_DATA) - self.create_user_in_tenant(self.tenant_x, data) + Delete a invalid enrollment (doesn't exist) + """ + user_data = next(FAKE_USER_DATA) + self.create_user(self.tenant_x, user_data) + data = { + "email": user_data["email"], + "course_id": self.course_id, + } - response = self.get_user_in_tenant(self.tenant_y, {"username": data["username"]}) + response = self.delete_enrollment(self.tenant_x, data=data) response_data = response.json() + print(f"\n\nResponse data: {response_data}\n\n") self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertIn("detail", response_data) - self.assertEqual( - response_data["detail"], - f"No user found by {{'username': '{data['username']}'}} on site {self.tenant_y['domain']}.", - ) - - @ddt_data( - ("username", "user-not-found"), - ("email", "user-not-found@mail.com"), - ) - @unpack - def test_get_user_in_tenant_user_not_found(self, param: str, value: str) -> None: - """ - Test getting a user in a tenant that does not exist. - - Open edX definitions tested: - - `get_edxapp_user` - Expected result: - - The status code is 404. - - The response contains an error message. + def test_delete_invalid_enrollment_for_site(self) -> None: """ - response = self.get_user_in_tenant(self.tenant_x, {param: value}) + Delete a invalid enrollment (enrollment from other site) + """ + user_data = next(FAKE_USER_DATA) + self.create_user(self.tenant_x, user_data) + self.create_enrollment(self.tenant_x, user_data) + data = { + "email": user_data["email"], + "course_id": self.course_id, + } + + response = self.delete_enrollment(self.tenant_y, data=data) response_data = response.json() + print(f"\n\nResponse data: {response_data}\n\n") self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertIn("detail", response_data) - self.assertEqual( - response_data["detail"], - f"No user found by {{'{param}': '{value}'}} on site {self.tenant_x['domain']}.", - ) - def test_update_user_in_tenant_success(self) -> None: + def test_update_valid_enrollment_change_is_active(self) -> None: """ - Test updating a user in a tenant. + Update an existing enrollment; change is_active flag + """ + user_data = next(FAKE_USER_DATA) + self.create_user(self.tenant_x, user_data) + self.create_enrollment(self.tenant_x, user_data) + # You can use email or username to update the enrollment + # Add dd_data to test both cases + data = { + "email": user_data["email"], + "course_id": self.course_id, + "is_active": False, + "mode": self.mode, + } + expected_response = { + "user": user_data["username"], + "course_id": self.course_id, + "mode": self.mode, + "is_active": False, + "enrollment_attributes": None, + } - Open edX definitions tested: - - `get_edxapp_user` - - `get_user_profile` - - `check_edxapp_account_conflicts` - - `get_user_read_only_serializer` + response = self.update_enrollment(self.tenant_x, data=data) - Expected result: - - The status code is 200. - - The user is updated successfully in the tenant with the provided data. + response_data = response.json() + print(f"\n\nResponse data: {response_data}\n\n") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertDictContainsSubset(expected_response, response_data) + + def test_update_valid_enrollment_change_valid_mode(self) -> None: + """ + Update an existing enrollment; change mode """ - data = next(FAKE_USER_DATA) - self.create_user_in_tenant(self.tenant_x, data) - updated_data = next(FAKE_USER_DATA) - updated_data["username"] = data["username"] - updated_data["email"] = data["email"] + user_data = next(FAKE_USER_DATA) + self.create_user(self.tenant_x, user_data) + self.create_enrollment(self.tenant_x, user_data) + # You can use email or username to update the enrollment + # Add dd_data to test both cases + data = { + "email": user_data["email"], + "course_id": self.course_id, + "is_active": True, + "mode": "honor", + } + expected_response = { + "user": user_data["username"], + "course_id": self.course_id, + "mode": "honor", + "is_active": True, + "enrollment_attributes": None, + } - response = self.update_user_in_tenant(self.tenant_x, user_data=updated_data) + response = self.update_enrollment(self.tenant_x, data=data) response_data = response.json() + print(f"\n\nResponse data: {response_data}\n\n") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response_data["username"], data["username"]) - self.assertEqual(response_data["email"], data["email"]) - self.assertEqual(response_data["name"], updated_data["fullname"]) - self.assertEqual(response_data["mailing_address"], updated_data["mailing_address"]) - self.assertEqual(response_data["year_of_birth"], updated_data["year_of_birth"]) - self.assertEqual(response_data["gender"], updated_data["gender"]) - self.assertEqual(response_data["level_of_education"], updated_data["level_of_education"]) - self.assertEqual(response_data["goals"], updated_data["goals"]) - self.assertTrue(response_data["is_active"]) - - @ddt_data( - ("username", "user-not-found"), - ("email", "user-not-found@mail.com"), - ) - @unpack - def test_update_user_in_tenant_user_not_found(self, param: str, value: str) -> None: + self.assertDictContainsSubset(expected_response, response_data) + + def test_update_valid_enrollment_change_invalid_mode(self) -> None: + """ + Update an existing enrollment; change to invalid mode """ - Test updating a user in a tenant that does not exist. + user_data = next(FAKE_USER_DATA) + self.create_user(self.tenant_x, user_data) + self.create_enrollment(self.tenant_x, user_data) + + data = { + "email": user_data["email"], + "course_id": self.course_id, + "is_active": True, + "mode": "masters", + } - Open edX definitions tested: - - `get_edxapp_user` + response = self.update_enrollment(self.tenant_x, data=data) + + response_data = response.json() + print(f"\n\nResponse data: {response_data}\n\n") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - Expected result: - - The status code is 404. - - The response contains an error message. + def test_update_invalid_enrollment_change_valid_mode(self) -> None: + """ + Update an non-existent enrollment; change mode """ - response = self.update_user_in_tenant(self.tenant_x, {param: value}) + user_data = next(FAKE_USER_DATA) + self.create_user(self.tenant_x, user_data) + data = { + "email": user_data["email"], + "course_id": self.course_id, + "is_active": True, + "mode": "honor", + } + + response = self.update_enrollment(self.tenant_x, data=data) response_data = response.json() - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertIn("detail", response_data) - self.assertEqual( - response_data["detail"], - f"No user found by {{'{param}': '{value}'}} on site {self.tenant_x['domain']}.", - ) + print(f"\n\nResponse data: {response_data}\n\n") + # Why is this returning 202? + self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) + def test_update_invalid_enrollment_change_is_active(self) -> None: + """ + Update an non-existent enrollment; change is_active flag + """ + user_data = next(FAKE_USER_DATA) + self.create_user(self.tenant_x, user_data) + data = { + "email": user_data["email"], + "course_id": self.course_id, + "is_active": False, + "mode": self.mode, + } -class TestInfoView(BaseAPIIntegrationTest): - """ - Integration test suite for the info view. - """ + response = self.update_enrollment(self.tenant_x, data=data) - def setUp(self): + response_data = response.json() + print(f"\n\nResponse data: {response_data}\n\n") + # Why is this returning 202? + self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) + + def test_update_valid_enrollment_change_is_active_force_post(self) -> None: """ - Set up the test suite. + Update an existing enrollment using POST with force=True; change is_active flag """ - self.url = f"{settings['EOX_CORE_API_BASE']}{reverse('eox-info')}" - super().setUp() + user_data = next(FAKE_USER_DATA) + self.create_user(self.tenant_x, user_data) + self.create_enrollment(self.tenant_x, user_data) + data = { + "email": user_data["email"], + "course_id": self.course_id, + "is_active": False, + "mode": self.mode, + "force": True, + } + expected_response = { + "username": user_data["username"], + "is_active": False, + "course_id": self.course_id, + } - def test_info_view_success(self) -> None: - """Test the info view. + response = self.create_enrollment(self.tenant_x, data=data) - Expected result: - - The status code is 200. - - The response contains the version, name and git commit hash. + response_data = response.json() + print(f"\n\nResponse data: {response_data}\n\n") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertDictContainsSubset(expected_response, response_data) + + def test_update_valid_enrollment_change_valid_mode_force_post(self) -> None: """ - response = self.make_request(self.default_site, "GET", url=self.url, with_auth=False) + Update an existing enrollment; change mode + """ + user_data = next(FAKE_USER_DATA) + self.create_user(self.tenant_x, user_data) + self.create_enrollment(self.tenant_x, user_data) + data = { + "email": user_data["email"], + "course_id": self.course_id, + "is_active": True, + "mode": "honor", + "force": True, + } + expected_response = { + "user": user_data["username"], + "is_active": True, + "course_id": data["course_id"], + "mode": "honor", + } + + response = self.create_enrollment(self.tenant_x, data=data) response_data = response.json() + print(f"\n\nResponse data: {response_data}\n\n") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertIn("version", response_data) - self.assertIn("name", response_data) - self.assertIn("git", response_data) + self.assertDictContainsSubset(expected_response, response_data) + + +# class TestInfoView(BaseAPIIntegrationTest): +# """ +# Integration test suite for the info view. +# """ + +# def setUp(self): +# """ +# Set up the test suite. +# """ +# self.url = f"{settings['EOX_CORE_API_BASE']}{reverse('eox-info')}" +# super().setUp() + +# def test_info_view_success(self) -> None: +# """Test the info view. + +# Expected result: +# - The status code is 200. +# - 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) +# self.assertIn("git", response_data) From c5c25c01cc5adfbdd58e64ad1eb32d1ee32f1d22 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Mon, 23 Sep 2024 11:47:43 -0500 Subject: [PATCH 05/32] feat: add fixtures for course modes --- fixtures/initial_data.json | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/fixtures/initial_data.json b/fixtures/initial_data.json index 372e2fe75..cd51fa9a8 100644 --- a/fixtures/initial_data.json +++ b/fixtures/initial_data.json @@ -168,5 +168,45 @@ "updated": "2024-09-06T13:30:17.347Z", "algorithm": "" } + }, + { + "model": "course_modes.coursemode", + "pk": 1, + "fields": { + "course": "course-v1:OpenedX+DemoX+DemoCourse", + "mode_slug": "audit", + "mode_display_name": "audit mode", + "min_price": 0, + "currency": "usd", + "_expiration_datetime": null, + "expiration_datetime_is_explicit": false, + "expiration_date": null, + "suggested_prices": "", + "description": null, + "sku": null, + "android_sku": null, + "ios_sku": null, + "bulk_sku": null + } + }, + { + "model": "course_modes.coursemode", + "pk": 2, + "fields": { + "course": "course-v1:OpenedX+DemoX+DemoCourse", + "mode_slug": "honor", + "mode_display_name": "honor mode", + "min_price": 0, + "currency": "usd", + "_expiration_datetime": null, + "expiration_datetime_is_explicit": false, + "expiration_date": null, + "suggested_prices": "", + "description": null, + "sku": null, + "android_sku": null, + "ios_sku": null, + "bulk_sku": null + } } ] From eb43544d6f01c4990c9da4858f10199e1050ba1b Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Mon, 23 Sep 2024 11:49:04 -0500 Subject: [PATCH 06/32] test: add remaining integration tests --- .../api/v1/tests/integration/test_views.py | 948 +++++++++--------- 1 file changed, 459 insertions(+), 489 deletions(-) diff --git a/eox_core/api/v1/tests/integration/test_views.py b/eox_core/api/v1/tests/integration/test_views.py index 2df64cd50..04412ce56 100644 --- a/eox_core/api/v1/tests/integration/test_views.py +++ b/eox_core/api/v1/tests/integration/test_views.py @@ -219,219 +219,219 @@ def delete_enrollment(self, tenant: dict, data: dict | None = None) -> requests. return make_request(tenant, "DELETE", url=ENROLLMENT_URL, data=data) -# @ddt -# class TestUsersAPIIntegration(BaseAPIIntegrationTest, UsersAPIRequestMixin): -# """Integration test suite for the Users API""" - -# def setUp(self): -# """Set up the test suite""" -# super().setUp() - -# @ddt_data( -# {"is_staff": False, "is_superuser": False}, -# {"is_staff": True, "is_superuser": False}, -# {"is_staff": False, "is_superuser": True}, -# {"is_staff": True, "is_superuser": True}, -# ) -# def test_create_user_in_tenant_success(self, permissions: dict) -> None: -# """ -# Test creating a user in a tenant. - -# Open edX definitions tested: -# - `create_edxapp_user` -# - `check_edxapp_account_conflicts` - -# Expected result: -# - The status code is 200. -# - The user is created successfully in the tenant with the provided data. -# """ -# data = next(FAKE_USER_DATA) -# data.update(permissions) - -# response = self.create_user(self.tenant_x, data) - -# response_data = response.json() -# self.assertEqual(response.status_code, status.HTTP_200_OK) -# self.assertEqual(response_data["email"], data["email"]) -# self.assertEqual(response_data["username"], data["username"]) -# self.assertTrue(response_data["is_active"]) -# self.assertFalse(response_data["is_staff"]) -# self.assertFalse(response_data["is_superuser"]) - -# def test_create_user_missing_required_fields(self) -> None: -# """ -# Test creating a user in a tenant with invalid data. - -# Open edX definitions tested: -# - `check_edxapp_account_conflicts` - -# Expected result: -# - The status code is 400. -# - The response contains the missing fields. -# - The user is not created in the tenant. -# """ -# data = next(FAKE_USER_DATA) -# del data["email"] -# del data["username"] - -# response = self.create_user(self.tenant_x, data) - -# response_data = response.json() -# self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) -# self.assertIn("email", response_data) -# self.assertIn("username", response_data) - -# def test_create_user_in_tenant_user_already_exists(self) -> None: -# """ -# Test creating a user in a tenant that already exists. - -# Open edX definitions tested: -# - `check_edxapp_account_conflicts` - -# Expected result: -# - The status code is 400. -# - The response contains an error message. -# - The user is not created in the tenant. -# """ -# data = next(FAKE_USER_DATA) -# self.create_user(self.tenant_x, data) - -# response = self.create_user(self.tenant_x, data) - -# response_data = response.json() -# self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) -# self.assertIn("non_field_errors", response_data) - -# @ddt_data("username", "email") -# def test_get_user_in_tenant_success(self, query_param: str) -> None: -# """ -# Test getting a user in a tenant. - -# Open edX definitions tested: -# - `get_edxapp_user` - -# Expected result: -# - The status code is 200. -# - The response contains the user data. -# """ -# data = next(FAKE_USER_DATA) -# self.create_user(self.tenant_x, data) - -# response = self.get_user(self.tenant_x, {query_param: data[query_param]}) - -# response_data = response.json() -# self.assertEqual(response.status_code, status.HTTP_200_OK) -# self.assertEqual(response_data[query_param], data[query_param]) - -# def test_get_user_of_another_tenant(self) -> None: -# """ -# Test getting a user that belongs to another tenant. - -# Open edX definitions tested: -# - `get_edxapp_user` - -# Expected result: -# - The status code is 404. -# - The response contains an error message. -# """ -# data = next(FAKE_USER_DATA) -# self.create_user(self.tenant_x, data) - -# response = self.get_user(self.tenant_y, {"username": data["username"]}) - -# response_data = response.json() -# self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) -# self.assertIn("detail", response_data) -# self.assertEqual( -# response_data["detail"], -# f"No user found by {{'username': '{data['username']}'}} on site {self.tenant_y['domain']}.", -# ) - -# @ddt_data( -# ("username", "user-not-found"), -# ("email", "user-not-found@mail.com"), -# ) -# @unpack -# def test_get_user_in_tenant_user_not_found(self, param: str, value: str) -> None: -# """ -# Test getting a user in a tenant that does not exist. - -# Open edX definitions tested: -# - `get_edxapp_user` - -# Expected result: -# - The status code is 404. -# - The response contains an error message. -# """ -# response = self.get_user(self.tenant_x, {param: value}) - -# response_data = response.json() -# self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) -# self.assertIn("detail", response_data) -# self.assertEqual( -# response_data["detail"], -# f"No user found by {{'{param}': '{value}'}} on site {self.tenant_x['domain']}.", -# ) - -# def test_update_user_in_tenant_success(self) -> None: -# """ -# Test updating a user in a tenant. - -# Open edX definitions tested: -# - `get_edxapp_user` -# - `get_user_profile` -# - `check_edxapp_account_conflicts` -# - `get_user_read_only_serializer` - -# Expected result: -# - The status code is 200. -# - The user is updated successfully in the tenant with the provided data. -# """ -# data = next(FAKE_USER_DATA) -# self.create_user(self.tenant_x, data) -# updated_data = next(FAKE_USER_DATA) -# updated_data["username"] = data["username"] -# updated_data["email"] = data["email"] - -# response = self.update_user(self.tenant_x, data=updated_data) - -# response_data = response.json() -# self.assertEqual(response.status_code, status.HTTP_200_OK) -# self.assertEqual(response_data["username"], data["username"]) -# self.assertEqual(response_data["email"], data["email"]) -# self.assertEqual(response_data["name"], updated_data["fullname"]) -# self.assertEqual(response_data["mailing_address"], updated_data["mailing_address"]) -# self.assertEqual(response_data["year_of_birth"], updated_data["year_of_birth"]) -# self.assertEqual(response_data["gender"], updated_data["gender"]) -# self.assertEqual(response_data["level_of_education"], updated_data["level_of_education"]) -# self.assertEqual(response_data["goals"], updated_data["goals"]) -# self.assertTrue(response_data["is_active"]) - -# @ddt_data( -# ("username", "user-not-found"), -# ("email", "user-not-found@mail.com"), -# ) -# @unpack -# def test_update_user_in_tenant_user_not_found(self, param: str, value: str) -> None: -# """ -# Test updating a user in a tenant that does not exist. - -# Open edX definitions tested: -# - `get_edxapp_user` - -# Expected result: -# - The status code is 404. -# - The response contains an error message. -# """ -# response = self.update_user(self.tenant_x, {param: value}) - -# response_data = response.json() -# self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) -# self.assertIn("detail", response_data) -# self.assertEqual( -# response_data["detail"], -# f"No user found by {{'{param}': '{value}'}} on site {self.tenant_x['domain']}.", -# ) +@ddt +class TestUsersAPIIntegration(BaseAPIIntegrationTest, UsersAPIRequestMixin): + """Integration test suite for the Users API""" + + def setUp(self): + """Set up the test suite""" + super().setUp() + + @ddt.data( + {"is_staff": False, "is_superuser": False}, + {"is_staff": True, "is_superuser": False}, + {"is_staff": False, "is_superuser": True}, + {"is_staff": True, "is_superuser": True}, + ) + def test_create_user_in_tenant_success(self, permissions: dict) -> None: + """ + Test creating a user in a tenant. + + Open edX definitions tested: + - `create_edxapp_user` + - `check_edxapp_account_conflicts` + + Expected result: + - The status code is 200. + - The user is created successfully in the tenant with the provided data. + """ + data = next(FAKE_USER_DATA) + data.update(permissions) + + response = self.create_user(self.tenant_x, data) + + response_data = response.json() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response_data["email"], data["email"]) + self.assertEqual(response_data["username"], data["username"]) + self.assertTrue(response_data["is_active"]) + self.assertFalse(response_data["is_staff"]) + self.assertFalse(response_data["is_superuser"]) + + def test_create_user_missing_required_fields(self) -> None: + """ + Test creating a user in a tenant with invalid data. + + Open edX definitions tested: + - `check_edxapp_account_conflicts` + + Expected result: + - The status code is 400. + - The response contains the missing fields. + - The user is not created in the tenant. + """ + data = next(FAKE_USER_DATA) + del data["email"] + del data["username"] + + response = self.create_user(self.tenant_x, data) + + response_data = response.json() + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn("email", response_data) + self.assertIn("username", response_data) + + def test_create_user_in_tenant_user_already_exists(self) -> None: + """ + Test creating a user in a tenant that already exists. + + Open edX definitions tested: + - `check_edxapp_account_conflicts` + + Expected result: + - The status code is 400. + - The response contains an error message. + - The user is not created in the tenant. + """ + data = next(FAKE_USER_DATA) + self.create_user(self.tenant_x, data) + + response = self.create_user(self.tenant_x, data) + + response_data = response.json() + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn("non_field_errors", response_data) + + @ddt.data("username", "email") + def test_get_user_in_tenant_success(self, query_param: str) -> None: + """ + Test getting a user in a tenant. + + Open edX definitions tested: + - `get_edxapp_user` + + Expected result: + - The status code is 200. + - The response contains the user data. + """ + data = next(FAKE_USER_DATA) + self.create_user(self.tenant_x, data) + + response = self.get_user(self.tenant_x, {query_param: data[query_param]}) + + response_data = response.json() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response_data[query_param], data[query_param]) + + def test_get_user_of_another_tenant(self) -> None: + """ + Test getting a user that belongs to another tenant. + + Open edX definitions tested: + - `get_edxapp_user` + + Expected result: + - The status code is 404. + - The response contains an error message. + """ + data = next(FAKE_USER_DATA) + self.create_user(self.tenant_x, data) + + response = self.get_user(self.tenant_y, {"username": data["username"]}) + + response_data = response.json() + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertIn("detail", response_data) + self.assertEqual( + response_data["detail"], + f"No user found by {{'username': '{data['username']}'}} on site {self.tenant_y['domain']}.", + ) + + @ddt.data( + ("username", "user-not-found"), + ("email", "user-not-found@mail.com"), + ) + @ddt.unpack + def test_get_user_in_tenant_user_not_found(self, param: str, value: str) -> None: + """ + Test getting a user in a tenant that does not exist. + + Open edX definitions tested: + - `get_edxapp_user` + + Expected result: + - The status code is 404. + - The response contains an error message. + """ + response = self.get_user(self.tenant_x, {param: value}) + + response_data = response.json() + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertIn("detail", response_data) + self.assertEqual( + response_data["detail"], + f"No user found by {{'{param}': '{value}'}} on site {self.tenant_x['domain']}.", + ) + + def test_update_user_in_tenant_success(self) -> None: + """ + Test updating a user in a tenant. + + Open edX definitions tested: + - `get_edxapp_user` + - `get_user_profile` + - `check_edxapp_account_conflicts` + - `get_user_read_only_serializer` + + Expected result: + - The status code is 200. + - The user is updated successfully in the tenant with the provided data. + """ + data = next(FAKE_USER_DATA) + self.create_user(self.tenant_x, data) + updated_data = next(FAKE_USER_DATA) + updated_data["username"] = data["username"] + updated_data["email"] = data["email"] + + response = self.update_user(self.tenant_x, data=updated_data) + + response_data = response.json() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response_data["username"], data["username"]) + self.assertEqual(response_data["email"], data["email"]) + self.assertEqual(response_data["name"], updated_data["fullname"]) + self.assertEqual(response_data["mailing_address"], updated_data["mailing_address"]) + self.assertEqual(response_data["year_of_birth"], updated_data["year_of_birth"]) + self.assertEqual(response_data["gender"], updated_data["gender"]) + self.assertEqual(response_data["level_of_education"], updated_data["level_of_education"]) + self.assertEqual(response_data["goals"], updated_data["goals"]) + self.assertTrue(response_data["is_active"]) + + @ddt.data( + ("username", "user-not-found"), + ("email", "user-not-found@mail.com"), + ) + @ddt.unpack + def test_update_user_in_tenant_user_not_found(self, param: str, value: str) -> None: + """ + Test updating a user in a tenant that does not exist. + + Open edX definitions tested: + - `get_edxapp_user` + + Expected result: + - The status code is 404. + - The response contains an error message. + """ + response = self.update_user(self.tenant_x, {param: value}) + + response_data = response.json() + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertIn("detail", response_data) + self.assertEqual( + response_data["detail"], + f"No user found by {{'{param}': '{value}'}} on site {self.tenant_x['domain']}.", + ) @ddt.ddt @@ -444,112 +444,112 @@ def setUp(self): self.mode = "audit" return super().setUp() - # @ddt.data("email", "username") - # def test_create_enrollment_valid_user_mode_course(self, param: str) -> None: - # """ - # Create enrollment with a valid user, valid course and valid mode - # """ - # user_data = next(FAKE_USER_DATA) - # self.create_user(self.tenant_x, user_data) - # data = { - # param: user_data[param], - # "course_id": self.course_id, - # "mode": self.mode, - # } - - # response = self.create_enrollment(self.tenant_x, data) - - # response_data = response.json() - # self.assertEqual(response.status_code, status.HTTP_200_OK) - # self.assertEqual(response_data["username"], user_data["username"]) - # self.assertEqual(response_data["mode"], data["mode"]) - # self.assertEqual(response_data["course_id"], data["course_id"]) - # self.assertTrue(response_data["is_active"]) - # self.assertIn("created", response_data) - - # @ddt.data("email", "username") - # def test_force_create_enrollment_valid_user_mode_course(self, param: str) -> None: - # """ - # Create enrollment with a valid user, valid course, valid mode using force - # """ - # user_data = next(FAKE_USER_DATA) - # self.create_user(self.tenant_x, user_data) - # data = { - # param: user_data[param], - # "course_id": self.course_id, - # "mode": self.mode, - # "force": True, - # } - - # response = self.create_enrollment(self.tenant_x, data) - - # response_data = response.json() - # self.assertEqual(response.status_code, status.HTTP_200_OK) - # self.assertEqual(response_data["username"], user_data["username"]) - # self.assertEqual(response_data["mode"], data["mode"]) - # self.assertEqual(response_data["course_id"], data["course_id"]) - # self.assertTrue(response_data["is_active"]) - # self.assertIn("created", response_data) - - # @ddt.data("email", "username") - # def test_create_valid_course_mode_invalid_user(self, param: str) -> None: - # """ - # Create enrollment with a valid course, valid mode, and a non-existent user - # """ - # data = { - # param: param, - # "course_id": self.course_id, - # "mode": self.mode, - # } - - # response = self.create_enrollment(self.tenant_x, 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"], ["User not found"]) - - # @ddt.data("email", "username") - # def test_create_valid_course_mode_invalid_user_for_site(self, param: str) -> None: - # """ - # Create enrollment with a valid course, valid mode, and a user from another site - # """ - # user_data = next(FAKE_USER_DATA) - # self.create_user(self.tenant_y, user_data) - # data = { - # param: user_data[param], - # "course_id": self.course_id, - # "mode": self.mode, - # } - - # response = self.create_enrollment(self.tenant_x, data) - - # response_data = response.json() - # self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - # self.assertEqual( - # response_data["error"]["detail"], - # f"No user found by {{'{param}': '{data[param]}'}} on site {self.tenant_x['domain']}.", - # ) - - # @ddt.data("email", "username") - # def test_create_valid_user_mode_invalid_course(self, param: str) -> None: - # """ - # Create enrollment with a valid user, valid mode, and non-existent course - # """ - # user_data = next(FAKE_USER_DATA) - # self.create_user(self.tenant_x, user_data) - # data = { - # param: user_data[param], - # "course_id": "course-v1:OpenedX+DemoX+NonExistentCourse", - # "mode": self.mode, - # } - - # response = self.create_enrollment(self.tenant_x, 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"], ["Course not found"]) + @ddt.data("email", "username") + def test_create_enrollment_valid_user_mode_course(self, param: str) -> None: + """ + Create enrollment with a valid user, valid course and valid mode + """ + user_data = next(FAKE_USER_DATA) + self.create_user(self.tenant_x, user_data) + data = { + param: user_data[param], + "course_id": self.course_id, + "mode": self.mode, + } + + response = self.create_enrollment(self.tenant_x, data) + + response_data = response.json() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response_data["username"], user_data["username"]) + self.assertEqual(response_data["mode"], data["mode"]) + self.assertEqual(response_data["course_id"], data["course_id"]) + self.assertTrue(response_data["is_active"]) + self.assertIn("created", response_data) + + @ddt.data("email", "username") + def test_force_create_enrollment_valid_user_mode_course(self, param: str) -> None: + """ + Create enrollment with a valid user, valid course, valid mode using force + """ + user_data = next(FAKE_USER_DATA) + self.create_user(self.tenant_x, user_data) + data = { + param: user_data[param], + "course_id": self.course_id, + "mode": self.mode, + "force": True, + } + + response = self.create_enrollment(self.tenant_x, data) + + response_data = response.json() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response_data["username"], user_data["username"]) + self.assertEqual(response_data["mode"], data["mode"]) + self.assertEqual(response_data["course_id"], data["course_id"]) + self.assertTrue(response_data["is_active"]) + self.assertIn("created", response_data) + + @ddt.data("email", "username") + def test_create_valid_course_mode_invalid_user(self, param: str) -> None: + """ + Create enrollment with a valid course, valid mode, and a non-existent user + """ + data = { + param: param, + "course_id": self.course_id, + "mode": self.mode, + } + + response = self.create_enrollment(self.tenant_x, 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"], ["User not found"]) + + @ddt.data("email", "username") + def test_create_valid_course_mode_invalid_user_for_site(self, param: str) -> None: + """ + Create enrollment with a valid course, valid mode, and a user from another site + """ + user_data = next(FAKE_USER_DATA) + self.create_user(self.tenant_y, user_data) + data = { + param: user_data[param], + "course_id": self.course_id, + "mode": self.mode, + } + + response = self.create_enrollment(self.tenant_x, data) + + response_data = response.json() + self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual( + response_data["error"]["detail"], + f"No user found by {{'{param}': '{data[param]}'}} on site {self.tenant_x['domain']}.", + ) + + @ddt.data("email", "username") + def test_create_valid_user_mode_invalid_course(self, param: str) -> None: + """ + Create enrollment with a valid user, valid mode, and non-existent course + """ + user_data = next(FAKE_USER_DATA) + self.create_user(self.tenant_x, user_data) + data = { + param: user_data[param], + "course_id": "course-v1:OpenedX+DemoX+NonExistentCourse", + "mode": self.mode, + } + + response = self.create_enrollment(self.tenant_x, 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"], ["Course not found"]) @ddt.data("email", "username") def test_create_valid_user_mode_invalid_course_for_site(self, param: str) -> None: @@ -567,54 +567,85 @@ def test_create_valid_user_mode_invalid_course_for_site(self, param: str) -> Non response = self.create_enrollment(self.tenant_y, data) response_data = response.json() - print(f"\n\nResponse data: {response_data}\n\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.course_id}"]) @ddt.data("email", "username") - def test_force_create_valid_user_course_invalid_mode(self, param: str) -> None: + def test_create_valid_user_course_invalid_mode(self, param: str) -> None: """ Create enrollment with a valid user, valid course, and a not available mode """ user_data = next(FAKE_USER_DATA) + self.create_user(self.tenant_x, user_data) data = { param: user_data[param], "course_id": self.course_id, "mode": "masters", - "force": True, } - response = self.create_enrollment(self.tenant_y, data) + response = self.create_enrollment(self.tenant_x, data) response_data = response.json() - print(f"\n\nResponse data: {response_data}\n\n") 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"]) + + @ddt.data("email", "username") + def test_force_create_valid_user_course_mode_not_allowed(self, param: str) -> None: + """ + Forece create enrollment with a valid user, valid course, and a not available mode + """ + user_data = next(FAKE_USER_DATA) + self.create_user(self.tenant_x, user_data) + data = { + param: user_data[param], + "course_id": self.course_id, + "mode": "masters", + "force": True, + } + + response = self.create_enrollment(self.tenant_x, data) + + response_data = response.json() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response_data["username"], user_data["username"]) + self.assertEqual(response_data["mode"], data["mode"]) + self.assertEqual(response_data["course_id"], data["course_id"]) + self.assertTrue(response_data["is_active"]) + self.assertIn("created", response_data) @ddt.data("email", "username") - def test_read_valid_email_course(self, param: str) -> None: + def test_get_enrollment_success(self, param: str) -> None: """ - Get a valid enrollment + Get a valid enrollment. """ user_data = next(FAKE_USER_DATA) enrollment_data = { param: user_data[param], "course_id": self.course_id, + "mode": self.mode, } - self.create_user(self.tenant_y, user_data) + self.create_user(self.tenant_x, user_data) self.create_enrollment(self.tenant_x, enrollment_data) response = self.get_enrollment(self.tenant_x, data=enrollment_data) response_data = response.json() - print(f"\n\nResponse data: {response_data}\n\n") self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response_data["username"], user_data["username"]) + self.assertEqual(response_data["mode"], enrollment_data["mode"]) + self.assertEqual(response_data["course_id"], enrollment_data["course_id"]) + self.assertTrue(response_data["is_active"]) + self.assertIn("created", response_data) @ddt.data("email", "username") - def test_read_invalid_enrollment(self, param: str) -> None: + def test_get_enrollment_does_not_exist(self, param: str) -> None: """ Get a invalid enrollment (doesn't exist) """ user_data = next(FAKE_USER_DATA) - self.create_user(self.tenant_y, user_data) + self.create_user(self.tenant_x, user_data) data = { param: user_data[param], "course_id": self.course_id, @@ -623,8 +654,12 @@ def test_read_invalid_enrollment(self, param: str) -> None: response = self.get_enrollment(self.tenant_x, data=data) response_data = response.json() - print(f"\n\nResponse data: {response_data}\n\n") self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertIn("detail", response_data) + self.assertEqual( + response_data["detail"], + f"No user found by {{'{param}': '{user_data[param]}'}} on site {self.tenant_x['domain']}.", + ) @ddt.data("email", "username") def test_read_invalid_enrollment_for_site(self, param: str) -> None: @@ -635,6 +670,7 @@ def test_read_invalid_enrollment_for_site(self, param: str) -> None: enrollment_data = { param: user_data[param], "course_id": self.course_id, + "mode": self.mode, } self.create_user(self.tenant_x, user_data) self.create_enrollment(self.tenant_x, enrollment_data) @@ -642,8 +678,12 @@ def test_read_invalid_enrollment_for_site(self, param: str) -> None: response = self.get_enrollment(self.tenant_y, data=enrollment_data) response_data = response.json() - print(f"\n\nResponse data: {response_data}\n\n") self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertIn("detail", response_data) + self.assertEqual( + response_data["detail"], + f"No user found by {{'{param}': '{user_data[param]}'}} on site {self.tenant_y['domain']}.", + ) @ddt.data("email", "username") def test_delete_valid_enrollment(self, param: str) -> None: @@ -654,251 +694,181 @@ def test_delete_valid_enrollment(self, param: str) -> None: enrollment_data = { param: user_data[param], "course_id": self.course_id, + "mode": self.mode, } self.create_user(self.tenant_x, user_data) self.create_enrollment(self.tenant_x, enrollment_data) response = self.delete_enrollment(self.tenant_x, data=enrollment_data) - response_data = response.json() - print(f"\n\nResponse data: {response_data}\n\n") self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) - def test_delete_invalid_enrollment(self) -> None: + @ddt.data("email", "username") + def test_delete_invalid_enrollment(self, param: str) -> None: """ Delete a invalid enrollment (doesn't exist) """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_x, user_data) data = { - "email": user_data["email"], + param: user_data[param], "course_id": self.course_id, } response = self.delete_enrollment(self.tenant_x, data=data) response_data = response.json() - print(f"\n\nResponse data: {response_data}\n\n") self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertIn("detail", response_data) + self.assertEqual( + response_data["detail"], + f"No enrollment found for user: `{user_data['username']}` on course_id `{self.course_id}`", + ) - def test_delete_invalid_enrollment_for_site(self) -> None: + @ddt.data("email", "username") + def test_delete_invalid_enrollment_for_site(self, param: str) -> None: """ Delete a invalid enrollment (enrollment from other site) """ user_data = next(FAKE_USER_DATA) - self.create_user(self.tenant_x, user_data) - self.create_enrollment(self.tenant_x, user_data) - data = { - "email": user_data["email"], + enrollment_data = { + param: user_data[param], "course_id": self.course_id, + "mode": self.mode, } + self.create_user(self.tenant_x, user_data) + self.create_enrollment(self.tenant_x, enrollment_data) - response = self.delete_enrollment(self.tenant_y, data=data) + response = self.delete_enrollment(self.tenant_y, data=enrollment_data) response_data = response.json() - print(f"\n\nResponse data: {response_data}\n\n") self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertIn("detail", response_data) + self.assertEqual( + response_data["detail"], + f"No user found by {{'{param}': '{user_data[param]}'}} on site {self.tenant_y['domain']}.", + ) - def test_update_valid_enrollment_change_is_active(self) -> None: + @ddt.data("email", "username") + def test_update_valid_enrollment_change_is_active_mode_field(self, param: str) -> None: """ - Update an existing enrollment; change is_active flag + Update an existing enrollment; change is_active and mode field """ user_data = next(FAKE_USER_DATA) - self.create_user(self.tenant_x, user_data) - self.create_enrollment(self.tenant_x, user_data) - # You can use email or username to update the enrollment - # Add dd_data to test both cases - data = { - "email": user_data["email"], + enrollment_data = { + param: user_data[param], "course_id": self.course_id, "is_active": False, "mode": self.mode, } - expected_response = { - "user": user_data["username"], - "course_id": self.course_id, - "mode": self.mode, - "is_active": False, - "enrollment_attributes": None, - } - - response = self.update_enrollment(self.tenant_x, data=data) - - response_data = response.json() - print(f"\n\nResponse data: {response_data}\n\n") - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertDictContainsSubset(expected_response, response_data) - - def test_update_valid_enrollment_change_valid_mode(self) -> None: - """ - Update an existing enrollment; change mode - """ - user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_x, user_data) - self.create_enrollment(self.tenant_x, user_data) - # You can use email or username to update the enrollment - # Add dd_data to test both cases - data = { - "email": user_data["email"], - "course_id": self.course_id, - "is_active": True, - "mode": "honor", - } - expected_response = { - "user": user_data["username"], - "course_id": self.course_id, - "mode": "honor", - "is_active": True, - "enrollment_attributes": None, - } + self.create_enrollment(self.tenant_x, enrollment_data) + enrollment_data["is_active"] = True + enrollment_data["mode"] = "honor" - response = self.update_enrollment(self.tenant_x, data=data) + response = self.update_enrollment(self.tenant_x, data=enrollment_data) response_data = response.json() - print(f"\n\nResponse data: {response_data}\n\n") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertDictContainsSubset(expected_response, response_data) + self.assertEqual(response_data["user"], user_data["username"]) + self.assertEqual(response_data["course_id"], enrollment_data["course_id"]) + self.assertEqual(response_data["mode"], enrollment_data["mode"]) + self.assertTrue(response_data["is_active"]) - def test_update_valid_enrollment_change_invalid_mode(self) -> None: + @ddt.data("email", "username") + def test_update_valid_enrollment_change_invalid_mode(self, param: str) -> None: """ Update an existing enrollment; change to invalid mode """ user_data = next(FAKE_USER_DATA) - self.create_user(self.tenant_x, user_data) - self.create_enrollment(self.tenant_x, user_data) - - data = { - "email": user_data["email"], + enrollment_data = { + param: user_data[param], "course_id": self.course_id, "is_active": True, - "mode": "masters", + "mode": self.mode, } - - response = self.update_enrollment(self.tenant_x, data=data) - - response_data = response.json() - print(f"\n\nResponse data: {response_data}\n\n") - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - - def test_update_invalid_enrollment_change_valid_mode(self) -> None: - """ - Update an non-existent enrollment; change mode - """ - user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_x, user_data) - data = { - "email": user_data["email"], - "course_id": self.course_id, - "is_active": True, - "mode": "honor", - } + self.create_enrollment(self.tenant_x, enrollment_data) + enrollment_data["mode"] = "masters" - response = self.update_enrollment(self.tenant_x, data=data) + response = self.update_enrollment(self.tenant_x, data=enrollment_data) response_data = response.json() - print(f"\n\nResponse data: {response_data}\n\n") - # Why is this returning 202? - self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) + 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"]) - def test_update_invalid_enrollment_change_is_active(self) -> None: + @ddt.data("email", "username") + def test_update_invalid_enrollment_change_valid_mode(self, param: str) -> None: """ - Update an non-existent enrollment; change is_active flag + Update an non-existent enrollment; change is_active and mode field """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_x, user_data) data = { - "email": user_data["email"], + param: user_data[param], "course_id": self.course_id, "is_active": False, - "mode": self.mode, + "mode": "honor", } response = self.update_enrollment(self.tenant_x, data=data) response_data = response.json() - print(f"\n\nResponse data: {response_data}\n\n") - # Why is this returning 202? self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual(response_data["error"]["detail"], f"No enrollment found for {user_data['username']}") - def test_update_valid_enrollment_change_is_active_force_post(self) -> None: + @ddt.data("email", "username") + def test_update_valid_enrollment_force_post(self, param: str) -> None: """ - Update an existing enrollment using POST with force=True; change is_active flag + Update an existing enrollment using POST with force=True. """ user_data = next(FAKE_USER_DATA) - self.create_user(self.tenant_x, user_data) - self.create_enrollment(self.tenant_x, user_data) - data = { - "email": user_data["email"], + enrollment_data = { + param: user_data[param], "course_id": self.course_id, "is_active": False, "mode": self.mode, "force": True, } - expected_response = { - "username": user_data["username"], - "is_active": False, - "course_id": self.course_id, - } + self.create_user(self.tenant_x, user_data) + self.create_enrollment(self.tenant_x, enrollment_data) + enrollment_data["is_active"] = True + enrollment_data["mode"] = "honor" - response = self.create_enrollment(self.tenant_x, data=data) + response = self.create_enrollment(self.tenant_x, data=enrollment_data) response_data = response.json() - print(f"\n\nResponse data: {response_data}\n\n") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertDictContainsSubset(expected_response, response_data) + self.assertEqual(response_data["username"], user_data["username"]) + self.assertEqual(response_data["course_id"], enrollment_data["course_id"]) + self.assertEqual(response_data["mode"], enrollment_data["mode"]) + self.assertTrue(response_data["is_active"]) + - def test_update_valid_enrollment_change_valid_mode_force_post(self) -> None: +class TestInfoView(BaseAPIIntegrationTest): + """ + Integration test suite for the info view. + """ + + def setUp(self): """ - Update an existing enrollment; change mode + Set up the test suite. """ - user_data = next(FAKE_USER_DATA) - self.create_user(self.tenant_x, user_data) - self.create_enrollment(self.tenant_x, user_data) - data = { - "email": user_data["email"], - "course_id": self.course_id, - "is_active": True, - "mode": "honor", - "force": True, - } - expected_response = { - "user": user_data["username"], - "is_active": True, - "course_id": data["course_id"], - "mode": "honor", - } + self.url = f"{settings['EOX_CORE_API_BASE']}{reverse('eox-info')}" + super().setUp() - response = self.create_enrollment(self.tenant_x, data=data) + def test_info_view_success(self) -> None: + """Test the info view. + + Expected result: + - The status code is 200. + - 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() - print(f"\n\nResponse data: {response_data}\n\n") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertDictContainsSubset(expected_response, response_data) - - -# class TestInfoView(BaseAPIIntegrationTest): -# """ -# Integration test suite for the info view. -# """ - -# def setUp(self): -# """ -# Set up the test suite. -# """ -# self.url = f"{settings['EOX_CORE_API_BASE']}{reverse('eox-info')}" -# super().setUp() - -# def test_info_view_success(self) -> None: -# """Test the info view. - -# Expected result: -# - The status code is 200. -# - 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) -# self.assertIn("git", response_data) + self.assertIn("version", response_data) + self.assertIn("name", response_data) + self.assertIn("git", response_data) From 5ec008ac6101cde1530489c4e0cfe271254073df Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Mon, 23 Sep 2024 11:51:25 -0500 Subject: [PATCH 07/32] chore: fix pylint errors --- eox_core/api/v1/tests/integration/test_views.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/eox_core/api/v1/tests/integration/test_views.py b/eox_core/api/v1/tests/integration/test_views.py index 04412ce56..2d4980f5b 100644 --- a/eox_core/api/v1/tests/integration/test_views.py +++ b/eox_core/api/v1/tests/integration/test_views.py @@ -1,6 +1,7 @@ """ Integration test suite for the API v1 views. """ +# pylint: disable=too-many-lines from __future__ import annotations @@ -223,10 +224,6 @@ def delete_enrollment(self, tenant: dict, data: dict | None = None) -> requests. class TestUsersAPIIntegration(BaseAPIIntegrationTest, UsersAPIRequestMixin): """Integration test suite for the Users API""" - def setUp(self): - """Set up the test suite""" - super().setUp() - @ddt.data( {"is_staff": False, "is_superuser": False}, {"is_staff": True, "is_superuser": False}, From 041273a64550c9c953cabca33ab1bd76ca84c38a Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Mon, 23 Sep 2024 12:03:43 -0500 Subject: [PATCH 08/32] fix: add missing ddt function in class decorator --- eox_core/api/v1/tests/integration/test_views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eox_core/api/v1/tests/integration/test_views.py b/eox_core/api/v1/tests/integration/test_views.py index 2d4980f5b..f9c069dab 100644 --- a/eox_core/api/v1/tests/integration/test_views.py +++ b/eox_core/api/v1/tests/integration/test_views.py @@ -220,7 +220,7 @@ def delete_enrollment(self, tenant: dict, data: dict | None = None) -> requests. return make_request(tenant, "DELETE", url=ENROLLMENT_URL, data=data) -@ddt +@ddt.ddt class TestUsersAPIIntegration(BaseAPIIntegrationTest, UsersAPIRequestMixin): """Integration test suite for the Users API""" From 50a67dc009e67213bf2e0fa0b2b6cf9715264909 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Mon, 23 Sep 2024 14:05:40 -0500 Subject: [PATCH 09/32] chore: move users data to fake_users file --- .../api/v1/tests/integration/test_views.py | 59 +- .../v1/tests/integration/utils/__init__.py | 0 .../v1/tests/integration/utils/fake_users.py | 591 ++++++++++++++++++ 3 files changed, 618 insertions(+), 32 deletions(-) create mode 100644 eox_core/api/v1/tests/integration/utils/__init__.py create mode 100644 eox_core/api/v1/tests/integration/utils/fake_users.py diff --git a/eox_core/api/v1/tests/integration/test_views.py b/eox_core/api/v1/tests/integration/test_views.py index f9c069dab..cb2c3942c 100644 --- a/eox_core/api/v1/tests/integration/test_views.py +++ b/eox_core/api/v1/tests/integration/test_views.py @@ -1,7 +1,6 @@ """ Integration test suite for the API v1 views. """ -# pylint: disable=too-many-lines from __future__ import annotations @@ -52,7 +51,7 @@ def make_request( with_auth: bool = True, ) -> requests.Response: """ - Make a request to a tenant. + Make a request to a site (default site or tenant). Args: tenant (dict): The tenant data. @@ -471,20 +470,20 @@ def test_force_create_enrollment_valid_user_mode_course(self, param: str) -> Non """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_x, user_data) - data = { + enrollment_data = { param: user_data[param], "course_id": self.course_id, "mode": self.mode, "force": True, } - response = self.create_enrollment(self.tenant_x, data) + response = self.create_enrollment(self.tenant_x, enrollment_data) response_data = response.json() self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response_data["username"], user_data["username"]) - self.assertEqual(response_data["mode"], data["mode"]) - self.assertEqual(response_data["course_id"], data["course_id"]) + self.assertEqual(response_data["mode"], enrollment_data["mode"]) + self.assertEqual(response_data["course_id"], enrollment_data["course_id"]) self.assertTrue(response_data["is_active"]) self.assertIn("created", response_data) @@ -513,19 +512,19 @@ def test_create_valid_course_mode_invalid_user_for_site(self, param: str) -> Non """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_y, user_data) - data = { + enrollment_data = { param: user_data[param], "course_id": self.course_id, "mode": self.mode, } - response = self.create_enrollment(self.tenant_x, data) + response = self.create_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 user found by {{'{param}': '{data[param]}'}} on site {self.tenant_x['domain']}.", + f"No user found by {{'{param}': '{enrollment_data[param]}'}} on site {self.tenant_x['domain']}.", ) @ddt.data("email", "username") @@ -535,13 +534,13 @@ def test_create_valid_user_mode_invalid_course(self, param: str) -> None: """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_x, user_data) - data = { + enrollment_data = { param: user_data[param], "course_id": "course-v1:OpenedX+DemoX+NonExistentCourse", "mode": self.mode, } - response = self.create_enrollment(self.tenant_x, data) + response = self.create_enrollment(self.tenant_x, enrollment_data) response_data = response.json() self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) @@ -555,13 +554,13 @@ def test_create_valid_user_mode_invalid_course_for_site(self, param: str) -> Non """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_y, user_data) - data = { + enrollment_data = { param: user_data[param], "course_id": self.course_id, "mode": self.mode, } - response = self.create_enrollment(self.tenant_y, data) + response = self.create_enrollment(self.tenant_y, enrollment_data) response_data = response.json() self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) @@ -575,13 +574,13 @@ def test_create_valid_user_course_invalid_mode(self, param: str) -> None: """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_x, user_data) - data = { + enrollment_data = { param: user_data[param], "course_id": self.course_id, "mode": "masters", } - response = self.create_enrollment(self.tenant_x, data) + response = self.create_enrollment(self.tenant_x, enrollment_data) response_data = response.json() self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) @@ -591,24 +590,24 @@ def test_create_valid_user_course_invalid_mode(self, param: str) -> None: @ddt.data("email", "username") def test_force_create_valid_user_course_mode_not_allowed(self, param: str) -> None: """ - Forece create enrollment with a valid user, valid course, and a not available mode + Force create enrollment with a valid user, valid course, and a not available mode """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_x, user_data) - data = { + enrollment_data = { param: user_data[param], "course_id": self.course_id, "mode": "masters", "force": True, } - response = self.create_enrollment(self.tenant_x, data) + response = self.create_enrollment(self.tenant_x, enrollment_data) response_data = response.json() self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response_data["username"], user_data["username"]) - self.assertEqual(response_data["mode"], data["mode"]) - self.assertEqual(response_data["course_id"], data["course_id"]) + self.assertEqual(response_data["mode"], enrollment_data["mode"]) + self.assertEqual(response_data["course_id"], enrollment_data["course_id"]) self.assertTrue(response_data["is_active"]) self.assertIn("created", response_data) @@ -643,20 +642,16 @@ def test_get_enrollment_does_not_exist(self, param: str) -> None: """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_x, user_data) - data = { + enrollment_data = { param: user_data[param], "course_id": self.course_id, } - response = self.get_enrollment(self.tenant_x, data=data) + response = self.get_enrollment(self.tenant_x, data=enrollment_data) response_data = response.json() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertIn("detail", response_data) - self.assertEqual( - response_data["detail"], - f"No user found by {{'{param}': '{user_data[param]}'}} on site {self.tenant_x['domain']}.", - ) + self.assertEqual(response_data, [f"No enrollment found for user:`{user_data['username']}`"]) @ddt.data("email", "username") def test_read_invalid_enrollment_for_site(self, param: str) -> None: @@ -701,18 +696,18 @@ def test_delete_valid_enrollment(self, param: str) -> None: self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) @ddt.data("email", "username") - def test_delete_invalid_enrollment(self, param: str) -> None: + def test_delete_enrollment_does_not_exist(self, param: str) -> None: """ Delete a invalid enrollment (doesn't exist) """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_x, user_data) - data = { + enrollment_data = { param: user_data[param], "course_id": self.course_id, } - response = self.delete_enrollment(self.tenant_x, data=data) + response = self.delete_enrollment(self.tenant_x, data=enrollment_data) response_data = response.json() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) @@ -802,14 +797,14 @@ def test_update_invalid_enrollment_change_valid_mode(self, param: str) -> None: """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_x, user_data) - data = { + enrollment_data = { param: user_data[param], "course_id": self.course_id, "is_active": False, "mode": "honor", } - response = self.update_enrollment(self.tenant_x, data=data) + response = self.update_enrollment(self.tenant_x, data=enrollment_data) response_data = response.json() self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) diff --git a/eox_core/api/v1/tests/integration/utils/__init__.py b/eox_core/api/v1/tests/integration/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/eox_core/api/v1/tests/integration/utils/fake_users.py b/eox_core/api/v1/tests/integration/utils/fake_users.py new file mode 100644 index 000000000..c187a82bf --- /dev/null +++ b/eox_core/api/v1/tests/integration/utils/fake_users.py @@ -0,0 +1,591 @@ +"""Fake user data for testing purposes.""" + +FAKE_USER_DATA = iter( + [ + { + "username": "athickpenny0", + "email": "athickpenny0@indiegogo.com", + "fullname": "Antoni Thickpenny", + "password": "jD3_u)67VfHce", + "activate_user": True, + "mailing_address": "70736 Haas Parkway", + "year_of_birth": 1996, + "gender": "f", + "level_of_education": "p", + "city": "Solikamsk", + "goals": "Maecenas leo odio, condimentum id", + }, + { + "username": "smenchenton1", + "email": "smenchenton1@networksolutions.com", + "fullname": "Sonia Menchenton", + "password": "hO9\\Pni)", + "activate_user": True, + "mailing_address": "7543 Eagle Crest Terrace", + "year_of_birth": 1998, + "gender": "m", + "level_of_education": "m", + "city": "Diourbel", + "goals": "Sed sagittis.", + }, + { + "username": "arealff2", + "email": "arealff2@phpbb.com", + "fullname": "Alexis Realff", + "password": 'zY2(yq!(>4"_', + "activate_user": True, + "mailing_address": "0 Grim Drive", + "year_of_birth": 2005, + "gender": "f", + "level_of_education": "b", + "city": "Gongyi", + "goals": "Proin interdum mauris non ligula pellentesque ultrices.", + }, + { + "username": "ddilon3", + "email": "ddilon3@geocities.com", + "fullname": "Dotty Dilon", + "password": 'mV9"3zRdRr#bTP', + "activate_user": True, + "mailing_address": "6859 Lerdahl Road", + "year_of_birth": 2002, + "gender": "m", + "level_of_education": "a", + "city": "La Curva", + "goals": "Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl.", + }, + { + "username": "lties4", + "email": "lties4@addthis.com", + "fullname": "Loreen Ties", + "password": 'iU6@R"`/t3>/DT', + "activate_user": True, + "mailing_address": "64 Clemons Terrace", + "year_of_birth": 1994, + "gender": "f", + "level_of_education": "hs", + "city": "Xianyang", + "goals": "In est risus, auctor sed, tristique in, tempus sit amet, sem.", + }, + { + "username": "lbattisson5", + "email": "lbattisson5@360.cn", + "fullname": "Lynett Battisson", + "password": "oB3?1JWFM\\=S>", + "activate_user": True, + "mailing_address": "89 Vermont Pass", + "year_of_birth": 2000, + "gender": "m", + "level_of_education": "jhs", + "city": "Lyudinovo", + "goals": "Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.", + }, + { + "username": "jniset6", + "email": "jniset6@virginia.edu", + "fullname": "Jacquelin Niset", + "password": "aC2/anhtj", + "activate_user": True, + "mailing_address": "83984 8th Pass", + "year_of_birth": 2003, + "gender": "f", + "level_of_education": "el", + "city": "Erie", + "goals": "Donec quis orci eget orci vehicula condimentum.", + }, + { + "username": "mdoumenc7", + "email": "mdoumenc7@chicagotribune.com", + "fullname": "Martin Doumenc", + "password": "dQ9't{0(", + "activate_user": True, + "mailing_address": "8 Scoville Plaza", + "year_of_birth": 1993, + "gender": "f", + "level_of_education": "none", + "city": "Boaco", + "goals": "Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae.", + }, + { + "username": "lchampagne8", + "email": "lchampagne8@buzzfeed.com", + "fullname": "Locke Champagne", + "password": "tW7_SYC1qUf!K'0C", + "activate_user": True, + "mailing_address": "79 Milwaukee Place", + "year_of_birth": 1996, + "gender": "f", + "level_of_education": "other", + "city": "Linjiang", + "goals": "In hac habitasse platea dictumst.", + }, + { + "username": "zczyz9", + "email": "zczyz9@gravatar.com", + "fullname": "Zolly Czyz", + "password": "jN3~/&1ZMG%", + "activate_user": True, + "mailing_address": "5 Victoria Place", + "year_of_birth": 2000, + "gender": "f", + "level_of_education": "p", + "city": "'s-Hertogenbosch", + "goals": "Suspendisse potenti.", + }, + { + "username": "jdoe1", + "email": "jdoe1@google.com", + "fullname": "John Doe", + "password": "bL2~#63pTr!Kx", + "activate_user": True, + "mailing_address": "123 Elm Street", + "year_of_birth": 1995, + "gender": "m", + "level_of_education": "b", + "city": "New York", + "goals": "Curabitur pretium tincidunt lacus.", + }, + { + "username": "msmith2", + "email": "msmith2@outlook.com", + "fullname": "Mary Smith", + "password": "gF4_%6Dm#5wJ", + "activate_user": True, + "mailing_address": "456 Oak Avenue", + "year_of_birth": 1993, + "gender": "f", + "level_of_education": "m", + "city": "Chicago", + "goals": "Aenean massa.", + }, + { + "username": "rwilliams3", + "email": "rwilliams3@aol.com", + "fullname": "Robert Williams", + "password": "kL9*~Yx2pW!", + "activate_user": False, + "mailing_address": "789 Pine Boulevard", + "year_of_birth": 1988, + "gender": "m", + "level_of_education": "el", + "city": "Los Angeles", + "goals": "Etiam ultricies nisi vel augue.", + }, + { + "username": "emurphy4", + "email": "emurphy4@yahoo.com", + "fullname": "Emily Murphy", + "password": "qN2%8Rc!xV&Z", + "activate_user": True, + "mailing_address": "321 Maple Road", + "year_of_birth": 1990, + "gender": "f", + "level_of_education": "p", + "city": "San Francisco", + "goals": "Donec pede justo, fringilla vel, aliquet nec.", + }, + { + "username": "jbrown5", + "email": "jbrown5@live.com", + "fullname": "James Brown", + "password": "hM8~P9s#tL!B", + "activate_user": False, + "mailing_address": "654 Cedar Lane", + "year_of_birth": 1992, + "gender": "m", + "level_of_education": "b", + "city": "Miami", + "goals": "Nullam quis ante.", + }, + { + "username": "adaniels6", + "email": "adaniels6@hotmail.com", + "fullname": "Alice Daniels", + "password": "vF5~#2Dk!sP", + "activate_user": True, + "mailing_address": "987 Birch Court", + "year_of_birth": 1997, + "gender": "f", + "level_of_education": "m", + "city": "Dallas", + "goals": "Vivamus elementum semper nisi.", + }, + { + "username": "mlee7", + "email": "mlee7@protonmail.com", + "fullname": "Michael Lee", + "password": "zQ7%4Yn!rT&", + "activate_user": False, + "mailing_address": "159 Redwood Drive", + "year_of_birth": 1991, + "gender": "m", + "level_of_education": "hs", + "city": "Seattle", + "goals": "Aenean vulputate eleifend tellus.", + }, + { + "username": "skelly8", + "email": "skelly8@gmail.com", + "fullname": "Sarah Kelly", + "password": "bN1*9Ts!vQ#", + "activate_user": True, + "mailing_address": "753 Cypress Street", + "year_of_birth": 1994, + "gender": "f", + "level_of_education": "b", + "city": "Austin", + "goals": "In enim justo, rhoncus ut.", + }, + { + "username": "wjohnson9", + "email": "wjohnson9@icloud.com", + "fullname": "William Johnson", + "password": "xP3%7Dl!bK&", + "activate_user": True, + "mailing_address": "246 Spruce Way", + "year_of_birth": 1989, + "gender": "m", + "level_of_education": "el", + "city": "Boston", + "goals": "Phasellus viverra nulla ut metus varius laoreet.", + }, + { + "username": "cwatson10", + "email": "cwatson10@msn.com", + "fullname": "Catherine Watson", + "password": "yJ6$2!NpQ#t", + "activate_user": False, + "mailing_address": "369 Willow Terrace", + "year_of_birth": 1998, + "gender": "f", + "level_of_education": "p", + "city": "Denver", + "goals": "Quisque rutrum.", + }, + { + "username": "jmartinez11", + "email": "jmartinez11@ymail.com", + "fullname": "Juan Martinez", + "password": "rM6@8Xc!pY#", + "activate_user": True, + "mailing_address": "123 Palm Street", + "year_of_birth": 1992, + "gender": "m", + "level_of_education": "b", + "city": "Houston", + "goals": "Aliquam erat volutpat.", + }, + { + "username": "lrodriguez12", + "email": "lrodriguez12@rediffmail.com", + "fullname": "Laura Rodriguez", + "password": "cH9*2Ml!gQ&", + "activate_user": True, + "mailing_address": "456 Willow Avenue", + "year_of_birth": 1994, + "gender": "f", + "level_of_education": "m", + "city": "Phoenix", + "goals": "Fusce vulputate eleifend sapien.", + }, + { + "username": "dthomas13", + "email": "dthomas13@gmail.com", + "fullname": "David Thomas", + "password": "wL3~7Rs!fK%", + "activate_user": False, + "mailing_address": "789 Aspen Drive", + "year_of_birth": 1987, + "gender": "m", + "level_of_education": "el", + "city": "Philadelphia", + "goals": "Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae.", + }, + { + "username": "kwhite14", + "email": "kwhite14@yahoo.com", + "fullname": "Katherine White", + "password": "pV4@1Lz!mH#", + "activate_user": True, + "mailing_address": "987 Oak Terrace", + "year_of_birth": 1999, + "gender": "f", + "level_of_education": "b", + "city": "San Diego", + "goals": "Praesent nec nisl a purus blandit viverra.", + }, + { + "username": "cgarcia15", + "email": "cgarcia15@outlook.com", + "fullname": "Carlos Garcia", + "password": "zK6#3Wx!dP&", + "activate_user": False, + "mailing_address": "654 Pine Boulevard", + "year_of_birth": 1993, + "gender": "m", + "level_of_education": "p", + "city": "San Antonio", + "goals": "Integer tincidunt.", + }, + { + "username": "bwalker16", + "email": "bwalker16@protonmail.com", + "fullname": "Barbara Walker", + "password": "jQ2@6Cm!yX%", + "activate_user": True, + "mailing_address": "321 Maple Lane", + "year_of_birth": 1995, + "gender": "f", + "level_of_education": "m", + "city": "Dallas", + "goals": "Nulla consequat massa quis enim.", + }, + { + "username": "rhall17", + "email": "rhall17@gmail.com", + "fullname": "Richard Hall", + "password": "uD5@9Vl!pT&", + "activate_user": True, + "mailing_address": "753 Cedar Court", + "year_of_birth": 1990, + "gender": "m", + "level_of_education": "b", + "city": "Jacksonville", + "goals": "Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem.", + }, + { + "username": "ashaw18", + "email": "ashaw18@live.com", + "fullname": "Alice Shaw", + "password": "tF1@8Nm!sJ#", + "activate_user": False, + "mailing_address": "246 Spruce Street", + "year_of_birth": 1997, + "gender": "f", + "level_of_education": "el", + "city": "San Jose", + "goals": "Nulla facilisi.", + }, + { + "username": "pjones19", + "email": "pjones19@msn.com", + "fullname": "Peter Jones", + "password": "vX4$7Gj!fR&", + "activate_user": True, + "mailing_address": "369 Birch Lane", + "year_of_birth": 1986, + "gender": "m", + "level_of_education": "hs", + "city": "Columbus", + "goals": "Cras ultricies mi eu turpis hendrerit fringilla.", + }, + { + "username": "mblake20", + "email": "mblake20@facebook.com", + "fullname": "Martin Blake", + "password": "rF3~xYk!8H@", + "activate_user": True, + "mailing_address": "123 Walnut St", + "year_of_birth": 1995, + "gender": "m", + "level_of_education": "b", + "city": "Cleveland", + "goals": "Etiam ut purus mattis mauris sodales aliquam.", + }, + { + "username": "rperry21", + "email": "rperry21@twitter.com", + "fullname": "Rachel Perry", + "password": "sV9~#Kd@5", + "activate_user": False, + "mailing_address": "456 Birch Road", + "year_of_birth": 1993, + "gender": "f", + "level_of_education": "m", + "city": "Raleigh", + "goals": "Curabitur sodales ligula in libero.", + }, + { + "username": "tjohnson22", + "email": "tjohnson22@gmail.com", + "fullname": "Timothy Johnson", + "password": "dK6~1Lp!9H&", + "activate_user": True, + "mailing_address": "789 Aspen Lane", + "year_of_birth": 1991, + "gender": "m", + "level_of_education": "hs", + "city": "Atlanta", + "goals": "Mauris ipsum. Nulla metus metus, ullamcorper vel, tincidunt sed.", + }, + { + "username": "skhan23", + "email": "skhan23@outlook.com", + "fullname": "Sana Khan", + "password": "pJ3!4Xv#zG", + "activate_user": False, + "mailing_address": "963 Spruce St", + "year_of_birth": 1997, + "gender": "f", + "level_of_education": "p", + "city": "Houston", + "goals": "Nunc feugiat mi a tellus consequat imperdiet.", + }, + { + "username": "lclark24", + "email": "lclark24@yahoo.com", + "fullname": "Laura Clark", + "password": "mB8@#Pk!2X", + "activate_user": True, + "mailing_address": "321 Oak St", + "year_of_birth": 1998, + "gender": "f", + "level_of_education": "jhs", + "city": "Phoenix", + "goals": "Vivamus vestibulum sagittis sapien.", + }, + { + "username": "gbaker25", + "email": "gbaker25@icloud.com", + "fullname": "George Baker", + "password": "hG4~2Yr!8L@", + "activate_user": True, + "mailing_address": "654 Pine Avenue", + "year_of_birth": 1989, + "gender": "m", + "level_of_education": "el", + "city": "San Antonio", + "goals": "Phasellus viverra nulla ut metus varius laoreet.", + }, + { + "username": "ajames26", + "email": "ajames26@msn.com", + "fullname": "Angela James", + "password": "nX7@1Bw!3V#", + "activate_user": True, + "mailing_address": "987 Birch Court", + "year_of_birth": 1995, + "gender": "f", + "level_of_education": "b", + "city": "San Diego", + "goals": "Nulla facilisi.", + }, + { + "username": "jmiller27", + "email": "jmiller27@protonmail.com", + "fullname": "John Miller", + "password": "tK5@8Nz!rG#", + "activate_user": False, + "mailing_address": "753 Maple Street", + "year_of_birth": 1993, + "gender": "m", + "level_of_education": "hs", + "city": "Las Vegas", + "goals": "Etiam sollicitudin, ipsum eu pulvinar rutrum.", + }, + { + "username": "zsmith28", + "email": "zsmith28@aol.com", + "fullname": "Zachary Smith", + "password": "wM9!3Lc@7K&", + "activate_user": True, + "mailing_address": "456 Cedar Lane", + "year_of_birth": 1990, + "gender": "m", + "level_of_education": "el", + "city": "Orlando", + "goals": "Nullam quis ante.", + }, + { + "username": "pphillips29", + "email": "pphillips29@ymail.com", + "fullname": "Pamela Phillips", + "password": "cT1@6Yh!8J#", + "activate_user": True, + "mailing_address": "789 Walnut Ave", + "year_of_birth": 1994, + "gender": "f", + "level_of_education": "p", + "city": "Dallas", + "goals": "Praesent nec nisl a purus blandit viverra.", + }, + { + "username": "cmorris30", + "email": "cmorris30@rediffmail.com", + "fullname": "Charles Morris", + "password": "fG5~2Ux@9J&", + "activate_user": True, + "mailing_address": "963 Oak Terrace", + "year_of_birth": 1992, + "gender": "m", + "level_of_education": "b", + "city": "Seattle", + "goals": "Integer tincidunt.", + }, + { + "username": "dwatson31", + "email": "dwatson31@live.com", + "fullname": "Daniel Watson", + "password": "zN3!5Pq@7L#", + "activate_user": False, + "mailing_address": "159 Spruce Lane", + "year_of_birth": 1996, + "gender": "m", + "level_of_education": "el", + "city": "Boston", + "goals": "Aliquam erat volutpat.", + }, + { + "username": "ljones32", + "email": "ljones32@buzzfeed.com", + "fullname": "Lydia Jones", + "password": "gK8!3Tf@2M&", + "activate_user": True, + "mailing_address": "753 Birch Road", + "year_of_birth": 1991, + "gender": "f", + "level_of_education": "b", + "city": "Austin", + "goals": "Phasellus viverra nulla ut metus varius laoreet.", + }, + { + "username": "dsanchez33", + "email": "dsanchez33@gravatar.com", + "fullname": "David Sanchez", + "password": "bL5!8Wx@3V#", + "activate_user": True, + "mailing_address": "246 Cedar Court", + "year_of_birth": 1995, + "gender": "m", + "level_of_education": "m", + "city": "Columbus", + "goals": "Praesent nec nisl a purus blandit viverra.", + }, + { + "username": "jgarcia34", + "email": "jgarcia34@yahoo.com", + "fullname": "Jose Garcia", + "password": "hM6@3Qr!5K&", + "activate_user": True, + "mailing_address": "963 Maple Drive", + "year_of_birth": 1989, + "gender": "m", + "level_of_education": "hs", + "city": "Jacksonville", + "goals": "Curabitur sodales ligula in libero.", + }, + { + "username": "yhernandez35", + "email": "yhernandez35@protonmail.com", + "fullname": "Yolanda Hernandez", + "password": "mT4!2Zj@7V#", + "activate_user": True, + "mailing_address": "321 Birch Street", + "year_of_birth": 1994, + "gender": "f", + "level_of_education": "p", + "city": "Los Angeles", + "goals": "Curabitur sodales ligula in libero.", + }, + ] +) From 4cdee58fd84e2ee17d59cd3f06964dd3d5a81e88 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Mon, 23 Sep 2024 15:40:07 -0500 Subject: [PATCH 10/32] chore: remove utils folder --- .../api/v1/tests/integration/test_views.py | 1 + .../v1/tests/integration/utils/__init__.py | 0 .../v1/tests/integration/utils/fake_users.py | 591 ------------------ 3 files changed, 1 insertion(+), 591 deletions(-) delete mode 100644 eox_core/api/v1/tests/integration/utils/__init__.py delete mode 100644 eox_core/api/v1/tests/integration/utils/fake_users.py diff --git a/eox_core/api/v1/tests/integration/test_views.py b/eox_core/api/v1/tests/integration/test_views.py index cb2c3942c..96150f448 100644 --- a/eox_core/api/v1/tests/integration/test_views.py +++ b/eox_core/api/v1/tests/integration/test_views.py @@ -2,6 +2,7 @@ Integration test suite for the API v1 views. """ +# pylint: disable=too-many-lines from __future__ import annotations import ddt diff --git a/eox_core/api/v1/tests/integration/utils/__init__.py b/eox_core/api/v1/tests/integration/utils/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/eox_core/api/v1/tests/integration/utils/fake_users.py b/eox_core/api/v1/tests/integration/utils/fake_users.py deleted file mode 100644 index c187a82bf..000000000 --- a/eox_core/api/v1/tests/integration/utils/fake_users.py +++ /dev/null @@ -1,591 +0,0 @@ -"""Fake user data for testing purposes.""" - -FAKE_USER_DATA = iter( - [ - { - "username": "athickpenny0", - "email": "athickpenny0@indiegogo.com", - "fullname": "Antoni Thickpenny", - "password": "jD3_u)67VfHce", - "activate_user": True, - "mailing_address": "70736 Haas Parkway", - "year_of_birth": 1996, - "gender": "f", - "level_of_education": "p", - "city": "Solikamsk", - "goals": "Maecenas leo odio, condimentum id", - }, - { - "username": "smenchenton1", - "email": "smenchenton1@networksolutions.com", - "fullname": "Sonia Menchenton", - "password": "hO9\\Pni)", - "activate_user": True, - "mailing_address": "7543 Eagle Crest Terrace", - "year_of_birth": 1998, - "gender": "m", - "level_of_education": "m", - "city": "Diourbel", - "goals": "Sed sagittis.", - }, - { - "username": "arealff2", - "email": "arealff2@phpbb.com", - "fullname": "Alexis Realff", - "password": 'zY2(yq!(>4"_', - "activate_user": True, - "mailing_address": "0 Grim Drive", - "year_of_birth": 2005, - "gender": "f", - "level_of_education": "b", - "city": "Gongyi", - "goals": "Proin interdum mauris non ligula pellentesque ultrices.", - }, - { - "username": "ddilon3", - "email": "ddilon3@geocities.com", - "fullname": "Dotty Dilon", - "password": 'mV9"3zRdRr#bTP', - "activate_user": True, - "mailing_address": "6859 Lerdahl Road", - "year_of_birth": 2002, - "gender": "m", - "level_of_education": "a", - "city": "La Curva", - "goals": "Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl.", - }, - { - "username": "lties4", - "email": "lties4@addthis.com", - "fullname": "Loreen Ties", - "password": 'iU6@R"`/t3>/DT', - "activate_user": True, - "mailing_address": "64 Clemons Terrace", - "year_of_birth": 1994, - "gender": "f", - "level_of_education": "hs", - "city": "Xianyang", - "goals": "In est risus, auctor sed, tristique in, tempus sit amet, sem.", - }, - { - "username": "lbattisson5", - "email": "lbattisson5@360.cn", - "fullname": "Lynett Battisson", - "password": "oB3?1JWFM\\=S>", - "activate_user": True, - "mailing_address": "89 Vermont Pass", - "year_of_birth": 2000, - "gender": "m", - "level_of_education": "jhs", - "city": "Lyudinovo", - "goals": "Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.", - }, - { - "username": "jniset6", - "email": "jniset6@virginia.edu", - "fullname": "Jacquelin Niset", - "password": "aC2/anhtj", - "activate_user": True, - "mailing_address": "83984 8th Pass", - "year_of_birth": 2003, - "gender": "f", - "level_of_education": "el", - "city": "Erie", - "goals": "Donec quis orci eget orci vehicula condimentum.", - }, - { - "username": "mdoumenc7", - "email": "mdoumenc7@chicagotribune.com", - "fullname": "Martin Doumenc", - "password": "dQ9't{0(", - "activate_user": True, - "mailing_address": "8 Scoville Plaza", - "year_of_birth": 1993, - "gender": "f", - "level_of_education": "none", - "city": "Boaco", - "goals": "Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae.", - }, - { - "username": "lchampagne8", - "email": "lchampagne8@buzzfeed.com", - "fullname": "Locke Champagne", - "password": "tW7_SYC1qUf!K'0C", - "activate_user": True, - "mailing_address": "79 Milwaukee Place", - "year_of_birth": 1996, - "gender": "f", - "level_of_education": "other", - "city": "Linjiang", - "goals": "In hac habitasse platea dictumst.", - }, - { - "username": "zczyz9", - "email": "zczyz9@gravatar.com", - "fullname": "Zolly Czyz", - "password": "jN3~/&1ZMG%", - "activate_user": True, - "mailing_address": "5 Victoria Place", - "year_of_birth": 2000, - "gender": "f", - "level_of_education": "p", - "city": "'s-Hertogenbosch", - "goals": "Suspendisse potenti.", - }, - { - "username": "jdoe1", - "email": "jdoe1@google.com", - "fullname": "John Doe", - "password": "bL2~#63pTr!Kx", - "activate_user": True, - "mailing_address": "123 Elm Street", - "year_of_birth": 1995, - "gender": "m", - "level_of_education": "b", - "city": "New York", - "goals": "Curabitur pretium tincidunt lacus.", - }, - { - "username": "msmith2", - "email": "msmith2@outlook.com", - "fullname": "Mary Smith", - "password": "gF4_%6Dm#5wJ", - "activate_user": True, - "mailing_address": "456 Oak Avenue", - "year_of_birth": 1993, - "gender": "f", - "level_of_education": "m", - "city": "Chicago", - "goals": "Aenean massa.", - }, - { - "username": "rwilliams3", - "email": "rwilliams3@aol.com", - "fullname": "Robert Williams", - "password": "kL9*~Yx2pW!", - "activate_user": False, - "mailing_address": "789 Pine Boulevard", - "year_of_birth": 1988, - "gender": "m", - "level_of_education": "el", - "city": "Los Angeles", - "goals": "Etiam ultricies nisi vel augue.", - }, - { - "username": "emurphy4", - "email": "emurphy4@yahoo.com", - "fullname": "Emily Murphy", - "password": "qN2%8Rc!xV&Z", - "activate_user": True, - "mailing_address": "321 Maple Road", - "year_of_birth": 1990, - "gender": "f", - "level_of_education": "p", - "city": "San Francisco", - "goals": "Donec pede justo, fringilla vel, aliquet nec.", - }, - { - "username": "jbrown5", - "email": "jbrown5@live.com", - "fullname": "James Brown", - "password": "hM8~P9s#tL!B", - "activate_user": False, - "mailing_address": "654 Cedar Lane", - "year_of_birth": 1992, - "gender": "m", - "level_of_education": "b", - "city": "Miami", - "goals": "Nullam quis ante.", - }, - { - "username": "adaniels6", - "email": "adaniels6@hotmail.com", - "fullname": "Alice Daniels", - "password": "vF5~#2Dk!sP", - "activate_user": True, - "mailing_address": "987 Birch Court", - "year_of_birth": 1997, - "gender": "f", - "level_of_education": "m", - "city": "Dallas", - "goals": "Vivamus elementum semper nisi.", - }, - { - "username": "mlee7", - "email": "mlee7@protonmail.com", - "fullname": "Michael Lee", - "password": "zQ7%4Yn!rT&", - "activate_user": False, - "mailing_address": "159 Redwood Drive", - "year_of_birth": 1991, - "gender": "m", - "level_of_education": "hs", - "city": "Seattle", - "goals": "Aenean vulputate eleifend tellus.", - }, - { - "username": "skelly8", - "email": "skelly8@gmail.com", - "fullname": "Sarah Kelly", - "password": "bN1*9Ts!vQ#", - "activate_user": True, - "mailing_address": "753 Cypress Street", - "year_of_birth": 1994, - "gender": "f", - "level_of_education": "b", - "city": "Austin", - "goals": "In enim justo, rhoncus ut.", - }, - { - "username": "wjohnson9", - "email": "wjohnson9@icloud.com", - "fullname": "William Johnson", - "password": "xP3%7Dl!bK&", - "activate_user": True, - "mailing_address": "246 Spruce Way", - "year_of_birth": 1989, - "gender": "m", - "level_of_education": "el", - "city": "Boston", - "goals": "Phasellus viverra nulla ut metus varius laoreet.", - }, - { - "username": "cwatson10", - "email": "cwatson10@msn.com", - "fullname": "Catherine Watson", - "password": "yJ6$2!NpQ#t", - "activate_user": False, - "mailing_address": "369 Willow Terrace", - "year_of_birth": 1998, - "gender": "f", - "level_of_education": "p", - "city": "Denver", - "goals": "Quisque rutrum.", - }, - { - "username": "jmartinez11", - "email": "jmartinez11@ymail.com", - "fullname": "Juan Martinez", - "password": "rM6@8Xc!pY#", - "activate_user": True, - "mailing_address": "123 Palm Street", - "year_of_birth": 1992, - "gender": "m", - "level_of_education": "b", - "city": "Houston", - "goals": "Aliquam erat volutpat.", - }, - { - "username": "lrodriguez12", - "email": "lrodriguez12@rediffmail.com", - "fullname": "Laura Rodriguez", - "password": "cH9*2Ml!gQ&", - "activate_user": True, - "mailing_address": "456 Willow Avenue", - "year_of_birth": 1994, - "gender": "f", - "level_of_education": "m", - "city": "Phoenix", - "goals": "Fusce vulputate eleifend sapien.", - }, - { - "username": "dthomas13", - "email": "dthomas13@gmail.com", - "fullname": "David Thomas", - "password": "wL3~7Rs!fK%", - "activate_user": False, - "mailing_address": "789 Aspen Drive", - "year_of_birth": 1987, - "gender": "m", - "level_of_education": "el", - "city": "Philadelphia", - "goals": "Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae.", - }, - { - "username": "kwhite14", - "email": "kwhite14@yahoo.com", - "fullname": "Katherine White", - "password": "pV4@1Lz!mH#", - "activate_user": True, - "mailing_address": "987 Oak Terrace", - "year_of_birth": 1999, - "gender": "f", - "level_of_education": "b", - "city": "San Diego", - "goals": "Praesent nec nisl a purus blandit viverra.", - }, - { - "username": "cgarcia15", - "email": "cgarcia15@outlook.com", - "fullname": "Carlos Garcia", - "password": "zK6#3Wx!dP&", - "activate_user": False, - "mailing_address": "654 Pine Boulevard", - "year_of_birth": 1993, - "gender": "m", - "level_of_education": "p", - "city": "San Antonio", - "goals": "Integer tincidunt.", - }, - { - "username": "bwalker16", - "email": "bwalker16@protonmail.com", - "fullname": "Barbara Walker", - "password": "jQ2@6Cm!yX%", - "activate_user": True, - "mailing_address": "321 Maple Lane", - "year_of_birth": 1995, - "gender": "f", - "level_of_education": "m", - "city": "Dallas", - "goals": "Nulla consequat massa quis enim.", - }, - { - "username": "rhall17", - "email": "rhall17@gmail.com", - "fullname": "Richard Hall", - "password": "uD5@9Vl!pT&", - "activate_user": True, - "mailing_address": "753 Cedar Court", - "year_of_birth": 1990, - "gender": "m", - "level_of_education": "b", - "city": "Jacksonville", - "goals": "Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem.", - }, - { - "username": "ashaw18", - "email": "ashaw18@live.com", - "fullname": "Alice Shaw", - "password": "tF1@8Nm!sJ#", - "activate_user": False, - "mailing_address": "246 Spruce Street", - "year_of_birth": 1997, - "gender": "f", - "level_of_education": "el", - "city": "San Jose", - "goals": "Nulla facilisi.", - }, - { - "username": "pjones19", - "email": "pjones19@msn.com", - "fullname": "Peter Jones", - "password": "vX4$7Gj!fR&", - "activate_user": True, - "mailing_address": "369 Birch Lane", - "year_of_birth": 1986, - "gender": "m", - "level_of_education": "hs", - "city": "Columbus", - "goals": "Cras ultricies mi eu turpis hendrerit fringilla.", - }, - { - "username": "mblake20", - "email": "mblake20@facebook.com", - "fullname": "Martin Blake", - "password": "rF3~xYk!8H@", - "activate_user": True, - "mailing_address": "123 Walnut St", - "year_of_birth": 1995, - "gender": "m", - "level_of_education": "b", - "city": "Cleveland", - "goals": "Etiam ut purus mattis mauris sodales aliquam.", - }, - { - "username": "rperry21", - "email": "rperry21@twitter.com", - "fullname": "Rachel Perry", - "password": "sV9~#Kd@5", - "activate_user": False, - "mailing_address": "456 Birch Road", - "year_of_birth": 1993, - "gender": "f", - "level_of_education": "m", - "city": "Raleigh", - "goals": "Curabitur sodales ligula in libero.", - }, - { - "username": "tjohnson22", - "email": "tjohnson22@gmail.com", - "fullname": "Timothy Johnson", - "password": "dK6~1Lp!9H&", - "activate_user": True, - "mailing_address": "789 Aspen Lane", - "year_of_birth": 1991, - "gender": "m", - "level_of_education": "hs", - "city": "Atlanta", - "goals": "Mauris ipsum. Nulla metus metus, ullamcorper vel, tincidunt sed.", - }, - { - "username": "skhan23", - "email": "skhan23@outlook.com", - "fullname": "Sana Khan", - "password": "pJ3!4Xv#zG", - "activate_user": False, - "mailing_address": "963 Spruce St", - "year_of_birth": 1997, - "gender": "f", - "level_of_education": "p", - "city": "Houston", - "goals": "Nunc feugiat mi a tellus consequat imperdiet.", - }, - { - "username": "lclark24", - "email": "lclark24@yahoo.com", - "fullname": "Laura Clark", - "password": "mB8@#Pk!2X", - "activate_user": True, - "mailing_address": "321 Oak St", - "year_of_birth": 1998, - "gender": "f", - "level_of_education": "jhs", - "city": "Phoenix", - "goals": "Vivamus vestibulum sagittis sapien.", - }, - { - "username": "gbaker25", - "email": "gbaker25@icloud.com", - "fullname": "George Baker", - "password": "hG4~2Yr!8L@", - "activate_user": True, - "mailing_address": "654 Pine Avenue", - "year_of_birth": 1989, - "gender": "m", - "level_of_education": "el", - "city": "San Antonio", - "goals": "Phasellus viverra nulla ut metus varius laoreet.", - }, - { - "username": "ajames26", - "email": "ajames26@msn.com", - "fullname": "Angela James", - "password": "nX7@1Bw!3V#", - "activate_user": True, - "mailing_address": "987 Birch Court", - "year_of_birth": 1995, - "gender": "f", - "level_of_education": "b", - "city": "San Diego", - "goals": "Nulla facilisi.", - }, - { - "username": "jmiller27", - "email": "jmiller27@protonmail.com", - "fullname": "John Miller", - "password": "tK5@8Nz!rG#", - "activate_user": False, - "mailing_address": "753 Maple Street", - "year_of_birth": 1993, - "gender": "m", - "level_of_education": "hs", - "city": "Las Vegas", - "goals": "Etiam sollicitudin, ipsum eu pulvinar rutrum.", - }, - { - "username": "zsmith28", - "email": "zsmith28@aol.com", - "fullname": "Zachary Smith", - "password": "wM9!3Lc@7K&", - "activate_user": True, - "mailing_address": "456 Cedar Lane", - "year_of_birth": 1990, - "gender": "m", - "level_of_education": "el", - "city": "Orlando", - "goals": "Nullam quis ante.", - }, - { - "username": "pphillips29", - "email": "pphillips29@ymail.com", - "fullname": "Pamela Phillips", - "password": "cT1@6Yh!8J#", - "activate_user": True, - "mailing_address": "789 Walnut Ave", - "year_of_birth": 1994, - "gender": "f", - "level_of_education": "p", - "city": "Dallas", - "goals": "Praesent nec nisl a purus blandit viverra.", - }, - { - "username": "cmorris30", - "email": "cmorris30@rediffmail.com", - "fullname": "Charles Morris", - "password": "fG5~2Ux@9J&", - "activate_user": True, - "mailing_address": "963 Oak Terrace", - "year_of_birth": 1992, - "gender": "m", - "level_of_education": "b", - "city": "Seattle", - "goals": "Integer tincidunt.", - }, - { - "username": "dwatson31", - "email": "dwatson31@live.com", - "fullname": "Daniel Watson", - "password": "zN3!5Pq@7L#", - "activate_user": False, - "mailing_address": "159 Spruce Lane", - "year_of_birth": 1996, - "gender": "m", - "level_of_education": "el", - "city": "Boston", - "goals": "Aliquam erat volutpat.", - }, - { - "username": "ljones32", - "email": "ljones32@buzzfeed.com", - "fullname": "Lydia Jones", - "password": "gK8!3Tf@2M&", - "activate_user": True, - "mailing_address": "753 Birch Road", - "year_of_birth": 1991, - "gender": "f", - "level_of_education": "b", - "city": "Austin", - "goals": "Phasellus viverra nulla ut metus varius laoreet.", - }, - { - "username": "dsanchez33", - "email": "dsanchez33@gravatar.com", - "fullname": "David Sanchez", - "password": "bL5!8Wx@3V#", - "activate_user": True, - "mailing_address": "246 Cedar Court", - "year_of_birth": 1995, - "gender": "m", - "level_of_education": "m", - "city": "Columbus", - "goals": "Praesent nec nisl a purus blandit viverra.", - }, - { - "username": "jgarcia34", - "email": "jgarcia34@yahoo.com", - "fullname": "Jose Garcia", - "password": "hM6@3Qr!5K&", - "activate_user": True, - "mailing_address": "963 Maple Drive", - "year_of_birth": 1989, - "gender": "m", - "level_of_education": "hs", - "city": "Jacksonville", - "goals": "Curabitur sodales ligula in libero.", - }, - { - "username": "yhernandez35", - "email": "yhernandez35@protonmail.com", - "fullname": "Yolanda Hernandez", - "password": "mT4!2Zj@7V#", - "activate_user": True, - "mailing_address": "321 Birch Street", - "year_of_birth": 1994, - "gender": "f", - "level_of_education": "p", - "city": "Los Angeles", - "goals": "Curabitur sodales ligula in libero.", - }, - ] -) From cb0039b3571e32d0085ee870a295827a5202a7a6 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Mon, 23 Sep 2024 17:34:12 -0500 Subject: [PATCH 11/32] docs: update methods docstring in the tests --- .../api/v1/tests/integration/test_views.py | 255 +++++++++++++++--- eox_core/settings/test.py | 1 + 2 files changed, 219 insertions(+), 37 deletions(-) diff --git a/eox_core/api/v1/tests/integration/test_views.py b/eox_core/api/v1/tests/integration/test_views.py index 96150f448..73c0e7693 100644 --- a/eox_core/api/v1/tests/integration/test_views.py +++ b/eox_core/api/v1/tests/integration/test_views.py @@ -107,7 +107,7 @@ def get_tenant_data(self, prefix: str = "") -> dict: If no prefix is provided, the default site data is returned. Args: - prefix (str): The tenant prefix. + prefix (str, optional): The tenant prefix. Defaults to "". Returns: dict: The tenant data. @@ -435,39 +435,91 @@ def test_update_user_in_tenant_user_not_found(self, param: str, value: str) -> N class TestEnrollmentAPIIntegration(BaseAPIIntegrationTest, UsersAPIRequestMixin, EnrollmentAPIRequestMixin): """Integration test suite for the Enrollment API""" - def setUp(self): + def setUp(self) -> None: """Set up the test suite""" - self.course_id = "course-v1:OpenedX+DemoX+DemoCourse" + self.course_id = settings["COURSE_ID"] self.mode = "audit" - return super().setUp() + super().setUp() @ddt.data("email", "username") - def test_create_enrollment_valid_user_mode_course(self, param: str) -> None: + def test_create_enrollment_success(self, param: str) -> None: """ - Create enrollment with a valid user, valid course and valid mode + Test creating a enrollment with a valid user, course and mode in a tenant. + + Open edX definitions tested: + - `get_edxapp_user` + - `create_enrollment` + - `check_edxapp_account_conflicts` + - `check_edxapp_enrollment_is_valid` + + Expected result: + - The status code is 200. + - The enrollment is created successfully in the tenant with the provided data. """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_x, user_data) - data = { + enrollment_data = { param: user_data[param], "course_id": self.course_id, "mode": self.mode, } - response = self.create_enrollment(self.tenant_x, data) + response = self.create_enrollment(self.tenant_x, enrollment_data) response_data = response.json() self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response_data["username"], user_data["username"]) - self.assertEqual(response_data["mode"], data["mode"]) - self.assertEqual(response_data["course_id"], data["course_id"]) + self.assertEqual(response_data["mode"], enrollment_data["mode"]) + self.assertEqual(response_data["course_id"], enrollment_data["course_id"]) self.assertTrue(response_data["is_active"]) self.assertIn("created", response_data) + @ddt.data( + ("mode", {"mode": ["This field is required."]}), + ("course_id", {"non_field_errors": ["You have to provide a course_id or bundle_id"]}), + ("username", {"non_field_errors": ["Email or username needed"]}), + ) + @ddt.unpack + def test_create_enrollment_missing_required_fields(self, param: str, error: list | dict) -> None: + """ + Test creating a enrollment with missing required fields. + + Open edX definitions tested: + - `check_edxapp_enrollment_is_valid` + + Expected result: + - The status code is 400. + - The response contains a message about the missing field. + """ + user_data = next(FAKE_USER_DATA) + self.create_user(self.tenant_x, user_data) + enrollment_data = { + "username": user_data["username"], + "mode": self.mode, + "course_id": self.course_id, + } + enrollment_data.pop(param) + + response = self.create_enrollment(self.tenant_x, enrollment_data) + + response_data = response.json() + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response_data, error) + @ddt.data("email", "username") - def test_force_create_enrollment_valid_user_mode_course(self, param: str) -> None: + def test_force_create_enrollment_success(self, param: str) -> None: """ - Create enrollment with a valid user, valid course, valid mode using force + Test force creating a enrollment with a valid user, course and mode in a tenant. + + Open edX definitions tested: + - `get_edxapp_user` + - `create_enrollment` + - `check_edxapp_account_conflicts` + - `check_edxapp_enrollment_is_valid` + + Expected result: + - The status code is 200. + - The enrollment is created successfully in the tenant with the provided data. """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_x, user_data) @@ -491,15 +543,23 @@ def test_force_create_enrollment_valid_user_mode_course(self, param: str) -> Non @ddt.data("email", "username") def test_create_valid_course_mode_invalid_user(self, param: str) -> None: """ - Create enrollment with a valid course, valid mode, and a non-existent user + Test creating a enrollment with a valid course, valid mode, and a non-existent user in a tenant. + + Open edX definitions tested: + - `check_edxapp_enrollment_is_valid` + + Expected result: + - The status code is 400. + - The response contains an error message about the user not found. + - The enrollment is not created in the tenant. """ - data = { + enrollment_data = { param: param, "course_id": self.course_id, "mode": self.mode, } - response = self.create_enrollment(self.tenant_x, data) + response = self.create_enrollment(self.tenant_x, enrollment_data) response_data = response.json() self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) @@ -507,9 +567,17 @@ def test_create_valid_course_mode_invalid_user(self, param: str) -> None: self.assertEqual(response_data["non_field_errors"], ["User not found"]) @ddt.data("email", "username") - def test_create_valid_course_mode_invalid_user_for_site(self, param: str) -> None: + def test_create_valid_course_mode_invalid_user_for_tenant(self, param: str) -> None: """ - Create enrollment with a valid course, valid mode, and a user from another site + Test creating a enrollment with a valid course, valid mode, and a user from another tenant. + + Open edX definitions tested: + - `check_edxapp_enrollment_is_valid` + + Expected result: + - The status code is 202. + - The response contains an error message about the user not found on the tenant. + - The enrollment is not created in the tenant. """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_y, user_data) @@ -531,7 +599,15 @@ def test_create_valid_course_mode_invalid_user_for_site(self, param: str) -> Non @ddt.data("email", "username") def test_create_valid_user_mode_invalid_course(self, param: str) -> None: """ - Create enrollment with a valid user, valid mode, and non-existent course + Test creating a enrollment with a valid user, valid mode, and a non-existent course in a tenant. + + Open edX definitions tested: + - `check_edxapp_enrollment_is_valid` + + Expected result: + - The status code is 400. + - The response contains an error message about the course not found. + - The enrollment is not created in the tenant. """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_x, user_data) @@ -549,9 +625,17 @@ def test_create_valid_user_mode_invalid_course(self, param: str) -> None: self.assertEqual(response_data["non_field_errors"], ["Course not found"]) @ddt.data("email", "username") - def test_create_valid_user_mode_invalid_course_for_site(self, param: str) -> None: + def test_create_valid_user_mode_invalid_course_for_tenant(self, param: str) -> None: """ - Create enrollment with a valid user, valid mode, and a course from another site + Test creating a enrollment with a valid user, valid mode, and a course from another tenant. + + Open edX definitions tested: + - `check_edxapp_enrollment_is_valid` + - `validate_org` + + Expected result: + - The status code is 400. + - The response contains an error message about the course not found on the tenant. """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_y, user_data) @@ -571,7 +655,15 @@ def test_create_valid_user_mode_invalid_course_for_site(self, param: str) -> Non @ddt.data("email", "username") def test_create_valid_user_course_invalid_mode(self, param: str) -> None: """ - Create enrollment with a valid user, valid course, and a not available mode + Test creating a enrollment with a valid user, valid course, and a not available mode in a tenant. + + Open edX definitions tested: + - `check_edxapp_enrollment_is_valid` + - `api.validate_course_mode` + + Expected result: + - The status code is 400. + - The response contains an error message about the mode not found. """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_x, user_data) @@ -591,7 +683,17 @@ def test_create_valid_user_course_invalid_mode(self, param: str) -> None: @ddt.data("email", "username") def test_force_create_valid_user_course_mode_not_allowed(self, param: str) -> None: """ - Force create enrollment with a valid user, valid course, and a not available mode + Test creating a enrollment with a valid user, valid course, and a not available mode in a tenant. + + Open edX definitions tested: + - `get_edxapp_user` + - `create_enrollment` + - `check_edxapp_account_conflicts` + - `check_edxapp_enrollment_is_valid` + + Expected result: + - The status code is 200. + - The enrollment is created successfully in the tenant with the provided data. """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_x, user_data) @@ -615,7 +717,15 @@ def test_force_create_valid_user_course_mode_not_allowed(self, param: str) -> No @ddt.data("email", "username") def test_get_enrollment_success(self, param: str) -> None: """ - Get a valid enrollment. + Test getting a enrollment with a valid user, course and mode in a tenant. + + Open edX definitions tested: + - `get_edxapp_user` + - `get_enrollment` + + Expected result: + - The status code is 200. + - The response contains the enrollment data. """ user_data = next(FAKE_USER_DATA) enrollment_data = { @@ -639,7 +749,15 @@ def test_get_enrollment_success(self, param: str) -> None: @ddt.data("email", "username") def test_get_enrollment_does_not_exist(self, param: str) -> None: """ - Get a invalid enrollment (doesn't exist) + Test getting a enrollment that does not exist in a tenant. + + Open edX definitions tested: + - `get_edxapp_user` + - `get_enrollment` + + Expected result: + - The status code is 404. + - The response contains an error message about the enrollment not found. """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_x, user_data) @@ -655,9 +773,16 @@ def test_get_enrollment_does_not_exist(self, param: str) -> None: self.assertEqual(response_data, [f"No enrollment found for user:`{user_data['username']}`"]) @ddt.data("email", "username") - def test_read_invalid_enrollment_for_site(self, param: str) -> None: + def test_get_enrollment_not_found_in_tenant(self, param: str) -> None: """ - Get a invalid enrollment (enrollment from other site) + Test getting a enrollment that belongs to another tenant. + + Open edX definitions tested: + - `get_edxapp_user` + + Expected result: + - The status code is 404. + - The response contains an error message about the user not found on the tenant. """ user_data = next(FAKE_USER_DATA) enrollment_data = { @@ -679,9 +804,17 @@ def test_read_invalid_enrollment_for_site(self, param: str) -> None: ) @ddt.data("email", "username") - def test_delete_valid_enrollment(self, param: str) -> None: + def test_delete_enrollment_success(self, param: str) -> None: """ - Delete a valid enrollment + Test deleting a enrollment with a valid user, course and mode in a tenant. + + Open edX definitions tested: + - `get_edxapp_user` + - `delete_enrollment` + + Expected result: + - The status code is 204. + - The enrollment is deleted successfully in the tenant. """ user_data = next(FAKE_USER_DATA) enrollment_data = { @@ -699,7 +832,15 @@ def test_delete_valid_enrollment(self, param: str) -> None: @ddt.data("email", "username") def test_delete_enrollment_does_not_exist(self, param: str) -> None: """ - Delete a invalid enrollment (doesn't exist) + Test deleting a enrollment that does not exist in a tenant. + + Open edX definitions tested: + - `get_edxapp_user` + - `delete_enrollment` + + Expected result: + - The status code is 404. + - The response contains an error message about the enrollment not found. """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_x, user_data) @@ -719,9 +860,17 @@ def test_delete_enrollment_does_not_exist(self, param: str) -> None: ) @ddt.data("email", "username") - def test_delete_invalid_enrollment_for_site(self, param: str) -> None: + def test_delete_invalid_enrollment_for_tenant(self, param: str) -> None: """ - Delete a invalid enrollment (enrollment from other site) + Test deleting a enrollment that belongs to another tenant. + + Open edX definitions tested: + - `get_edxapp_user` + - `delete_enrollment` + + Expected result: + - The status code is 404. + - The response contains an error message about the user not found on the tenant. """ user_data = next(FAKE_USER_DATA) enrollment_data = { @@ -745,7 +894,15 @@ def test_delete_invalid_enrollment_for_site(self, param: str) -> None: @ddt.data("email", "username") def test_update_valid_enrollment_change_is_active_mode_field(self, param: str) -> None: """ - Update an existing enrollment; change is_active and mode field + Test updating an existing enrollment. Update is_active and mode field. + + Open edX definitions tested: + - `get_edxapp_user` + - `update_enrollment` + + Expected result: + - The status code is 200. + - The enrollment is updated successfully in the tenant with the provided data. """ user_data = next(FAKE_USER_DATA) enrollment_data = { @@ -769,9 +926,17 @@ def test_update_valid_enrollment_change_is_active_mode_field(self, param: str) - self.assertTrue(response_data["is_active"]) @ddt.data("email", "username") - def test_update_valid_enrollment_change_invalid_mode(self, param: str) -> None: + def test_update_valid_enrollment_update_invalid_mode(self, param: str) -> None: """ - Update an existing enrollment; change to invalid mode + Test updating an existing enrollment. Update to invalid mode. + + Open edX definitions tested: + - `get_edxapp_user` + - `update_enrollment` + + Expected result: + - The status code is 400. + - The response contains an error message about the mode not found. """ user_data = next(FAKE_USER_DATA) enrollment_data = { @@ -792,9 +957,17 @@ def test_update_valid_enrollment_change_invalid_mode(self, param: str) -> None: self.assertEqual(response_data["non_field_errors"], ["Mode not found"]) @ddt.data("email", "username") - def test_update_invalid_enrollment_change_valid_mode(self, param: str) -> None: + def test_update_enrollment_does_not_exist(self, param: str) -> None: """ - Update an non-existent enrollment; change is_active and mode field + Test updating an enrollment that does not exist in a tenant. + + Open edX definitions tested: + - `get_edxapp_user` + - `update_enrollment` + + Expected result: + - The status code is 202. + - The response contains an error message about the enrollment not found. """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_x, user_data) @@ -814,7 +987,15 @@ def test_update_invalid_enrollment_change_valid_mode(self, param: str) -> None: @ddt.data("email", "username") def test_update_valid_enrollment_force_post(self, param: str) -> None: """ - Update an existing enrollment using POST with force=True. + Test updating an existing enrollment. Update is_active and mode field with force mode. + + Open edX definitions tested: + - `get_edxapp_user` + - `update_enrollment` + + Expected result: + - The status code is 200. + - The enrollment is updated successfully in the tenant with the provided data. """ user_data = next(FAKE_USER_DATA) enrollment_data = { diff --git a/eox_core/settings/test.py b/eox_core/settings/test.py index 2b3d296cf..0a9329e04 100644 --- a/eox_core/settings/test.py +++ b/eox_core/settings/test.py @@ -112,4 +112,5 @@ def plugin_settings(settings): # pylint: disable=function-redefined "API_TIMEOUT": 5, "CLIENT_ID": "client_id", "CLIENT_SECRET": "client_secret", + "COURSE_ID": os.environ.get("COURSE_ID", "course-v1:OpenedX+DemoX+DemoCourse") } From 7b10550388404382894da638da292f603020ea05 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Mon, 23 Sep 2024 18:23:30 -0500 Subject: [PATCH 12/32] feat: update matrix in integration tests workflow --- .github/workflows/integration-test.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index f2e5ad859..6c3e428c4 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -7,7 +7,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - tutor_version: ['<18.0.0', '<19.0.0'] + include: + - tutor_version: '<18.0.0' + course_id: 'course-v1:OpenedX+DemoX+Demo_Course' + - tutor_version: '<19.0.0' + course_id: 'course-v1:OpenedX+DemoX+DemoCourse' steps: - name: Run Integration Tests uses: eduNEXT/integration-test-in-tutor@mjh/run-integration-tests-outside-container @@ -17,3 +21,5 @@ jobs: openedx_extra_pip_requeriments: 'eox-tenant' shell_file_to_run: 'scripts/execute_integration_tests.sh' fixtures_file: 'fixtures/initial_data.json' + env: + COURSE_ID: ${{ matrix.course_id }} From f8533d72887b1ddfbe4629cb65746f25298c24b8 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Mon, 23 Sep 2024 19:01:23 -0500 Subject: [PATCH 13/32] fix: use correct course_id --- .github/workflows/integration-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 6c3e428c4..640f875a4 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -9,7 +9,7 @@ jobs: matrix: include: - tutor_version: '<18.0.0' - course_id: 'course-v1:OpenedX+DemoX+Demo_Course' + course_id: 'course-v1:edX+DemoX+Demo_Course' - tutor_version: '<19.0.0' course_id: 'course-v1:OpenedX+DemoX+DemoCourse' steps: From 9dbce66a4a98d6b88aadebceacb5e4fc4cdce359 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Tue, 24 Sep 2024 09:16:00 -0500 Subject: [PATCH 14/32] fix: add organizations and course modes for course_id of quince --- fixtures/initial_data.json | 79 +++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 22 deletions(-) diff --git a/fixtures/initial_data.json b/fixtures/initial_data.json index cd51fa9a8..987be7622 100644 --- a/fixtures/initial_data.json +++ b/fixtures/initial_data.json @@ -53,26 +53,20 @@ } }, { - "model": "organizations.organization", - "pk": 1, + "model": "eox_tenant.tenantorganization", + "pk": 4, "fields": { - "created": "2024-09-11T17:15:30.708Z", - "modified": "2024-09-11T17:15:30.708Z", - "name": "TenantX", - "short_name": "Tenant-x", - "description": null, - "logo": "", - "active": true + "name": "edX" } }, { "model": "organizations.organization", - "pk": 2, + "pk": 3, "fields": { "created": "2024-09-11T17:15:35.694Z", "modified": "2024-09-11T17:15:35.694Z", - "name": "TenantY", - "short_name": "Tenant-y", + "name": "OpenedX", + "short_name": "openedx", "description": null, "logo": "", "active": true @@ -80,12 +74,12 @@ }, { "model": "organizations.organization", - "pk": 2, + "pk": 4, "fields": { "created": "2024-09-11T17:15:35.694Z", "modified": "2024-09-11T17:15:35.694Z", - "name": "OpenedX", - "short_name": "openedx", + "name": "edX", + "short_name": "edx", "description": null, "logo": "", "active": true @@ -102,12 +96,13 @@ "SITE_NAME": "tenant-x.local.edly.io", "course_org_filter": [ "TenantX", - "OpenedX" + "OpenedX", + "edX" ] }, - "studio_configs": "{}", - "theming_configs": "{}", - "meta": "{}", + "studio_configs": {}, + "theming_configs": {}, + "meta": {}, "organizations": [ 1 ] @@ -126,9 +121,9 @@ "TenantY" ] }, - "studio_configs": "{}", - "theming_configs": "{}", - "meta": "{}", + "studio_configs": {}, + "theming_configs": {}, + "meta": {}, "organizations": [ 2 ] @@ -208,5 +203,45 @@ "ios_sku": null, "bulk_sku": null } + }, + { + "model": "course_modes.coursemode", + "pk": 3, + "fields": { + "course": "course-v1:edX+DemoX+Demo_Course", + "mode_slug": "audit", + "mode_display_name": "audit mode", + "min_price": 0, + "currency": "usd", + "_expiration_datetime": null, + "expiration_datetime_is_explicit": false, + "expiration_date": null, + "suggested_prices": "", + "description": null, + "sku": null, + "android_sku": null, + "ios_sku": null, + "bulk_sku": null + } + }, + { + "model": "course_modes.coursemode", + "pk": 4, + "fields": { + "course": "course-v1:edX+DemoX+Demo_Course", + "mode_slug": "honor", + "mode_display_name": "honor mode", + "min_price": 0, + "currency": "usd", + "_expiration_datetime": null, + "expiration_datetime_is_explicit": false, + "expiration_date": null, + "suggested_prices": "", + "description": null, + "sku": null, + "android_sku": null, + "ios_sku": null, + "bulk_sku": null + } } ] From 2305410a3b2495c3b22d472b34f47895ec3d231c Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Tue, 24 Sep 2024 10:17:17 -0500 Subject: [PATCH 15/32] chore: add test_enrollment_integration file --- .../test_enrollment_integration.py | 582 ++++++++++++++++++ 1 file changed, 582 insertions(+) create mode 100644 eox_core/tests/integration/test_enrollment_integration.py diff --git a/eox_core/tests/integration/test_enrollment_integration.py b/eox_core/tests/integration/test_enrollment_integration.py new file mode 100644 index 000000000..57cba454b --- /dev/null +++ b/eox_core/tests/integration/test_enrollment_integration.py @@ -0,0 +1,582 @@ +""" +Integration test suite. + +This suite performs multiple http requests to guarantee +that all CRUD operations are handled correctly. +""" +import json +from os import environ + +import pytest +import requests +from django.test import TestCase + + +@pytest.mark.skipif(not environ.get("TEST_INTEGRATION"), reason="Run only explicitly") +class TestEnrollmentIntegration(TestCase): # pragma: no cover + # pylint: disable=too-many-public-methods + """Test suite""" + data = {} + + @classmethod + def setUpClass(cls): + with open("eox_core/tests/integration/test_data", encoding="utf-8") as file_obj: + cls.data = json.load(file_obj) + cls.data["endpoint"] = "eox-core/api/v1/enrollment/" + site1_data = { + "client_id": cls.data["site1_data"]["client_id"], + "client_secret": cls.data["site1_data"]["client_secret"], + "grant_type": "client_credentials", + } + site2_data = { + "client_id": cls.data["site2_data"]["client_id"], + "client_secret": cls.data["site2_data"]["client_secret"], + "grant_type": "client_credentials", + } + request_url = f"{cls.data['site1_data']['base_url']}/oauth2/access_token/" + response_site1 = requests.post(request_url, data=site1_data, timeout=10) + response_site1.raise_for_status() + cls.data["site1_data"]["token"] = response_site1.json()["access_token"] + request_url = f"{cls.data['site2_data']['base_url']}/oauth2/access_token/" + response_site2 = requests.post(request_url, data=site2_data, timeout=10) + response_site2.raise_for_status() + cls.data["site2_data"]["token"] = response_site2.json()["access_token"] + + @classmethod + def tearDownClass(cls): + delete_enrollment(cls.data) + + def test_read_valid_email_course(self): + # pylint: disable=invalid-name + """ + Get a valid enrollment + """ + create_enrollment(self.data) + site1_data = self.data["site1_data"] + data = { + "email": site1_data["user_email"], + "course_id": site1_data["course"]["id"], + } + headers = { + "Authorization": f"Bearer {site1_data['token']}", + "Host": site1_data["host"], + } + expected_response = { + "username": site1_data["user_id"], + "course_id": data["course_id"], + } + request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" + + response = requests.get(request_url, data=data, headers=headers, timeout=10) + response_content = response.json() + + self.assertEqual(response.status_code, 200) + self.assertDictContainsSubset(expected_response, response_content) + + def test_read_invalid_enrollment(self): + # pylint: disable=invalid-name + """ + Get a invalid enrollment (doesn't exist) + """ + delete_enrollment(self.data) + site1_data = self.data["site1_data"] + data = { + "username": site1_data["user_id"], + "course_id": site1_data["course"]["id"], + } + headers = { + "Authorization": f"Bearer {site1_data['token']}", + "Host": site1_data["host"], + } + request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" + + response = requests.get(request_url, data=data, headers=headers, timeout=10) + + self.assertEqual(response.status_code, 404) + + def test_read_invalid_enrollment_for_site(self): + # pylint: disable=invalid-name + """ + Get a invalid enrollment (enrollment from other site) + """ + create_enrollment(self.data) + site1_data = self.data["site1_data"] + site2_data = self.data["site2_data"] + data = { + "username": site1_data["user_id"], + "course_id": site1_data["course"]["id"], + } + headers = { + "Authorization": f"Bearer {site2_data['token']}", + "Host": site2_data["host"], + } + request_url = f"{site2_data['base_url']}/{self.data['endpoint']}" + + response = requests.get(request_url, data=data, headers=headers, timeout=10) + + self.assertEqual(response.status_code, 404) + + def test_create_enrollment_valid_user_mode_course(self): + # pylint: disable=invalid-name + """ + Create enrollment with a valid user, valid course, + valid mode + """ + delete_enrollment(self.data) + site1_data = self.data["site1_data"] + data = { + "email": site1_data["user_email"], + "course_id": site1_data["course"]["id"], + "mode": site1_data["course"]["mode"], + } + headers = { + "Authorization": f"Bearer {site1_data['token']}", + "Host": site1_data["host"], + } + request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" + + response = requests.post(request_url, data=data, headers=headers, timeout=10) + + self.assertEqual(response.status_code, 200) + + def test_force_create_enrollment_valid_user_mode_course(self): + # pylint: disable=invalid-name + """ + Create enrollment with a valid user, valid course, + valid mode using force + """ + delete_enrollment(self.data) + site1_data = self.data["site1_data"] + data = { + "email": site1_data["user_email"], + "course_id": site1_data["course"]["id"], + "mode": site1_data["course"]["mode"], + "force": True, + } + headers = { + "Authorization": f"Bearer {site1_data['token']}", + "Host": site1_data["host"], + } + request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" + + response = requests.post(request_url, data=data, headers=headers, timeout=10) + + self.assertEqual(response.status_code, 200) + + def test_create_valid_course_mode_invalid_user(self): + # pylint: disable=invalid-name + """ + Create enrollment with a valid course, valid mode, + and a non-existent user + """ + site1_data = self.data["site1_data"] + data = { + "username": site1_data["fake_user"], + "course_id": site1_data["course"]["id"], + "mode": site1_data["course"]["mode"], + } + headers = { + "Authorization": f"Bearer {site1_data['token']}", + "Host": site1_data["host"], + } + request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" + + response = requests.post(request_url, data=data, headers=headers, timeout=10) + + self.assertEqual(response.status_code, 400) + + def test_create_valid_course_mode_invalid_user_for_site(self): + # pylint: disable=invalid-name + """ + Create enrollment with a valid course, valid mode, + and a user from another site + """ + site1_data = self.data["site1_data"] + site2_data = self.data["site2_data"] + data = { + "email": site2_data["user_email"], + "course_id": site1_data["course"]["id"], + "mode": site1_data["course"]["mode"], + } + headers = { + "Authorization": f"Bearer {site1_data['token']}", + "Host": site1_data["host"], + } + request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" + + response = requests.post(request_url, data=data, headers=headers, timeout=10) + + self.assertEqual(response.status_code, 202) + + def test_create_valid_user_mode_invalid_course(self): + # pylint: disable=invalid-name + """ + Create enrollment with a valid user, valid mode, + and non-existent course + """ + site1_data = self.data["site1_data"] + data = { + "email": site1_data["user_email"], + "course_id": "fake_course_id", + "mode": "audit", + "force": True, + } + headers = { + "Authorization": f"Bearer {site1_data['token']}", + "Host": site1_data["host"], + } + request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" + + response = requests.post(request_url, data=data, headers=headers, timeout=10) + + self.assertEqual(response.status_code, 400) + + def test_create_valid_user_mode_invalid_course_for_site(self): + # pylint: disable=invalid-name + """ + Create enrollment with a valid user, valid mode, + and a course from another site + """ + site1_data = self.data["site1_data"] + site2_data = self.data["site2_data"] + data = { + "email": site2_data["user_email"], + "course_id": site1_data["course"]["id"], + "mode": site1_data["course"]["mode"], + "force": True, + } + headers = { + "Authorization": f"Bearer {site2_data['token']}", + "Host": site2_data["host"], + } + request_url = f"{site2_data['base_url']}/{self.data['endpoint']}" + + response = requests.post(request_url, data=data, headers=headers, timeout=10) + + self.assertEqual(response.status_code, 400) + + # NOTE: Mode changes are not working correctly on devstack + def test_force_create_valid_user_course_invalid_mode(self): + # pylint: disable=invalid-name + """ + Create enrollment with a valid user, valid course, + and a not available mode + """ + delete_enrollment(self.data) + site1_data = self.data["site1_data"] + data = { + "email": site1_data["user_email"], + "course_id": site1_data["course"]["id"], + "mode": "masters", + "force": 1, + } + headers = { + "Authorization": f"Bearer {site1_data['token']}", + "Host": site1_data["host"], + } + request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" + + response = requests.post(request_url, data=data, headers=headers, timeout=10) + + self.assertEqual(response.status_code, 400) + + def test_delete_valid_enrollment(self): + # pylint: disable=invalid-name + """ + Delete a valid enrollment + """ + create_enrollment(self.data) + site1_data = self.data["site1_data"] + data = { + "email": site1_data["user_email"], + "course_id": site1_data["course"]["id"], + } + headers = { + "Authorization": f"Bearer {site1_data['token']}", + "Host": site1_data["host"], + } + request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" + + response = requests.delete(request_url, data=data, headers=headers, timeout=10) + + self.assertEqual(response.status_code, 204) + + def test_delete_invalid_enrollment(self): + # pylint: disable=invalid-name + """ + Delete a invalid enrollment(doesn't exist) + """ + delete_enrollment(self.data) + site1_data = self.data["site1_data"] + data = { + "email": site1_data["user_email"], + "course_id": site1_data["course"]["id"], + } + headers = { + "Authorization": f"Bearer {site1_data['token']}", + "Host": site1_data["host"], + } + request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" + + response = requests.delete(request_url, data=data, headers=headers, timeout=10) + + self.assertEqual(response.status_code, 404) + + def test_delete_invalid_enrollment_for_site(self): + # pylint: disable=invalid-name + """ + Delete a invalid enrollment (enrollment from other site) + """ + create_enrollment(self.data) + site1_data = self.data["site1_data"] + site2_data = self.data["site2_data"] + data = { + "email": site1_data["user_email"], + "course_id": site1_data["course"]["id"], + } + headers = { + "Authorization": f"Bearer {site2_data['token']}", + "Host": site2_data["host"], + } + request_url = f"{site2_data['base_url']}/{self.data['endpoint']}" + + response = requests.delete(request_url, data=data, headers=headers, timeout=10) + + self.assertEqual(response.status_code, 404) + + def test_update_valid_enrollment_change_is_active(self): + # pylint: disable=invalid-name + """ + Update an existing enrollment; change is_active flag + """ + create_enrollment(self.data) + site1_data = self.data["site1_data"] + data = { + "email": site1_data["user_email"], + "course_id": site1_data["course"]["id"], + "is_active": False, + "mode": site1_data["course"]["mode"], + } + headers = { + "Authorization": f"Bearer {site1_data['token']}", + "Host": site1_data["host"], + } + expected_response = { + "user": site1_data["user_id"], + "is_active": False, + "course_id": data["course_id"], + } + request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" + + response = requests.put(request_url, data=data, headers=headers, timeout=10) + response_content = response.json() + + self.assertEqual(response.status_code, 200) + self.assertDictContainsSubset(expected_response, response_content) + + # NOTE: Mode changes are not working correctly on devstack + def test_update_valid_enrollment_change_valid_mode(self): + # pylint: disable=invalid-name + """ + Update an existing enrollment; change mode + """ + create_enrollment(self.data) + site1_data = self.data["site1_data"] + data = { + "email": site1_data["user_email"], + "course_id": site1_data["course"]["id"], + "is_active": True, + "mode": "honor", + } + headers = { + "Authorization": f"Bearer {site1_data['token']}", + "Host": site1_data["host"], + } + expected_response = { + "user": site1_data["user_id"], + "is_active": True, + "course_id": data["course_id"], + "mode": "honor", + } + request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" + + response = requests.put(request_url, data=data, headers=headers, timeout=10) + response_content = response.json() + + self.assertEqual(response.status_code, 200) + self.assertDictContainsSubset(expected_response, response_content) + + def test_update_valid_enrollment_change_invalid_mode(self): + # pylint: disable=invalid-name + """ + Update an existing enrollment; change to invalid mode + """ + create_enrollment(self.data) + site1_data = self.data["site1_data"] + data = { + "email": site1_data["user_email"], + "course_id": site1_data["course"]["id"], + "is_active": True, + "mode": "masters", + } + headers = { + "Authorization": f"Bearer {site1_data['token']}", + "Host": site1_data["host"], + } + request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" + + response = requests.put(request_url, data=data, headers=headers, timeout=10) + + self.assertEqual(response.status_code, 400) + + def test_update_invalid_enrollment_change_valid_mode(self): + # pylint: disable=invalid-name + """ + Update an non-existent enrollment; change mode + """ + delete_enrollment(self.data) + site1_data = self.data["site1_data"] + data = { + "email": site1_data["user_email"], + "course_id": site1_data["course"]["id"], + "is_active": True, + "mode": "honor", + } + headers = { + "Authorization": f"Bearer {site1_data['token']}", + "Host": site1_data["host"], + } + request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" + + response = requests.put(request_url, data=data, headers=headers, timeout=10) + + self.assertEqual(response.status_code, 202) + + def test_update_invalid_enrollment_change_is_active(self): + # pylint: disable=invalid-name + """ + Update an non-existent enrollment; change is_active flag + """ + delete_enrollment(self.data) + site1_data = self.data["site1_data"] + data = { + "email": site1_data["user_email"], + "course_id": site1_data["course"]["id"], + "is_active": False, + "mode": "audit", + } + headers = { + "Authorization": f"Bearer {site1_data['token']}", + "Host": site1_data["host"], + } + request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" + + response = requests.put(request_url, data=data, headers=headers, timeout=10) + + self.assertEqual(response.status_code, 202) + + def test_update_valid_enrollment_change_is_active_force_post(self): + # pylint: disable=invalid-name + """ + Update an existing enrollment using POST with force=True; + change is_active flag + """ + create_enrollment(self.data) + site1_data = self.data["site1_data"] + data = { + "email": site1_data["user_email"], + "course_id": site1_data["course"]["id"], + "is_active": False, + "mode": site1_data["course"]["mode"], + "force": True, + } + headers = { + "Authorization": f"Bearer {site1_data['token']}", + "Host": site1_data["host"], + } + expected_response = { + "username": site1_data["user_id"], + "is_active": False, + "course_id": data["course_id"], + } + request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" + + response = requests.post(request_url, data=data, headers=headers, timeout=10) + response_content = response.json() + + self.assertEqual(response.status_code, 200) + self.assertDictContainsSubset(expected_response, response_content) + + # NOTE: Mode changes are not working correctly on devstack + def test_update_valid_enrollment_change_valid_mode_force_post(self): + # pylint: disable=invalid-name + """ + Update an existing enrollment; change mode + """ + create_enrollment(self.data) + site1_data = self.data["site1_data"] + data = { + "email": site1_data["user_email"], + "course_id": site1_data["course"]["id"], + "is_active": True, + "mode": "honor", + "force": True, + } + headers = { + "Authorization": f"Bearer {site1_data['token']}", + "Host": site1_data["host"], + } + expected_response = { + "user": site1_data["user_id"], + "is_active": True, + "course_id": data["course_id"], + "mode": "honor", + } + request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" + + response = requests.put(request_url, data=data, headers=headers, timeout=10) + response_content = response.json() + + self.assertEqual(response.status_code, 200) + self.assertDictContainsSubset(expected_response, response_content) + + +def create_enrollment(data): + """ + Auxiliary function to setUp test fixtures. Creates/enables a new enrollment. + + :param data: dictionary with all the parameters needed to create an enrollment. + """ + req_data = { + "email": data["site1_data"]["user_email"], + "course_id": data["site1_data"]["course"]["id"], + "mode": data["site1_data"]["course"]["mode"], + } + headers = { + "Authorization": f"Bearer {data['site1_data']['token']}", + "Host": data["site1_data"]["host"], + } + request_url = f"{data['site1_data']['base_url']}/{data['endpoint']}" + response = requests.post(request_url, data=req_data, headers=headers, timeout=10) + response.raise_for_status() + + +def delete_enrollment(data): + """ + Auxiliary function to setUp test fixtures. Deletes an enrollment if exists. + + :param data: dictionary with all the parameters needed to delete an enrollment. + """ + req_data = { + "email": data["site1_data"]["user_email"], + "course_id": data["site1_data"]["course"]["id"], + } + headers = { + "Authorization": f"Bearer {data['site1_data']['token']}", + "Host": data["site1_data"]["host"], + } + request_url = f"{data['site1_data']['base_url']}/{data['endpoint']}" + response = requests.delete(request_url, data=req_data, headers=headers, timeout=10) + if response.status_code == 404: + return + response.raise_for_status() From 486246bd2c526d495b11f464300b9c695734d167 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Thu, 26 Sep 2024 08:27:28 -0500 Subject: [PATCH 16/32] chore: add missing letter --- .../api/v1/tests/integration/test_views.py | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/eox_core/api/v1/tests/integration/test_views.py b/eox_core/api/v1/tests/integration/test_views.py index 73c0e7693..b65732243 100644 --- a/eox_core/api/v1/tests/integration/test_views.py +++ b/eox_core/api/v1/tests/integration/test_views.py @@ -444,7 +444,7 @@ def setUp(self) -> None: @ddt.data("email", "username") def test_create_enrollment_success(self, param: str) -> None: """ - Test creating a enrollment with a valid user, course and mode in a tenant. + Test creating an enrollment with a valid user, course and mode in a tenant. Open edX definitions tested: - `get_edxapp_user` @@ -482,7 +482,7 @@ def test_create_enrollment_success(self, param: str) -> None: @ddt.unpack def test_create_enrollment_missing_required_fields(self, param: str, error: list | dict) -> None: """ - Test creating a enrollment with missing required fields. + Test creating an enrollment with missing required fields. Open edX definitions tested: - `check_edxapp_enrollment_is_valid` @@ -509,7 +509,7 @@ def test_create_enrollment_missing_required_fields(self, param: str, error: list @ddt.data("email", "username") def test_force_create_enrollment_success(self, param: str) -> None: """ - Test force creating a enrollment with a valid user, course and mode in a tenant. + Test force creating an enrollment with a valid user, course and mode in a tenant. Open edX definitions tested: - `get_edxapp_user` @@ -543,7 +543,7 @@ def test_force_create_enrollment_success(self, param: str) -> None: @ddt.data("email", "username") def test_create_valid_course_mode_invalid_user(self, param: str) -> None: """ - Test creating a enrollment with a valid course, valid mode, and a non-existent user in a tenant. + Test creating an enrollment with a valid course, valid mode, and a non-existent user in a tenant. Open edX definitions tested: - `check_edxapp_enrollment_is_valid` @@ -569,7 +569,7 @@ def test_create_valid_course_mode_invalid_user(self, param: str) -> None: @ddt.data("email", "username") def test_create_valid_course_mode_invalid_user_for_tenant(self, param: str) -> None: """ - Test creating a enrollment with a valid course, valid mode, and a user from another tenant. + Test creating an enrollment with a valid course, valid mode, and a user from another tenant. Open edX definitions tested: - `check_edxapp_enrollment_is_valid` @@ -599,7 +599,7 @@ def test_create_valid_course_mode_invalid_user_for_tenant(self, param: str) -> N @ddt.data("email", "username") def test_create_valid_user_mode_invalid_course(self, param: str) -> None: """ - Test creating a enrollment with a valid user, valid mode, and a non-existent course in a tenant. + Test creating an enrollment with a valid user, valid mode, and a non-existent course in a tenant. Open edX definitions tested: - `check_edxapp_enrollment_is_valid` @@ -627,7 +627,7 @@ def test_create_valid_user_mode_invalid_course(self, param: str) -> None: @ddt.data("email", "username") def test_create_valid_user_mode_invalid_course_for_tenant(self, param: str) -> None: """ - Test creating a enrollment with a valid user, valid mode, and a course from another tenant. + Test creating an enrollment with a valid user, valid mode, and a course from another tenant. Open edX definitions tested: - `check_edxapp_enrollment_is_valid` @@ -655,7 +655,7 @@ def test_create_valid_user_mode_invalid_course_for_tenant(self, param: str) -> N @ddt.data("email", "username") def test_create_valid_user_course_invalid_mode(self, param: str) -> None: """ - Test creating a enrollment with a valid user, valid course, and a not available mode in a tenant. + Test creating an enrollment with a valid user, valid course, and a not available mode in a tenant. Open edX definitions tested: - `check_edxapp_enrollment_is_valid` @@ -683,7 +683,7 @@ def test_create_valid_user_course_invalid_mode(self, param: str) -> None: @ddt.data("email", "username") def test_force_create_valid_user_course_mode_not_allowed(self, param: str) -> None: """ - Test creating a enrollment with a valid user, valid course, and a not available mode in a tenant. + Test creating an enrollment with a valid user, valid course, and a not available mode in a tenant. Open edX definitions tested: - `get_edxapp_user` @@ -717,7 +717,7 @@ def test_force_create_valid_user_course_mode_not_allowed(self, param: str) -> No @ddt.data("email", "username") def test_get_enrollment_success(self, param: str) -> None: """ - Test getting a enrollment with a valid user, course and mode in a tenant. + Test getting an enrollment with a valid user, course and mode in a tenant. Open edX definitions tested: - `get_edxapp_user` @@ -749,7 +749,7 @@ def test_get_enrollment_success(self, param: str) -> None: @ddt.data("email", "username") def test_get_enrollment_does_not_exist(self, param: str) -> None: """ - Test getting a enrollment that does not exist in a tenant. + Test getting an enrollment that does not exist in a tenant. Open edX definitions tested: - `get_edxapp_user` @@ -775,7 +775,7 @@ def test_get_enrollment_does_not_exist(self, param: str) -> None: @ddt.data("email", "username") def test_get_enrollment_not_found_in_tenant(self, param: str) -> None: """ - Test getting a enrollment that belongs to another tenant. + Test getting an enrollment that belongs to another tenant. Open edX definitions tested: - `get_edxapp_user` @@ -806,7 +806,7 @@ def test_get_enrollment_not_found_in_tenant(self, param: str) -> None: @ddt.data("email", "username") def test_delete_enrollment_success(self, param: str) -> None: """ - Test deleting a enrollment with a valid user, course and mode in a tenant. + Test deleting an enrollment with a valid user, course and mode in a tenant. Open edX definitions tested: - `get_edxapp_user` @@ -832,7 +832,7 @@ def test_delete_enrollment_success(self, param: str) -> None: @ddt.data("email", "username") def test_delete_enrollment_does_not_exist(self, param: str) -> None: """ - Test deleting a enrollment that does not exist in a tenant. + Test deleting an enrollment that does not exist in a tenant. Open edX definitions tested: - `get_edxapp_user` @@ -862,7 +862,7 @@ def test_delete_enrollment_does_not_exist(self, param: str) -> None: @ddt.data("email", "username") def test_delete_invalid_enrollment_for_tenant(self, param: str) -> None: """ - Test deleting a enrollment that belongs to another tenant. + Test deleting an enrollment that belongs to another tenant. Open edX definitions tested: - `get_edxapp_user` From 843c9b1ef752732ec71d64370899906dffbe4aa8 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Thu, 26 Sep 2024 12:08:30 -0500 Subject: [PATCH 17/32] refactor: include force field in ddt data when creating enrollment --- .../api/v1/tests/integration/test_views.py | 47 ++++--------------- 1 file changed, 10 insertions(+), 37 deletions(-) diff --git a/eox_core/api/v1/tests/integration/test_views.py b/eox_core/api/v1/tests/integration/test_views.py index b65732243..3fff41af7 100644 --- a/eox_core/api/v1/tests/integration/test_views.py +++ b/eox_core/api/v1/tests/integration/test_views.py @@ -441,8 +441,14 @@ def setUp(self) -> None: self.mode = "audit" super().setUp() - @ddt.data("email", "username") - def test_create_enrollment_success(self, param: str) -> None: + @ddt.data( + ("email", True), + ("email", False), + ("username", True), + ("username", False), + ) + @ddt.unpack + def test_create_enrollment_success(self, param: str, force_value: bool) -> None: """ Test creating an enrollment with a valid user, course and mode in a tenant. @@ -454,7 +460,7 @@ def test_create_enrollment_success(self, param: str) -> None: Expected result: - The status code is 200. - - The enrollment is created successfully in the tenant with the provided data. + - The response indicates the enrollment was created successfully. """ user_data = next(FAKE_USER_DATA) self.create_user(self.tenant_x, user_data) @@ -462,6 +468,7 @@ def test_create_enrollment_success(self, param: str) -> None: param: user_data[param], "course_id": self.course_id, "mode": self.mode, + "force": force_value, } response = self.create_enrollment(self.tenant_x, enrollment_data) @@ -506,40 +513,6 @@ def test_create_enrollment_missing_required_fields(self, param: str, error: list self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(response_data, error) - @ddt.data("email", "username") - def test_force_create_enrollment_success(self, param: str) -> None: - """ - Test force creating an enrollment with a valid user, course and mode in a tenant. - - Open edX definitions tested: - - `get_edxapp_user` - - `create_enrollment` - - `check_edxapp_account_conflicts` - - `check_edxapp_enrollment_is_valid` - - Expected result: - - The status code is 200. - - The enrollment is created successfully in the tenant with the provided data. - """ - user_data = next(FAKE_USER_DATA) - self.create_user(self.tenant_x, user_data) - enrollment_data = { - param: user_data[param], - "course_id": self.course_id, - "mode": self.mode, - "force": True, - } - - response = self.create_enrollment(self.tenant_x, enrollment_data) - - response_data = response.json() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response_data["username"], user_data["username"]) - self.assertEqual(response_data["mode"], enrollment_data["mode"]) - self.assertEqual(response_data["course_id"], enrollment_data["course_id"]) - self.assertTrue(response_data["is_active"]) - self.assertIn("created", response_data) - @ddt.data("email", "username") def test_create_valid_course_mode_invalid_user(self, param: str) -> None: """ From 1e547becf51233f8f79805ff28c0c5fd15335312 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Thu, 26 Sep 2024 12:36:53 -0500 Subject: [PATCH 18/32] test: add value for each param --- eox_core/api/v1/tests/integration/test_views.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/eox_core/api/v1/tests/integration/test_views.py b/eox_core/api/v1/tests/integration/test_views.py index 3fff41af7..f0474ef9a 100644 --- a/eox_core/api/v1/tests/integration/test_views.py +++ b/eox_core/api/v1/tests/integration/test_views.py @@ -513,8 +513,12 @@ def test_create_enrollment_missing_required_fields(self, param: str, error: list self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(response_data, error) - @ddt.data("email", "username") - def test_create_valid_course_mode_invalid_user(self, param: str) -> None: + @ddt.data( + ("email", "user-not-found"), + ("username", "user-not-found@mail.com"), + ) + @ddt.unpack + def test_create_valid_course_mode_invalid_user(self, param: str, value: str) -> None: """ Test creating an enrollment with a valid course, valid mode, and a non-existent user in a tenant. @@ -527,7 +531,7 @@ def test_create_valid_course_mode_invalid_user(self, param: str) -> None: - The enrollment is not created in the tenant. """ enrollment_data = { - param: param, + param: value, "course_id": self.course_id, "mode": self.mode, } From d6e84e2440cab4249f3e3c73a06cd84cd168696c Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Thu, 26 Sep 2024 12:56:24 -0500 Subject: [PATCH 19/32] docs: update docstring and name of methods --- .../api/v1/tests/integration/test_views.py | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/eox_core/api/v1/tests/integration/test_views.py b/eox_core/api/v1/tests/integration/test_views.py index f0474ef9a..5edb569eb 100644 --- a/eox_core/api/v1/tests/integration/test_views.py +++ b/eox_core/api/v1/tests/integration/test_views.py @@ -630,9 +630,9 @@ def test_create_valid_user_mode_invalid_course_for_tenant(self, param: str) -> N self.assertEqual(response_data["course_id"], [f"Invalid course_id {self.course_id}"]) @ddt.data("email", "username") - def test_create_valid_user_course_invalid_mode(self, param: str) -> None: + def test_create_valid_user_course_unavailable_mode(self, param: str) -> None: """ - Test creating an enrollment with a valid user, valid course, and a not available mode in a tenant. + Test creating an enrollment with a valid user, valid course, and an unavailable mode in a tenant. Open edX definitions tested: - `check_edxapp_enrollment_is_valid` @@ -658,9 +658,11 @@ def test_create_valid_user_course_invalid_mode(self, param: str) -> None: self.assertEqual(response_data["non_field_errors"], ["Mode not found"]) @ddt.data("email", "username") - def test_force_create_valid_user_course_mode_not_allowed(self, param: str) -> None: + def test_force_create_valid_user_course_unavailable_mode(self, param: str) -> None: """ - Test creating an enrollment with a valid user, valid course, and a not available mode in a tenant. + Test force creating an enrollment with a valid user, valid course, and an unavailable mode in a tenant. + + When `force = True`, the enrollment is created even if the mode is not available. Open edX definitions tested: - `get_edxapp_user` @@ -871,7 +873,7 @@ def test_delete_invalid_enrollment_for_tenant(self, param: str) -> None: @ddt.data("email", "username") def test_update_valid_enrollment_change_is_active_mode_field(self, param: str) -> None: """ - Test updating an existing enrollment. Update is_active and mode field. + Test updating an existing enrollment. Update `is_active` and `mode` field. Open edX definitions tested: - `get_edxapp_user` @@ -903,9 +905,9 @@ def test_update_valid_enrollment_change_is_active_mode_field(self, param: str) - self.assertTrue(response_data["is_active"]) @ddt.data("email", "username") - def test_update_valid_enrollment_update_invalid_mode(self, param: str) -> None: + def test_update_valid_enrollment_update_unavailable_mode(self, param: str) -> None: """ - Test updating an existing enrollment. Update to invalid mode. + Test updating an existing enrollment. Update to an unavailable mode. Open edX definitions tested: - `get_edxapp_user` @@ -952,7 +954,7 @@ def test_update_enrollment_does_not_exist(self, param: str) -> None: param: user_data[param], "course_id": self.course_id, "is_active": False, - "mode": "honor", + "mode": self.mode, } response = self.update_enrollment(self.tenant_x, data=enrollment_data) @@ -962,9 +964,11 @@ def test_update_enrollment_does_not_exist(self, param: str) -> None: self.assertEqual(response_data["error"]["detail"], f"No enrollment found for {user_data['username']}") @ddt.data("email", "username") - def test_update_valid_enrollment_force_post(self, param: str) -> None: + def test_update_valid_enrollment_using_force_flag(self, param: str) -> None: """ - Test updating an existing enrollment. Update is_active and mode field with force mode. + Test updating an existing enrollment. Update `is_active` and `mode` field using force flag. + + When `force = True`, the enrollment is updated even if the mode is not available. Open edX definitions tested: - `get_edxapp_user` From 31fada0bb821d261902b58d5984281e16aa31e19 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Thu, 26 Sep 2024 13:20:38 -0500 Subject: [PATCH 20/32] chore: add f flag to show a short test summary info --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1c977e9d4..922ab2dc9 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ python-quality-test: run-tests: python-test python-quality-test run-integration-tests: install-dev-dependencies - pytest -rP ./eox_core --ignore-glob='**/unit/*' --ignore-glob='**/edxapp_wrapper/*' + pytest -rPf ./eox_core --ignore-glob='**/unit/*' --ignore-glob='**/edxapp_wrapper/*' upgrade: export CUSTOM_COMPILE_COMMAND=make upgrade upgrade: ## update the requirements/*.txt files with the latest packages satisfying requirements/*.in From a710bccfdeeb299a093c3727b91692d5d583afb1 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Thu, 26 Sep 2024 14:17:00 -0500 Subject: [PATCH 21/32] refactor: create user in the setup method --- .../v1/tests/integration/data/fake_users.py | 52 +++++++++++ .../api/v1/tests/integration/test_views.py | 88 ++++++------------- 2 files changed, 81 insertions(+), 59 deletions(-) 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 53d4d0330..1329137f1 100644 --- a/eox_core/api/v1/tests/integration/data/fake_users.py +++ b/eox_core/api/v1/tests/integration/data/fake_users.py @@ -626,5 +626,57 @@ "city": "Charleston", "goals": "Aenean vulputate eleifend tellus.", }, + { + "username": "gwalker33", + "email": "gwalker33@protonmail.com", + "fullname": "Gary Walker", + "password": "zP7%1Yt!dB@", + "activate_user": True, + "mailing_address": "321 Birch Road", + "year_of_birth": 1996, + "gender": "m", + "level_of_education": "hs", + "city": "Sacramento", + "goals": "Praesent vestibulum dapibus nibh.", + }, + { + "username": "tturner34", + "email": "tturner34@live.com", + "fullname": "Tina Turner", + "password": "wJ4@5Lm!tQ#", + "activate_user": False, + "mailing_address": "753 Pine Street", + "year_of_birth": 1988, + "gender": "f", + "level_of_education": "m", + "city": "Portland", + "goals": "Duis lobortis massa nec est.", + }, + { + "username": "jwhite35", + "email": "jwhite35@gmail.com", + "fullname": "Jack White", + "password": "qB2%9Cv!xF@", + "activate_user": True, + "mailing_address": "159 Spruce Lane", + "year_of_birth": 1995, + "gender": "m", + "level_of_education": "hs", + "city": "New Orleans", + "goals": "Vestibulum volutpat pretium libero.", + }, + { + "username": "gwalker33", + "email": "gwalker33@protonmail.com", + "fullname": "Gary Walker", + "password": "zP7%1Yt!dB@", + "activate_user": True, + "mailing_address": "321 Birch Road", + "year_of_birth": 1996, + "gender": "m", + "level_of_education": "hs", + "city": "Sacramento", + "goals": "Praesent vestibulum dapibus nibh.", + }, ] ) diff --git a/eox_core/api/v1/tests/integration/test_views.py b/eox_core/api/v1/tests/integration/test_views.py index 5edb569eb..6a8a8c321 100644 --- a/eox_core/api/v1/tests/integration/test_views.py +++ b/eox_core/api/v1/tests/integration/test_views.py @@ -437,9 +437,11 @@ class TestEnrollmentAPIIntegration(BaseAPIIntegrationTest, UsersAPIRequestMixin, def setUp(self) -> None: """Set up the test suite""" + super().setUp() self.course_id = settings["COURSE_ID"] self.mode = "audit" - super().setUp() + self.user = next(FAKE_USER_DATA) + self.create_user(self.tenant_x, self.user) @ddt.data( ("email", True), @@ -462,10 +464,8 @@ def test_create_enrollment_success(self, param: str, force_value: bool) -> None: - The status code is 200. - The response indicates the enrollment was created successfully. """ - user_data = next(FAKE_USER_DATA) - self.create_user(self.tenant_x, user_data) enrollment_data = { - param: user_data[param], + param: self.user[param], "course_id": self.course_id, "mode": self.mode, "force": force_value, @@ -475,7 +475,7 @@ def test_create_enrollment_success(self, param: str, force_value: bool) -> None: response_data = response.json() self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response_data["username"], user_data["username"]) + self.assertEqual(response_data["username"], self.user["username"]) self.assertEqual(response_data["mode"], enrollment_data["mode"]) self.assertEqual(response_data["course_id"], enrollment_data["course_id"]) self.assertTrue(response_data["is_active"]) @@ -498,10 +498,8 @@ def test_create_enrollment_missing_required_fields(self, param: str, error: list - The status code is 400. - The response contains a message about the missing field. """ - user_data = next(FAKE_USER_DATA) - self.create_user(self.tenant_x, user_data) enrollment_data = { - "username": user_data["username"], + "username": self.user["username"], "mode": self.mode, "course_id": self.course_id, } @@ -586,10 +584,8 @@ def test_create_valid_user_mode_invalid_course(self, param: str) -> None: - The response contains an error message about the course not found. - The enrollment is not created in the tenant. """ - user_data = next(FAKE_USER_DATA) - self.create_user(self.tenant_x, user_data) enrollment_data = { - param: user_data[param], + param: self.user[param], "course_id": "course-v1:OpenedX+DemoX+NonExistentCourse", "mode": self.mode, } @@ -614,10 +610,8 @@ def test_create_valid_user_mode_invalid_course_for_tenant(self, param: str) -> N - The status code is 400. - The response contains an error message about the course not found on the tenant. """ - user_data = next(FAKE_USER_DATA) - self.create_user(self.tenant_y, user_data) enrollment_data = { - param: user_data[param], + param: self.user[param], "course_id": self.course_id, "mode": self.mode, } @@ -642,10 +636,8 @@ def test_create_valid_user_course_unavailable_mode(self, param: str) -> None: - The status code is 400. - The response contains an error message about the mode not found. """ - user_data = next(FAKE_USER_DATA) - self.create_user(self.tenant_x, user_data) enrollment_data = { - param: user_data[param], + param: self.user[param], "course_id": self.course_id, "mode": "masters", } @@ -674,10 +666,8 @@ def test_force_create_valid_user_course_unavailable_mode(self, param: str) -> No - The status code is 200. - The enrollment is created successfully in the tenant with the provided data. """ - user_data = next(FAKE_USER_DATA) - self.create_user(self.tenant_x, user_data) enrollment_data = { - param: user_data[param], + param: self.user[param], "course_id": self.course_id, "mode": "masters", "force": True, @@ -687,7 +677,7 @@ def test_force_create_valid_user_course_unavailable_mode(self, param: str) -> No response_data = response.json() self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response_data["username"], user_data["username"]) + self.assertEqual(response_data["username"], self.user["username"]) self.assertEqual(response_data["mode"], enrollment_data["mode"]) self.assertEqual(response_data["course_id"], enrollment_data["course_id"]) self.assertTrue(response_data["is_active"]) @@ -706,20 +696,18 @@ def test_get_enrollment_success(self, param: str) -> None: - The status code is 200. - The response contains the enrollment data. """ - user_data = next(FAKE_USER_DATA) enrollment_data = { - param: user_data[param], + param: self.user[param], "course_id": self.course_id, "mode": self.mode, } - self.create_user(self.tenant_x, user_data) self.create_enrollment(self.tenant_x, enrollment_data) response = self.get_enrollment(self.tenant_x, data=enrollment_data) response_data = response.json() self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response_data["username"], user_data["username"]) + self.assertEqual(response_data["username"], self.user["username"]) self.assertEqual(response_data["mode"], enrollment_data["mode"]) self.assertEqual(response_data["course_id"], enrollment_data["course_id"]) self.assertTrue(response_data["is_active"]) @@ -738,10 +726,8 @@ def test_get_enrollment_does_not_exist(self, param: str) -> None: - The status code is 404. - The response contains an error message about the enrollment not found. """ - user_data = next(FAKE_USER_DATA) - self.create_user(self.tenant_x, user_data) enrollment_data = { - param: user_data[param], + param: self.user[param], "course_id": self.course_id, } @@ -749,7 +735,7 @@ def test_get_enrollment_does_not_exist(self, param: str) -> None: response_data = response.json() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertEqual(response_data, [f"No enrollment found for user:`{user_data['username']}`"]) + self.assertEqual(response_data, [f"No enrollment found for user:`{self.user['username']}`"]) @ddt.data("email", "username") def test_get_enrollment_not_found_in_tenant(self, param: str) -> None: @@ -763,13 +749,11 @@ def test_get_enrollment_not_found_in_tenant(self, param: str) -> None: - The status code is 404. - The response contains an error message about the user not found on the tenant. """ - user_data = next(FAKE_USER_DATA) enrollment_data = { - param: user_data[param], + param: self.user[param], "course_id": self.course_id, "mode": self.mode, } - self.create_user(self.tenant_x, user_data) self.create_enrollment(self.tenant_x, enrollment_data) response = self.get_enrollment(self.tenant_y, data=enrollment_data) @@ -779,7 +763,7 @@ def test_get_enrollment_not_found_in_tenant(self, param: str) -> None: self.assertIn("detail", response_data) self.assertEqual( response_data["detail"], - f"No user found by {{'{param}': '{user_data[param]}'}} on site {self.tenant_y['domain']}.", + f"No user found by {{'{param}': '{self.user[param]}'}} on site {self.tenant_y['domain']}.", ) @ddt.data("email", "username") @@ -795,13 +779,11 @@ def test_delete_enrollment_success(self, param: str) -> None: - The status code is 204. - The enrollment is deleted successfully in the tenant. """ - user_data = next(FAKE_USER_DATA) enrollment_data = { - param: user_data[param], + param: self.user[param], "course_id": self.course_id, "mode": self.mode, } - self.create_user(self.tenant_x, user_data) self.create_enrollment(self.tenant_x, enrollment_data) response = self.delete_enrollment(self.tenant_x, data=enrollment_data) @@ -821,10 +803,8 @@ def test_delete_enrollment_does_not_exist(self, param: str) -> None: - The status code is 404. - The response contains an error message about the enrollment not found. """ - user_data = next(FAKE_USER_DATA) - self.create_user(self.tenant_x, user_data) enrollment_data = { - param: user_data[param], + param: self.user[param], "course_id": self.course_id, } @@ -835,7 +815,7 @@ def test_delete_enrollment_does_not_exist(self, param: str) -> None: self.assertIn("detail", response_data) self.assertEqual( response_data["detail"], - f"No enrollment found for user: `{user_data['username']}` on course_id `{self.course_id}`", + f"No enrollment found for user: `{self.user['username']}` on course_id `{self.course_id}`", ) @ddt.data("email", "username") @@ -851,13 +831,11 @@ def test_delete_invalid_enrollment_for_tenant(self, param: str) -> None: - The status code is 404. - The response contains an error message about the user not found on the tenant. """ - user_data = next(FAKE_USER_DATA) enrollment_data = { - param: user_data[param], + param: self.user[param], "course_id": self.course_id, "mode": self.mode, } - self.create_user(self.tenant_x, user_data) self.create_enrollment(self.tenant_x, enrollment_data) response = self.delete_enrollment(self.tenant_y, data=enrollment_data) @@ -867,7 +845,7 @@ def test_delete_invalid_enrollment_for_tenant(self, param: str) -> None: self.assertIn("detail", response_data) self.assertEqual( response_data["detail"], - f"No user found by {{'{param}': '{user_data[param]}'}} on site {self.tenant_y['domain']}.", + f"No user found by {{'{param}': '{self.user[param]}'}} on site {self.tenant_y['domain']}.", ) @ddt.data("email", "username") @@ -883,14 +861,12 @@ def test_update_valid_enrollment_change_is_active_mode_field(self, param: str) - - The status code is 200. - The enrollment is updated successfully in the tenant with the provided data. """ - user_data = next(FAKE_USER_DATA) enrollment_data = { - param: user_data[param], + param: self.user[param], "course_id": self.course_id, "is_active": False, "mode": self.mode, } - self.create_user(self.tenant_x, user_data) self.create_enrollment(self.tenant_x, enrollment_data) enrollment_data["is_active"] = True enrollment_data["mode"] = "honor" @@ -899,7 +875,7 @@ def test_update_valid_enrollment_change_is_active_mode_field(self, param: str) - response_data = response.json() self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response_data["user"], user_data["username"]) + self.assertEqual(response_data["user"], self.user["username"]) self.assertEqual(response_data["course_id"], enrollment_data["course_id"]) self.assertEqual(response_data["mode"], enrollment_data["mode"]) self.assertTrue(response_data["is_active"]) @@ -917,14 +893,12 @@ def test_update_valid_enrollment_update_unavailable_mode(self, param: str) -> No - The status code is 400. - The response contains an error message about the mode not found. """ - user_data = next(FAKE_USER_DATA) enrollment_data = { - param: user_data[param], + param: self.user[param], "course_id": self.course_id, "is_active": True, "mode": self.mode, } - self.create_user(self.tenant_x, user_data) self.create_enrollment(self.tenant_x, enrollment_data) enrollment_data["mode"] = "masters" @@ -948,10 +922,8 @@ def test_update_enrollment_does_not_exist(self, param: str) -> None: - The status code is 202. - The response contains an error message about the enrollment not found. """ - user_data = next(FAKE_USER_DATA) - self.create_user(self.tenant_x, user_data) enrollment_data = { - param: user_data[param], + param: self.user[param], "course_id": self.course_id, "is_active": False, "mode": self.mode, @@ -961,7 +933,7 @@ def test_update_enrollment_does_not_exist(self, param: str) -> None: response_data = response.json() self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) - self.assertEqual(response_data["error"]["detail"], f"No enrollment found for {user_data['username']}") + self.assertEqual(response_data["error"]["detail"], f"No enrollment found for {self.user['username']}") @ddt.data("email", "username") def test_update_valid_enrollment_using_force_flag(self, param: str) -> None: @@ -978,15 +950,13 @@ def test_update_valid_enrollment_using_force_flag(self, param: str) -> None: - The status code is 200. - The enrollment is updated successfully in the tenant with the provided data. """ - user_data = next(FAKE_USER_DATA) enrollment_data = { - param: user_data[param], + param: self.user[param], "course_id": self.course_id, "is_active": False, "mode": self.mode, "force": True, } - self.create_user(self.tenant_x, user_data) self.create_enrollment(self.tenant_x, enrollment_data) enrollment_data["is_active"] = True enrollment_data["mode"] = "honor" @@ -995,7 +965,7 @@ def test_update_valid_enrollment_using_force_flag(self, param: str) -> None: response_data = response.json() self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response_data["username"], user_data["username"]) + self.assertEqual(response_data["username"], self.user["username"]) self.assertEqual(response_data["course_id"], enrollment_data["course_id"]) self.assertEqual(response_data["mode"], enrollment_data["mode"]) self.assertTrue(response_data["is_active"]) From 05a0e18cf2c853ab1702fc9e3c62e257ee6e7b0a Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Thu, 26 Sep 2024 17:12:41 -0500 Subject: [PATCH 22/32] chore: remove old enrollments api integrations tests --- .../test_enrollment_integration.py | 582 ------------------ 1 file changed, 582 deletions(-) delete mode 100644 eox_core/tests/integration/test_enrollment_integration.py diff --git a/eox_core/tests/integration/test_enrollment_integration.py b/eox_core/tests/integration/test_enrollment_integration.py deleted file mode 100644 index 57cba454b..000000000 --- a/eox_core/tests/integration/test_enrollment_integration.py +++ /dev/null @@ -1,582 +0,0 @@ -""" -Integration test suite. - -This suite performs multiple http requests to guarantee -that all CRUD operations are handled correctly. -""" -import json -from os import environ - -import pytest -import requests -from django.test import TestCase - - -@pytest.mark.skipif(not environ.get("TEST_INTEGRATION"), reason="Run only explicitly") -class TestEnrollmentIntegration(TestCase): # pragma: no cover - # pylint: disable=too-many-public-methods - """Test suite""" - data = {} - - @classmethod - def setUpClass(cls): - with open("eox_core/tests/integration/test_data", encoding="utf-8") as file_obj: - cls.data = json.load(file_obj) - cls.data["endpoint"] = "eox-core/api/v1/enrollment/" - site1_data = { - "client_id": cls.data["site1_data"]["client_id"], - "client_secret": cls.data["site1_data"]["client_secret"], - "grant_type": "client_credentials", - } - site2_data = { - "client_id": cls.data["site2_data"]["client_id"], - "client_secret": cls.data["site2_data"]["client_secret"], - "grant_type": "client_credentials", - } - request_url = f"{cls.data['site1_data']['base_url']}/oauth2/access_token/" - response_site1 = requests.post(request_url, data=site1_data, timeout=10) - response_site1.raise_for_status() - cls.data["site1_data"]["token"] = response_site1.json()["access_token"] - request_url = f"{cls.data['site2_data']['base_url']}/oauth2/access_token/" - response_site2 = requests.post(request_url, data=site2_data, timeout=10) - response_site2.raise_for_status() - cls.data["site2_data"]["token"] = response_site2.json()["access_token"] - - @classmethod - def tearDownClass(cls): - delete_enrollment(cls.data) - - def test_read_valid_email_course(self): - # pylint: disable=invalid-name - """ - Get a valid enrollment - """ - create_enrollment(self.data) - site1_data = self.data["site1_data"] - data = { - "email": site1_data["user_email"], - "course_id": site1_data["course"]["id"], - } - headers = { - "Authorization": f"Bearer {site1_data['token']}", - "Host": site1_data["host"], - } - expected_response = { - "username": site1_data["user_id"], - "course_id": data["course_id"], - } - request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" - - response = requests.get(request_url, data=data, headers=headers, timeout=10) - response_content = response.json() - - self.assertEqual(response.status_code, 200) - self.assertDictContainsSubset(expected_response, response_content) - - def test_read_invalid_enrollment(self): - # pylint: disable=invalid-name - """ - Get a invalid enrollment (doesn't exist) - """ - delete_enrollment(self.data) - site1_data = self.data["site1_data"] - data = { - "username": site1_data["user_id"], - "course_id": site1_data["course"]["id"], - } - headers = { - "Authorization": f"Bearer {site1_data['token']}", - "Host": site1_data["host"], - } - request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" - - response = requests.get(request_url, data=data, headers=headers, timeout=10) - - self.assertEqual(response.status_code, 404) - - def test_read_invalid_enrollment_for_site(self): - # pylint: disable=invalid-name - """ - Get a invalid enrollment (enrollment from other site) - """ - create_enrollment(self.data) - site1_data = self.data["site1_data"] - site2_data = self.data["site2_data"] - data = { - "username": site1_data["user_id"], - "course_id": site1_data["course"]["id"], - } - headers = { - "Authorization": f"Bearer {site2_data['token']}", - "Host": site2_data["host"], - } - request_url = f"{site2_data['base_url']}/{self.data['endpoint']}" - - response = requests.get(request_url, data=data, headers=headers, timeout=10) - - self.assertEqual(response.status_code, 404) - - def test_create_enrollment_valid_user_mode_course(self): - # pylint: disable=invalid-name - """ - Create enrollment with a valid user, valid course, - valid mode - """ - delete_enrollment(self.data) - site1_data = self.data["site1_data"] - data = { - "email": site1_data["user_email"], - "course_id": site1_data["course"]["id"], - "mode": site1_data["course"]["mode"], - } - headers = { - "Authorization": f"Bearer {site1_data['token']}", - "Host": site1_data["host"], - } - request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" - - response = requests.post(request_url, data=data, headers=headers, timeout=10) - - self.assertEqual(response.status_code, 200) - - def test_force_create_enrollment_valid_user_mode_course(self): - # pylint: disable=invalid-name - """ - Create enrollment with a valid user, valid course, - valid mode using force - """ - delete_enrollment(self.data) - site1_data = self.data["site1_data"] - data = { - "email": site1_data["user_email"], - "course_id": site1_data["course"]["id"], - "mode": site1_data["course"]["mode"], - "force": True, - } - headers = { - "Authorization": f"Bearer {site1_data['token']}", - "Host": site1_data["host"], - } - request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" - - response = requests.post(request_url, data=data, headers=headers, timeout=10) - - self.assertEqual(response.status_code, 200) - - def test_create_valid_course_mode_invalid_user(self): - # pylint: disable=invalid-name - """ - Create enrollment with a valid course, valid mode, - and a non-existent user - """ - site1_data = self.data["site1_data"] - data = { - "username": site1_data["fake_user"], - "course_id": site1_data["course"]["id"], - "mode": site1_data["course"]["mode"], - } - headers = { - "Authorization": f"Bearer {site1_data['token']}", - "Host": site1_data["host"], - } - request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" - - response = requests.post(request_url, data=data, headers=headers, timeout=10) - - self.assertEqual(response.status_code, 400) - - def test_create_valid_course_mode_invalid_user_for_site(self): - # pylint: disable=invalid-name - """ - Create enrollment with a valid course, valid mode, - and a user from another site - """ - site1_data = self.data["site1_data"] - site2_data = self.data["site2_data"] - data = { - "email": site2_data["user_email"], - "course_id": site1_data["course"]["id"], - "mode": site1_data["course"]["mode"], - } - headers = { - "Authorization": f"Bearer {site1_data['token']}", - "Host": site1_data["host"], - } - request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" - - response = requests.post(request_url, data=data, headers=headers, timeout=10) - - self.assertEqual(response.status_code, 202) - - def test_create_valid_user_mode_invalid_course(self): - # pylint: disable=invalid-name - """ - Create enrollment with a valid user, valid mode, - and non-existent course - """ - site1_data = self.data["site1_data"] - data = { - "email": site1_data["user_email"], - "course_id": "fake_course_id", - "mode": "audit", - "force": True, - } - headers = { - "Authorization": f"Bearer {site1_data['token']}", - "Host": site1_data["host"], - } - request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" - - response = requests.post(request_url, data=data, headers=headers, timeout=10) - - self.assertEqual(response.status_code, 400) - - def test_create_valid_user_mode_invalid_course_for_site(self): - # pylint: disable=invalid-name - """ - Create enrollment with a valid user, valid mode, - and a course from another site - """ - site1_data = self.data["site1_data"] - site2_data = self.data["site2_data"] - data = { - "email": site2_data["user_email"], - "course_id": site1_data["course"]["id"], - "mode": site1_data["course"]["mode"], - "force": True, - } - headers = { - "Authorization": f"Bearer {site2_data['token']}", - "Host": site2_data["host"], - } - request_url = f"{site2_data['base_url']}/{self.data['endpoint']}" - - response = requests.post(request_url, data=data, headers=headers, timeout=10) - - self.assertEqual(response.status_code, 400) - - # NOTE: Mode changes are not working correctly on devstack - def test_force_create_valid_user_course_invalid_mode(self): - # pylint: disable=invalid-name - """ - Create enrollment with a valid user, valid course, - and a not available mode - """ - delete_enrollment(self.data) - site1_data = self.data["site1_data"] - data = { - "email": site1_data["user_email"], - "course_id": site1_data["course"]["id"], - "mode": "masters", - "force": 1, - } - headers = { - "Authorization": f"Bearer {site1_data['token']}", - "Host": site1_data["host"], - } - request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" - - response = requests.post(request_url, data=data, headers=headers, timeout=10) - - self.assertEqual(response.status_code, 400) - - def test_delete_valid_enrollment(self): - # pylint: disable=invalid-name - """ - Delete a valid enrollment - """ - create_enrollment(self.data) - site1_data = self.data["site1_data"] - data = { - "email": site1_data["user_email"], - "course_id": site1_data["course"]["id"], - } - headers = { - "Authorization": f"Bearer {site1_data['token']}", - "Host": site1_data["host"], - } - request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" - - response = requests.delete(request_url, data=data, headers=headers, timeout=10) - - self.assertEqual(response.status_code, 204) - - def test_delete_invalid_enrollment(self): - # pylint: disable=invalid-name - """ - Delete a invalid enrollment(doesn't exist) - """ - delete_enrollment(self.data) - site1_data = self.data["site1_data"] - data = { - "email": site1_data["user_email"], - "course_id": site1_data["course"]["id"], - } - headers = { - "Authorization": f"Bearer {site1_data['token']}", - "Host": site1_data["host"], - } - request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" - - response = requests.delete(request_url, data=data, headers=headers, timeout=10) - - self.assertEqual(response.status_code, 404) - - def test_delete_invalid_enrollment_for_site(self): - # pylint: disable=invalid-name - """ - Delete a invalid enrollment (enrollment from other site) - """ - create_enrollment(self.data) - site1_data = self.data["site1_data"] - site2_data = self.data["site2_data"] - data = { - "email": site1_data["user_email"], - "course_id": site1_data["course"]["id"], - } - headers = { - "Authorization": f"Bearer {site2_data['token']}", - "Host": site2_data["host"], - } - request_url = f"{site2_data['base_url']}/{self.data['endpoint']}" - - response = requests.delete(request_url, data=data, headers=headers, timeout=10) - - self.assertEqual(response.status_code, 404) - - def test_update_valid_enrollment_change_is_active(self): - # pylint: disable=invalid-name - """ - Update an existing enrollment; change is_active flag - """ - create_enrollment(self.data) - site1_data = self.data["site1_data"] - data = { - "email": site1_data["user_email"], - "course_id": site1_data["course"]["id"], - "is_active": False, - "mode": site1_data["course"]["mode"], - } - headers = { - "Authorization": f"Bearer {site1_data['token']}", - "Host": site1_data["host"], - } - expected_response = { - "user": site1_data["user_id"], - "is_active": False, - "course_id": data["course_id"], - } - request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" - - response = requests.put(request_url, data=data, headers=headers, timeout=10) - response_content = response.json() - - self.assertEqual(response.status_code, 200) - self.assertDictContainsSubset(expected_response, response_content) - - # NOTE: Mode changes are not working correctly on devstack - def test_update_valid_enrollment_change_valid_mode(self): - # pylint: disable=invalid-name - """ - Update an existing enrollment; change mode - """ - create_enrollment(self.data) - site1_data = self.data["site1_data"] - data = { - "email": site1_data["user_email"], - "course_id": site1_data["course"]["id"], - "is_active": True, - "mode": "honor", - } - headers = { - "Authorization": f"Bearer {site1_data['token']}", - "Host": site1_data["host"], - } - expected_response = { - "user": site1_data["user_id"], - "is_active": True, - "course_id": data["course_id"], - "mode": "honor", - } - request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" - - response = requests.put(request_url, data=data, headers=headers, timeout=10) - response_content = response.json() - - self.assertEqual(response.status_code, 200) - self.assertDictContainsSubset(expected_response, response_content) - - def test_update_valid_enrollment_change_invalid_mode(self): - # pylint: disable=invalid-name - """ - Update an existing enrollment; change to invalid mode - """ - create_enrollment(self.data) - site1_data = self.data["site1_data"] - data = { - "email": site1_data["user_email"], - "course_id": site1_data["course"]["id"], - "is_active": True, - "mode": "masters", - } - headers = { - "Authorization": f"Bearer {site1_data['token']}", - "Host": site1_data["host"], - } - request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" - - response = requests.put(request_url, data=data, headers=headers, timeout=10) - - self.assertEqual(response.status_code, 400) - - def test_update_invalid_enrollment_change_valid_mode(self): - # pylint: disable=invalid-name - """ - Update an non-existent enrollment; change mode - """ - delete_enrollment(self.data) - site1_data = self.data["site1_data"] - data = { - "email": site1_data["user_email"], - "course_id": site1_data["course"]["id"], - "is_active": True, - "mode": "honor", - } - headers = { - "Authorization": f"Bearer {site1_data['token']}", - "Host": site1_data["host"], - } - request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" - - response = requests.put(request_url, data=data, headers=headers, timeout=10) - - self.assertEqual(response.status_code, 202) - - def test_update_invalid_enrollment_change_is_active(self): - # pylint: disable=invalid-name - """ - Update an non-existent enrollment; change is_active flag - """ - delete_enrollment(self.data) - site1_data = self.data["site1_data"] - data = { - "email": site1_data["user_email"], - "course_id": site1_data["course"]["id"], - "is_active": False, - "mode": "audit", - } - headers = { - "Authorization": f"Bearer {site1_data['token']}", - "Host": site1_data["host"], - } - request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" - - response = requests.put(request_url, data=data, headers=headers, timeout=10) - - self.assertEqual(response.status_code, 202) - - def test_update_valid_enrollment_change_is_active_force_post(self): - # pylint: disable=invalid-name - """ - Update an existing enrollment using POST with force=True; - change is_active flag - """ - create_enrollment(self.data) - site1_data = self.data["site1_data"] - data = { - "email": site1_data["user_email"], - "course_id": site1_data["course"]["id"], - "is_active": False, - "mode": site1_data["course"]["mode"], - "force": True, - } - headers = { - "Authorization": f"Bearer {site1_data['token']}", - "Host": site1_data["host"], - } - expected_response = { - "username": site1_data["user_id"], - "is_active": False, - "course_id": data["course_id"], - } - request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" - - response = requests.post(request_url, data=data, headers=headers, timeout=10) - response_content = response.json() - - self.assertEqual(response.status_code, 200) - self.assertDictContainsSubset(expected_response, response_content) - - # NOTE: Mode changes are not working correctly on devstack - def test_update_valid_enrollment_change_valid_mode_force_post(self): - # pylint: disable=invalid-name - """ - Update an existing enrollment; change mode - """ - create_enrollment(self.data) - site1_data = self.data["site1_data"] - data = { - "email": site1_data["user_email"], - "course_id": site1_data["course"]["id"], - "is_active": True, - "mode": "honor", - "force": True, - } - headers = { - "Authorization": f"Bearer {site1_data['token']}", - "Host": site1_data["host"], - } - expected_response = { - "user": site1_data["user_id"], - "is_active": True, - "course_id": data["course_id"], - "mode": "honor", - } - request_url = f"{site1_data['base_url']}/{self.data['endpoint']}" - - response = requests.put(request_url, data=data, headers=headers, timeout=10) - response_content = response.json() - - self.assertEqual(response.status_code, 200) - self.assertDictContainsSubset(expected_response, response_content) - - -def create_enrollment(data): - """ - Auxiliary function to setUp test fixtures. Creates/enables a new enrollment. - - :param data: dictionary with all the parameters needed to create an enrollment. - """ - req_data = { - "email": data["site1_data"]["user_email"], - "course_id": data["site1_data"]["course"]["id"], - "mode": data["site1_data"]["course"]["mode"], - } - headers = { - "Authorization": f"Bearer {data['site1_data']['token']}", - "Host": data["site1_data"]["host"], - } - request_url = f"{data['site1_data']['base_url']}/{data['endpoint']}" - response = requests.post(request_url, data=req_data, headers=headers, timeout=10) - response.raise_for_status() - - -def delete_enrollment(data): - """ - Auxiliary function to setUp test fixtures. Deletes an enrollment if exists. - - :param data: dictionary with all the parameters needed to delete an enrollment. - """ - req_data = { - "email": data["site1_data"]["user_email"], - "course_id": data["site1_data"]["course"]["id"], - } - headers = { - "Authorization": f"Bearer {data['site1_data']['token']}", - "Host": data["site1_data"]["host"], - } - request_url = f"{data['site1_data']['base_url']}/{data['endpoint']}" - response = requests.delete(request_url, data=req_data, headers=headers, timeout=10) - if response.status_code == 404: - return - response.raise_for_status() From 69692fa07ef30e5dffdc668ef586b731ab72f569 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Fri, 27 Sep 2024 11:42:05 -0500 Subject: [PATCH 23/32] test: check the applications state after each update --- .../api/v1/tests/integration/test_views.py | 64 ++++++++++++++++--- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/eox_core/api/v1/tests/integration/test_views.py b/eox_core/api/v1/tests/integration/test_views.py index 6a8a8c321..b88a9c446 100644 --- a/eox_core/api/v1/tests/integration/test_views.py +++ b/eox_core/api/v1/tests/integration/test_views.py @@ -5,6 +5,8 @@ # pylint: disable=too-many-lines from __future__ import annotations +from copy import deepcopy + import ddt import requests from django.conf import settings as ds @@ -21,12 +23,9 @@ ENROLLMENT_URL = f"{settings['EOX_CORE_API_BASE']}{reverse('eox-api:eox-api:edxapp-enrollment')}" -def get_access_token(tenant_base_url: str) -> str: +def get_access_token() -> str: """ - Get an access token for a tenant. - - Args: - tenant_base_url (str): The tenant base URL. + Get an access token for all requests in the test suite. Returns: str: The access token. @@ -36,11 +35,14 @@ def get_access_token(tenant_base_url: str) -> str: "client_secret": settings["CLIENT_SECRET"], "grant_type": "client_credentials", } - url = f"{tenant_base_url}/oauth2/access_token/" + url = f"http://{settings['LMS_BASE']}/oauth2/access_token/" response = requests.post(url, data=data, timeout=settings["API_TIMEOUT"]) return response.json()["access_token"] +ACCESS_TOKEN = get_access_token() + + # pylint: disable=too-many-arguments def make_request( tenant: dict, @@ -68,8 +70,7 @@ def make_request( """ headers = {"Host": tenant["domain"]} if with_auth: - access_token = get_access_token(tenant["base_url"]) - headers["Authorization"] = f"Bearer {access_token}" + headers["Authorization"] = f"Bearer {ACCESS_TOKEN}" full_url = f"{tenant['base_url']}/{url}" method = method.upper() @@ -463,6 +464,7 @@ def test_create_enrollment_success(self, param: str, force_value: bool) -> None: Expected result: - The status code is 200. - The response indicates the enrollment was created successfully. + - The enrollment is created in the tenant with the provided data. """ enrollment_data = { param: self.user[param], @@ -480,6 +482,8 @@ 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) + self.assertEqual(enrollment_response.status_code, status.HTTP_200_OK) @ddt.data( ("mode", {"mode": ["This field is required."]}), @@ -497,12 +501,14 @@ def test_create_enrollment_missing_required_fields(self, param: str, error: list Expected result: - The status code is 400. - The response contains a message about the missing field. + - The enrollment is not created in the tenant. """ enrollment_data = { "username": self.user["username"], "mode": self.mode, "course_id": self.course_id, } + enrollment_data_copy = deepcopy(enrollment_data) enrollment_data.pop(param) response = self.create_enrollment(self.tenant_x, enrollment_data) @@ -510,6 +516,8 @@ 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) + self.assertEqual(enrollment_response.status_code, status.HTTP_404_NOT_FOUND) @ddt.data( ("email", "user-not-found"), @@ -540,6 +548,8 @@ 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) + self.assertEqual(enrollment_response.status_code, status.HTTP_404_NOT_FOUND) @ddt.data("email", "username") def test_create_valid_course_mode_invalid_user_for_tenant(self, param: str) -> None: @@ -570,6 +580,8 @@ 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) + self.assertEqual(enrollment_response.status_code, status.HTTP_404_NOT_FOUND) @ddt.data("email", "username") def test_create_valid_user_mode_invalid_course(self, param: str) -> None: @@ -596,6 +608,8 @@ 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) + self.assertEqual(enrollment_response.status_code, status.HTTP_404_NOT_FOUND) @ddt.data("email", "username") def test_create_valid_user_mode_invalid_course_for_tenant(self, param: str) -> None: @@ -609,6 +623,7 @@ def test_create_valid_user_mode_invalid_course_for_tenant(self, param: str) -> N Expected result: - The status code is 400. - The response contains an error message about the course not found on the tenant. + - The enrollment is not created in the tenant. """ enrollment_data = { param: self.user[param], @@ -622,6 +637,8 @@ 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.course_id}"]) + enrollment_response = self.get_enrollment(self.tenant_x, data=enrollment_data) + self.assertEqual(enrollment_response.status_code, status.HTTP_404_NOT_FOUND) @ddt.data("email", "username") def test_create_valid_user_course_unavailable_mode(self, param: str) -> None: @@ -635,6 +652,7 @@ def test_create_valid_user_course_unavailable_mode(self, param: str) -> None: Expected result: - The status code is 400. - The response contains an error message about the mode not found. + - The enrollment is not created in the tenant. """ enrollment_data = { param: self.user[param], @@ -648,6 +666,8 @@ 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) + self.assertEqual(enrollment_response.status_code, status.HTTP_404_NOT_FOUND) @ddt.data("email", "username") def test_force_create_valid_user_course_unavailable_mode(self, param: str) -> None: @@ -682,6 +702,8 @@ 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) + self.assertEqual(enrollment_response.status_code, status.HTTP_200_OK) @ddt.data("email", "username") def test_get_enrollment_success(self, param: str) -> None: @@ -789,6 +811,8 @@ def test_delete_enrollment_success(self, param: str) -> None: response = self.delete_enrollment(self.tenant_x, data=enrollment_data) self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + enrollment_response = self.get_enrollment(self.tenant_x, data=enrollment_data) + self.assertEqual(enrollment_response.status_code, status.HTTP_404_NOT_FOUND) @ddt.data("email", "username") def test_delete_enrollment_does_not_exist(self, param: str) -> None: @@ -830,6 +854,7 @@ def test_delete_invalid_enrollment_for_tenant(self, param: str) -> None: Expected result: - The status code is 404. - The response contains an error message about the user not found on the tenant. + - The enrollment is not deleted in the tenant. """ enrollment_data = { param: self.user[param], @@ -847,6 +872,8 @@ 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) + self.assertEqual(enrollment_response.status_code, status.HTTP_200_OK) @ddt.data("email", "username") def test_update_valid_enrollment_change_is_active_mode_field(self, param: str) -> None: @@ -879,6 +906,11 @@ 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_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.data("email", "username") def test_update_valid_enrollment_update_unavailable_mode(self, param: str) -> None: @@ -892,6 +924,7 @@ def test_update_valid_enrollment_update_unavailable_mode(self, param: str) -> No Expected result: - The status code is 400. - The response contains an error message about the mode not found. + - The enrollment is not updated in the tenant. """ enrollment_data = { param: self.user[param], @@ -899,6 +932,7 @@ def test_update_valid_enrollment_update_unavailable_mode(self, param: str) -> No "is_active": True, "mode": self.mode, } + enrollment_data_copy = deepcopy(enrollment_data) self.create_enrollment(self.tenant_x, enrollment_data) enrollment_data["mode"] = "masters" @@ -908,6 +942,10 @@ def test_update_valid_enrollment_update_unavailable_mode(self, param: str) -> No 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_data = enrollment_response.json() + self.assertEqual(enrollment_response.status_code, status.HTTP_200_OK) + self.assertEqual(enrollment_response_data["mode"], self.mode) @ddt.data("email", "username") def test_update_enrollment_does_not_exist(self, param: str) -> None: @@ -934,6 +972,8 @@ def test_update_enrollment_does_not_exist(self, param: str) -> None: 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) + self.assertEqual(enrollment_response.status_code, status.HTTP_404_NOT_FOUND) @ddt.data("email", "username") def test_update_valid_enrollment_using_force_flag(self, param: str) -> None: @@ -948,7 +988,8 @@ def test_update_valid_enrollment_using_force_flag(self, param: str) -> None: Expected result: - The status code is 200. - - The enrollment is updated successfully in the tenant with the provided data. + - The response indicates the enrollment was updated successfully. + - The enrollment is updated in the tenant with the provided data. """ enrollment_data = { param: self.user[param], @@ -969,6 +1010,11 @@ 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_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"]) class TestInfoView(BaseAPIIntegrationTest): From 658b2fbf443af1ca1c148b4fe53322d41a83282b Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Fri, 27 Sep 2024 11:52:38 -0500 Subject: [PATCH 24/32] chore: remove tutor command to import demo course in shell file --- scripts/execute_integration_tests.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/execute_integration_tests.sh b/scripts/execute_integration_tests.sh index 531e4aab9..3408c29b2 100644 --- a/scripts/execute_integration_tests.sh +++ b/scripts/execute_integration_tests.sh @@ -1,3 +1,2 @@ #!/bin/bash -tutor local do importdemocourse make run-integration-tests From 8914e37abe313d3e46e0a69fd16385819e572e4a Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Fri, 27 Sep 2024 11:54:53 -0500 Subject: [PATCH 25/32] ci: update workflow to use new version of integration test in tutor action --- .github/workflows/integration-test.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 640f875a4..267d4b50b 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -7,19 +7,15 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - include: - - tutor_version: '<18.0.0' - course_id: 'course-v1:edX+DemoX+Demo_Course' - - tutor_version: '<19.0.0' - course_id: 'course-v1:OpenedX+DemoX+DemoCourse' + tutor_version: ['<18.0.0', '<19.0.0'] steps: - name: Run Integration Tests uses: eduNEXT/integration-test-in-tutor@mjh/run-integration-tests-outside-container with: tutor_version: ${{ matrix.tutor_version }} app_name: 'eox-core' - openedx_extra_pip_requeriments: 'eox-tenant' + openedx_extra_pip_requirements: 'eox-tenant' shell_file_to_run: 'scripts/execute_integration_tests.sh' fixtures_file: 'fixtures/initial_data.json' - env: - COURSE_ID: ${{ matrix.course_id }} + openedx_imports_test_file_path: 'eox_core/edxapp_wrapper/tests/integration/test_backends.py' + openedx_imports_test_function_name: 'test_current_settings_code_imports' From a7bae952580f4da2a3a4badf241cc624084138e3 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Fri, 27 Sep 2024 11:58:59 -0500 Subject: [PATCH 26/32] chore: use demo_course_id variable name instead course_id --- .../api/v1/tests/integration/test_views.py | 40 +++++++++---------- eox_core/settings/test.py | 2 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/eox_core/api/v1/tests/integration/test_views.py b/eox_core/api/v1/tests/integration/test_views.py index b88a9c446..f9cf9693d 100644 --- a/eox_core/api/v1/tests/integration/test_views.py +++ b/eox_core/api/v1/tests/integration/test_views.py @@ -439,7 +439,7 @@ class TestEnrollmentAPIIntegration(BaseAPIIntegrationTest, UsersAPIRequestMixin, def setUp(self) -> None: """Set up the test suite""" super().setUp() - self.course_id = settings["COURSE_ID"] + self.demo_course_id = settings["DEMO_COURSE_ID"] self.mode = "audit" self.user = next(FAKE_USER_DATA) self.create_user(self.tenant_x, self.user) @@ -468,7 +468,7 @@ def test_create_enrollment_success(self, param: str, force_value: bool) -> None: """ enrollment_data = { param: self.user[param], - "course_id": self.course_id, + "course_id": self.demo_course_id, "mode": self.mode, "force": force_value, } @@ -506,7 +506,7 @@ def test_create_enrollment_missing_required_fields(self, param: str, error: list enrollment_data = { "username": self.user["username"], "mode": self.mode, - "course_id": self.course_id, + "course_id": self.demo_course_id, } enrollment_data_copy = deepcopy(enrollment_data) enrollment_data.pop(param) @@ -538,7 +538,7 @@ def test_create_valid_course_mode_invalid_user(self, param: str, value: str) -> """ enrollment_data = { param: value, - "course_id": self.course_id, + "course_id": self.demo_course_id, "mode": self.mode, } @@ -568,7 +568,7 @@ def test_create_valid_course_mode_invalid_user_for_tenant(self, param: str) -> N self.create_user(self.tenant_y, user_data) enrollment_data = { param: user_data[param], - "course_id": self.course_id, + "course_id": self.demo_course_id, "mode": self.mode, } @@ -627,7 +627,7 @@ def test_create_valid_user_mode_invalid_course_for_tenant(self, param: str) -> N """ enrollment_data = { param: self.user[param], - "course_id": self.course_id, + "course_id": self.demo_course_id, "mode": self.mode, } @@ -636,7 +636,7 @@ def test_create_valid_user_mode_invalid_course_for_tenant(self, param: str) -> N response_data = response.json() 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.course_id}"]) + 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) self.assertEqual(enrollment_response.status_code, status.HTTP_404_NOT_FOUND) @@ -656,7 +656,7 @@ def test_create_valid_user_course_unavailable_mode(self, param: str) -> None: """ enrollment_data = { param: self.user[param], - "course_id": self.course_id, + "course_id": self.demo_course_id, "mode": "masters", } @@ -688,7 +688,7 @@ def test_force_create_valid_user_course_unavailable_mode(self, param: str) -> No """ enrollment_data = { param: self.user[param], - "course_id": self.course_id, + "course_id": self.demo_course_id, "mode": "masters", "force": True, } @@ -720,7 +720,7 @@ def test_get_enrollment_success(self, param: str) -> None: """ enrollment_data = { param: self.user[param], - "course_id": self.course_id, + "course_id": self.demo_course_id, "mode": self.mode, } self.create_enrollment(self.tenant_x, enrollment_data) @@ -750,7 +750,7 @@ def test_get_enrollment_does_not_exist(self, param: str) -> None: """ enrollment_data = { param: self.user[param], - "course_id": self.course_id, + "course_id": self.demo_course_id, } response = self.get_enrollment(self.tenant_x, data=enrollment_data) @@ -773,7 +773,7 @@ def test_get_enrollment_not_found_in_tenant(self, param: str) -> None: """ enrollment_data = { param: self.user[param], - "course_id": self.course_id, + "course_id": self.demo_course_id, "mode": self.mode, } self.create_enrollment(self.tenant_x, enrollment_data) @@ -803,7 +803,7 @@ def test_delete_enrollment_success(self, param: str) -> None: """ enrollment_data = { param: self.user[param], - "course_id": self.course_id, + "course_id": self.demo_course_id, "mode": self.mode, } self.create_enrollment(self.tenant_x, enrollment_data) @@ -829,7 +829,7 @@ def test_delete_enrollment_does_not_exist(self, param: str) -> None: """ enrollment_data = { param: self.user[param], - "course_id": self.course_id, + "course_id": self.demo_course_id, } response = self.delete_enrollment(self.tenant_x, data=enrollment_data) @@ -839,7 +839,7 @@ def test_delete_enrollment_does_not_exist(self, param: str) -> None: self.assertIn("detail", response_data) self.assertEqual( response_data["detail"], - f"No enrollment found for user: `{self.user['username']}` on course_id `{self.course_id}`", + f"No enrollment found for user: `{self.user['username']}` on course_id `{self.demo_course_id}`", ) @ddt.data("email", "username") @@ -858,7 +858,7 @@ def test_delete_invalid_enrollment_for_tenant(self, param: str) -> None: """ enrollment_data = { param: self.user[param], - "course_id": self.course_id, + "course_id": self.demo_course_id, "mode": self.mode, } self.create_enrollment(self.tenant_x, enrollment_data) @@ -890,7 +890,7 @@ def test_update_valid_enrollment_change_is_active_mode_field(self, param: str) - """ enrollment_data = { param: self.user[param], - "course_id": self.course_id, + "course_id": self.demo_course_id, "is_active": False, "mode": self.mode, } @@ -928,7 +928,7 @@ def test_update_valid_enrollment_update_unavailable_mode(self, param: str) -> No """ enrollment_data = { param: self.user[param], - "course_id": self.course_id, + "course_id": self.demo_course_id, "is_active": True, "mode": self.mode, } @@ -962,7 +962,7 @@ def test_update_enrollment_does_not_exist(self, param: str) -> None: """ enrollment_data = { param: self.user[param], - "course_id": self.course_id, + "course_id": self.demo_course_id, "is_active": False, "mode": self.mode, } @@ -993,7 +993,7 @@ def test_update_valid_enrollment_using_force_flag(self, param: str) -> None: """ enrollment_data = { param: self.user[param], - "course_id": self.course_id, + "course_id": self.demo_course_id, "is_active": False, "mode": self.mode, "force": True, diff --git a/eox_core/settings/test.py b/eox_core/settings/test.py index 0a9329e04..02387b9bd 100644 --- a/eox_core/settings/test.py +++ b/eox_core/settings/test.py @@ -112,5 +112,5 @@ def plugin_settings(settings): # pylint: disable=function-redefined "API_TIMEOUT": 5, "CLIENT_ID": "client_id", "CLIENT_SECRET": "client_secret", - "COURSE_ID": os.environ.get("COURSE_ID", "course-v1:OpenedX+DemoX+DemoCourse") + "DEMO_COURSE_ID": os.environ.get("DEMO_COURSE_ID", "course-v1:OpenedX+DemoX+DemoCourse") } From 6ba8bb037018840e6dd118d65d54d27d82e2a253 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Fri, 27 Sep 2024 13:32:27 -0500 Subject: [PATCH 27/32] chore: avoid extra changes in fixtures --- fixtures/initial_data.json | 36 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/fixtures/initial_data.json b/fixtures/initial_data.json index 987be7622..5d417adf6 100644 --- a/fixtures/initial_data.json +++ b/fixtures/initial_data.json @@ -63,8 +63,8 @@ "model": "organizations.organization", "pk": 3, "fields": { - "created": "2024-09-11T17:15:35.694Z", - "modified": "2024-09-11T17:15:35.694Z", + "created": "2024-09-11T17:15:30.708Z", + "modified": "2024-09-11T17:15:30.708Z", "name": "OpenedX", "short_name": "openedx", "description": null, @@ -90,19 +90,10 @@ "pk": 1, "fields": { "external_key": "tenant-x-key", - "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": {}, + "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 ] @@ -113,17 +104,10 @@ "pk": 2, "fields": { "external_key": "tenant-y-key", - "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": {}, + "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": "{}", "organizations": [ 2 ] From 543ab5175aa46b1e50c4b6093d6fcd309f9f576e Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Fri, 27 Sep 2024 14:04:47 -0500 Subject: [PATCH 28/32] Revert "chore: avoid extra changes in fixtures" This reverts commit 6ba8bb037018840e6dd118d65d54d27d82e2a253. --- fixtures/initial_data.json | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/fixtures/initial_data.json b/fixtures/initial_data.json index 5d417adf6..987be7622 100644 --- a/fixtures/initial_data.json +++ b/fixtures/initial_data.json @@ -63,8 +63,8 @@ "model": "organizations.organization", "pk": 3, "fields": { - "created": "2024-09-11T17:15:30.708Z", - "modified": "2024-09-11T17:15:30.708Z", + "created": "2024-09-11T17:15:35.694Z", + "modified": "2024-09-11T17:15:35.694Z", "name": "OpenedX", "short_name": "openedx", "description": null, @@ -90,10 +90,19 @@ "pk": 1, "fields": { "external_key": "tenant-x-key", - "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": "{}", + "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 ] From cb6ce0b3cff311b21050f538520ffae136f66fed Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Fri, 27 Sep 2024 14:55:07 -0500 Subject: [PATCH 29/32] chore: use a string instead of object in fixtures --- fixtures/initial_data.json | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/fixtures/initial_data.json b/fixtures/initial_data.json index 987be7622..0e9a8c2b6 100644 --- a/fixtures/initial_data.json +++ b/fixtures/initial_data.json @@ -90,19 +90,10 @@ "pk": 1, "fields": { "external_key": "tenant-x-key", - "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": {}, + "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": "{}", "organizations": [ 1 ] @@ -113,17 +104,10 @@ "pk": 2, "fields": { "external_key": "tenant-y-key", - "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": {}, + "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": "{}", "organizations": [ 2 ] From 5a98fd81246ecd1f4f665c4b2dfc62c1ace8261d Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Fri, 27 Sep 2024 15:01:22 -0500 Subject: [PATCH 30/32] ci: add tutor nightly in integration tests workflow --- .github/workflows/integration-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 267d4b50b..b9fff2795 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - tutor_version: ['<18.0.0', '<19.0.0'] + tutor_version: ['<18.0.0', '<19.0.0', 'nightly'] steps: - name: Run Integration Tests uses: eduNEXT/integration-test-in-tutor@mjh/run-integration-tests-outside-container From 88ee4f32df507a3e56ca8a44fe1cab7e3d162cce Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Fri, 27 Sep 2024 15:12:15 -0500 Subject: [PATCH 31/32] fix: fix typo --- fixtures/initial_data.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fixtures/initial_data.json b/fixtures/initial_data.json index 0e9a8c2b6..0194ce4f7 100644 --- a/fixtures/initial_data.json +++ b/fixtures/initial_data.json @@ -90,7 +90,7 @@ "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}", + "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": "{}", From c5607d3997414780f497ca7324e98051e9ed5a92 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Fri, 27 Sep 2024 15:15:23 -0500 Subject: [PATCH 32/32] chore: update dates in fixtures --- fixtures/initial_data.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fixtures/initial_data.json b/fixtures/initial_data.json index 0194ce4f7..16f2bf8bd 100644 --- a/fixtures/initial_data.json +++ b/fixtures/initial_data.json @@ -63,8 +63,8 @@ "model": "organizations.organization", "pk": 3, "fields": { - "created": "2024-09-11T17:15:35.694Z", - "modified": "2024-09-11T17:15:35.694Z", + "created": "2024-09-11T17:15:30.708Z", + "modified": "2024-09-11T17:15:30.708Z", "name": "OpenedX", "short_name": "openedx", "description": null,