Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add integration tests for the support api AP-1527 #300

Merged
merged 34 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
dd29714
refactor: move get_access_token, make_request, and BaseIntegrationTes…
BryanttV Oct 30, 2024
84d6d8e
chore: add new fake users to FAKE_USERS_DATA
BryanttV Oct 30, 2024
1bc2968
chore: use add-plugins-step branch of integration tests action
BryanttV Oct 30, 2024
ce13bac
chore: remove nightly from integration tests workflow
BryanttV Oct 30, 2024
634c326
fix: move integration test utils to separate file
BryanttV Oct 30, 2024
e276525
test: add support api integration tests
BryanttV Oct 30, 2024
d9bae9d
refactor: move fake_users.py to tests/integration/data
BryanttV Oct 31, 2024
363f073
test: add more integration tests for support api
BryanttV Oct 31, 2024
8bd0273
chore: update import of fake_users
BryanttV Oct 31, 2024
6641c59
test: add test for update username and create oauth application
BryanttV Oct 31, 2024
d360a07
chore: update users to use activate_user=True
BryanttV Nov 1, 2024
a28a892
chore: fix typo in function
BryanttV Nov 1, 2024
3a1a31f
chore: remove failed test
BryanttV Nov 1, 2024
b6a9b18
chore: add .coveragerc file to exclude specific paths from coverage r…
BryanttV Nov 1, 2024
47d6c45
docs: update class docstring
BryanttV Nov 1, 2024
b876b1d
chore: remove print statements
BryanttV Nov 1, 2024
3923dc5
chore: update username and email of users
BryanttV Nov 1, 2024
8e06e32
chore: remove .coveragerc
BryanttV Nov 6, 2024
69920b2
style: format code
BryanttV Nov 6, 2024
e7600a9
chore: only execute support api tests
BryanttV Nov 6, 2024
545e70e
ci: use branch to print lms logs
BryanttV Nov 6, 2024
8bc0af3
chore: add command for populate retirement states in integration test…
BryanttV Nov 6, 2024
b2ca8e9
chore: remove command for populate retirement states in integration t…
BryanttV Nov 6, 2024
fdd7153
chore: remove print statements
BryanttV Nov 6, 2024
0d52639
docs: improve test docstring
BryanttV Nov 7, 2024
978532b
test: add tests for delete and update user of another tenant
BryanttV Nov 7, 2024
b3f8ea6
chore: remove ignore flag for api tests
BryanttV Nov 7, 2024
4ef5d8e
ci: add tutor extra commands shell file
BryanttV Nov 7, 2024
c29dc78
chore: use new user backend
BryanttV Nov 12, 2024
d69c50e
test: check if the user exists in the corresponding tenant
BryanttV Nov 12, 2024
1a7b19d
ci: use bav/add-custom-plugins-step branch in integration tests workflow
BryanttV Nov 12, 2024
359f038
ci: use main branch in integration tests workflow
BryanttV Nov 12, 2024
be370e2
chore: use new user backend
BryanttV Nov 12, 2024
586f62c
ci: add nightly version in integration tests workflow
BryanttV Nov 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
tutor_version: ['<18.0.0', '<19.0.0', 'nightly']
tutor_version: ['<18.0.0', '<19.0.0', "nightly"]
steps:
- name: Run Integration Tests
uses: eduNEXT/integration-test-in-tutor@main
with:
tutor_version: ${{ matrix.tutor_version }}
tutor_plugins: 'forum'
app_name: 'eox-core'
openedx_extra_pip_requirements: 'eox-tenant'
shell_file_to_run: 'scripts/execute_integration_tests.sh'
fixtures_file: 'fixtures/initial_data.json'
openedx_imports_test_file_path: 'eox_core/edxapp_wrapper/tests/integration/test_backends.py'
tutor_extra_commands_path: 'scripts/execute_tutor_extra_commands.sh'
356 changes: 356 additions & 0 deletions eox_core/api/support/v1/tests/integration/test_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,356 @@
"""Support API integration tests."""

from __future__ import annotations

import ddt
import requests
from django.conf import settings as ds
from django.urls import reverse
from rest_framework import status

from eox_core.api.v1.tests.integration.test_views import UsersAPIRequestMixin
from eox_core.tests.integration.data.fake_users import FAKE_USER_DATA
from eox_core.tests.integration.utils import BaseIntegrationTest, make_request

settings = ds.INTEGRATION_TEST_SETTINGS


class SupportAPIRequestMixin:
"""Mixin class for the Pre Enrollments API request methods."""

DELETE_USER_URL = f"{settings['EOX_CORE_API_BASE']}{reverse('eox-support-api:eox-support-api:edxapp-user')}"
UPDATE_USERNAME_URL = (
f"{settings['EOX_CORE_API_BASE']}{reverse('eox-support-api:eox-support-api:edxapp-replace-username')}"
)
OAUTH_APP_URL = (
f"{settings['EOX_CORE_API_BASE']}{reverse('eox-support-api:eox-support-api:edxapp-oauth-application')}"
)

