From 3d3ac9c2e09b9c00d9be8f4b1820d6bd116d2346 Mon Sep 17 00:00:00 2001 From: "Douglas Cerna (Soy Douglas)" Date: Wed, 30 Oct 2024 02:01:27 +0000 Subject: [PATCH 1/5] Convert pipeline test into pytest tests --- tests/locations/test_pipeline.py | 307 ++++++++++++++++--------------- 1 file changed, 157 insertions(+), 150 deletions(-) diff --git a/tests/locations/test_pipeline.py b/tests/locations/test_pipeline.py index 4aed4ae37..88cff4fbf 100644 --- a/tests/locations/test_pipeline.py +++ b/tests/locations/test_pipeline.py @@ -3,190 +3,197 @@ from urllib.parse import ParseResult from urllib.parse import urlparse -from django.test import TestCase +import pytest +import pytest_django +from django.test import Client from django.urls import reverse from locations import models FIXTURES_DIR = pathlib.Path(__file__).parent / "fixtures" -class TestPipeline(TestCase): - fixture_files = ["base.json", "pipelines.json"] - fixtures = [FIXTURES_DIR / f for f in fixture_files] +@pytest.fixture +def pipeline() -> models.Pipeline: + return models.Pipeline.objects.create(remote_name="127.0.0.1") - def test_parse_and_fix_url(self): - pipeline = models.Pipeline.objects.get(pk=1) - res = pipeline.parse_and_fix_url(pipeline.remote_name) - assert isinstance(res, ParseResult) - assert res.geturl() == "http://127.0.0.1" - pipeline = models.Pipeline.objects.get(pk=2) - res = pipeline.parse_and_fix_url(pipeline.remote_name) - assert res == urlparse("") +@pytest.mark.django_db +def test_parse_and_fix_url(pipeline: models.Pipeline) -> None: + res = pipeline.parse_and_fix_url(pipeline.remote_name) + assert isinstance(res, ParseResult) + assert res.geturl() == "http://127.0.0.1" - url = "https://archivematica-dashboard" - assert pipeline.parse_and_fix_url(url) == urlparse(url) - url = "https://foo@bar:ss.qa.usip.tld:1234/dev/" - assert pipeline.parse_and_fix_url(url) == urlparse(url) +@pytest.mark.django_db +def test_parse_and_fix_url_with_empty_remote_name(pipeline: models.Pipeline) -> None: + pipeline.remote_name = "" + pipeline.save() - @mock.patch("requests.request") - def test_request_api(self, request): - pipeline = models.Pipeline.objects.get(pk=1) + res = pipeline.parse_and_fix_url(pipeline.remote_name) + assert res == urlparse("") - method = "GET" - url = "http://127.0.0.1/api/processing-configuration/default" - headers = {"Authorization": "ApiKey None:None"} + url = "https://archivematica-dashboard" + assert pipeline.parse_and_fix_url(url) == urlparse(url) - pipeline._request_api(method, "processing-configuration/default") - request.assert_called_with( - method, url, allow_redirects=True, data=None, headers=headers, verify=True - ) + url = "https://foo@bar:ss.qa.usip.tld:1234/dev/" + assert pipeline.parse_and_fix_url(url) == urlparse(url) + + +@pytest.mark.django_db +@mock.patch("requests.request") +def test_request_api( + request: mock.Mock, + pipeline: models.Pipeline, + settings: pytest_django.fixtures.SettingsWrapper, +) -> None: + method = "GET" + url = "http://127.0.0.1/api/processing-configuration/default" + headers = {"Authorization": "ApiKey None:None"} - with self.settings(INSECURE_SKIP_VERIFY=True): - pipeline._request_api(method, "processing-configuration/default") - request.assert_called_with( - method, - url, - allow_redirects=True, - data=None, - headers=headers, - verify=False, - ) - - @mock.patch( - "locations.models.Pipeline._request_api", - side_effect=[ - mock.Mock( - **{ - "status_code": 200, - "json.return_value": { - "message": "Fetched unapproved transfers successfully.", - "results": [ - { - "directory": "Foobar1", - "type": "standard", - "uuid": "090b7f5b-637b-400b-9014-3eb58986fe8f", - } - ], - }, - } - ) - ], + pipeline._request_api(method, "processing-configuration/default") + request.assert_called_with( + method, url, allow_redirects=True, data=None, headers=headers, verify=True ) - def test_list_unapproved_transfers(self, request_api): - pipeline = models.Pipeline.objects.get(pk=3) - result = pipeline.list_unapproved_transfers() - - assert isinstance(result, dict) is True - assert result["message"] == "Fetched unapproved transfers successfully." - assert len(result["results"]) == 1 - assert result["results"][0]["directory"] == "Foobar1" - assert result["results"][0]["type"] == "standard" - assert result["results"][0]["uuid"] == "090b7f5b-637b-400b-9014-3eb58986fe8f" - - @mock.patch( - "locations.models.Pipeline._request_api", - side_effect=[ - mock.Mock( - **{ - "status_code": 200, - "json.return_value": { - "message": "Approval successful.", - "uuid": "090b7f5b-637b-400b-9014-3eb58986fe8f", - }, - } - ) - ], + + settings.INSECURE_SKIP_VERIFY = True + pipeline._request_api(method, "processing-configuration/default") + request.assert_called_with( + method, + url, + allow_redirects=True, + data=None, + headers=headers, + verify=False, ) - def test_approve_transfer(self, request_api): - pipeline = models.Pipeline.objects.get(pk=3) - result = pipeline.approve_transfer("Foobar1", "standard") - assert result["message"] == "Approval successful." - assert result["uuid"] == "090b7f5b-637b-400b-9014-3eb58986fe8f" +@pytest.mark.django_db +@mock.patch( + "locations.models.Pipeline._request_api", + side_effect=[ + mock.Mock( + **{ + "status_code": 200, + "json.return_value": { + "message": "Fetched unapproved transfers successfully.", + "results": [ + { + "directory": "Foobar1", + "type": "standard", + "uuid": "090b7f5b-637b-400b-9014-3eb58986fe8f", + } + ], + }, + } + ) + ], +) +def test_list_unapproved_transfers( + request_api: mock.Mock, pipeline: models.Pipeline +) -> None: + result = pipeline.list_unapproved_transfers() + + assert isinstance(result, dict) is True + assert result["message"] == "Fetched unapproved transfers successfully." + assert len(result["results"]) == 1 + assert result["results"][0]["directory"] == "Foobar1" + assert result["results"][0]["type"] == "standard" + assert result["results"][0]["uuid"] == "090b7f5b-637b-400b-9014-3eb58986fe8f" + + +@pytest.mark.django_db +@mock.patch( + "locations.models.Pipeline._request_api", + side_effect=[ + mock.Mock( + **{ + "status_code": 200, + "json.return_value": { + "message": "Approval successful.", + "uuid": "090b7f5b-637b-400b-9014-3eb58986fe8f", + }, + } + ) + ], +) +def test_approve_transfer(request_api: mock.Mock, pipeline: models.Pipeline) -> None: + result = pipeline.approve_transfer("Foobar1", "standard") -class TestPipelineViews(TestCase): - fixture_files = ["base.json", "pipelines.json"] - fixtures = [FIXTURES_DIR / f for f in fixture_files] + assert result["message"] == "Approval successful." + assert result["uuid"] == "090b7f5b-637b-400b-9014-3eb58986fe8f" - def setUp(self): - self.client.login(username="test", password="test") - def test_view_create_pipeline(self): - url = reverse("locations:pipeline_create") +def test_view_create_pipeline(admin_client: Client) -> None: + url = reverse("locations:pipeline_create") - resp = self.client.get(url, follow=True) - form = resp.context["form"] + resp = admin_client.get(url, follow=True) + form = resp.context["form"] - assert resp.status_code == 200 - assert form.initial["enabled"] is True - assert form.initial["create_default_locations"] is True + assert resp.status_code == 200 + assert form.initial["enabled"] is True + assert form.initial["create_default_locations"] is True - def test_view_create_pipeline_invalid_post(self): - url = reverse("locations:pipeline_create") - resp = self.client.post(url, follow=True, data={}) - form = resp.context["form"] +def test_view_create_pipeline_invalid_post(admin_client: Client) -> None: + url = reverse("locations:pipeline_create") - assert form.is_valid() is False + resp = admin_client.post(url, follow=True, data={}) + form = resp.context["form"] - def test_view_create_pipeline_post(self): - url = reverse("locations:pipeline_create") + assert form.is_valid() is False - resp = self.client.post( - url, follow=True, data={"uuid": "0d9d6be9-2751-4e81-b85f-fe4e51a1f789"} - ) - messages = list(resp.context["messages"]) - self.assertRedirects(resp, reverse("locations:pipeline_list")) - assert models.Pipeline.objects.filter( - uuid="0d9d6be9-2751-4e81-b85f-fe4e51a1f789" - ).exists() - assert str(messages[0]) == "Pipeline saved." +def test_view_create_pipeline_post(admin_client: Client) -> None: + url = reverse("locations:pipeline_create") - def test_view_edit_pipeline(self): - url = reverse( - "locations:pipeline_edit", args=["b25f6b71-3ebf-4fcc-823c-1feb0a2553dd"] - ) + resp = admin_client.post( + url, follow=True, data={"uuid": "0d9d6be9-2751-4e81-b85f-fe4e51a1f789"} + ) + messages = list(resp.context["messages"]) - resp = self.client.get(url, follow=True) - form = resp.context["form"] + assert models.Pipeline.objects.filter( + uuid="0d9d6be9-2751-4e81-b85f-fe4e51a1f789" + ).exists() + assert str(messages[0]) == "Pipeline saved." - assert form.initial["enabled"] is True - assert "create_default_locations" not in form.initial - def test_view_edit_pipeline_invalid_post(self): - url = reverse( - "locations:pipeline_edit", args=["b25f6b71-3ebf-4fcc-823c-1feb0a2553dd"] - ) +def test_view_edit_pipeline(admin_client: Client, pipeline: models.Pipeline) -> None: + url = reverse("locations:pipeline_edit", args=[pipeline.uuid]) - resp = self.client.post(url, follow=True, data={}) - form = resp.context["form"] + resp = admin_client.get(url, follow=True) + form = resp.context["form"] - assert form.is_valid() is False + assert form.initial["enabled"] is True + assert "create_default_locations" not in form.initial - def test_view_edit_pipeline_post(self): - url = reverse( - "locations:pipeline_edit", args=["b25f6b71-3ebf-4fcc-823c-1feb0a2553dd"] - ) - resp = self.client.post( - url, - follow=True, - data={ - "uuid": "b25f6b71-3ebf-4fcc-823c-1feb0a2553dd", - "description": "Pipeline 3ebf", - }, - ) - messages = list(resp.context["messages"]) - - self.assertRedirects(resp, reverse("locations:pipeline_list")) - assert ( - models.Pipeline.objects.get( - uuid="b25f6b71-3ebf-4fcc-823c-1feb0a2553dd" - ).description - == "Pipeline 3ebf" - ) - assert str(messages[0]) == "Pipeline saved." +def test_view_edit_pipeline_invalid_post( + admin_client: Client, pipeline: models.Pipeline +) -> None: + url = reverse("locations:pipeline_edit", args=[pipeline.uuid]) + + resp = admin_client.post(url, follow=True, data={}) + form = resp.context["form"] + + assert form.is_valid() is False + + +def test_view_edit_pipeline_post( + admin_client: Client, pipeline: models.Pipeline +) -> None: + url = reverse("locations:pipeline_edit", args=[pipeline.uuid]) + + resp = admin_client.post( + url, + follow=True, + data={ + "uuid": str(pipeline.uuid), + "description": "Pipeline 3ebf", + }, + ) + messages = list(resp.context["messages"]) + + assert ( + models.Pipeline.objects.get(uuid=pipeline.uuid).description == "Pipeline 3ebf" + ) + assert str(messages[0]) == "Pipeline saved." From f957eae4f124319aab11b5131f4a48da260bcc2f Mon Sep 17 00:00:00 2001 From: "Douglas Cerna (Soy Douglas)" Date: Wed, 30 Oct 2024 15:47:57 +0000 Subject: [PATCH 2/5] Add tests for pipeline detail view --- tests/locations/test_pipeline.py | 40 ++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/tests/locations/test_pipeline.py b/tests/locations/test_pipeline.py index 88cff4fbf..5d74d5d92 100644 --- a/tests/locations/test_pipeline.py +++ b/tests/locations/test_pipeline.py @@ -1,4 +1,5 @@ import pathlib +import uuid from unittest import mock from urllib.parse import ParseResult from urllib.parse import urlparse @@ -14,7 +15,12 @@ @pytest.fixture def pipeline() -> models.Pipeline: - return models.Pipeline.objects.create(remote_name="127.0.0.1") + return models.Pipeline.objects.create( + description="My pipeline", + remote_name="127.0.0.1", + api_username="user", + api_key="key", + ) @pytest.mark.django_db @@ -48,7 +54,7 @@ def test_request_api( ) -> None: method = "GET" url = "http://127.0.0.1/api/processing-configuration/default" - headers = {"Authorization": "ApiKey None:None"} + headers = {"Authorization": f"ApiKey {pipeline.api_username}:{pipeline.api_key}"} pipeline._request_api(method, "processing-configuration/default") request.assert_called_with( @@ -197,3 +203,33 @@ def test_view_edit_pipeline_post( models.Pipeline.objects.get(uuid=pipeline.uuid).description == "Pipeline 3ebf" ) assert str(messages[0]) == "Pipeline saved." + + +def test_pipeline_detail_view_shows_pipeline_fields( + admin_client: Client, pipeline: models.Pipeline +) -> None: + response = admin_client.get( + reverse("locations:pipeline_detail", kwargs={"uuid": pipeline.uuid}) + ) + assert response.status_code == 200 + + content = response.content.decode() + assert f"
{pipeline.uuid}
" in content + assert f"
{pipeline.description}
" in content + assert f"
{pipeline.remote_name}
" in content + assert f"
{pipeline.api_username} / {pipeline.api_key}
" in content + assert "No locations currently exist" in content + + +def test_pipeline_detail_view_warns_if_pipeline_does_not_exist( + admin_client: Client, pipeline: models.Pipeline +) -> None: + pipeline_uuid = uuid.uuid4() + response = admin_client.get( + reverse("locations:pipeline_detail", kwargs={"uuid": pipeline_uuid}), + follow=True, + ) + assert response.status_code == 200 + + content = response.content.decode() + assert f"Pipeline {pipeline_uuid} does not exist." in content From f08b5305638bc14962e25eb1a487a499fb5cba19 Mon Sep 17 00:00:00 2001 From: "Douglas Cerna (Soy Douglas)" Date: Wed, 30 Oct 2024 15:54:08 +0000 Subject: [PATCH 3/5] Extend pipeline edit tests --- tests/locations/test_pipeline.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/tests/locations/test_pipeline.py b/tests/locations/test_pipeline.py index 5d74d5d92..7ef40c63a 100644 --- a/tests/locations/test_pipeline.py +++ b/tests/locations/test_pipeline.py @@ -169,8 +169,14 @@ def test_view_edit_pipeline(admin_client: Client, pipeline: models.Pipeline) -> resp = admin_client.get(url, follow=True) form = resp.context["form"] - assert form.initial["enabled"] is True - assert "create_default_locations" not in form.initial + assert form.initial == { + "uuid": pipeline.uuid, + "description": pipeline.description, + "remote_name": pipeline.remote_name, + "api_username": pipeline.api_username, + "api_key": pipeline.api_key, + "enabled": pipeline.enabled, + } def test_view_edit_pipeline_invalid_post( @@ -188,20 +194,29 @@ def test_view_edit_pipeline_post( admin_client: Client, pipeline: models.Pipeline ) -> None: url = reverse("locations:pipeline_edit", args=[pipeline.uuid]) + description = "Pipeline 3ebf" + remote_name = "localhost" + api_username = "newapiusername" + api_key = "newapikey" resp = admin_client.post( url, follow=True, data={ "uuid": str(pipeline.uuid), - "description": "Pipeline 3ebf", + "description": description, + "remote_name": remote_name, + "api_username": api_username, + "api_key": api_key, }, ) messages = list(resp.context["messages"]) - assert ( - models.Pipeline.objects.get(uuid=pipeline.uuid).description == "Pipeline 3ebf" - ) + pipeline.refresh_from_db() + assert pipeline.description == description + assert pipeline.remote_name == remote_name + assert pipeline.api_username == api_username + assert pipeline.api_key == api_key assert str(messages[0]) == "Pipeline saved." From 84239fefb1dc7d14683446c071f2270bc574c673 Mon Sep 17 00:00:00 2001 From: "Douglas Cerna (Soy Douglas)" Date: Wed, 6 Nov 2024 16:10:34 +0000 Subject: [PATCH 4/5] Convert user tests into pytest tests --- pyproject.toml | 1 + tests/administration/test_users.py | 249 +++++++++++++++-------------- 2 files changed, 131 insertions(+), 119 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5a6ca1014..32cdbb9c3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -94,6 +94,7 @@ ignore_errors = true [[tool.mypy.overrides]] module = [ "common.helpers", + "tests.administration.test_users", "tests.integration.*", "tests.storage_service.test_helpers", "tests.storage_service.test_oidc", diff --git a/tests/administration/test_users.py b/tests/administration/test_users.py index 5a7ca9e53..11b499454 100644 --- a/tests/administration/test_users.py +++ b/tests/administration/test_users.py @@ -1,125 +1,136 @@ -from unittest import skipIf +from typing import Type +import pytest +import pytest_django from administration import roles -from django.conf import settings from django.contrib.auth.models import User -from django.test import TestCase +from django.test import Client from django.urls import reverse -class TestUserManagement(TestCase): - def setUp(self): - self.user = User.objects.create_user( - username="admin", - password="admin", - email="admin@example.com", - is_superuser=True, - ) - self.client.login(username="admin", password="admin") - - def as_reader(self): - self.user.set_role(roles.USER_ROLE_READER) - - def as_manager(self): - self.user.set_role(roles.USER_ROLE_MANAGER) - - def test_list_users(self): - """The user list is available to all users.""" - resp = self.client.get(reverse("administration:user_list")) - - self.assertContains(resp, "admin@example.com") - - @skipIf(not settings.ALLOW_USER_EDITS, "User edits are disabled") - def test_create_user_as_admin(self): - """Only administrators are allowed to create new users.""" - resp = self.client.post( - reverse("administration:user_create"), - { - "username": "demo", - "email": "demo@example.com", - "role": "manager", - "password1": "ck61Qc873.KxoZ5G", - "password2": "ck61Qc873.KxoZ5G", - }, - ) - - self.assertRedirects( - resp, - reverse("administration:user_list"), - status_code=302, - target_status_code=200, - fetch_redirect_response=True, - ) - assert User.objects.filter(username="demo").exists() is True - - @skipIf(not settings.ALLOW_USER_EDITS, "User edits are disabled") - def test_create_user_as_non_admin(self): - """Only administrators are allowed to create new users.""" - self.as_reader() - resp = self.client.post( - reverse("administration:user_create"), - { - "username": "demo", - "email": "demo@example.com", - "role": "manager", - "password1": "ck61Qc873.KxoZ5G", - "password2": "ck61Qc873.KxoZ5G", - }, - ) - - self.assertRedirects( - resp, - reverse("administration:user_list"), - status_code=302, - target_status_code=200, - fetch_redirect_response=True, - ) - assert User.objects.filter(username="demo").exists() is False - - @skipIf(not settings.ALLOW_USER_EDITS, "User edits are disabled") - def test_edit_user_promote_as_manager(self): - """Only administrators are allowed to promote/demote users.""" - test = User.objects.create_user( - username="test", password="ck61Qc873.KxoZ5G", email="test@example.com" - ) - resp = self.client.post( - reverse("administration:user_edit", kwargs={"id": test.pk}), - { - "user": "Edit User", - "username": "test", - "email": "test@example.com", - "role": "manager", - }, - follow=True, - ) - - assert list(resp.context["messages"])[0].message == "User information saved." - test.refresh_from_db() - assert test.get_role() == roles.USER_ROLE_MANAGER - - @skipIf(not settings.ALLOW_USER_EDITS, "User edits are disabled") - def test_edit_user_promotion_requires_admin(self): - """Only administrators are allowed to promote/demote users.""" - self.as_manager() - test = User.objects.create_user( - username="test", password="ck61Qc873.KxoZ5G", email="test@example.com" - ) - resp = self.client.post( - reverse("administration:user_edit", kwargs={"id": test.pk}), - { - "user": "Edit User", - "username": "test", - "email": "test@example.com", - "role": "manager", - }, - ) - - self.assertRedirects( - resp, - reverse("administration:user_list"), - status_code=302, - target_status_code=200, - fetch_redirect_response=True, - ) - test.refresh_from_db() - assert test.get_role() == roles.USER_ROLE_READER +def as_reader(user: User) -> None: + user.set_role(roles.USER_ROLE_READER) + + +def as_manager(user: User) -> None: + user.set_role(roles.USER_ROLE_MANAGER) + + +@pytest.mark.django_db +def test_list_users(admin_client: Client) -> None: + """The user list is available to all users.""" + resp = admin_client.get(reverse("administration:user_list")) + + assert "admin@example.com" in resp.content.decode() + + +@pytest.fixture +def settings( + settings: pytest_django.fixtures.SettingsWrapper, +) -> pytest_django.fixtures.SettingsWrapper: + settings.ALLOW_USER_EDITS = True + + return settings + + +@pytest.mark.django_db +def test_create_user_as_admin( + admin_client: Client, settings: pytest_django.fixtures.SettingsWrapper +) -> None: + """Only administrators are allowed to create new users.""" + resp = admin_client.post( + reverse("administration:user_create"), + { + "username": "demo", + "email": "demo@example.com", + "role": "manager", + "password1": "ck61Qc873.KxoZ5G", + "password2": "ck61Qc873.KxoZ5G", + }, + follow=True, + ) + assert resp.status_code == 200 + + assert "demo@example.com" in resp.content.decode() + assert User.objects.filter(username="demo").exists() + + +@pytest.mark.django_db +def test_create_user_as_non_admin( + admin_client: Client, + settings: pytest_django.fixtures.SettingsWrapper, + django_user_model: Type[User], +) -> None: + """Only administrators are allowed to create new users.""" + as_reader(django_user_model.objects.get(username="admin")) + + resp = admin_client.post( + reverse("administration:user_create"), + { + "username": "demo", + "email": "demo@example.com", + "role": "manager", + "password1": "ck61Qc873.KxoZ5G", + "password2": "ck61Qc873.KxoZ5G", + }, + follow=True, + ) + assert resp.status_code == 200 + + assert "demo@example.com" not in resp.content.decode() + assert not User.objects.filter(username="demo").exists() + + +@pytest.mark.django_db +def test_edit_user_promote_as_manager( + admin_client: Client, + settings: pytest_django.fixtures.SettingsWrapper, + django_user_model: Type[User], +) -> None: + """Only administrators are allowed to promote/demote users.""" + test = django_user_model.objects.create_user( + username="test", password="ck61Qc873.KxoZ5G", email="test@example.com" + ) + resp = admin_client.post( + reverse("administration:user_edit", kwargs={"id": test.pk}), + { + "user": "Edit User", + "username": "test", + "email": "test@example.com", + "role": "manager", + }, + follow=True, + ) + assert resp.status_code == 200 + + assert list(resp.context["messages"])[0].message == "User information saved." + test.refresh_from_db() + assert test.get_role() == roles.USER_ROLE_MANAGER + + +@pytest.mark.django_db +def test_edit_user_promotion_requires_admin( + admin_client: Client, + settings: pytest_django.fixtures.SettingsWrapper, + django_user_model: Type[User], +) -> None: + """Only administrators are allowed to promote/demote users.""" + as_manager(django_user_model.objects.get(username="admin")) + test = django_user_model.objects.create_user( + username="test", password="ck61Qc873.KxoZ5G", email="test@example.com" + ) + + resp = admin_client.post( + reverse("administration:user_edit", kwargs={"id": test.pk}), + { + "user": "Edit User", + "username": "test", + "email": "test@example.com", + "role": "manager", + }, + follow=True, + ) + assert resp.status_code == 200 + + test.refresh_from_db() + assert test.get_role() == roles.USER_ROLE_READER From 02a3160f3422b7c570b2fd18e1e06a8597015837 Mon Sep 17 00:00:00 2001 From: "Douglas Cerna (Soy Douglas)" Date: Thu, 7 Nov 2024 14:44:39 +0000 Subject: [PATCH 5/5] Add test for updating the user password --- tests/administration/test_users.py | 98 ++++++++++++++++++++++++++---- 1 file changed, 85 insertions(+), 13 deletions(-) diff --git a/tests/administration/test_users.py b/tests/administration/test_users.py index 11b499454..f534a630e 100644 --- a/tests/administration/test_users.py +++ b/tests/administration/test_users.py @@ -1,4 +1,8 @@ +import hmac +import uuid +from hashlib import sha1 from typing import Type +from unittest import mock import pytest import pytest_django @@ -6,6 +10,7 @@ from django.contrib.auth.models import User from django.test import Client from django.urls import reverse +from tastypie.models import ApiKey def as_reader(user: User) -> None: @@ -81,18 +86,22 @@ def test_create_user_as_non_admin( assert not User.objects.filter(username="demo").exists() +@pytest.fixture +def user(django_user_model: Type[User]) -> User: + return django_user_model.objects.create_user( + username="test", password="ck61Qc873.KxoZ5G", email="test@example.com" + ) + + @pytest.mark.django_db def test_edit_user_promote_as_manager( admin_client: Client, settings: pytest_django.fixtures.SettingsWrapper, - django_user_model: Type[User], + user: User, ) -> None: """Only administrators are allowed to promote/demote users.""" - test = django_user_model.objects.create_user( - username="test", password="ck61Qc873.KxoZ5G", email="test@example.com" - ) resp = admin_client.post( - reverse("administration:user_edit", kwargs={"id": test.pk}), + reverse("administration:user_edit", kwargs={"id": user.pk}), { "user": "Edit User", "username": "test", @@ -104,8 +113,8 @@ def test_edit_user_promote_as_manager( assert resp.status_code == 200 assert list(resp.context["messages"])[0].message == "User information saved." - test.refresh_from_db() - assert test.get_role() == roles.USER_ROLE_MANAGER + user.refresh_from_db() + assert user.get_role() == roles.USER_ROLE_MANAGER @pytest.mark.django_db @@ -113,15 +122,13 @@ def test_edit_user_promotion_requires_admin( admin_client: Client, settings: pytest_django.fixtures.SettingsWrapper, django_user_model: Type[User], + user: User, ) -> None: """Only administrators are allowed to promote/demote users.""" as_manager(django_user_model.objects.get(username="admin")) - test = django_user_model.objects.create_user( - username="test", password="ck61Qc873.KxoZ5G", email="test@example.com" - ) resp = admin_client.post( - reverse("administration:user_edit", kwargs={"id": test.pk}), + reverse("administration:user_edit", kwargs={"id": user.pk}), { "user": "Edit User", "username": "test", @@ -132,5 +139,70 @@ def test_edit_user_promotion_requires_admin( ) assert resp.status_code == 200 - test.refresh_from_db() - assert test.get_role() == roles.USER_ROLE_READER + user.refresh_from_db() + assert user.get_role() == roles.USER_ROLE_READER + + +@pytest.fixture +def admin_user_apikey(admin_client: Client, django_user_model: Type[User]) -> ApiKey: + return ApiKey.objects.create(user=django_user_model.objects.get(username="admin")) + + +@pytest.mark.django_db +def test_user_edit_view_updates_password( + admin_client: Client, + settings: pytest_django.fixtures.SettingsWrapper, + django_user_model: Type[User], + admin_user_apikey: ApiKey, +) -> None: + user = django_user_model.objects.get(username="admin") + assert user.check_password("password") + new_password = "ck61Qc873.KxoZ5G" + + response = admin_client.post( + reverse("administration:user_edit", kwargs={"id": user.pk}), + { + "new_password1": new_password, + "new_password2": new_password, + "password": "1", + }, + follow=True, + ) + assert response.status_code == 200 + + user.refresh_from_db() + assert user.check_password(new_password) + assert "Password changed" in response.content.decode() + + +@pytest.mark.django_db +def test_user_edit_view_regenerates_api_key( + admin_client: Client, + settings: pytest_django.fixtures.SettingsWrapper, + django_user_model: Type[User], + admin_user_apikey: ApiKey, +) -> None: + user = django_user_model.objects.get(username="admin") + assert user.check_password("password") + new_password = "ck61Qc873.KxoZ5G" + expected_uuid = uuid.uuid4() + expected_key = hmac.new(expected_uuid.bytes, digestmod=sha1).hexdigest() + + with mock.patch("uuid.uuid4", return_value=expected_uuid): + response = admin_client.post( + reverse("administration:user_edit", kwargs={"id": user.pk}), + { + "new_password1": new_password, + "new_password2": new_password, + "password": "1", + }, + follow=True, + ) + assert response.status_code == 200 + + user.refresh_from_db() + assert user.check_password(new_password) + assert "Password changed" in response.content.decode() + + admin_user_apikey.refresh_from_db() + assert admin_user_apikey.key == expected_key