-
-
Notifications
You must be signed in to change notification settings - Fork 694
PoC for E2E tests for protected routes authentication and mentorship tests #5454
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
base: main
Are you sure you want to change the base?
Changes from 13 commits
b4fcaeb
e9e4f38
0081d28
5e405e4
4bd6a49
21fcd50
605eab2
fdcec7e
514e0ec
343b890
e008605
e349b49
dbff05e
6083289
54d07b3
79e357a
a102d68
c5b9b31
6752f36
69121bf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| """E2E-only login view.""" | ||
|
|
||
| import json | ||
|
|
||
| from django.conf import settings | ||
| from django.contrib.auth import login | ||
| from django.http import Http404, HttpRequest, JsonResponse | ||
| from django.views.decorators.csrf import csrf_exempt | ||
| from django.views.decorators.http import require_POST | ||
|
|
||
| from apps.nest.models import User | ||
|
|
||
|
|
||
| @csrf_exempt | ||
|
Check failure on line 14 in backend/src/apps/nest/api/internal/views/e2e_login.py
|
||
|
Mr-Rahul-Paul marked this conversation as resolved.
Outdated
|
||
| @require_POST | ||
| def e2e_login(request: HttpRequest) -> JsonResponse: | ||
| """Log in a seeded e2e user and set the Django session cookie.""" | ||
| if not settings.IS_E2E_ENVIRONMENT: | ||
| raise Http404 | ||
|
|
||
| try: | ||
| payload = json.loads(request.body or b"{}") | ||
| except json.JSONDecodeError: | ||
| return JsonResponse({"message": "Invalid JSON.", "ok": False}, status=400) | ||
|
|
||
| username = (payload.get("username") or "").strip() | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
|
||
| if not username: | ||
| return JsonResponse({"message": "username is required.", "ok": False}, status=400) | ||
|
|
||
| try: | ||
| user = User.objects.get(username=username) | ||
| except User.DoesNotExist as exc: | ||
| raise Http404 from exc | ||
|
|
||
| login(request, user) | ||
|
|
||
| return JsonResponse({"ok": True, "username": username}) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| """Seed deterministic users for end-to-end tests.""" | ||
|
|
||
| from django.conf import settings | ||
| from django.contrib.contenttypes.models import ContentType | ||
| from django.core.management.base import BaseCommand, CommandError | ||
| from django.utils import timezone | ||
|
|
||
| from apps.core.utils import index | ||
| from apps.github.models.user import User as GithubUser | ||
| from apps.mentorship.models import Mentee, Mentor | ||
| from apps.nest.models import User as NestUser | ||
| from apps.owasp.models.entity_member import EntityMember | ||
| from apps.owasp.models.project import Project | ||
|
|
||
| E2E_USERS = ( | ||
| ("e2e-mentee", "mentee"), | ||
| ("e2e-mentor", "mentor"), | ||
| ("e2e-user", ""), | ||
| ) | ||
|
|
||
|
|
||
| class Command(BaseCommand): | ||
| help = "Seed e2e test users." | ||
|
|
||
| def handle(self, *_args, **_options) -> None: | ||
| """Create GitHub, Nest, and mentorship users for e2e tests.""" | ||
| if not settings.IS_E2E_ENVIRONMENT: | ||
| error_message = "This command can only run in the e2e environment." | ||
| raise CommandError(error_message) | ||
|
|
||
| now = timezone.now() | ||
| with index.disable_indexing(): | ||
| for login, role in E2E_USERS: | ||
| github_user, _ = GithubUser.objects.get_or_create( | ||
| login=login, | ||
| defaults={ | ||
| "created_at": now, | ||
| "email": f"{login}@example.com", | ||
| "name": login, | ||
| "node_id": f"e2e_node_{login}", | ||
| "updated_at": now, | ||
| }, | ||
| ) | ||
| nest_user, _ = NestUser.objects.get_or_create( | ||
| username=login, | ||
| defaults={ | ||
| "email": f"{login}@example.com", | ||
| "github_user": github_user, | ||
| }, | ||
| ) | ||
| if nest_user.github_user_id != github_user.id: | ||
| nest_user.github_user = github_user | ||
| nest_user.save(update_fields=["github_user"]) | ||
| if role == "mentor": | ||
| Mentor.objects.get_or_create( | ||
| github_user=github_user, | ||
| defaults={"nest_user": nest_user}, | ||
| ) | ||
| elif role == "mentee": | ||
| Mentee.objects.get_or_create( | ||
| github_user=github_user, | ||
| defaults={"nest_user": nest_user}, | ||
| ) | ||
| elif login == "e2e-user": | ||
| project, _ = Project.objects.get_or_create( | ||
| key="www-project-e2e", | ||
| defaults={"name": "E2E Project"}, | ||
| ) | ||
| membership, _ = EntityMember.objects.get_or_create( | ||
| entity_id=project.id, | ||
| entity_type=ContentType.objects.get_for_model(Project), | ||
| member_name=login, | ||
| role=EntityMember.Role.LEADER, | ||
| defaults={ | ||
| "is_active": True, | ||
| "is_reviewed": True, | ||
| "member": github_user, | ||
| }, | ||
| ) | ||
| membership.is_active = True | ||
| membership.is_reviewed = True | ||
| membership.member = github_user | ||
| membership.save(update_fields=["is_active", "is_reviewed", "member"]) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| import json | ||
| from http import HTTPStatus | ||
| from unittest.mock import MagicMock, patch | ||
|
|
||
| import pytest | ||
| from django.http import Http404 | ||
| from django.test import RequestFactory | ||
|
|
||
| from apps.nest.api.internal.views.e2e_login import e2e_login | ||
| from apps.nest.models import User | ||
|
|
||
|
|
||
| def _post(body: dict | str): | ||
| data = body if isinstance(body, str) else json.dumps(body) | ||
| return RequestFactory().post( | ||
| "/e2e/login/", | ||
| data=data, | ||
| content_type="application/json", | ||
| ) | ||
|
|
||
|
|
||
| class TestE2ELoginView: | ||
| def test_returns_404_outside_e2e(self): | ||
| with ( | ||
| patch( | ||
| "apps.nest.api.internal.views.e2e_login.settings.IS_E2E_ENVIRONMENT", | ||
| new=False, | ||
| ), | ||
| pytest.raises(Http404), | ||
| ): | ||
|
Check warning on line 30 in backend/tests/unit/apps/nest/api/internal/views/e2e_login_test.py
|
||
| e2e_login(_post({"username": "e2e-mentor"})) | ||
|
|
||
| def test_returns_400_for_invalid_json(self): | ||
| with patch( | ||
| "apps.nest.api.internal.views.e2e_login.settings.IS_E2E_ENVIRONMENT", | ||
| new=True, | ||
| ): | ||
| response = e2e_login(_post("{")) | ||
|
|
||
| assert response.status_code == HTTPStatus.BAD_REQUEST | ||
| assert json.loads(response.content)["ok"] is False | ||
|
|
||
| def test_returns_400_without_username(self): | ||
| with patch( | ||
| "apps.nest.api.internal.views.e2e_login.settings.IS_E2E_ENVIRONMENT", | ||
| new=True, | ||
| ): | ||
| response = e2e_login(_post({})) | ||
|
|
||
| assert response.status_code == HTTPStatus.BAD_REQUEST | ||
| assert json.loads(response.content)["ok"] is False | ||
|
|
||
| def test_returns_404_for_unknown_user(self): | ||
| with ( | ||
| patch( | ||
| "apps.nest.api.internal.views.e2e_login.settings.IS_E2E_ENVIRONMENT", | ||
| new=True, | ||
| ), | ||
| patch( | ||
| "apps.nest.api.internal.views.e2e_login.User.objects.get", | ||
| side_effect=User.DoesNotExist, | ||
| ), | ||
| pytest.raises(Http404), | ||
| ): | ||
|
Check warning on line 64 in backend/tests/unit/apps/nest/api/internal/views/e2e_login_test.py
|
||
| e2e_login(_post({"username": "missing"})) | ||
|
|
||
| def test_logs_in_user(self): | ||
| user = MagicMock() | ||
| request = _post({"username": "e2e-mentor"}) | ||
|
|
||
| with ( | ||
| patch( | ||
| "apps.nest.api.internal.views.e2e_login.settings.IS_E2E_ENVIRONMENT", | ||
| new=True, | ||
| ), | ||
| patch( | ||
| "apps.nest.api.internal.views.e2e_login.User.objects.get", | ||
| return_value=user, | ||
| ) as mock_get, | ||
| patch("apps.nest.api.internal.views.e2e_login.login") as mock_login, | ||
| ): | ||
| response = e2e_login(request) | ||
|
|
||
| mock_get.assert_called_once_with(username="e2e-mentor") | ||
| mock_login.assert_called_once_with(request, user) | ||
| assert response.status_code == HTTPStatus.OK | ||
| assert json.loads(response.content) == {"ok": True, "username": "e2e-mentor"} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| from unittest.mock import MagicMock, patch | ||
|
|
||
| import pytest | ||
| from django.core.management import call_command | ||
| from django.core.management.base import CommandError | ||
|
|
||
| from apps.nest.management.commands.e2e_seed_users import E2E_USERS, Command | ||
|
|
||
|
|
||
| class TestE2ESeedUsersCommand: | ||
| def test_metadata(self): | ||
| assert Command.help == "Seed e2e test users." | ||
| assert E2E_USERS == ( | ||
| ("e2e-mentee", "mentee"), | ||
| ("e2e-mentor", "mentor"), | ||
| ("e2e-user", ""), | ||
| ) | ||
|
|
||
| def test_requires_e2e_environment(self): | ||
| with ( | ||
| patch( | ||
| "apps.nest.management.commands.e2e_seed_users.settings.IS_E2E_ENVIRONMENT", | ||
| new=False, | ||
| ), | ||
| pytest.raises(CommandError, match="e2e environment"), | ||
| ): | ||
| call_command("e2e_seed_users") | ||
|
|
||
| @patch("apps.nest.management.commands.e2e_seed_users.index.disable_indexing") | ||
| @patch("apps.nest.management.commands.e2e_seed_users.ContentType") | ||
| @patch("apps.nest.management.commands.e2e_seed_users.EntityMember") | ||
| @patch("apps.nest.management.commands.e2e_seed_users.Project") | ||
| @patch("apps.nest.management.commands.e2e_seed_users.Mentee") | ||
| @patch("apps.nest.management.commands.e2e_seed_users.Mentor") | ||
| @patch("apps.nest.management.commands.e2e_seed_users.NestUser") | ||
| @patch("apps.nest.management.commands.e2e_seed_users.GithubUser") | ||
| def test_creates_users( | ||
| self, | ||
| mock_github_user, | ||
| mock_nest_user, | ||
| mock_mentor, | ||
| mock_mentee, | ||
| mock_project, | ||
| mock_entity_member, | ||
| mock_content_type, | ||
| mock_disable_indexing, | ||
| ): | ||
| github_user = MagicMock() | ||
| github_user.id = 10 | ||
| nest_user = MagicMock() | ||
| nest_user.github_user_id = 10 | ||
| project = MagicMock(id=1) | ||
| membership = MagicMock() | ||
| mock_github_user.objects.get_or_create.return_value = (github_user, True) | ||
| mock_nest_user.objects.get_or_create.return_value = (nest_user, True) | ||
| mock_project.objects.get_or_create.return_value = (project, True) | ||
| mock_entity_member.objects.get_or_create.return_value = (membership, True) | ||
|
|
||
| with patch( | ||
| "apps.nest.management.commands.e2e_seed_users.settings.IS_E2E_ENVIRONMENT", | ||
| new=True, | ||
| ): | ||
| call_command("e2e_seed_users") | ||
|
|
||
| mock_disable_indexing.assert_called_once() | ||
| assert mock_github_user.objects.get_or_create.call_count == 3 | ||
| assert mock_nest_user.objects.get_or_create.call_count == 3 | ||
| mock_mentor.objects.get_or_create.assert_called_once_with( | ||
| github_user=github_user, | ||
| defaults={"nest_user": nest_user}, | ||
| ) | ||
| mock_mentee.objects.get_or_create.assert_called_once_with( | ||
| github_user=github_user, | ||
| defaults={"nest_user": nest_user}, | ||
| ) | ||
| mock_project.objects.get_or_create.assert_called_once_with( | ||
| key="www-project-e2e", | ||
| defaults={"name": "E2E Project"}, | ||
| ) | ||
| mock_entity_member.objects.get_or_create.assert_called_once_with( | ||
| entity_id=project.id, | ||
| entity_type=mock_content_type.objects.get_for_model.return_value, | ||
| member_name="e2e-user", | ||
| role=mock_entity_member.Role.LEADER, | ||
| defaults={ | ||
| "is_active": True, | ||
| "is_reviewed": True, | ||
| "member": github_user, | ||
| }, | ||
| ) | ||
| membership.save.assert_called_once_with( | ||
| update_fields=["is_active", "is_reviewed", "member"] | ||
| ) |
Uh oh!
There was an error while loading. Please reload this page.