diff --git a/tests/unit/email/test_init.py b/tests/unit/email/test_init.py index 759fbde61243..2a0717d34516 100644 --- a/tests/unit/email/test_init.py +++ b/tests/unit/email/test_init.py @@ -1280,6 +1280,426 @@ def test_removed_project_email_to_owner( ] +class TestYankedReleaseEmail: + def test_send_yanked_project_release_email_to_maintainer( + self, pyramid_request, pyramid_config, monkeypatch + ): + stub_user = pretend.stub( + username="username", + name="", + email="email@example.com", + primary_email=pretend.stub(email="email@example.com", verified=True), + ) + stub_submitter_user = pretend.stub( + username="submitterusername", + name="", + email="submiteremail@example.com", + primary_email=pretend.stub( + email="submiteremail@example.com", verified=True + ), + ) + + subject_renderer = pyramid_config.testing_add_renderer( + "email/yanked-project-release/subject.txt" + ) + subject_renderer.string_response = "Email Subject" + body_renderer = pyramid_config.testing_add_renderer( + "email/yanked-project-release/body.txt" + ) + body_renderer.string_response = "Email Body" + html_renderer = pyramid_config.testing_add_renderer( + "email/yanked-project-release/body.html" + ) + html_renderer.string_response = "Email HTML Body" + + send_email = pretend.stub( + delay=pretend.call_recorder(lambda *args, **kwargs: None) + ) + pyramid_request.task = pretend.call_recorder(lambda *args, **kwargs: send_email) + monkeypatch.setattr(email, "send_email", send_email) + + release = pretend.stub( + version="0.0.0", + project=pretend.stub(name="test_project"), + created=datetime.datetime(2017, 2, 5, 0, 0, 0, 0), + ) + + result = email.send_yanked_project_release_email( + pyramid_request, + [stub_user, stub_submitter_user], + release=release, + submitter_name=stub_submitter_user.username, + submitter_role="Owner", + recipient_role="Maintainer", + ) + + assert result == { + "project": release.project.name, + "release": release.version, + "release_date": release.created.strftime("%Y-%m-%d"), + "submitter": stub_submitter_user.username, + "submitter_role": "owner", + "recipient_role_descr": "a maintainer", + } + + subject_renderer.assert_(project="test_project") + subject_renderer.assert_(release="0.0.0") + body_renderer.assert_(project="test_project") + body_renderer.assert_(release="0.0.0") + body_renderer.assert_(release_date=release.created.strftime("%Y-%m-%d")) + body_renderer.assert_(submitter=stub_submitter_user.username) + body_renderer.assert_(submitter_role="owner") + body_renderer.assert_(recipient_role_descr="a maintainer") + + assert pyramid_request.task.calls == [ + pretend.call(send_email), + pretend.call(send_email), + ] + + assert send_email.delay.calls == [ + pretend.call( + "username ", + attr.asdict( + EmailMessage( + subject="Email Subject", + body_text="Email Body", + body_html=( + "\n\n" + "

Email HTML Body

\n\n" + ), + ), + ), + ), + pretend.call( + "submitterusername ", + attr.asdict( + EmailMessage( + subject="Email Subject", + body_text="Email Body", + body_html=( + "\n\n" + "

Email HTML Body

\n\n" + ), + ) + ), + ), + ] + + def test_send_yanked_project_release_email_to_owner( + self, pyramid_request, pyramid_config, monkeypatch + ): + stub_user = pretend.stub( + username="username", + name="", + email="email@example.com", + primary_email=pretend.stub(email="email@example.com", verified=True), + ) + stub_submitter_user = pretend.stub( + username="submitterusername", + name="", + email="submiteremail@example.com", + primary_email=pretend.stub( + email="submiteremail@example.com", verified=True + ), + ) + + subject_renderer = pyramid_config.testing_add_renderer( + "email/yanked-project-release/subject.txt" + ) + subject_renderer.string_response = "Email Subject" + body_renderer = pyramid_config.testing_add_renderer( + "email/yanked-project-release/body.txt" + ) + body_renderer.string_response = "Email Body" + html_renderer = pyramid_config.testing_add_renderer( + "email/yanked-project-release/body.html" + ) + html_renderer.string_response = "Email HTML Body" + + send_email = pretend.stub( + delay=pretend.call_recorder(lambda *args, **kwargs: None) + ) + pyramid_request.task = pretend.call_recorder(lambda *args, **kwargs: send_email) + monkeypatch.setattr(email, "send_email", send_email) + + release = pretend.stub( + version="0.0.0", + project=pretend.stub(name="test_project"), + created=datetime.datetime(2017, 2, 5, 0, 0, 0, 0), + ) + + result = email.send_yanked_project_release_email( + pyramid_request, + [stub_user, stub_submitter_user], + release=release, + submitter_name=stub_submitter_user.username, + submitter_role="Owner", + recipient_role="Owner", + ) + + assert result == { + "project": release.project.name, + "release": release.version, + "release_date": release.created.strftime("%Y-%m-%d"), + "submitter": stub_submitter_user.username, + "submitter_role": "owner", + "recipient_role_descr": "an owner", + } + + subject_renderer.assert_(project="test_project") + subject_renderer.assert_(release="0.0.0") + body_renderer.assert_(project="test_project") + body_renderer.assert_(release="0.0.0") + body_renderer.assert_(release_date=release.created.strftime("%Y-%m-%d")) + body_renderer.assert_(submitter=stub_submitter_user.username) + body_renderer.assert_(submitter_role="owner") + body_renderer.assert_(recipient_role_descr="an owner") + + assert pyramid_request.task.calls == [ + pretend.call(send_email), + pretend.call(send_email), + ] + + assert send_email.delay.calls == [ + pretend.call( + "username ", + attr.asdict( + EmailMessage( + subject="Email Subject", + body_text="Email Body", + body_html=( + "\n\n" + "

Email HTML Body

\n\n" + ), + ), + ), + ), + pretend.call( + "submitterusername ", + attr.asdict( + EmailMessage( + subject="Email Subject", + body_text="Email Body", + body_html=( + "\n\n" + "

Email HTML Body

\n\n" + ), + ) + ), + ), + ] + + +class TestUnyankedReleaseEmail: + def test_send_unyanked_project_release_email_to_maintainer( + self, pyramid_request, pyramid_config, monkeypatch + ): + stub_user = pretend.stub( + username="username", + name="", + email="email@example.com", + primary_email=pretend.stub(email="email@example.com", verified=True), + ) + stub_submitter_user = pretend.stub( + username="submitterusername", + name="", + email="submiteremail@example.com", + primary_email=pretend.stub( + email="submiteremail@example.com", verified=True + ), + ) + + subject_renderer = pyramid_config.testing_add_renderer( + "email/unyanked-project-release/subject.txt" + ) + subject_renderer.string_response = "Email Subject" + body_renderer = pyramid_config.testing_add_renderer( + "email/unyanked-project-release/body.txt" + ) + body_renderer.string_response = "Email Body" + html_renderer = pyramid_config.testing_add_renderer( + "email/unyanked-project-release/body.html" + ) + html_renderer.string_response = "Email HTML Body" + + send_email = pretend.stub( + delay=pretend.call_recorder(lambda *args, **kwargs: None) + ) + pyramid_request.task = pretend.call_recorder(lambda *args, **kwargs: send_email) + monkeypatch.setattr(email, "send_email", send_email) + + release = pretend.stub( + version="0.0.0", + project=pretend.stub(name="test_project"), + created=datetime.datetime(2017, 2, 5, 0, 0, 0, 0), + ) + + result = email.send_unyanked_project_release_email( + pyramid_request, + [stub_user, stub_submitter_user], + release=release, + submitter_name=stub_submitter_user.username, + submitter_role="Owner", + recipient_role="Maintainer", + ) + + assert result == { + "project": release.project.name, + "release": release.version, + "release_date": release.created.strftime("%Y-%m-%d"), + "submitter": stub_submitter_user.username, + "submitter_role": "owner", + "recipient_role_descr": "a maintainer", + } + + subject_renderer.assert_(project="test_project") + subject_renderer.assert_(release="0.0.0") + body_renderer.assert_(project="test_project") + body_renderer.assert_(release="0.0.0") + body_renderer.assert_(release_date=release.created.strftime("%Y-%m-%d")) + body_renderer.assert_(submitter=stub_submitter_user.username) + body_renderer.assert_(submitter_role="owner") + body_renderer.assert_(recipient_role_descr="a maintainer") + + assert pyramid_request.task.calls == [ + pretend.call(send_email), + pretend.call(send_email), + ] + + assert send_email.delay.calls == [ + pretend.call( + "username ", + attr.asdict( + EmailMessage( + subject="Email Subject", + body_text="Email Body", + body_html=( + "\n\n" + "

Email HTML Body

\n\n" + ), + ), + ), + ), + pretend.call( + "submitterusername ", + attr.asdict( + EmailMessage( + subject="Email Subject", + body_text="Email Body", + body_html=( + "\n\n" + "

Email HTML Body

\n\n" + ), + ) + ), + ), + ] + + def test_send_unyanked_project_release_email_to_owner( + self, pyramid_request, pyramid_config, monkeypatch + ): + stub_user = pretend.stub( + username="username", + name="", + email="email@example.com", + primary_email=pretend.stub(email="email@example.com", verified=True), + ) + stub_submitter_user = pretend.stub( + username="submitterusername", + name="", + email="submiteremail@example.com", + primary_email=pretend.stub( + email="submiteremail@example.com", verified=True + ), + ) + + subject_renderer = pyramid_config.testing_add_renderer( + "email/unyanked-project-release/subject.txt" + ) + subject_renderer.string_response = "Email Subject" + body_renderer = pyramid_config.testing_add_renderer( + "email/unyanked-project-release/body.txt" + ) + body_renderer.string_response = "Email Body" + html_renderer = pyramid_config.testing_add_renderer( + "email/unyanked-project-release/body.html" + ) + html_renderer.string_response = "Email HTML Body" + + send_email = pretend.stub( + delay=pretend.call_recorder(lambda *args, **kwargs: None) + ) + pyramid_request.task = pretend.call_recorder(lambda *args, **kwargs: send_email) + monkeypatch.setattr(email, "send_email", send_email) + + release = pretend.stub( + version="0.0.0", + project=pretend.stub(name="test_project"), + created=datetime.datetime(2017, 2, 5, 0, 0, 0, 0), + ) + + result = email.send_unyanked_project_release_email( + pyramid_request, + [stub_user, stub_submitter_user], + release=release, + submitter_name=stub_submitter_user.username, + submitter_role="Owner", + recipient_role="Owner", + ) + + assert result == { + "project": release.project.name, + "release": release.version, + "release_date": release.created.strftime("%Y-%m-%d"), + "submitter": stub_submitter_user.username, + "submitter_role": "owner", + "recipient_role_descr": "an owner", + } + + subject_renderer.assert_(project="test_project") + subject_renderer.assert_(release="0.0.0") + body_renderer.assert_(project="test_project") + body_renderer.assert_(release="0.0.0") + body_renderer.assert_(release_date=release.created.strftime("%Y-%m-%d")) + body_renderer.assert_(submitter=stub_submitter_user.username) + body_renderer.assert_(submitter_role="owner") + body_renderer.assert_(recipient_role_descr="an owner") + + assert pyramid_request.task.calls == [ + pretend.call(send_email), + pretend.call(send_email), + ] + + assert send_email.delay.calls == [ + pretend.call( + "username ", + attr.asdict( + EmailMessage( + subject="Email Subject", + body_text="Email Body", + body_html=( + "\n\n" + "

Email HTML Body

\n\n" + ), + ), + ), + ), + pretend.call( + "submitterusername ", + attr.asdict( + EmailMessage( + subject="Email Subject", + body_text="Email Body", + body_html=( + "\n\n" + "

Email HTML Body

\n\n" + ), + ) + ), + ), + ] + + class TestRemovedReleaseEmail: def test_send_removed_project_release_email_to_maintainer( self, pyramid_request, pyramid_config, monkeypatch @@ -1385,7 +1805,7 @@ def test_send_removed_project_release_email_to_maintainer( ), ] - def test_send_removed_project_release_emai_to_owner( + def test_send_removed_project_release_email_to_owner( self, pyramid_request, pyramid_config, monkeypatch ): stub_user = pretend.stub( diff --git a/tests/unit/manage/test_views.py b/tests/unit/manage/test_views.py index 5bb3f585a44f..15f8e5e95d0f 100644 --- a/tests/unit/manage/test_views.py +++ b/tests/unit/manage/test_views.py @@ -2559,7 +2559,9 @@ class TestManageProjectRelease: def test_manage_project_release(self): files = pretend.stub() project = pretend.stub() - release = pretend.stub(project=project, files=pretend.stub(all=lambda: files)) + release = pretend.stub( + project=project, files=pretend.stub(all=lambda: files), yanked=False + ) request = pretend.stub() view = views.ManageProjectRelease(release, request) @@ -2611,6 +2613,332 @@ def test_delete_project_release_disallow_deletion(self, monkeypatch): ) ] + def test_yank_project_release(self, monkeypatch): + release = pretend.stub( + version="1.2.3", + canonical_version="1.2.3", + project=pretend.stub( + name="foobar", record_event=pretend.call_recorder(lambda *a, **kw: None) + ), + created=datetime.datetime(2017, 2, 5, 17, 18, 18, 462_634), + yanked=False, + ) + request = pretend.stub( + POST={"confirm_yank_version": release.version}, + method="POST", + db=pretend.stub(add=pretend.call_recorder(lambda a: None),), + flags=pretend.stub(enabled=pretend.call_recorder(lambda *a: False)), + route_path=pretend.call_recorder(lambda *a, **kw: "/the-redirect"), + session=pretend.stub(flash=pretend.call_recorder(lambda *a, **kw: None)), + user=pretend.stub(username=pretend.stub()), + remote_addr=pretend.stub(), + ) + journal_obj = pretend.stub() + journal_cls = pretend.call_recorder(lambda **kw: journal_obj) + + get_user_role_in_project = pretend.call_recorder( + lambda project_name, username, req: "Owner" + ) + monkeypatch.setattr(views, "get_user_role_in_project", get_user_role_in_project) + get_project_contributors = pretend.call_recorder( + lambda project_name, request: [request.user] + ) + monkeypatch.setattr(views, "get_project_contributors", get_project_contributors) + + monkeypatch.setattr(views, "JournalEntry", journal_cls) + send_yanked_project_release_email = pretend.call_recorder( + lambda req, contrib, **k: None + ) + monkeypatch.setattr( + views, + "send_yanked_project_release_email", + send_yanked_project_release_email, + ) + + view = views.ManageProjectRelease(release, request) + + result = view.yank_project_release() + + assert isinstance(result, HTTPSeeOther) + assert result.headers["Location"] == "/the-redirect" + + assert release.yanked + + assert get_user_role_in_project.calls == [ + pretend.call(release.project.name, request.user.username, request,), + pretend.call(release.project.name, request.user.username, request,), + ] + assert get_project_contributors.calls == [ + pretend.call(release.project.name, request,) + ] + + assert send_yanked_project_release_email.calls == [ + pretend.call( + request, + request.user, + release=release, + submitter_name=request.user.username, + submitter_role="Owner", + recipient_role="Owner", + ) + ] + + assert request.db.add.calls == [pretend.call(journal_obj)] + assert journal_cls.calls == [ + pretend.call( + name=release.project.name, + action="yank release", + version=release.version, + submitted_by=request.user, + submitted_from=request.remote_addr, + ) + ] + assert request.session.flash.calls == [ + pretend.call(f"Yanked release {release.version!r}", queue="success") + ] + assert request.route_path.calls == [ + pretend.call("manage.project.releases", project_name=release.project.name) + ] + assert release.project.record_event.calls == [ + pretend.call( + tag="project:release:yank", + ip_address=request.remote_addr, + additional={ + "submitted_by": request.user.username, + "canonical_version": release.canonical_version, + }, + ) + ] + + def test_yank_project_release_no_confirm(self): + release = pretend.stub( + version="1.2.3", project=pretend.stub(name="foobar"), yanked=False + ) + request = pretend.stub( + POST={"confirm_yank_version": ""}, + method="POST", + flags=pretend.stub(enabled=pretend.call_recorder(lambda *a: False)), + route_path=pretend.call_recorder(lambda *a, **kw: "/the-redirect"), + session=pretend.stub(flash=pretend.call_recorder(lambda *a, **kw: None)), + ) + view = views.ManageProjectRelease(release, request) + + result = view.yank_project_release() + + assert isinstance(result, HTTPSeeOther) + assert result.headers["Location"] == "/the-redirect" + + assert not release.yanked + + assert request.session.flash.calls == [ + pretend.call("Confirm the request", queue="error") + ] + assert request.route_path.calls == [ + pretend.call( + "manage.project.release", + project_name=release.project.name, + version=release.version, + ) + ] + + def test_yank_project_release_bad_confirm(self): + release = pretend.stub( + version="1.2.3", project=pretend.stub(name="foobar"), yanked=False + ) + request = pretend.stub( + POST={"confirm_yank_version": "invalid"}, + method="POST", + flags=pretend.stub(enabled=pretend.call_recorder(lambda *a: False)), + route_path=pretend.call_recorder(lambda *a, **kw: "/the-redirect"), + session=pretend.stub(flash=pretend.call_recorder(lambda *a, **kw: None)), + ) + view = views.ManageProjectRelease(release, request) + + result = view.yank_project_release() + + assert isinstance(result, HTTPSeeOther) + assert result.headers["Location"] == "/the-redirect" + + assert not release.yanked + + assert request.session.flash.calls == [ + pretend.call( + "Could not yank release - " + + f"'invalid' is not the same as {release.version!r}", + queue="error", + ) + ] + assert request.route_path.calls == [ + pretend.call( + "manage.project.release", + project_name=release.project.name, + version=release.version, + ) + ] + + def test_unyank_project_release(self, monkeypatch): + release = pretend.stub( + version="1.2.3", + canonical_version="1.2.3", + project=pretend.stub( + name="foobar", record_event=pretend.call_recorder(lambda *a, **kw: None) + ), + created=datetime.datetime(2017, 2, 5, 17, 18, 18, 462_634), + yanked=True, + ) + request = pretend.stub( + POST={"confirm_unyank_version": release.version}, + method="POST", + db=pretend.stub(add=pretend.call_recorder(lambda a: None),), + flags=pretend.stub(enabled=pretend.call_recorder(lambda *a: False)), + route_path=pretend.call_recorder(lambda *a, **kw: "/the-redirect"), + session=pretend.stub(flash=pretend.call_recorder(lambda *a, **kw: None)), + user=pretend.stub(username=pretend.stub()), + remote_addr=pretend.stub(), + ) + journal_obj = pretend.stub() + journal_cls = pretend.call_recorder(lambda **kw: journal_obj) + + get_user_role_in_project = pretend.call_recorder( + lambda project_name, username, req: "Owner" + ) + monkeypatch.setattr(views, "get_user_role_in_project", get_user_role_in_project) + get_project_contributors = pretend.call_recorder( + lambda project_name, request: [request.user] + ) + monkeypatch.setattr(views, "get_project_contributors", get_project_contributors) + + monkeypatch.setattr(views, "JournalEntry", journal_cls) + send_unyanked_project_release_email = pretend.call_recorder( + lambda req, contrib, **k: None + ) + monkeypatch.setattr( + views, + "send_unyanked_project_release_email", + send_unyanked_project_release_email, + ) + + view = views.ManageProjectRelease(release, request) + + result = view.unyank_project_release() + + assert isinstance(result, HTTPSeeOther) + assert result.headers["Location"] == "/the-redirect" + + assert not release.yanked + + assert get_user_role_in_project.calls == [ + pretend.call(release.project.name, request.user.username, request,), + pretend.call(release.project.name, request.user.username, request,), + ] + assert get_project_contributors.calls == [ + pretend.call(release.project.name, request,) + ] + + assert send_unyanked_project_release_email.calls == [ + pretend.call( + request, + request.user, + release=release, + submitter_name=request.user.username, + submitter_role="Owner", + recipient_role="Owner", + ) + ] + + assert request.db.add.calls == [pretend.call(journal_obj)] + assert journal_cls.calls == [ + pretend.call( + name=release.project.name, + action="unyank release", + version=release.version, + submitted_by=request.user, + submitted_from=request.remote_addr, + ) + ] + assert request.session.flash.calls == [ + pretend.call(f"Un-yanked release {release.version!r}", queue="success") + ] + assert request.route_path.calls == [ + pretend.call("manage.project.releases", project_name=release.project.name) + ] + assert release.project.record_event.calls == [ + pretend.call( + tag="project:release:unyank", + ip_address=request.remote_addr, + additional={ + "submitted_by": request.user.username, + "canonical_version": release.canonical_version, + }, + ) + ] + + def test_unyank_project_release_no_confirm(self): + release = pretend.stub( + version="1.2.3", project=pretend.stub(name="foobar"), yanked=True + ) + request = pretend.stub( + POST={"confirm_unyank_version": ""}, + method="POST", + flags=pretend.stub(enabled=pretend.call_recorder(lambda *a: False)), + route_path=pretend.call_recorder(lambda *a, **kw: "/the-redirect"), + session=pretend.stub(flash=pretend.call_recorder(lambda *a, **kw: None)), + ) + view = views.ManageProjectRelease(release, request) + + result = view.unyank_project_release() + + assert isinstance(result, HTTPSeeOther) + assert result.headers["Location"] == "/the-redirect" + + assert release.yanked + + assert request.session.flash.calls == [ + pretend.call("Confirm the request", queue="error") + ] + assert request.route_path.calls == [ + pretend.call( + "manage.project.release", + project_name=release.project.name, + version=release.version, + ) + ] + + def test_unyank_project_release_bad_confirm(self): + release = pretend.stub( + version="1.2.3", project=pretend.stub(name="foobar"), yanked=True + ) + request = pretend.stub( + POST={"confirm_unyank_version": "invalid"}, + method="POST", + flags=pretend.stub(enabled=pretend.call_recorder(lambda *a: False)), + route_path=pretend.call_recorder(lambda *a, **kw: "/the-redirect"), + session=pretend.stub(flash=pretend.call_recorder(lambda *a, **kw: None)), + ) + view = views.ManageProjectRelease(release, request) + + result = view.unyank_project_release() + + assert isinstance(result, HTTPSeeOther) + assert result.headers["Location"] == "/the-redirect" + + assert release.yanked + + assert request.session.flash.calls == [ + pretend.call( + "Could not un-yank release - " + + f"'invalid' is not the same as {release.version!r}", + queue="error", + ) + ] + assert request.route_path.calls == [ + pretend.call( + "manage.project.release", + project_name=release.project.name, + version=release.version, + ) + ] + def test_delete_project_release(self, monkeypatch): release = pretend.stub( version="1.2.3", @@ -2621,7 +2949,7 @@ def test_delete_project_release(self, monkeypatch): created=datetime.datetime(2017, 2, 5, 17, 18, 18, 462_634), ) request = pretend.stub( - POST={"confirm_version": release.version}, + POST={"confirm_delete_version": release.version}, method="POST", db=pretend.stub( delete=pretend.call_recorder(lambda a: None), @@ -2715,7 +3043,7 @@ def test_delete_project_release(self, monkeypatch): def test_delete_project_release_no_confirm(self): release = pretend.stub(version="1.2.3", project=pretend.stub(name="foobar")) request = pretend.stub( - POST={"confirm_version": ""}, + POST={"confirm_delete_version": ""}, method="POST", db=pretend.stub(delete=pretend.call_recorder(lambda a: None)), flags=pretend.stub(enabled=pretend.call_recorder(lambda *a: False)), @@ -2747,7 +3075,7 @@ def test_delete_project_release_no_confirm(self): def test_delete_project_release_bad_confirm(self): release = pretend.stub(version="1.2.3", project=pretend.stub(name="foobar")) request = pretend.stub( - POST={"confirm_version": "invalid"}, + POST={"confirm_delete_version": "invalid"}, method="POST", db=pretend.stub(delete=pretend.call_recorder(lambda a: None)), flags=pretend.stub(enabled=pretend.call_recorder(lambda *a: False)),