def delete_user(self, tenant: dict, params: dict | None = None) -> requests.Response:
"""
Delete an edxapp user in a tenant.

Args:
tenant (dict): The tenant data.
params (dict, optional): The query parameters for the request.

Returns:
requests.Response: The response object.
"""
return make_request(tenant, "DELETE", url=self.DELETE_USER_URL, params=params)

def update_username(self, tenant: dict, params: dict | None = None, data: dict | None = None) -> requests.Response:
"""
Update an edxapp user's username in a tenant.

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 make_request(tenant, "PATCH", url=self.UPDATE_USERNAME_URL, params=params, data=data)

def create_oauth_application(self, tenant: dict, data: dict | None = None) -> requests.Response:
"""
Create an oauth application in a tenant.

Args:
tenant (dict): The tenant data.
data (dict, optional): The body data for the request.

Returns:
requests.Response: The response object.
"""
return make_request(tenant, "POST", url=self.OAUTH_APP_URL, json=data)


@ddt.ddt
class TestEdxAppUserAPIIntegration(
SupportAPIRequestMixin,
BaseIntegrationTest,
UsersAPIRequestMixin,
):
"""Integration tests for the EdxApp User API."""

@ddt.data("username", "email")
def test_delete_user_in_tenant_success(self, query_param: str) -> None:
"""
Test delete an edxapp user in a tenant.

Open edX definitions tested:
- `get_edxapp_user`
- `delete_edxapp_user`

Expected result:
- The status code is 200.
- The response indicates the user was removed successfully from the tenant.
- The user is not found in the tenant.
"""
data = next(FAKE_USER_DATA)
self.create_user(self.tenant_x, data)

response = self.delete_user(self.tenant_x, {query_param: data[query_param]})
response_data = response.json()
get_response = self.get_user(self.tenant_x, {"email": data["email"]})
get_response_data = get_response.json()

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response_data, f"The user {data['username']} <{data['email']}> has been removed")
self.assertEqual(get_response.status_code, status.HTTP_404_NOT_FOUND)
self.assertEqual(
get_response_data,
{"detail": f"No user found by {{'email': '{data['email']}'}} on site {self.tenant_x['domain']}."},
)

@ddt.data(
("username", "user-not-found"),
("email", "[email protected]"),
)
@ddt.unpack
def test_delete_user_in_tenant_not_found(self, query_param: str, value: str) -> None:
"""
Test delete an edxapp user in a tenant that does not exist.

Open edX definitions tested:
- `get_edxapp_user`

Expected result:
- The status code is 404.
- The response indicates the user was not found in the tenant.
"""
response = self.delete_user(self.tenant_x, {query_param: value})
response_data = response.json()
mariajgrimaldi marked this conversation as resolved.
Show resolved Hide resolved

self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
self.assertEqual(
response_data["detail"],
f"No user found by {{'{query_param}': '{value}'}} on site {self.tenant_x['domain']}.",
)

@ddt.data("username", "email")
def test_delete_user_of_another_tenant(self, query_param: str) -> None:
"""
Test delete an edxapp user of another tenant.

Open edX definitions tested:
- `get_edxapp_user`

Expected result:
- The status code is 404.
- The response indicates the user was not found in the tenant.
"""
data = next(FAKE_USER_DATA)
self.create_user(self.tenant_x, data)

response = self.delete_user(self.tenant_y, {query_param: data[query_param]})
response_data = response.json()
get_response = self.get_user(self.tenant_x, {"username": data["username"]})

self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
magajh marked this conversation as resolved.
Show resolved Hide resolved
self.assertEqual(
response_data["detail"],
f"No user found by {{'{query_param}': '{data[query_param]}'}} on site {self.tenant_y['domain']}.",
)
self.assertEqual(get_response.status_code, status.HTTP_200_OK)

def test_delete_user_missing_required_fields(self) -> None:
"""
Test delete an edxapp user in a tenant without providing the username or email.

Expected result:
- The status code is 400.
- The response indicates the username or email is required.
"""
response = self.delete_user(self.tenant_x)
response_data = response.json()

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
BryanttV marked this conversation as resolved.
Show resolved Hide resolved
self.assertEqual(response_data, ["Email or username needed"])

@ddt.data("username", "email")
def test_update_username_in_tenant_success(self, query_param: str) -> None:
"""
Test update an edxapp user's username in a tenant.

Open edX definitions tested:
- `get_edxapp_user`
- `check_edxapp_account_conflicts`
- `replace_username_cs_user`
- `get_user_read_only_serializer`

