diff --git a/src/apps/applets/tests/fixtures/applets.py b/src/apps/applets/tests/fixtures/applets.py index 93cc6c15f64..1cffa66a509 100644 --- a/src/apps/applets/tests/fixtures/applets.py +++ b/src/apps/applets/tests/fixtures/applets.py @@ -364,6 +364,14 @@ async def applet_three( await teardown_applet(global_session, applet.id) +@pytest.fixture +async def lucy_applet_three_subject(session: AsyncSession, lucy: User, applet_three: AppletFull) -> SubjectSchema: + query = select(SubjectSchema).where(SubjectSchema.user_id == lucy.id, SubjectSchema.applet_id == applet_three.id) + res = await session.execute(query, execution_options={"synchronize_session": False}) + model = res.scalars().one() + return model + + @pytest.fixture(autouse=True, scope="session") async def applet_four( global_session: AsyncSession, diff --git a/src/apps/subjects/tests/tests.py b/src/apps/subjects/tests/tests.py index cd3a5a719a5..68b82b39585 100644 --- a/src/apps/subjects/tests/tests.py +++ b/src/apps/subjects/tests/tests.py @@ -197,6 +197,40 @@ async def applet_one_lucy_reviewer_with_subject( return applet_one +@pytest.fixture +async def applet_three_lucy_owner(session: AsyncSession, applet_three: AppletFull, lucy) -> AppletFull: + await UserAppletAccessService(session, lucy.id, applet_three.id).add_role(lucy.id, Role.OWNER) + return applet_three + + +@pytest.fixture +async def applet_one_tom_owner(session: AsyncSession, applet_one: AppletFull, tom) -> AppletFull: + await UserAppletAccessService(session, tom.id, applet_one.id).add_role(tom.id, Role.OWNER) + return applet_one + + +@pytest.fixture +async def tom_invitation_payload(tom: User) -> dict: + return dict( + email=tom.email_encrypted, + first_name=tom.first_name, + last_name=tom.last_name, + language="en", + role=Role.MANAGER + ) + + +@pytest.fixture +async def lucy_invitation_payload(lucy: User) -> dict: + return dict( + email=lucy.email_encrypted, + first_name=lucy.first_name, + last_name=lucy.last_name, + language="en", + role=Role.MANAGER + ) + + class TestSubjects(BaseTest): fixtures = [ "workspaces/fixtures/workspaces.json", @@ -207,6 +241,10 @@ class TestSubjects(BaseTest): subject_detail_url = "/subjects/{subject_id}" subject_relation_url = "/subjects/{subject_id}/relations/{source_subject_id}" answer_url = "/answers" + invite_accept_url = "/invitations/{key}/accept" + invite_respondent_url = "/invitations/{applet_id}/respondent" + invite_manager_url = "/invitations/{applet_id}/managers" + workspace_respondents_url = "/workspaces/{owner_id}/respondents" async def test_create_subject(self, client, tom: User, applet_one: AppletFull, create_shell_body): creator_id = str(tom.id) @@ -444,3 +482,66 @@ async def test_get_reviewer_subject( assert response.status_code == http.HTTPStatus.OK data = response.json() assert data + + async def test_cross_invitation_must_have_no_duplicates( + self, + client, + session, + tom: User, + lucy: User, + applet_one_tom_owner: AppletFull, + applet_three_lucy_owner: AppletFull, + tom_applet_one_subject: Subject, + lucy_applet_three_subject: Subject, + lucy_invitation_payload, + tom_invitation_payload + ): + async def lucy_login(): + await client.login(self.login_url, lucy.email_encrypted, "Test123") + + async def tom_login(): + await client.login(self.login_url, tom.email_encrypted, "Test1234!") + + # Login as Tom and send invitation to Lucy + await tom_login() + res = await client.post( + self.invite_manager_url.format(applet_id=str(applet_one_tom_owner.id)), + lucy_invitation_payload + ) + assert res.status_code == http.HTTPStatus.OK + lucy_invitation_key = res.json()["result"]["key"] + + # Login as Lucy and send invitation to Tom + await lucy_login() + res = await client.post( + self.invite_manager_url.format(applet_id=str(applet_three_lucy_owner.id)), + tom_invitation_payload + ) + assert res.status_code == http.HTTPStatus.OK + tom_invitation_key = res.json()["result"]["key"] + + # Accept invitations + await tom_login() + tom_accept_url = self.invite_accept_url.format(key=tom_invitation_key) + res = await client.post(tom_accept_url) + assert res.status_code == http.HTTPStatus.OK + + await lucy_login() + lucy_accept_url = self.invite_accept_url.format(key=lucy_invitation_key) + res = await client.post(lucy_accept_url) + assert res.status_code == http.HTTPStatus.OK + + url_tom_respondents = self.workspace_respondents_url.format(owner_id=tom.id) + url_lucy_respondents = self.workspace_respondents_url.format(owner_id=lucy.id) + + await tom_login() + res = await client.get(url_tom_respondents) + assert res.status_code == http.HTTPStatus.OK + tom_respondents_response = res.json() + assert tom_respondents_response["count"] == 2 + + await lucy_login() + res = await client.get(url_lucy_respondents) + assert res.status_code == http.HTTPStatus.OK + lucy_respondents_response = res.json() + assert lucy_respondents_response["count"] == 2 diff --git a/src/apps/workspaces/crud/user_applet_access.py b/src/apps/workspaces/crud/user_applet_access.py index 247a355dad7..859082010e1 100644 --- a/src/apps/workspaces/crud/user_applet_access.py +++ b/src/apps/workspaces/crud/user_applet_access.py @@ -502,7 +502,9 @@ async def get_workspace_respondents( isouter=True, ) query = query.where( - has_access, SubjectSchema.applet_id == applet_id if applet_id else True, SubjectSchema.soft_exists() + has_access, + UserAppletAccessSchema.owner_id == owner_id, + SubjectSchema.applet_id == applet_id if applet_id else True, SubjectSchema.soft_exists() ) query = query.group_by(