From 15bb14272ff9da87099ba78ac6388ed4d7805708 Mon Sep 17 00:00:00 2001 From: Jacob Lowe Date: Wed, 9 Oct 2024 18:22:05 -0500 Subject: [PATCH 01/16] initial mock project create tests w/ formatting changes --- server/portal/apps/projects/unit_test.py | 323 +++++++++++++++++------ 1 file changed, 240 insertions(+), 83 deletions(-) diff --git a/server/portal/apps/projects/unit_test.py b/server/portal/apps/projects/unit_test.py index e6f207cd3..6a86cb841 100644 --- a/server/portal/apps/projects/unit_test.py +++ b/server/portal/apps/projects/unit_test.py @@ -3,12 +3,15 @@ .. :module:: portal.apps.projects.unit_test :synopsis: Projects app unit tests. """ -from mock import MagicMock + +import logging +from unittest.mock import MagicMock + +import pytest # pyright: ignore + +from portal.apps.projects.exceptions import NotAuthorizedError from portal.apps.projects.models.base import Project from portal.apps.projects.models.metadata import ProjectMetadata -from portal.apps.projects.exceptions import NotAuthorizedError -import pytest -import logging LOGGER = logging.getLogger(__name__) @@ -17,107 +20,223 @@ @pytest.fixture def mock_service_account(mocker): - yield mocker.patch('portal.apps.projects.models.base.service_account', autospec=True) + yield mocker.patch( + "portal.apps.projects.models.base.service_account", autospec=True + ) @pytest.fixture() def mock_signal(mocker): - yield mocker.patch('portal.apps.signals.receivers.index_project') + yield mocker.patch("portal.apps.signals.receivers.index_project") @pytest.fixture() def mock_owner(django_user_model): - return django_user_model.objects.create_user(username='username', - password='password') + return django_user_model.objects.create_user( + username="username", password="password" + ) -@pytest.mark.skip(reason="TODOv3: update with new Shared Workspaces operations") -def test_project_init(mock_tapis_client, mock_storage_system, project_model, mock_signal): - 'Test project model init.' - mock_storage_system.return_value.description = 'my title' +# Start my fixtures - prj = project_model(mock_tapis_client, 'PRJ-123') - assert prj.project_id == 'PRJ-123' - mock_storage_system.assert_called_with( - mock_tapis_client, - id='{prefix}.{project_id}'.format( - prefix=Project.metadata_name, - project_id='PRJ-123' - ) +# Mock creation of a project +@pytest.fixture() +def mock_tapis_client(): + return MagicMock() + + +# TODO: Convert this to become a mocker patch of the Metadata object, similar to mock_create_project +# List of Metadata per project +@pytest.fixture() +def mock_metadata(): + metadata = MagicMock() + + def mock_init(title): + metadata.defaults = {"title": title} + + metadata.side_effect = mock_init + return metadata + + +# Potentially holds a list of projects by the user +@pytest.fixture() +def mock_storage_system(mocker): + mock = mocker.MagicMock() + mock.description = "All Project Storage" + + def mock_init(client, project_id): + mock.client = client + mock.project = project_id + mock.last_modified = "10-09-2024" + return mock + + mock.side_effect = mock_init + return mock + + +@pytest.fixture() +def mock_create_project(mocker): + mock = mocker.patch( + "portal.apps.projects.models.Project.storage", + autospec=True, ) - assert ProjectMetadata.objects.all().count() == 1 - assert ProjectMetadata.objects.get(project_id='PRJ-123', title='my title') + def mock_init(mock_storage_system, client, project_id, metadata=None, storage=None): + mock.client = client + mock.project_id = project_id + if storage is None: + mock.storage = mock_storage_system(client, mock.project_id) + return mock + + mock.side_effect = mock_init + return mock + + +def test_project_init( + mock_tapis_client, mock_storage_system, mock_metadata, mock_create_project +): + "Test project model init." + # Mock a project creation + assert mock_storage_system.description == "All Project Storage" + + """ + # Test with no initial storage or metadata + prj = mock_create_project(mock_tapis_client, "PRJ-123") + mock_create_project.assert_called_with(mock_tapis_client, "PRJ-123") + assert prj.project_id == "PRJ-123" + + prj2 = mock_create_project( + mock_tapis_client, "PRJ-124", mock_metadata, mock_storage_system + ) + mock_create_project.assert_called_with( + mock_tapis_client, "PRJ-124", mock_metadata, mock_storage_system + ) + assert prj2.storage.last_modified == "10-09-2024" + assert prj2.storage.description == "my test description" + """ + + id = "{prefix}.{project_id}".format( + prefix=Project.metadata_name, project_id="PRJ-123" + ) + + prj = mock_create_project( + client=mock_tapis_client, + project_id=id, + mock_storage_system=mock_storage_system, + ) + assert prj.project_id == id + + # With Storage Systems + # In order to assert this, this needs to be called in a create project, then storage data would have to be made + mock_storage_system.assert_called_with(mock_tapis_client, id) + # Without Storage Systems (not possible, code is commented out to be depreicated see models/base.py) + + # TODO: Uncomment out and test that metadata shows and passses these tests + # assert ProjectMetadata.objects.all().count() == 1 + # assert ProjectMetadata.objects.get(project_id="PRJ-123", title="my title") @pytest.mark.skip(reason="TODOv3: update with new Shared Workspaces operations") -def test_project_create(mock_owner, mock_tapis_client, service_account, mock_storage_system, project_model, mock_signal): - prj = project_model.create(mock_tapis_client, 'Test Title', 'PRJ-123', mock_owner) - project_model._create_dir.assert_called_with('PRJ-123') - mock_storage_system.assert_called_with(client=service_account(), - id='test.project.PRJ-123', - name='PRJ-123', - description='Test Title', - site='test') +def test_project_create( + mock_owner, + mock_tapis_client, + service_account, + mock_storage_system, + project_model, + mock_signal, +): + prj = project_model.create(mock_tapis_client, "Test Title", "PRJ-123", mock_owner) + project_model._create_dir.assert_called_with("PRJ-123") + mock_storage_system.assert_called_with( + client=service_account(), + id="test.project.PRJ-123", + name="PRJ-123", + description="Test Title", + site="test", + ) assert ProjectMetadata.objects.all().count() == 1 - assert ProjectMetadata.objects.get(project_id='PRJ-123', title='Test Title') + assert ProjectMetadata.objects.get(project_id="PRJ-123", title="Test Title") assert prj._ac == mock_tapis_client assert prj.storage.storage.port == 22 - assert prj.storage.storage.auth.username == 'wma_prtl' - assert prj.storage.storage.auth.private_key == ('-----BEGIN RSA PRIVATE KEY-----' - 'change this' - '-----END RSA PRIVATE KEY-----') + assert prj.storage.storage.auth.username == "wma_prtl" + assert prj.storage.storage.auth.private_key == ( + "-----BEGIN RSA PRIVATE KEY-----" "change this" "-----END RSA PRIVATE KEY-----" + ) @pytest.mark.skip(reason="TODOv3: update with new Shared Workspaces operations") -def test_listing(mock_storage_system, mock_tapis_client, mock_signal, mock_projects_storage_systems): - 'Test projects listing.' +def test_listing( + mock_storage_system, mock_tapis_client, mock_signal, mock_projects_storage_systems +): + "Test projects listing." mock_storage_system.search.return_value = mock_projects_storage_systems lst = list(Project.listing(mock_tapis_client)) mock_storage_system.search.assert_called_with( mock_tapis_client, - query={'id.like': '{}*'.format(Project.metadata_name), - 'type.eq': mock_storage_system.TYPES.STORAGE}, + query={ + "id.like": "{}*".format(Project.metadata_name), + "type.eq": mock_storage_system.TYPES.STORAGE, + }, offset=0, - limit=100 + limit=100, ) assert len(lst) == 2 @pytest.mark.skip(reason="TODOv3: update with new Shared Workspaces operations") -def test_add_member(mock_owner, django_user_model, mock_tapis_client, mock_storage_system, project_model, mock_signal, mock_service_account): - 'Test add member.' - - prj = project_model.create(mock_tapis_client, 'Test Title', 'PRJ-123', mock_owner) - prj.storage.roles.for_user.return_value = MagicMock(role='ADMIN', ADMIN='ADMIN') +def test_add_member( + mock_owner, + django_user_model, + mock_tapis_client, + mock_storage_system, + project_model, + mock_signal, + mock_service_account, +): + "Test add member." + + prj = project_model.create(mock_tapis_client, "Test Title", "PRJ-123", mock_owner) + prj.storage.roles.for_user.return_value = MagicMock(role="ADMIN", ADMIN="ADMIN") assert prj._can_edit_member(mock_owner) - mock_team_member = django_user_model.objects.create_user(username='teamMember', password='password') + mock_team_member = django_user_model.objects.create_user( + username="teamMember", password="password" + ) prj.add_member(mock_team_member) - prj.storage.roles.add.assert_called_with('teamMember', 'USER') + prj.storage.roles.add.assert_called_with("teamMember", "USER") assert prj.storage.roles.save.call_count == 1 - assert prj.metadata.team_members.get(username='teamMember') + assert prj.metadata.team_members.get(username="teamMember") prj.remove_member(mock_team_member) with pytest.raises(django_user_model.DoesNotExist): - prj.metadata.team_members.get(username='teamMember') + prj.metadata.team_members.get(username="teamMember") @pytest.mark.skip(reason="TODOv3: update with new Shared Workspaces operations") -def test_add_member_unauthorized(mock_owner, django_user_model, mock_tapis_client, mock_storage_system, project_model, mock_signal, mock_service_account): - 'Test add member.' - - prj = project_model.create(mock_tapis_client, 'Test Title', 'PRJ-123', mock_owner) - prj.storage.roles.for_user.return_value = MagicMock(role='USER', ADMIN='ADMIN') +def test_add_member_unauthorized( + mock_owner, + django_user_model, + mock_tapis_client, + mock_storage_system, + project_model, + mock_signal, + mock_service_account, +): + "Test add member." + + prj = project_model.create(mock_tapis_client, "Test Title", "PRJ-123", mock_owner) + prj.storage.roles.for_user.return_value = MagicMock(role="USER", ADMIN="ADMIN") assert not prj._can_edit_member(mock_owner) - mock_team_member = django_user_model.objects.create_user(username='teamMember', password='password') + mock_team_member = django_user_model.objects.create_user( + username="teamMember", password="password" + ) with pytest.raises(NotAuthorizedError): prj.add_member(mock_team_member) @@ -128,33 +247,53 @@ def test_add_member_unauthorized(mock_owner, django_user_model, mock_tapis_clien @pytest.mark.skip(reason="TODOv3: update with new Shared Workspaces operations") -def test_add_copi(mock_owner, django_user_model, mock_tapis_client, mock_storage_system, project_model, mock_signal, mock_service_account): - - prj = project_model.create(mock_tapis_client, 'Test Title', 'PRJ-123', mock_owner) - prj.storage.roles.for_user.return_value = MagicMock(role='ADMIN', ADMIN='ADMIN') +def test_add_copi( + mock_owner, + django_user_model, + mock_tapis_client, + mock_storage_system, + project_model, + mock_signal, + mock_service_account, +): + + prj = project_model.create(mock_tapis_client, "Test Title", "PRJ-123", mock_owner) + prj.storage.roles.for_user.return_value = MagicMock(role="ADMIN", ADMIN="ADMIN") assert prj._can_edit_member(mock_owner) - mock_copi = django_user_model.objects.create_user(username='coPi', password='password') + mock_copi = django_user_model.objects.create_user( + username="coPi", password="password" + ) prj.add_co_pi(mock_copi) - prj.storage.roles.add.assert_called_with('coPi', 'ADMIN') + prj.storage.roles.add.assert_called_with("coPi", "ADMIN") assert prj.storage.roles.save.call_count == 1 - assert prj.metadata.co_pis.get(username='coPi') + assert prj.metadata.co_pis.get(username="coPi") prj.remove_co_pi(mock_copi) with pytest.raises(django_user_model.DoesNotExist): - prj.metadata.team_members.get(username='teamMember') + prj.metadata.team_members.get(username="teamMember") @pytest.mark.skip(reason="TODOv3: update with new Shared Workspaces operations") -def test_add_copi_unauthorized(mock_owner, django_user_model, mock_tapis_client, mock_storage_system, project_model, mock_signal, mock_service_account): - 'Test add member.' - - prj = project_model.create(mock_tapis_client, 'Test Title', 'PRJ-123', mock_owner) - prj.storage.roles.for_user.return_value = MagicMock(role='USER', ADMIN='ADMIN') +def test_add_copi_unauthorized( + mock_owner, + django_user_model, + mock_tapis_client, + mock_storage_system, + project_model, + mock_signal, + mock_service_account, +): + "Test add member." + + prj = project_model.create(mock_tapis_client, "Test Title", "PRJ-123", mock_owner) + prj.storage.roles.for_user.return_value = MagicMock(role="USER", ADMIN="ADMIN") assert not prj._can_edit_member(mock_owner) - mock_copi = django_user_model.objects.create_user(username='coPi', password='password') + mock_copi = django_user_model.objects.create_user( + username="coPi", password="password" + ) with pytest.raises(NotAuthorizedError): prj.add_co_pi(mock_copi) @@ -165,32 +304,48 @@ def test_add_copi_unauthorized(mock_owner, django_user_model, mock_tapis_client, @pytest.mark.skip(reason="TODOv3: update with new Shared Workspaces operations") -def test_add_pi(mock_owner, django_user_model, mock_tapis_client, mock_storage_system, project_model, mock_signal, mock_service_account): - - prj = project_model.create(mock_tapis_client, 'Test Title', 'PRJ-123', mock_owner) - prj.storage.roles.for_user.return_value = MagicMock(role='ADMIN', ADMIN='ADMIN') +def test_add_pi( + mock_owner, + django_user_model, + mock_tapis_client, + mock_storage_system, + project_model, + mock_signal, + mock_service_account, +): + + prj = project_model.create(mock_tapis_client, "Test Title", "PRJ-123", mock_owner) + prj.storage.roles.for_user.return_value = MagicMock(role="ADMIN", ADMIN="ADMIN") assert prj._can_edit_member(mock_owner) - mock_pi = django_user_model.objects.create_user(username='pi', password='password') + mock_pi = django_user_model.objects.create_user(username="pi", password="password") prj.add_pi(mock_pi) - prj.storage.roles.add.assert_called_with('pi', 'OWNER') + prj.storage.roles.add.assert_called_with("pi", "OWNER") assert prj.storage.roles.save.call_count == 1 - assert prj.metadata.pi.username == 'pi' + assert prj.metadata.pi.username == "pi" prj.remove_pi(mock_pi) assert not prj.metadata.pi @pytest.mark.skip(reason="TODOv3: update with new Shared Workspaces operations") -def test_add_pi_unauthorized(mock_owner, django_user_model, mock_tapis_client, mock_storage_system, project_model, mock_signal, mock_service_account): - 'Test add member.' - - prj = project_model.create(mock_tapis_client, 'Test Title', 'PRJ-123', mock_owner) - prj.storage.roles.for_user.return_value = MagicMock(role='USER', ADMIN='ADMIN') +def test_add_pi_unauthorized( + mock_owner, + django_user_model, + mock_tapis_client, + mock_storage_system, + project_model, + mock_signal, + mock_service_account, +): + "Test add member." + + prj = project_model.create(mock_tapis_client, "Test Title", "PRJ-123", mock_owner) + prj.storage.roles.for_user.return_value = MagicMock(role="USER", ADMIN="ADMIN") assert not prj._can_edit_member(mock_owner) - mock_pi = django_user_model.objects.create_user(username='pi', password='password') + mock_pi = django_user_model.objects.create_user(username="pi", password="password") with pytest.raises(NotAuthorizedError): prj.add_pi(mock_pi) @@ -201,7 +356,9 @@ def test_add_pi_unauthorized(mock_owner, django_user_model, mock_tapis_client, m @pytest.mark.skip(reason="TODOv3: update with new Shared Workspaces operations") -def test_create_metadata(mock_owner, mock_tapis_client, mock_storage_system, project_model, mock_signal): +def test_create_metadata( + mock_owner, mock_tapis_client, mock_storage_system, project_model, mock_signal +): # Test creating metadata with no owner project_model._create_metadata("Project Title", "PRJ-123") assert ProjectMetadata.objects.get(project_id="PRJ-123").owner is None From d45a6b7d7a26c6c1dfbe276155c0598c854ae35e Mon Sep 17 00:00:00 2001 From: Jacob Lowe Date: Thu, 10 Oct 2024 14:49:16 -0500 Subject: [PATCH 02/16] test_project_init --- server/portal/apps/projects/unit_test.py | 112 ++++++++++------------- 1 file changed, 50 insertions(+), 62 deletions(-) diff --git a/server/portal/apps/projects/unit_test.py b/server/portal/apps/projects/unit_test.py index 6a86cb841..144154011 100644 --- a/server/portal/apps/projects/unit_test.py +++ b/server/portal/apps/projects/unit_test.py @@ -49,92 +49,80 @@ def mock_tapis_client(): # TODO: Convert this to become a mocker patch of the Metadata object, similar to mock_create_project # List of Metadata per project @pytest.fixture() -def mock_metadata(): - metadata = MagicMock() +def mock_metadata(mocker): + mock = mocker.patch( + "portal.apps.projects.models.Project._create_metadata", + autospec=True, + ) - def mock_init(title): - metadata.defaults = {"title": title} + def mock_init(title, project_id, owner=None): + mock_metadata = mocker.Mock() + mock_metadata.defaults = { + "title": title, + "project_id": project_id, + "owner": owner, + } + return mock_metadata - metadata.side_effect = mock_init - return metadata + mock.side_effect = mock_init + return mock -# Potentially holds a list of projects by the user @pytest.fixture() -def mock_storage_system(mocker): - mock = mocker.MagicMock() - mock.description = "All Project Storage" - - def mock_init(client, project_id): - mock.client = client - mock.project = project_id - mock.last_modified = "10-09-2024" - return mock - - mock.side_effect = mock_init +def mock_shared_systems(mocker): + mock = mocker.Mock() + mock.return_value = mocker.Mock( + description="All Project Storage", + storage=[], + ) return mock @pytest.fixture() -def mock_create_project(mocker): +def mock_create_project(mocker, mock_shared_systems, mock_metadata): mock = mocker.patch( - "portal.apps.projects.models.Project.storage", + "portal.apps.projects.models.Project.create", autospec=True, ) - def mock_init(mock_storage_system, client, project_id, metadata=None, storage=None): - mock.client = client - mock.project_id = project_id - if storage is None: - mock.storage = mock_storage_system(client, mock.project_id) - return mock + def mock_init(client, title, project_id, owner): + formatted_project_id = "{prefix}.{project_id}".format( + prefix=Project.metadata_name, project_id="PRJ-123" + ) + mock_project = mocker.Mock( + client=client, + project_id=project_id, + shared_systems=mock_shared_systems(client, id=formatted_project_id), + ) + metadata_instance = mock_metadata(title, formatted_project_id, owner) + mock_project.shared_systems.storage.append(metadata_instance) + return mock_project mock.side_effect = mock_init return mock -def test_project_init( - mock_tapis_client, mock_storage_system, mock_metadata, mock_create_project -): +def test_project_init(mock_tapis_client, mock_shared_systems, mock_create_project): "Test project model init." - # Mock a project creation - assert mock_storage_system.description == "All Project Storage" + mock_shared_systems_instance = mock_shared_systems.return_value - """ - # Test with no initial storage or metadata - prj = mock_create_project(mock_tapis_client, "PRJ-123") - mock_create_project.assert_called_with(mock_tapis_client, "PRJ-123") - assert prj.project_id == "PRJ-123" - - prj2 = mock_create_project( - mock_tapis_client, "PRJ-124", mock_metadata, mock_storage_system - ) - mock_create_project.assert_called_with( - mock_tapis_client, "PRJ-124", mock_metadata, mock_storage_system - ) - assert prj2.storage.last_modified == "10-09-2024" - assert prj2.storage.description == "my test description" - """ - - id = "{prefix}.{project_id}".format( - prefix=Project.metadata_name, project_id="PRJ-123" - ) + assert mock_shared_systems_instance.description == "All Project Storage" prj = mock_create_project( client=mock_tapis_client, - project_id=id, - mock_storage_system=mock_storage_system, + title="My Project", + project_id="PRJ-123", + owner=mock_owner, ) - assert prj.project_id == id - - # With Storage Systems - # In order to assert this, this needs to be called in a create project, then storage data would have to be made - mock_storage_system.assert_called_with(mock_tapis_client, id) - # Without Storage Systems (not possible, code is commented out to be depreicated see models/base.py) - - # TODO: Uncomment out and test that metadata shows and passses these tests - # assert ProjectMetadata.objects.all().count() == 1 - # assert ProjectMetadata.objects.get(project_id="PRJ-123", title="my title") + assert prj.project_id == "PRJ-123" + mock_shared_systems.assert_called_with( + mock_tapis_client, + id="{prefix}.{project_id}".format( + prefix=Project.metadata_name, project_id="PRJ-123" + ), + ) + assert len(mock_shared_systems_instance.storage) == 1 + assert mock_shared_systems_instance.storage[0].defaults["title"] == "My Project" @pytest.mark.skip(reason="TODOv3: update with new Shared Workspaces operations") From 33ccc19e2d5624f70a790d0cebec861ecebfe4da Mon Sep 17 00:00:00 2001 From: Jacob Lowe Date: Thu, 17 Oct 2024 17:05:14 -0500 Subject: [PATCH 03/16] Pivot to behavior based testing versus implementation based --- server/portal/apps/projects/unit_test.py | 149 +++++++++-------------- 1 file changed, 58 insertions(+), 91 deletions(-) diff --git a/server/portal/apps/projects/unit_test.py b/server/portal/apps/projects/unit_test.py index 144154011..b5fa8aa56 100644 --- a/server/portal/apps/projects/unit_test.py +++ b/server/portal/apps/projects/unit_test.py @@ -5,124 +5,91 @@ """ import logging -from unittest.mock import MagicMock +from unittest.mock import MagicMock, patch import pytest # pyright: ignore +from django.conf import settings +from tapipy.tapis import Tapis # pyright: ignore from portal.apps.projects.exceptions import NotAuthorizedError from portal.apps.projects.models.base import Project from portal.apps.projects.models.metadata import ProjectMetadata +from portal.apps.projects.workspace_operations import \ + shared_workspace_operations as ws_o LOGGER = logging.getLogger(__name__) pytestmark = pytest.mark.django_db -@pytest.fixture -def mock_service_account(mocker): - yield mocker.patch( - "portal.apps.projects.models.base.service_account", autospec=True - ) +# @pytest.fixture +# def mock_service_account(mocker): +# yield mocker.patch( +# "portal.apps.projects.models.base.service_account", autospec=True +# ) -@pytest.fixture() -def mock_signal(mocker): - yield mocker.patch("portal.apps.signals.receivers.index_project") +# @pytest.fixture() +# def mock_signal(mocker): +# yield mocker.patch("portal.apps.signals.receivers.index_project") -@pytest.fixture() -def mock_owner(django_user_model): - return django_user_model.objects.create_user( - username="username", password="password" - ) - - -# Start my fixtures +# @pytest.fixture() +# def mock_owner(django_user_model): +# return django_user_model.objects.create_user( +# username="username", password="password" +# ) # Mock creation of a project -@pytest.fixture() -def mock_tapis_client(): - return MagicMock() - - -# TODO: Convert this to become a mocker patch of the Metadata object, similar to mock_create_project -# List of Metadata per project -@pytest.fixture() -def mock_metadata(mocker): - mock = mocker.patch( - "portal.apps.projects.models.Project._create_metadata", - autospec=True, - ) - - def mock_init(title, project_id, owner=None): - mock_metadata = mocker.Mock() - mock_metadata.defaults = { - "title": title, - "project_id": project_id, - "owner": owner, - } - return mock_metadata - mock.side_effect = mock_init - return mock +# Minimal mocks, only do behavior based testing instead of implementation based testing @pytest.fixture() -def mock_shared_systems(mocker): - mock = mocker.Mock() - mock.return_value = mocker.Mock( - description="All Project Storage", - storage=[], - ) - return mock - - -@pytest.fixture() -def mock_create_project(mocker, mock_shared_systems, mock_metadata): - mock = mocker.patch( - "portal.apps.projects.models.Project.create", - autospec=True, - ) - - def mock_init(client, title, project_id, owner): - formatted_project_id = "{prefix}.{project_id}".format( - prefix=Project.metadata_name, project_id="PRJ-123" - ) - mock_project = mocker.Mock( - client=client, - project_id=project_id, - shared_systems=mock_shared_systems(client, id=formatted_project_id), - ) - metadata_instance = mock_metadata(title, formatted_project_id, owner) - mock_project.shared_systems.storage.append(metadata_instance) - return mock_project - - mock.side_effect = mock_init - return mock +def mock_tapis_client(): + with patch("tapipy.tapis.Tapis") as MockTapis: + mock_client = MockTapis.return_value + yield mock_client -def test_project_init(mock_tapis_client, mock_shared_systems, mock_create_project): +def test_project_init(mock_tapis_client): + assert 1 == 1 "Test project model init." - mock_shared_systems_instance = mock_shared_systems.return_value - - assert mock_shared_systems_instance.description == "All Project Storage" - - prj = mock_create_project( - client=mock_tapis_client, - title="My Project", - project_id="PRJ-123", - owner=mock_owner, + client = mock_tapis_client + # Create the shared workspace + project = ws_o.create_workspace_system( + client, + workspace_id="PRJ-123", + title="My New Workspace", + description="This is a test description", + owner=None, ) - assert prj.project_id == "PRJ-123" - mock_shared_systems.assert_called_with( - mock_tapis_client, - id="{prefix}.{project_id}".format( - prefix=Project.metadata_name, project_id="PRJ-123" - ), - ) - assert len(mock_shared_systems_instance.storage) == 1 - assert mock_shared_systems_instance.storage[0].defaults["title"] == "My Project" + + system_id = f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.PRJ-123" + assert project == system_id + + # Do the following below but with workspace operation functions + + # mock_shared_systems_instance = mock_shared_systems.return_value + + # assert mock_shared_systems_instance.description == "All Project Storage" + + # prj = mock_create_project( + # client=mock_tapis_client, + # title="My Project", + # project_id="PRJ-123", + # owner=mock_owner, + # ) + # assert prj.project_id == "PRJ-123" + # mock_shared_systems.assert_called_with( + # mock_tapis_client, + # id="{prefix}.{project_id}".format( + # prefix=Project.metadata_name, project_id="PRJ-123" + # ), + # ) + # assert len(mock_shared_systems_instance.storage) == 1 + # assert mock_shared_systems_instance.storage[0].defaults["title"] == "My Project" @pytest.mark.skip(reason="TODOv3: update with new Shared Workspaces operations") From f502095f26332e8b779eb69a99891981cb18c4a6 Mon Sep 17 00:00:00 2001 From: Jacob Lowe Date: Fri, 18 Oct 2024 15:27:56 -0500 Subject: [PATCH 04/16] test_project_init with mock tapis --- server/portal/apps/projects/unit_test.py | 86 ++++++++++++++++-------- 1 file changed, 58 insertions(+), 28 deletions(-) diff --git a/server/portal/apps/projects/unit_test.py b/server/portal/apps/projects/unit_test.py index b5fa8aa56..831ed6a01 100644 --- a/server/portal/apps/projects/unit_test.py +++ b/server/portal/apps/projects/unit_test.py @@ -50,46 +50,76 @@ def mock_tapis_client(): with patch("tapipy.tapis.Tapis") as MockTapis: mock_client = MockTapis.return_value + # Tapis server actions to Mock + # create_workspace_system calls + mock_client.systems.createSystem = MagicMock() + # get_project calls + mock_client.systems.getShareInfo = MagicMock() + mock_client.systems.getSystem = MagicMock() + mock_client.files.getPermissions = MagicMock() yield mock_client def test_project_init(mock_tapis_client): - assert 1 == 1 "Test project model init." + + # Assert Defs + result_system_id = f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.PRJ-123" + result_title = "My New Workspace" + result_description = "This is a test description" + result_created = "2024-10-18T00:00:00Z" + + # Mock Tapis Client client = mock_tapis_client + + # Mock shares return + mock_shares = MagicMock() + mock_shares.users = ["owner", "user"] + client.systems.getShareInfo.return_value = mock_shares + + # Mock system return + mock_system = MagicMock() + mock_system.owner = "owner" + mock_system.notes.title = result_title + mock_system.notes.description = result_description + mock_system.created = result_created + client.systems.getSystem.return_value = mock_system + + # Mock permission return + mock_permissions = MagicMock() + mock_permissions.permissions = "MODIFY" + client.files.getPermissions.return_value = mock_permissions + # Mocked get permissions based on username + def mock_get_permissions(systemId, path, username): + if username == "user": + return MagicMock(permission="MODIFY") + return MagicMock(permission="NONE") + client.files.getPermissions.side_effect = mock_get_permissions + # Create the shared workspace - project = ws_o.create_workspace_system( + # Does a Tapis call for client.systems.createSystem(**system_args) and stores it in the system + project_id = ws_o.create_workspace_system( client, workspace_id="PRJ-123", - title="My New Workspace", - description="This is a test description", + title=result_title, + description=result_description, owner=None, ) - system_id = f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.PRJ-123" - assert project == system_id - - # Do the following below but with workspace operation functions - - # mock_shared_systems_instance = mock_shared_systems.return_value - - # assert mock_shared_systems_instance.description == "All Project Storage" - - # prj = mock_create_project( - # client=mock_tapis_client, - # title="My Project", - # project_id="PRJ-123", - # owner=mock_owner, - # ) - # assert prj.project_id == "PRJ-123" - # mock_shared_systems.assert_called_with( - # mock_tapis_client, - # id="{prefix}.{project_id}".format( - # prefix=Project.metadata_name, project_id="PRJ-123" - # ), - # ) - # assert len(mock_shared_systems_instance.storage) == 1 - # assert mock_shared_systems_instance.storage[0].defaults["title"] == "My Project" + "Assert the results" + + # Assert Defs + assert project_id == result_system_id + project = ws_o.get_project(client, project_id) + assert project["title"] == result_title + assert project["description"] == result_description + assert project["created"] == result_created + assert project["projectId"] == result_system_id + assert len(project["members"]) == 2 + assert project["members"][0]["user"]["username"] == "owner" + assert project["members"][0]["access"] == "owner" + assert project["members"][1]["user"]["username"] == "user" + assert project["members"][1]["access"] == "edit" @pytest.mark.skip(reason="TODOv3: update with new Shared Workspaces operations") From 15e2709dc2872488e561bb804e27e532e8da13fa Mon Sep 17 00:00:00 2001 From: Jacob Lowe Date: Fri, 18 Oct 2024 16:09:10 -0500 Subject: [PATCH 05/16] prettier and pyright changes --- server/portal/apps/projects/unit_test.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/portal/apps/projects/unit_test.py b/server/portal/apps/projects/unit_test.py index 831ed6a01..a430033be 100644 --- a/server/portal/apps/projects/unit_test.py +++ b/server/portal/apps/projects/unit_test.py @@ -7,9 +7,8 @@ import logging from unittest.mock import MagicMock, patch -import pytest # pyright: ignore +import pytest from django.conf import settings -from tapipy.tapis import Tapis # pyright: ignore from portal.apps.projects.exceptions import NotAuthorizedError from portal.apps.projects.models.base import Project @@ -89,11 +88,13 @@ def test_project_init(mock_tapis_client): mock_permissions = MagicMock() mock_permissions.permissions = "MODIFY" client.files.getPermissions.return_value = mock_permissions + # Mocked get permissions based on username def mock_get_permissions(systemId, path, username): if username == "user": return MagicMock(permission="MODIFY") return MagicMock(permission="NONE") + client.files.getPermissions.side_effect = mock_get_permissions # Create the shared workspace From dbfc71c70ce66da21888f7a1c4800847ceb3f28a Mon Sep 17 00:00:00 2001 From: Jacob Lowe Date: Mon, 21 Oct 2024 11:46:16 -0500 Subject: [PATCH 06/16] test_project_create and generalized init mock --- server/portal/apps/projects/unit_test.py | 114 ++++++++++++++++------- 1 file changed, 79 insertions(+), 35 deletions(-) diff --git a/server/portal/apps/projects/unit_test.py b/server/portal/apps/projects/unit_test.py index a430033be..006082f7d 100644 --- a/server/portal/apps/projects/unit_test.py +++ b/server/portal/apps/projects/unit_test.py @@ -7,8 +7,8 @@ import logging from unittest.mock import MagicMock, patch -import pytest -from django.conf import settings +import pytest # pyright: ignore +from django.conf import settings # pyright: ignore from portal.apps.projects.exceptions import NotAuthorizedError from portal.apps.projects.models.base import Project @@ -21,11 +21,11 @@ pytestmark = pytest.mark.django_db -# @pytest.fixture -# def mock_service_account(mocker): -# yield mocker.patch( -# "portal.apps.projects.models.base.service_account", autospec=True -# ) +@pytest.fixture +def mock_service_account(mocker): + yield mocker.patch( + "portal.apps.projects.models.base.service_account", autospec=True + ) # @pytest.fixture() @@ -33,11 +33,11 @@ # yield mocker.patch("portal.apps.signals.receivers.index_project") -# @pytest.fixture() -# def mock_owner(django_user_model): -# return django_user_model.objects.create_user( -# username="username", password="password" -# ) +@pytest.fixture() +def mock_owner(django_user_model): + return django_user_model.objects.create_user( + username="username", password="password" + ) # Mock creation of a project @@ -59,11 +59,9 @@ def mock_tapis_client(): yield mock_client -def test_project_init(mock_tapis_client): - "Test project model init." - - # Assert Defs - result_system_id = f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.PRJ-123" +# Initial setup of mocks +@pytest.fixture() +def setup_mocks(mock_tapis_client): result_title = "My New Workspace" result_description = "This is a test description" result_created = "2024-10-18T00:00:00Z" @@ -85,11 +83,6 @@ def test_project_init(mock_tapis_client): client.systems.getSystem.return_value = mock_system # Mock permission return - mock_permissions = MagicMock() - mock_permissions.permissions = "MODIFY" - client.files.getPermissions.return_value = mock_permissions - - # Mocked get permissions based on username def mock_get_permissions(systemId, path, username): if username == "user": return MagicMock(permission="MODIFY") @@ -97,8 +90,22 @@ def mock_get_permissions(systemId, path, username): client.files.getPermissions.side_effect = mock_get_permissions + return client + + +def test_project_init(setup_mocks): + "Test project model init." + + # Assert Defs + result_system_id = f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.PRJ-123" + result_title = "My New Workspace" + result_description = "This is a test description" + result_created = "2024-10-18T00:00:00Z" + + # Mock Tapis Client + client = setup_mocks + # Create the shared workspace - # Does a Tapis call for client.systems.createSystem(**system_args) and stores it in the system project_id = ws_o.create_workspace_system( client, workspace_id="PRJ-123", @@ -107,9 +114,7 @@ def mock_get_permissions(systemId, path, username): owner=None, ) - "Assert the results" - - # Assert Defs + # Assert the results assert project_id == result_system_id project = ws_o.get_project(client, project_id) assert project["title"] == result_title @@ -123,15 +128,52 @@ def mock_get_permissions(systemId, path, username): assert project["members"][1]["access"] == "edit" -@pytest.mark.skip(reason="TODOv3: update with new Shared Workspaces operations") -def test_project_create( - mock_owner, - mock_tapis_client, - service_account, - mock_storage_system, - project_model, - mock_signal, -): +def test_project_create(setup_mocks, mock_owner, mock_service_account): + client = setup_mocks + result_system_id = f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.test.project-123" + with patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.service_account" + ) as mock_service_account, patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.increment_workspace_count" + ) as mock_increment_workspace_count, patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.create_workspace_dir" + ) as mock_create_workspace_dir, patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.set_workspace_acls" + ) as mock_set_workspace_acls: + + # Set return values for the mocks + mock_service_account.return_value = MagicMock() + + # Mock of increment_workspace_count + workspace_count = 122 # Example starting workspace + + def increment_workspace(): + nonlocal workspace_count + workspace_count += 1 + return workspace_count + + mock_increment_workspace_count.side_effect = increment_workspace + + # Create shared workspace test + project_id = ws_o.create_shared_workspace(client, "PRJ-123", mock_owner) + assert project_id == result_system_id + # Assert that the mocks were called + mock_service_account.assert_called_once() + mock_increment_workspace_count.assert_called_once() + mock_create_workspace_dir.assert_called_once_with( + f"{settings.PORTAL_PROJECTS_ID_PREFIX}-123" + ) + + mock_set_workspace_acls.assert_called_once_with( + mock_service_account.return_value, + settings.PORTAL_PROJECTS_ROOT_SYSTEM_NAME, + f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}-123", + mock_owner, + "add", + "writer", + ) + # TODO: Mocking the storage found at line 187 for storage and auth + """ Original Tests prj = project_model.create(mock_tapis_client, "Test Title", "PRJ-123", mock_owner) project_model._create_dir.assert_called_with("PRJ-123") mock_storage_system.assert_called_with( @@ -152,6 +194,8 @@ def test_project_create( "-----BEGIN RSA PRIVATE KEY-----" "change this" "-----END RSA PRIVATE KEY-----" ) + """ + @pytest.mark.skip(reason="TODOv3: update with new Shared Workspaces operations") def test_listing( From cd46e509064c325a280bccc8b3b65e36d2457938 Mon Sep 17 00:00:00 2001 From: Jacob Lowe Date: Mon, 21 Oct 2024 17:06:09 -0500 Subject: [PATCH 07/16] start listing --- server/portal/apps/projects/unit_test.py | 82 +++++++++++++++++------- 1 file changed, 60 insertions(+), 22 deletions(-) diff --git a/server/portal/apps/projects/unit_test.py b/server/portal/apps/projects/unit_test.py index 006082f7d..1de3bf230 100644 --- a/server/portal/apps/projects/unit_test.py +++ b/server/portal/apps/projects/unit_test.py @@ -11,7 +11,7 @@ from django.conf import settings # pyright: ignore from portal.apps.projects.exceptions import NotAuthorizedError -from portal.apps.projects.models.base import Project +# from portal.apps.projects.models.base import Project from portal.apps.projects.models.metadata import ProjectMetadata from portal.apps.projects.workspace_operations import \ shared_workspace_operations as ws_o @@ -129,8 +129,6 @@ def test_project_init(setup_mocks): def test_project_create(setup_mocks, mock_owner, mock_service_account): - client = setup_mocks - result_system_id = f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.test.project-123" with patch( "portal.apps.projects.workspace_operations.shared_workspace_operations.service_account" ) as mock_service_account, patch( @@ -141,6 +139,8 @@ def test_project_create(setup_mocks, mock_owner, mock_service_account): "portal.apps.projects.workspace_operations.shared_workspace_operations.set_workspace_acls" ) as mock_set_workspace_acls: + client = setup_mocks + result_system_id = f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.test.project-123" # Set return values for the mocks mock_service_account.return_value = MagicMock() @@ -197,25 +197,63 @@ def increment_workspace(): """ -@pytest.mark.skip(reason="TODOv3: update with new Shared Workspaces operations") -def test_listing( - mock_storage_system, mock_tapis_client, mock_signal, mock_projects_storage_systems -): - "Test projects listing." - mock_storage_system.search.return_value = mock_projects_storage_systems - - lst = list(Project.listing(mock_tapis_client)) - - mock_storage_system.search.assert_called_with( - mock_tapis_client, - query={ - "id.like": "{}*".format(Project.metadata_name), - "type.eq": mock_storage_system.TYPES.STORAGE, - }, - offset=0, - limit=100, - ) - assert len(lst) == 2 +def test_listing(setup_mocks, mock_owner): + with patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.service_account" + ) as mock_service_account, patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.increment_workspace_count" + ) as mock_increment_workspace_count, patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.create_workspace_dir" + ) as mock_create_workspace_dir, patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.set_workspace_acls" + ) as mock_set_workspace_acls, patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.list_projects" + ) as mock_list_projects: + "Test projects listing." + + # Mock Tapis Initial + client = setup_mocks + + # Create two projects/workspaces + project_id_1 = ws_o.create_shared_workspace(client, "PRJ-123", mock_owner) + project_id_2 = ws_o.create_shared_workspace(client, "PRJ-124", mock_owner) + + # Mock the return value of list_projects + # TODO: List Projects needs to match the output of the shared workspace operations + mock_list_projects.return_value = [ + {"project_id": project_id_1, "title": "Project 123"}, + {"project_id": project_id_2, "title": "Project 124"}, + ] + + # Call the function to list projects + projects = ws_o.list_projects(client) + + # Assert that the mocks were called + mock_increment_workspace_count.assert_called() + mock_create_workspace_dir.assert_called() + mock_set_workspace_acls.assert_called() + mock_list_projects.assert_called_once_with(client) + + # Verify the returned projects + assert len(projects) == 2 + assert projects[0]["project_id"] == project_id_1 + assert projects[0]["title"] == "Project 123" + assert projects[1]["project_id"] == project_id_2 + assert projects[1]["title"] == "Project 124" + """ + mock_storage_system.search.return_value = mock_projects_storage_systems + lst = list(Project.listing(mock_tapis_client)) + mock_storage_system.search.assert_called_with( + mock_tapis_client, + query={ + "id.like": "{}*".format(Project.metadata_name), + "type.eq": mock_storage_system.TYPES.STORAGE, + }, + offset=0, + limit=100, + ) + assert len(lst) == 2 + """ @pytest.mark.skip(reason="TODOv3: update with new Shared Workspaces operations") From 62182ccd11930bcd91afe58e80449bce9920bfbb Mon Sep 17 00:00:00 2001 From: Jacob Lowe Date: Mon, 21 Oct 2024 17:09:42 -0500 Subject: [PATCH 08/16] service account linting --- server/portal/apps/projects/unit_test.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/portal/apps/projects/unit_test.py b/server/portal/apps/projects/unit_test.py index 1de3bf230..875edd2d7 100644 --- a/server/portal/apps/projects/unit_test.py +++ b/server/portal/apps/projects/unit_test.py @@ -213,6 +213,8 @@ def test_listing(setup_mocks, mock_owner): # Mock Tapis Initial client = setup_mocks + # TODO: Remove + _ = mock_service_account # Create two projects/workspaces project_id_1 = ws_o.create_shared_workspace(client, "PRJ-123", mock_owner) From d89e8c6b8d23c50c0b6f2d41201590074cdd3cf6 Mon Sep 17 00:00:00 2001 From: Jacob Lowe Date: Tue, 22 Oct 2024 11:51:19 -0500 Subject: [PATCH 09/16] todo comments --- server/portal/apps/projects/unit_test.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/portal/apps/projects/unit_test.py b/server/portal/apps/projects/unit_test.py index 875edd2d7..fac939609 100644 --- a/server/portal/apps/projects/unit_test.py +++ b/server/portal/apps/projects/unit_test.py @@ -115,6 +115,7 @@ def test_project_init(setup_mocks): ) # Assert the results + # create_workspace_system main return assertion assert project_id == result_system_id project = ws_o.get_project(client, project_id) assert project["title"] == result_title @@ -155,7 +156,9 @@ def increment_workspace(): mock_increment_workspace_count.side_effect = increment_workspace # Create shared workspace test + # create_shared_workspace main return assertion project_id = ws_o.create_shared_workspace(client, "PRJ-123", mock_owner) + assert project_id == result_system_id # Assert that the mocks were called mock_service_account.assert_called_once() @@ -221,7 +224,8 @@ def test_listing(setup_mocks, mock_owner): project_id_2 = ws_o.create_shared_workspace(client, "PRJ-124", mock_owner) # Mock the return value of list_projects - # TODO: List Projects needs to match the output of the shared workspace operations + + # TODO: List Projects needs to match the output of the list of serialized listing shared workspace operations mock_list_projects.return_value = [ {"project_id": project_id_1, "title": "Project 123"}, {"project_id": project_id_2, "title": "Project 124"}, From e01d84051f0f9b4fd2dd5b6bb8fc948b45e05ea9 Mon Sep 17 00:00:00 2001 From: Jacob Lowe Date: Wed, 23 Oct 2024 13:58:27 -0500 Subject: [PATCH 10/16] test_add_member --- server/portal/apps/projects/unit_test.py | 113 ++++++++++++++++------- 1 file changed, 82 insertions(+), 31 deletions(-) diff --git a/server/portal/apps/projects/unit_test.py b/server/portal/apps/projects/unit_test.py index fac939609..a99593794 100644 --- a/server/portal/apps/projects/unit_test.py +++ b/server/portal/apps/projects/unit_test.py @@ -115,7 +115,6 @@ def test_project_init(setup_mocks): ) # Assert the results - # create_workspace_system main return assertion assert project_id == result_system_id project = ws_o.get_project(client, project_id) assert project["title"] == result_title @@ -156,7 +155,6 @@ def increment_workspace(): mock_increment_workspace_count.side_effect = increment_workspace # Create shared workspace test - # create_shared_workspace main return assertion project_id = ws_o.create_shared_workspace(client, "PRJ-123", mock_owner) assert project_id == result_system_id @@ -224,8 +222,6 @@ def test_listing(setup_mocks, mock_owner): project_id_2 = ws_o.create_shared_workspace(client, "PRJ-124", mock_owner) # Mock the return value of list_projects - - # TODO: List Projects needs to match the output of the list of serialized listing shared workspace operations mock_list_projects.return_value = [ {"project_id": project_id_1, "title": "Project 123"}, {"project_id": project_id_2, "title": "Project 124"}, @@ -262,34 +258,89 @@ def test_listing(setup_mocks, mock_owner): """ -@pytest.mark.skip(reason="TODOv3: update with new Shared Workspaces operations") -def test_add_member( - mock_owner, - django_user_model, - mock_tapis_client, - mock_storage_system, - project_model, - mock_signal, - mock_service_account, -): - "Test add member." - - prj = project_model.create(mock_tapis_client, "Test Title", "PRJ-123", mock_owner) - prj.storage.roles.for_user.return_value = MagicMock(role="ADMIN", ADMIN="ADMIN") - assert prj._can_edit_member(mock_owner) - - mock_team_member = django_user_model.objects.create_user( - username="teamMember", password="password" - ) - prj.add_member(mock_team_member) - - prj.storage.roles.add.assert_called_with("teamMember", "USER") - assert prj.storage.roles.save.call_count == 1 - assert prj.metadata.team_members.get(username="teamMember") +def test_add_member(setup_mocks, mock_owner, django_user_model): + with patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.service_account" + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.increment_workspace_count", + wraps=ws_o.increment_workspace_count, + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.create_workspace_system", + wraps=ws_o.create_workspace_system, + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.create_shared_workspace", + wraps=ws_o.create_shared_workspace, + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.create_workspace_dir", + wraps=ws_o.create_workspace_dir, + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.set_workspace_acls", + wraps=ws_o.set_workspace_acls, + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.add_user_to_workspace", + wraps=ws_o.add_user_to_workspace, + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.set_workspace_permissions", + wraps=ws_o.set_workspace_permissions, + ): + + "Test add member." + "Mocking add_user_to_workspace" - prj.remove_member(mock_team_member) - with pytest.raises(django_user_model.DoesNotExist): - prj.metadata.team_members.get(username="teamMember") + # Mock Tapis Initial + client = setup_mocks + # Create Test project to test workspace operation + system_id = ws_o.create_shared_workspace(client, "PRJ-123", mock_owner) + result_system_id = f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.test.project-2" + assert system_id == result_system_id + ws_o.service_account.assert_called() + ws_o.increment_workspace_count.assert_called() + ws_o.create_workspace_dir.assert_called_once_with("test.project-2") + ws_o.set_workspace_acls.assert_called() + ws_o.create_workspace_system.assert_called() + + # Start test of add user + # Assert that the mocks were called + project = ws_o.add_user_to_workspace( + client, system_id, "teamMember", role="writer" + ) + ws_o.service_account.assert_called() + ws_o.set_workspace_acls.assert_called() + # TODO: Fix why this is a test within a test project + client.systems.shareSystem.assert_called_once_with( + systemId="test.project.test.project.test.project-2", users=["teamMember"] + ) + ws_o.set_workspace_permissions.assert_called_once_with( + client, "teamMember", "test.project." + system_id, "writer" + ) + mock_users = [ + { + "user": { + "username": "owner", + "email": "", + "first_name": "", + "last_name": "", + }, + "access": "owner", + }, + { + "user": { + "username": "user", + "email": "", + "first_name": "", + "last_name": "", + }, + "access": "edit", + }, + ] + expected_project = { + "title": "My New Workspace", + "description": "This is a test description", + "created": "2024-10-18T00:00:00Z", + "projectId": system_id, + "members": mock_users, + } + assert project == expected_project @pytest.mark.skip(reason="TODOv3: update with new Shared Workspaces operations") From 58c2775a5aa3d647b00d26f8d51f3b7d933ca847 Mon Sep 17 00:00:00 2001 From: Jacob Lowe Date: Wed, 23 Oct 2024 15:53:57 -0500 Subject: [PATCH 11/16] test formatting to match test_add_member --- server/portal/apps/projects/unit_test.py | 230 ++++++++++------------- 1 file changed, 104 insertions(+), 126 deletions(-) diff --git a/server/portal/apps/projects/unit_test.py b/server/portal/apps/projects/unit_test.py index a99593794..6948844c8 100644 --- a/server/portal/apps/projects/unit_test.py +++ b/server/portal/apps/projects/unit_test.py @@ -94,135 +94,131 @@ def mock_get_permissions(systemId, path, username): def test_project_init(setup_mocks): - "Test project model init." - - # Assert Defs - result_system_id = f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.PRJ-123" - result_title = "My New Workspace" - result_description = "This is a test description" - result_created = "2024-10-18T00:00:00Z" - - # Mock Tapis Client - client = setup_mocks - - # Create the shared workspace - project_id = ws_o.create_workspace_system( - client, - workspace_id="PRJ-123", - title=result_title, - description=result_description, - owner=None, - ) - - # Assert the results - assert project_id == result_system_id - project = ws_o.get_project(client, project_id) - assert project["title"] == result_title - assert project["description"] == result_description - assert project["created"] == result_created - assert project["projectId"] == result_system_id - assert len(project["members"]) == 2 - assert project["members"][0]["user"]["username"] == "owner" - assert project["members"][0]["access"] == "owner" - assert project["members"][1]["user"]["username"] == "user" - assert project["members"][1]["access"] == "edit" - - -def test_project_create(setup_mocks, mock_owner, mock_service_account): with patch( - "portal.apps.projects.workspace_operations.shared_workspace_operations.service_account" - ) as mock_service_account, patch( - "portal.apps.projects.workspace_operations.shared_workspace_operations.increment_workspace_count" - ) as mock_increment_workspace_count, patch( - "portal.apps.projects.workspace_operations.shared_workspace_operations.create_workspace_dir" - ) as mock_create_workspace_dir, patch( - "portal.apps.projects.workspace_operations.shared_workspace_operations.set_workspace_acls" - ) as mock_set_workspace_acls: - - client = setup_mocks - result_system_id = f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.test.project-123" - # Set return values for the mocks - mock_service_account.return_value = MagicMock() - - # Mock of increment_workspace_count - workspace_count = 122 # Example starting workspace - - def increment_workspace(): - nonlocal workspace_count - workspace_count += 1 - return workspace_count + "portal.apps.projects.workspace_operations.shared_workspace_operations.create_workspace_system", + wraps=ws_o.create_workspace_system, + ): + "Test project model init." - mock_increment_workspace_count.side_effect = increment_workspace + # Assert Defs + result_system_id = f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.PRJ-123" + result_title = "My New Workspace" + result_description = "This is a test description" + result_created = "2024-10-18T00:00:00Z" - # Create shared workspace test - project_id = ws_o.create_shared_workspace(client, "PRJ-123", mock_owner) + # Mock Tapis Client + client = setup_mocks - assert project_id == result_system_id - # Assert that the mocks were called - mock_service_account.assert_called_once() - mock_increment_workspace_count.assert_called_once() - mock_create_workspace_dir.assert_called_once_with( - f"{settings.PORTAL_PROJECTS_ID_PREFIX}-123" + # Create the shared workspace + project_id = ws_o.create_workspace_system( + client, + workspace_id="PRJ-123", + title=result_title, + description=result_description, + owner=None, ) - - mock_set_workspace_acls.assert_called_once_with( - mock_service_account.return_value, - settings.PORTAL_PROJECTS_ROOT_SYSTEM_NAME, - f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}-123", - mock_owner, - "add", - "writer", + ws_o.create_workspace_system.assert_called_once_with( + client, + workspace_id="PRJ-123", + title=result_title, + description=result_description, + owner=None, ) - # TODO: Mocking the storage found at line 187 for storage and auth - """ Original Tests - prj = project_model.create(mock_tapis_client, "Test Title", "PRJ-123", mock_owner) - project_model._create_dir.assert_called_with("PRJ-123") - mock_storage_system.assert_called_with( - client=service_account(), - id="test.project.PRJ-123", - name="PRJ-123", - description="Test Title", - site="test", - ) - assert ProjectMetadata.objects.all().count() == 1 - assert ProjectMetadata.objects.get(project_id="PRJ-123", title="Test Title") - assert prj._ac == mock_tapis_client - assert prj.storage.storage.port == 22 + # Assert the results + assert project_id == result_system_id + project = ws_o.get_project(client, project_id) + assert project["title"] == result_title + assert project["description"] == result_description + assert project["created"] == result_created + assert project["projectId"] == result_system_id + assert len(project["members"]) == 2 + assert project["members"][0]["user"]["username"] == "owner" + assert project["members"][0]["access"] == "owner" + assert project["members"][1]["user"]["username"] == "user" + assert project["members"][1]["access"] == "edit" + + +def test_project_create(setup_mocks, mock_owner): + with patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.service_account", + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.increment_workspace_count", + wraps=ws_o.increment_workspace_count, + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.create_workspace_system", + wraps=ws_o.create_workspace_system, + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.create_shared_workspace", + wraps=ws_o.create_shared_workspace, + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.create_workspace_dir", + wraps=ws_o.create_workspace_dir, + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.set_workspace_acls", + wraps=ws_o.set_workspace_acls, + ): - assert prj.storage.storage.auth.username == "wma_prtl" - assert prj.storage.storage.auth.private_key == ( - "-----BEGIN RSA PRIVATE KEY-----" "change this" "-----END RSA PRIVATE KEY-----" - ) + "Test add member." + "Mocking add_user_to_workspace" - """ + # Mock Tapis Initial + client = setup_mocks + # Create Test project to test workspace operation + system_id = ws_o.create_shared_workspace(client, "PRJ-123", mock_owner) + result_system_id = f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.test.project-2" + assert system_id == result_system_id + ws_o.service_account.assert_called() + ws_o.increment_workspace_count.assert_called() + ws_o.create_workspace_dir.assert_called_once_with( + f"{settings.PORTAL_PROJECTS_ID_PREFIX}-2" + ) + ws_o.set_workspace_acls.assert_called() + ws_o.create_workspace_system.assert_called() def test_listing(setup_mocks, mock_owner): with patch( - "portal.apps.projects.workspace_operations.shared_workspace_operations.service_account" - ) as mock_service_account, patch( - "portal.apps.projects.workspace_operations.shared_workspace_operations.increment_workspace_count" - ) as mock_increment_workspace_count, patch( - "portal.apps.projects.workspace_operations.shared_workspace_operations.create_workspace_dir" - ) as mock_create_workspace_dir, patch( - "portal.apps.projects.workspace_operations.shared_workspace_operations.set_workspace_acls" - ) as mock_set_workspace_acls, patch( - "portal.apps.projects.workspace_operations.shared_workspace_operations.list_projects" - ) as mock_list_projects: - "Test projects listing." + "portal.apps.projects.workspace_operations.shared_workspace_operations.service_account", + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.increment_workspace_count", + wraps=ws_o.increment_workspace_count, + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.create_workspace_system", + wraps=ws_o.create_workspace_system, + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.create_shared_workspace", + wraps=ws_o.create_shared_workspace, + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.create_workspace_dir", + wraps=ws_o.create_workspace_dir, + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.list_projects", + wraps=ws_o.list_projects, + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.set_workspace_acls", + wraps=ws_o.set_workspace_acls, + ): # Mock Tapis Initial client = setup_mocks - # TODO: Remove - _ = mock_service_account # Create two projects/workspaces project_id_1 = ws_o.create_shared_workspace(client, "PRJ-123", mock_owner) project_id_2 = ws_o.create_shared_workspace(client, "PRJ-124", mock_owner) + # Assert that the mocks were called + ws_o.service_account.assert_called() + ws_o.increment_workspace_count.assert_called() + ws_o.create_workspace_dir.assert_any_call( + f"{settings.PORTAL_PROJECTS_ID_PREFIX}-2" + ) + assert ws_o.create_workspace_dir.call_count == 2 + ws_o.set_workspace_acls.assert_called() + ws_o.create_workspace_system.assert_called() + # TODO: Replace with the actual list_projects return value # Mock the return value of list_projects - mock_list_projects.return_value = [ + ws_o.list_projects.return_value = [ {"project_id": project_id_1, "title": "Project 123"}, {"project_id": project_id_2, "title": "Project 124"}, ] @@ -230,11 +226,7 @@ def test_listing(setup_mocks, mock_owner): # Call the function to list projects projects = ws_o.list_projects(client) - # Assert that the mocks were called - mock_increment_workspace_count.assert_called() - mock_create_workspace_dir.assert_called() - mock_set_workspace_acls.assert_called() - mock_list_projects.assert_called_once_with(client) + ws_o.list_projects.assert_called_once_with(client) # Verify the returned projects assert len(projects) == 2 @@ -242,20 +234,6 @@ def test_listing(setup_mocks, mock_owner): assert projects[0]["title"] == "Project 123" assert projects[1]["project_id"] == project_id_2 assert projects[1]["title"] == "Project 124" - """ - mock_storage_system.search.return_value = mock_projects_storage_systems - lst = list(Project.listing(mock_tapis_client)) - mock_storage_system.search.assert_called_with( - mock_tapis_client, - query={ - "id.like": "{}*".format(Project.metadata_name), - "type.eq": mock_storage_system.TYPES.STORAGE, - }, - offset=0, - limit=100, - ) - assert len(lst) == 2 - """ def test_add_member(setup_mocks, mock_owner, django_user_model): From 39092ed8233c024d1a0e476551295b4200fe964d Mon Sep 17 00:00:00 2001 From: Jacob Lowe Date: Thu, 24 Oct 2024 16:32:43 -0500 Subject: [PATCH 12/16] updated mock tapis for storage, disabled some tests temporarily later tests need the mock storage --- server/portal/apps/projects/unit_test.py | 114 ++++++++++------------- 1 file changed, 51 insertions(+), 63 deletions(-) diff --git a/server/portal/apps/projects/unit_test.py b/server/portal/apps/projects/unit_test.py index 6948844c8..7ca73c541 100644 --- a/server/portal/apps/projects/unit_test.py +++ b/server/portal/apps/projects/unit_test.py @@ -35,110 +35,96 @@ def mock_service_account(mocker): @pytest.fixture() def mock_owner(django_user_model): - return django_user_model.objects.create_user( - username="username", password="password" - ) + return "owner" # Mock creation of a project # Minimal mocks, only do behavior based testing instead of implementation based testing - @pytest.fixture() def mock_tapis_client(): - with patch("tapipy.tapis.Tapis") as MockTapis: - mock_client = MockTapis.return_value - # Tapis server actions to Mock - # create_workspace_system calls - mock_client.systems.createSystem = MagicMock() - # get_project calls + with patch( + "tapipy.tapis.Tapis" + ) as mock_client: + + mock_listing = [] + class DictObj: + def __init__(self, **entries): + # Recursive conversion of nested dictionaries into object for attribute reference + # This is needed because of the behavior of shared_workspace_operations.py and its usage of objects from JSON + for key, value in entries.items(): + if isinstance(value, dict): + self.__dict__[key] = DictObj(**value) + else: + self.__dict__[key] = value + # Mock createSystem with the side effect + def create_system_side_effect(**system_args): + system_created = "2024-10-18T00:00:00Z" + system_args['created'] = system_created + system_obj = DictObj(**system_args) + title = getattr(system_obj.notes, "title", None) + mock_listing.append(system_obj) + def get_system_side_effect(systemId): + # TODO: Change for multiple systems, tests only one system for now + return mock_listing[0] + + mock_client.systems.createSystem = MagicMock(side_effect=create_system_side_effect) mock_client.systems.getShareInfo = MagicMock() - mock_client.systems.getSystem = MagicMock() + mock_client.systems.getSystem = MagicMock(side_effect=get_system_side_effect) mock_client.files.getPermissions = MagicMock() + # TODO: Enable this for the list systems test + #mock_client.systems.getSystems = MagicMock(return_value=mock_listing) + mock_client.systems.getSystems = MagicMock() + yield mock_client -# Initial setup of mocks -@pytest.fixture() -def setup_mocks(mock_tapis_client): - result_title = "My New Workspace" - result_description = "This is a test description" - result_created = "2024-10-18T00:00:00Z" - - # Mock Tapis Client - client = mock_tapis_client - - # Mock shares return - mock_shares = MagicMock() - mock_shares.users = ["owner", "user"] - client.systems.getShareInfo.return_value = mock_shares - - # Mock system return - mock_system = MagicMock() - mock_system.owner = "owner" - mock_system.notes.title = result_title - mock_system.notes.description = result_description - mock_system.created = result_created - client.systems.getSystem.return_value = mock_system - - # Mock permission return - def mock_get_permissions(systemId, path, username): - if username == "user": - return MagicMock(permission="MODIFY") - return MagicMock(permission="NONE") - - client.files.getPermissions.side_effect = mock_get_permissions - return client - - -def test_project_init(setup_mocks): +def test_project_init(mock_tapis_client, mock_owner): with patch( "portal.apps.projects.workspace_operations.shared_workspace_operations.create_workspace_system", - wraps=ws_o.create_workspace_system, - ): + wraps=ws_o.create_workspace_system,),patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.get_project_user") as mock_get_project_user, patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.get_project",wraps=ws_o.get_project): "Test project model init." - # Assert Defs result_system_id = f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.PRJ-123" result_title = "My New Workspace" result_description = "This is a test description" result_created = "2024-10-18T00:00:00Z" + mock_get_project_user.return_value = {"username": "mock_user"} # Mock Tapis Client - client = setup_mocks - + client = mock_tapis_client # Create the shared workspace project_id = ws_o.create_workspace_system( client, workspace_id="PRJ-123", title=result_title, description=result_description, - owner=None, + owner=mock_owner, ) ws_o.create_workspace_system.assert_called_once_with( client, workspace_id="PRJ-123", title=result_title, description=result_description, - owner=None, + owner=mock_owner, ) # Assert the results assert project_id == result_system_id - project = ws_o.get_project(client, project_id) + project = ws_o.get_project(client, workspace_id="PRJ-123") assert project["title"] == result_title assert project["description"] == result_description assert project["created"] == result_created - assert project["projectId"] == result_system_id - assert len(project["members"]) == 2 - assert project["members"][0]["user"]["username"] == "owner" + assert project["projectId"] == "PRJ-123" + assert len(project["members"]) == 1 + assert project["members"][0]["user"]["username"] == "mock_user" assert project["members"][0]["access"] == "owner" - assert project["members"][1]["user"]["username"] == "user" - assert project["members"][1]["access"] == "edit" - +@pytest.mark.skip(reason="Needs to be updated with new Mocked Tapis Storage") def test_project_create(setup_mocks, mock_owner): with patch( "portal.apps.projects.workspace_operations.shared_workspace_operations.service_account", @@ -177,6 +163,7 @@ def test_project_create(setup_mocks, mock_owner): ws_o.create_workspace_system.assert_called() +@pytest.mark.skip(reason="Needs to be updated with new Mocked Tapis Storage") def test_listing(setup_mocks, mock_owner): with patch( "portal.apps.projects.workspace_operations.shared_workspace_operations.service_account", @@ -218,10 +205,10 @@ def test_listing(setup_mocks, mock_owner): # TODO: Replace with the actual list_projects return value # Mock the return value of list_projects - ws_o.list_projects.return_value = [ - {"project_id": project_id_1, "title": "Project 123"}, - {"project_id": project_id_2, "title": "Project 124"}, - ] + # ws_o.list_projects.return_value = [ + # {"project_id": project_id_1, "title": "Project 123"}, + # {"project_id": project_id_2, "title": "Project 124"}, + # ] # Call the function to list projects projects = ws_o.list_projects(client) @@ -236,6 +223,7 @@ def test_listing(setup_mocks, mock_owner): assert projects[1]["title"] == "Project 124" +@pytest.mark.skip(reason="Needs to be updated with new Mocked Tapis Storage") def test_add_member(setup_mocks, mock_owner, django_user_model): with patch( "portal.apps.projects.workspace_operations.shared_workspace_operations.service_account" From 56a5a0c385caf6fc0e87f0a490be01279565015d Mon Sep 17 00:00:00 2001 From: Jacob Lowe Date: Thu, 24 Oct 2024 16:34:10 -0500 Subject: [PATCH 13/16] linting and formatting --- server/portal/apps/projects/unit_test.py | 41 ++++++++++++++---------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/server/portal/apps/projects/unit_test.py b/server/portal/apps/projects/unit_test.py index 7ca73c541..8190b1785 100644 --- a/server/portal/apps/projects/unit_test.py +++ b/server/portal/apps/projects/unit_test.py @@ -35,20 +35,20 @@ def mock_service_account(mocker): @pytest.fixture() def mock_owner(django_user_model): - return "owner" + return "owner" # Mock creation of a project # Minimal mocks, only do behavior based testing instead of implementation based testing + @pytest.fixture() def mock_tapis_client(): - with patch( - "tapipy.tapis.Tapis" - ) as mock_client: - + with patch("tapipy.tapis.Tapis") as mock_client: + mock_listing = [] + class DictObj: def __init__(self, **entries): # Recursive conversion of nested dictionaries into object for attribute reference @@ -58,35 +58,41 @@ def __init__(self, **entries): self.__dict__[key] = DictObj(**value) else: self.__dict__[key] = value + # Mock createSystem with the side effect def create_system_side_effect(**system_args): system_created = "2024-10-18T00:00:00Z" - system_args['created'] = system_created + system_args["created"] = system_created system_obj = DictObj(**system_args) - title = getattr(system_obj.notes, "title", None) mock_listing.append(system_obj) + def get_system_side_effect(systemId): - # TODO: Change for multiple systems, tests only one system for now + # TODO: Change for multiple systems, tests only one system for now return mock_listing[0] - mock_client.systems.createSystem = MagicMock(side_effect=create_system_side_effect) + mock_client.systems.createSystem = MagicMock( + side_effect=create_system_side_effect + ) mock_client.systems.getShareInfo = MagicMock() mock_client.systems.getSystem = MagicMock(side_effect=get_system_side_effect) mock_client.files.getPermissions = MagicMock() # TODO: Enable this for the list systems test - #mock_client.systems.getSystems = MagicMock(return_value=mock_listing) + # mock_client.systems.getSystems = MagicMock(return_value=mock_listing) mock_client.systems.getSystems = MagicMock() - - yield mock_client + yield mock_client def test_project_init(mock_tapis_client, mock_owner): with patch( "portal.apps.projects.workspace_operations.shared_workspace_operations.create_workspace_system", - wraps=ws_o.create_workspace_system,),patch( - "portal.apps.projects.workspace_operations.shared_workspace_operations.get_project_user") as mock_get_project_user, patch( - "portal.apps.projects.workspace_operations.shared_workspace_operations.get_project",wraps=ws_o.get_project): + wraps=ws_o.create_workspace_system, + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.get_project_user" + ) as mock_get_project_user, patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.get_project", + wraps=ws_o.get_project, + ): "Test project model init." # Assert Defs result_system_id = f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.PRJ-123" @@ -96,7 +102,7 @@ def test_project_init(mock_tapis_client, mock_owner): mock_get_project_user.return_value = {"username": "mock_user"} # Mock Tapis Client - client = mock_tapis_client + client = mock_tapis_client # Create the shared workspace project_id = ws_o.create_workspace_system( client, @@ -119,11 +125,12 @@ def test_project_init(mock_tapis_client, mock_owner): assert project["title"] == result_title assert project["description"] == result_description assert project["created"] == result_created - assert project["projectId"] == "PRJ-123" + assert project["projectId"] == "PRJ-123" assert len(project["members"]) == 1 assert project["members"][0]["user"]["username"] == "mock_user" assert project["members"][0]["access"] == "owner" + @pytest.mark.skip(reason="Needs to be updated with new Mocked Tapis Storage") def test_project_create(setup_mocks, mock_owner): with patch( From a011ce91cea1311e3f36af20c454004a5afa1213 Mon Sep 17 00:00:00 2001 From: Jacob Lowe Date: Fri, 25 Oct 2024 13:31:49 -0500 Subject: [PATCH 14/16] mock get for project storage --- server/portal/apps/projects/unit_test.py | 87 +++++++++++++++++++----- 1 file changed, 69 insertions(+), 18 deletions(-) diff --git a/server/portal/apps/projects/unit_test.py b/server/portal/apps/projects/unit_test.py index 8190b1785..54580c7ed 100644 --- a/server/portal/apps/projects/unit_test.py +++ b/server/portal/apps/projects/unit_test.py @@ -53,23 +53,56 @@ class DictObj: def __init__(self, **entries): # Recursive conversion of nested dictionaries into object for attribute reference # This is needed because of the behavior of shared_workspace_operations.py and its usage of objects from JSON + # print("Start the DICT") for key, value in entries.items(): + # print(f"Init key {key} with value {value}") if isinstance(value, dict): self.__dict__[key] = DictObj(**value) else: self.__dict__[key] = value + # TODO: This could be the get project instead of just sending in a key of "id" send in the + # key value of the id to return the payload + def get(self, key): + if key in self.__dict__: + id = self.__dict__["id"] + created = self.__dict__["created"] + users = {"username": "mock_user"} + for value in self.__dict__.values(): + # If this is a dictionary object inside of the dictionary object + if isinstance(value, DictObj): + first_key = next(iter(value.__dict__), None) + if ( + first_key == "title" + ): # Then it is known it is the notes converted Dict + title = value.__dict__["title"] + description = value.__dict__["description"] + data = { + "title": title, + "description": description, + "created": created, + "projectId": id, + "members": users, + } + return data + # Mock createSystem with the side effect def create_system_side_effect(**system_args): system_created = "2024-10-18T00:00:00Z" system_args["created"] = system_created system_obj = DictObj(**system_args) + # print(system_obj) mock_listing.append(system_obj) def get_system_side_effect(systemId): # TODO: Change for multiple systems, tests only one system for now return mock_listing[0] + def get_project_side_effect(workspace_id): + for project in mock_listing: + # print(project.get("id")) + return project.get("id") + mock_client.systems.createSystem = MagicMock( side_effect=create_system_side_effect ) @@ -79,6 +112,7 @@ def get_system_side_effect(systemId): # TODO: Enable this for the list systems test # mock_client.systems.getSystems = MagicMock(return_value=mock_listing) mock_client.systems.getSystems = MagicMock() + mock_client.get_project = MagicMock(side_effect=get_project_side_effect) yield mock_client @@ -131,43 +165,60 @@ def test_project_init(mock_tapis_client, mock_owner): assert project["members"][0]["access"] == "owner" -@pytest.mark.skip(reason="Needs to be updated with new Mocked Tapis Storage") -def test_project_create(setup_mocks, mock_owner): +def test_project_create(mock_tapis_client, mock_owner): with patch( "portal.apps.projects.workspace_operations.shared_workspace_operations.service_account", - ), patch( - "portal.apps.projects.workspace_operations.shared_workspace_operations.increment_workspace_count", - wraps=ws_o.increment_workspace_count, - ), patch( - "portal.apps.projects.workspace_operations.shared_workspace_operations.create_workspace_system", - wraps=ws_o.create_workspace_system, ), patch( "portal.apps.projects.workspace_operations.shared_workspace_operations.create_shared_workspace", wraps=ws_o.create_shared_workspace, + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.increment_workspace_count", + wraps=ws_o.increment_workspace_count, ), patch( "portal.apps.projects.workspace_operations.shared_workspace_operations.create_workspace_dir", wraps=ws_o.create_workspace_dir, ), patch( "portal.apps.projects.workspace_operations.shared_workspace_operations.set_workspace_acls", wraps=ws_o.set_workspace_acls, + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.create_workspace_system", + wraps=ws_o.create_workspace_system, ): "Test add member." "Mocking add_user_to_workspace" - + # Intial assertions + title = "PRJ-123" + description = "" + created = "2024-10-18T00:00:00Z" # Mock Tapis Initial - client = setup_mocks - # Create Test project to test workspace operation - system_id = ws_o.create_shared_workspace(client, "PRJ-123", mock_owner) - result_system_id = f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.test.project-2" - assert system_id == result_system_id + client = mock_tapis_client + # Create Test project to test workspace operation, this is testing for no given description and it making a workspace system call + workspace_id = "test.project-2" + system_id = ws_o.create_shared_workspace(client, title, mock_owner) + + # Calls and Mock calls in create_shared_workspace ws_o.service_account.assert_called() ws_o.increment_workspace_count.assert_called() - ws_o.create_workspace_dir.assert_called_once_with( - f"{settings.PORTAL_PROJECTS_ID_PREFIX}-2" + ws_o.create_workspace_dir.assert_called_with(workspace_id) + ws_o.set_workspace_acls.assert_called() # TODO: Calls to make a workspace permission,might need to be mocked + ws_o.create_workspace_system.assert_called_once_with( + client, + workspace_id, + title, ) - ws_o.set_workspace_acls.assert_called() - ws_o.create_workspace_system.assert_called() + + # Mocking the ws_o.get_project, using the mocked version I made in the Tapis mock + project = client.get_project(workspace_id=system_id) + + # Check return payload data format + assert project["title"] == title + assert project["description"] == description + assert project["created"] == created + assert project["projectId"] == system_id + assert len(project["members"]) == 1 + print(f'Assert: {project["members"]["username"]}') + assert project["members"]["username"] == "mock_user" @pytest.mark.skip(reason="Needs to be updated with new Mocked Tapis Storage") From 6fafae33f87fcf1dc3bd81ee3af1fdd82ad709e8 Mon Sep 17 00:00:00 2001 From: Jacob Lowe Date: Fri, 25 Oct 2024 15:53:32 -0500 Subject: [PATCH 15/16] test_listing with storage --- server/portal/apps/projects/unit_test.py | 155 +++++++++++++++-------- 1 file changed, 99 insertions(+), 56 deletions(-) diff --git a/server/portal/apps/projects/unit_test.py b/server/portal/apps/projects/unit_test.py index 54580c7ed..55dd4cf48 100644 --- a/server/portal/apps/projects/unit_test.py +++ b/server/portal/apps/projects/unit_test.py @@ -53,9 +53,7 @@ class DictObj: def __init__(self, **entries): # Recursive conversion of nested dictionaries into object for attribute reference # This is needed because of the behavior of shared_workspace_operations.py and its usage of objects from JSON - # print("Start the DICT") for key, value in entries.items(): - # print(f"Init key {key} with value {value}") if isinstance(value, dict): self.__dict__[key] = DictObj(**value) else: @@ -66,6 +64,7 @@ def __init__(self, **entries): def get(self, key): if key in self.__dict__: id = self.__dict__["id"] + port = self.__dict__["port"] created = self.__dict__["created"] users = {"username": "mock_user"} for value in self.__dict__.values(): @@ -77,21 +76,27 @@ def get(self, key): ): # Then it is known it is the notes converted Dict title = value.__dict__["title"] description = value.__dict__["description"] - data = { - "title": title, - "description": description, - "created": created, - "projectId": id, - "members": users, - } - return data + else: + private_key = value.__dict__["privateKey"] + data = { + "title": title, + "description": description, + "created": created, + "projectId": id, + "port": port, + "members": users, + "privateKey": private_key, + } + return data # Mock createSystem with the side effect def create_system_side_effect(**system_args): system_created = "2024-10-18T00:00:00Z" system_args["created"] = system_created + system_args["updated"] = system_created + if "owner" not in system_args: + system_args["owner"] = None system_obj = DictObj(**system_args) - # print(system_obj) mock_listing.append(system_obj) def get_system_side_effect(systemId): @@ -100,23 +105,25 @@ def get_system_side_effect(systemId): def get_project_side_effect(workspace_id): for project in mock_listing: - # print(project.get("id")) return project.get("id") + def get_systems_side_effect(*args, **kwargs): + mock_get_Systems_return = mock_listing + return mock_get_Systems_return + mock_client.systems.createSystem = MagicMock( side_effect=create_system_side_effect ) mock_client.systems.getShareInfo = MagicMock() mock_client.systems.getSystem = MagicMock(side_effect=get_system_side_effect) mock_client.files.getPermissions = MagicMock() - # TODO: Enable this for the list systems test - # mock_client.systems.getSystems = MagicMock(return_value=mock_listing) - mock_client.systems.getSystems = MagicMock() + mock_client.systems.getSystems = MagicMock(side_effect=get_systems_side_effect) mock_client.get_project = MagicMock(side_effect=get_project_side_effect) yield mock_client +# Test for creating a project without the shared workspace component def test_project_init(mock_tapis_client, mock_owner): with patch( "portal.apps.projects.workspace_operations.shared_workspace_operations.create_workspace_system", @@ -165,6 +172,7 @@ def test_project_init(mock_tapis_client, mock_owner): assert project["members"][0]["access"] == "owner" +# Test for creating a shared workspace and if it creates a new project def test_project_create(mock_tapis_client, mock_owner): with patch( "portal.apps.projects.workspace_operations.shared_workspace_operations.service_account", @@ -184,9 +192,6 @@ def test_project_create(mock_tapis_client, mock_owner): "portal.apps.projects.workspace_operations.shared_workspace_operations.create_workspace_system", wraps=ws_o.create_workspace_system, ): - - "Test add member." - "Mocking add_user_to_workspace" # Intial assertions title = "PRJ-123" description = "" @@ -212,73 +217,111 @@ def test_project_create(mock_tapis_client, mock_owner): project = client.get_project(workspace_id=system_id) # Check return payload data format + assert project["port"] == 22 + assert project["privateKey"] == ( + "-----BEGIN RSA PRIVATE KEY-----" + "change this" + "-----END RSA PRIVATE KEY-----" + ) assert project["title"] == title assert project["description"] == description assert project["created"] == created assert project["projectId"] == system_id assert len(project["members"]) == 1 - print(f'Assert: {project["members"]["username"]}') assert project["members"]["username"] == "mock_user" -@pytest.mark.skip(reason="Needs to be updated with new Mocked Tapis Storage") -def test_listing(setup_mocks, mock_owner): +# Mock of counter for increment_workspace_count +@pytest.fixture +def increment_counter(): + counter = {"count": 0} + + def side_effect(): + counter["count"] += 1 + return f"{counter['count']}" + + return counter, side_effect + + +def test_listing(mock_tapis_client, mock_owner, increment_counter): + counter, side_effect = increment_counter with patch( "portal.apps.projects.workspace_operations.shared_workspace_operations.service_account", - ), patch( - "portal.apps.projects.workspace_operations.shared_workspace_operations.increment_workspace_count", - wraps=ws_o.increment_workspace_count, - ), patch( - "portal.apps.projects.workspace_operations.shared_workspace_operations.create_workspace_system", - wraps=ws_o.create_workspace_system, ), patch( "portal.apps.projects.workspace_operations.shared_workspace_operations.create_shared_workspace", wraps=ws_o.create_shared_workspace, + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.increment_workspace_count", + side_effect=side_effect, ), patch( "portal.apps.projects.workspace_operations.shared_workspace_operations.create_workspace_dir", wraps=ws_o.create_workspace_dir, - ), patch( - "portal.apps.projects.workspace_operations.shared_workspace_operations.list_projects", - wraps=ws_o.list_projects, ), patch( "portal.apps.projects.workspace_operations.shared_workspace_operations.set_workspace_acls", wraps=ws_o.set_workspace_acls, + ), patch( + "portal.apps.projects.workspace_operations.shared_workspace_operations.create_workspace_system", + wraps=ws_o.create_workspace_system, ): + # Intial assertions # Mock Tapis Initial - client = setup_mocks + client = mock_tapis_client - # Create two projects/workspaces - project_id_1 = ws_o.create_shared_workspace(client, "PRJ-123", mock_owner) - project_id_2 = ws_o.create_shared_workspace(client, "PRJ-124", mock_owner) - # Assert that the mocks were called + # First Project + title = "PRJ-123" + description = "" + created = "2024-10-18T00:00:00Z" + + # Create Test project to test workspace operation, this is testing for no given description and it making a workspace system call + workspace_id = "test.project-1" + system_id = ws_o.create_shared_workspace(client, title, mock_owner) + + # Calls and Mock calls in create_shared_workspace ws_o.service_account.assert_called() ws_o.increment_workspace_count.assert_called() - ws_o.create_workspace_dir.assert_any_call( - f"{settings.PORTAL_PROJECTS_ID_PREFIX}-2" + ws_o.create_workspace_dir.assert_called_with(workspace_id) + ws_o.set_workspace_acls.assert_called() # TODO: Calls to make a workspace permission,might need to be mocked + ws_o.create_workspace_system.assert_called_with( + client, + workspace_id, + title, ) - assert ws_o.create_workspace_dir.call_count == 2 - ws_o.set_workspace_acls.assert_called() - ws_o.create_workspace_system.assert_called() - - # TODO: Replace with the actual list_projects return value - # Mock the return value of list_projects - # ws_o.list_projects.return_value = [ - # {"project_id": project_id_1, "title": "Project 123"}, - # {"project_id": project_id_2, "title": "Project 124"}, - # ] + project = client.get_project(workspace_id=system_id) + assert project["description"] == description + assert project["created"] == created - # Call the function to list projects - projects = ws_o.list_projects(client) + # Second Project + title2 = "PRJ-456" + description2 = "" + created2 = "2024-10-18T00:00:00Z" - ws_o.list_projects.assert_called_once_with(client) + # Create Test project to test workspace operation, this is testing for a given description and it making a workspace system call + workspace_id2 = "test.project-2" + system_id2 = ws_o.create_shared_workspace(client, title2, mock_owner) - # Verify the returned projects - assert len(projects) == 2 - assert projects[0]["project_id"] == project_id_1 - assert projects[0]["title"] == "Project 123" - assert projects[1]["project_id"] == project_id_2 - assert projects[1]["title"] == "Project 124" + # Calls and Mock calls in create_shared_workspace + ws_o.service_account.assert_called() + ws_o.increment_workspace_count.assert_called() + ws_o.create_workspace_dir.assert_called_with(workspace_id2) + ws_o.set_workspace_acls.assert_called() # TODO: Calls to make a workspace permission, might need to be mocked + ws_o.create_workspace_system.assert_called_with( + client, + workspace_id2, + title2, + ) + project2 = client.get_project(workspace_id=system_id2) + assert project2["description"] == description2 + assert project2["created"] == created2 + + # Test the listing of list_projects + list = ws_o.list_projects(client) + fields = "id,host,description,notes,updated,owner,rootDir" + query = f"id.like.{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.*" + client.systems.getSystems.assert_called_with( + listType="ALL", search=query, select=fields, limit=-1 + ) + assert len(list) == 2 @pytest.mark.skip(reason="Needs to be updated with new Mocked Tapis Storage") From d9636df761789f43b0a9ef2f09587b325315c876 Mon Sep 17 00:00:00 2001 From: Jacob Lowe Date: Fri, 25 Oct 2024 16:56:02 -0500 Subject: [PATCH 16/16] test_add_member init --- server/portal/apps/projects/unit_test.py | 91 +++++++++--------------- 1 file changed, 35 insertions(+), 56 deletions(-) diff --git a/server/portal/apps/projects/unit_test.py b/server/portal/apps/projects/unit_test.py index 55dd4cf48..5a1c7f6b5 100644 --- a/server/portal/apps/projects/unit_test.py +++ b/server/portal/apps/projects/unit_test.py @@ -46,7 +46,6 @@ def mock_owner(django_user_model): @pytest.fixture() def mock_tapis_client(): with patch("tapipy.tapis.Tapis") as mock_client: - mock_listing = [] class DictObj: @@ -243,6 +242,7 @@ def side_effect(): return counter, side_effect +# Testing if there are two projects Tapis def test_listing(mock_tapis_client, mock_owner, increment_counter): counter, side_effect = increment_counter with patch( @@ -324,8 +324,7 @@ def test_listing(mock_tapis_client, mock_owner, increment_counter): assert len(list) == 2 -@pytest.mark.skip(reason="Needs to be updated with new Mocked Tapis Storage") -def test_add_member(setup_mocks, mock_owner, django_user_model): +def test_add_member(mock_tapis_client, mock_owner, django_user_model): with patch( "portal.apps.projects.workspace_operations.shared_workspace_operations.service_account" ), patch( @@ -350,64 +349,44 @@ def test_add_member(setup_mocks, mock_owner, django_user_model): "portal.apps.projects.workspace_operations.shared_workspace_operations.set_workspace_permissions", wraps=ws_o.set_workspace_permissions, ): - "Test add member." "Mocking add_user_to_workspace" - - # Mock Tapis Initial - client = setup_mocks - # Create Test project to test workspace operation - system_id = ws_o.create_shared_workspace(client, "PRJ-123", mock_owner) - result_system_id = f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.test.project-2" - assert system_id == result_system_id - ws_o.service_account.assert_called() - ws_o.increment_workspace_count.assert_called() - ws_o.create_workspace_dir.assert_called_once_with("test.project-2") - ws_o.set_workspace_acls.assert_called() - ws_o.create_workspace_system.assert_called() - - # Start test of add user - # Assert that the mocks were called - project = ws_o.add_user_to_workspace( - client, system_id, "teamMember", role="writer" + # Assert Defs + result_system_id = f"{settings.PORTAL_PROJECTS_SYSTEM_PREFIX}.PRJ-123" + result_title = "My New Workspace" + result_description = "This is a test description" + # result_created = "2024-10-18T00:00:00Z" + # mock_get_project_user.return_value = {"username": "mock_user"} + # Mock Tapis Client + client = mock_tapis_client + # Create the shared workspace + project_id = ws_o.create_workspace_system( + client, + workspace_id="PRJ-123", + title=result_title, + description=result_description, + owner=mock_owner, ) - ws_o.service_account.assert_called() - ws_o.set_workspace_acls.assert_called() - # TODO: Fix why this is a test within a test project - client.systems.shareSystem.assert_called_once_with( - systemId="test.project.test.project.test.project-2", users=["teamMember"] + ws_o.create_workspace_system.assert_called_once_with( + client, + workspace_id="PRJ-123", + title=result_title, + description=result_description, + owner=mock_owner, ) - ws_o.set_workspace_permissions.assert_called_once_with( - client, "teamMember", "test.project." + system_id, "writer" + ws_o.add_user_to_workspace( + client, "PRJ-123", username="new_username", role="writer" ) - mock_users = [ - { - "user": { - "username": "owner", - "email": "", - "first_name": "", - "last_name": "", - }, - "access": "owner", - }, - { - "user": { - "username": "user", - "email": "", - "first_name": "", - "last_name": "", - }, - "access": "edit", - }, - ] - expected_project = { - "title": "My New Workspace", - "description": "This is a test description", - "created": "2024-10-18T00:00:00Z", - "projectId": system_id, - "members": mock_users, - } - assert project == expected_project + ws_o.service_account.assert_called() + ws_o.set_workspace_acls.assert_called() + # TODO: User is added here, needs to be called with args, next step in TODO + ws_o.set_workspace_permissions.assert_called() + # Assertions + project = client.get_project(workspace_id=result_system_id) + # Check return payload data format + + assert project_id == result_system_id + assert project["port"] == 22 @pytest.mark.skip(reason="TODOv3: update with new Shared Workspaces operations")