Expected result:
- The status code is 200.
- The response indicates the username was updated successfully.
- The user is found in the tenant with the new username.
"""
data = next(FAKE_USER_DATA)
self.create_user(self.tenant_x, data)
new_username = f"new-username-{query_param}"

response = self.update_username(self.tenant_x, {query_param: data[query_param]}, {"new_username": new_username})
mariajgrimaldi marked this conversation as resolved.
Show resolved Hide resolved
response_data = response.json()
get_response = self.get_user(self.tenant_x, {"username": new_username})
get_response_data = get_response.json()

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response_data["username"], new_username)
self.assertEqual(get_response.status_code, status.HTTP_200_OK)
self.assertEqual(get_response_data["username"], new_username)

def test_update_username_in_tenant_not_found(self) -> None:
"""
Test update an edxapp user's username in a tenant that does not exist.

Open edX definitions tested:
- `get_edxapp_user`

Expected result:
- The status code is 404.
- The response indicates the user was not found in the tenant.
"""
response = self.update_username(
self.tenant_x,
{"username": "user-not-found"},
{"new_username": "new-username"},
)
response_data = response.json()

self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
self.assertEqual(
response_data["detail"],
f"No user found by {{'username': 'user-not-found'}} on site {self.tenant_x['domain']}.",
)

@ddt.data("username", "email")
def test_update_user_username_of_another_tenant(self, query_param: str) -> None:
"""
Test update an edxapp user's username of another tenant.

Open edX definitions tested:
- `get_edxapp_user`

Expected result:
- The status code is 404.
- The response indicates the user was not found in the tenant.
"""
data = next(FAKE_USER_DATA)
self.create_user(self.tenant_x, data)
new_username = f"new-username-{query_param}"

response = self.update_username(
self.tenant_y,
{query_param: data[query_param]},
{"new_username": new_username},
)
response_data = response.json()

self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
self.assertEqual(
response_data["detail"],
f"No user found by {{'{query_param}': '{data[query_param]}'}} on site {self.tenant_y['domain']}.",
)

def test_update_username_in_tenant_missing_params(self) -> None:
"""
Test update an edxapp user's username in a tenant without providing the username.

Expected result:
- The status code is 400.
- The response indicates the username is required.
"""
response = self.update_username(self.tenant_x)
response_data = response.json()

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response_data, ["Email or username needed"])

def test_update_username_in_tenant_missing_body(self) -> None:
"""
Test update an edxapp user's username in a tenant without providing the new username.

Expected result:
- The status code is 400.
- The response indicates the new username is required.
"""
data = next(FAKE_USER_DATA)
self.create_user(self.tenant_x, data)

response = self.update_username(self.tenant_x, params={"username": data["username"]})
response_data = response.json()

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response_data, {"new_username": ["This field is required."]})


@ddt.ddt
class TestOauthApplicationAPIIntegration(SupportAPIRequestMixin, BaseIntegrationTest, UsersAPIRequestMixin):
"""Integration tests for the Oauth Application API."""

@ddt.data(True, False)
def test_create_oauth_application_in_tenant_success(self, create_user: bool) -> None:
"""
Test create an oauth application in a tenant.

If the user exists, it will be used to create the oauth application.
If the user does not exist, it will be created and used to create the oauth application.

Open edX definitions tested:
- `get_edxapp_user`
- `create_edxapp_user`
- `UserSignupSource`

Expected result:
- The status code is 200.
- The response indicates the oauth application was created successfully.
"""
user_data = next(FAKE_USER_DATA)
if create_user:
self.create_user(self.tenant_x, user_data)
mariajgrimaldi marked this conversation as resolved.
Show resolved Hide resolved
data = {
"user": {
"fullname": user_data["fullname"],
"email": user_data["email"],
"username": user_data["username"],
"permissions": ["can_call_eox_core", "can_call_eox_tenant"],
},
"redirect_uris": f"http://{self.tenant_x['domain']}/",
"client_type": "confidential",
"authorization_grant_type": "client-credentials",
"name": "test-application",
"skip_authorization": True,
}

response = self.create_oauth_application(self.tenant_x, data)
response_data = response.json()

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response_data["user"], {"email": user_data["email"], "username": user_data["username"]})
self.assertEqual(response_data["redirect_uris"], f"http://{self.tenant_x['domain']}/")
self.assertEqual(response_data["client_type"], data["client_type"])
self.assertEqual(response_data["authorization_grant_type"], data["authorization_grant_type"])
self.assertEqual(response_data["name"], data["name"])
self.assertEqual(response_data["skip_authorization"], data["skip_authorization"])

def test_create_oauth_application_in_tenant_missing_required_fields(self) -> None:
"""
Test create an oauth application in a tenant without providing the required fields.

Expected result:
- The status code is 400.
- The response indicates the required fields are missing.
"""
response = self.create_oauth_application(self.tenant_x)
response_data = response.json()

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
response_data,
{
"user": ["This field is required."],
"client_type": ["This field is required."],
"authorization_grant_type": ["This field is required."],
},
)
Loading
Loading