import flask from werkzeug.datastructures import ImmutableMultiDict from core_notifications.helpers.ordering import QueryOrdering from core_notifications.helpers.pagination import QueryPagination from core_notifications.models.dna_account import DNAAccount from core_notifications.models.image import Image from core_notifications.models.template import Template, TemplateVersion from core_notifications.settings import Settings from core_notifications.templating import views from core_notifications.templating.forms import TemplateForm from core_notifications.templating.services import RenderLiquidService from tests.core_notifications.factories import ImageFactory, TemplateFactory from tests.core_notifications.pytest_helpers import UrlForThisMixin class TestListTemplatesView(UrlForThisMixin): ENDPOINT = "templating.list_templates" def test_failure_authentication(self, app): with app.test_client() as client: resp = client.get(flask.url_for(self.ENDPOINT)) assert resp.status_code == 302 assert ( resp.location == f"{app.config.get('ATLAS_LOGIN_URL')}?" f"next={flask.url_for(self.ENDPOINT)}" ) def test_failure_authorization(self, app, authenticated): with app.test_client() as client: resp = client.get(flask.url_for(self.ENDPOINT)) assert resp.status_code == 401 def test_list_templates_no_params_success( self, app, faker, mocker, db_session, authorized_admin ): templates_count = faker.pyint(min_value=2, max_value=10) templates = TemplateFactory.build_batch(templates_count) for template in templates: db_session.add(template) db_session.commit() mocker.spy(views, "render_template") with app.test_client() as client: resp = client.get(flask.url_for(self.ENDPOINT)) expected_ordering = QueryOrdering( Template.query.with_relations(), [Template.id, Template.name, Template.created_by_name], "", default_is_desc=True, ) expected_pagination = QueryPagination( page=1, per_page=Settings.ITEMS_PER_PAGE, query=expected_ordering.query, ) render_expects = mocker.call( "templating/list_templates.html", templates=list(reversed(templates)), ordering=expected_ordering, pagination=expected_pagination, ) assert resp.status_code == 200 assert views.render_template.call_args_list == [render_expects] def test_list_templates_with_params_success( self, app, faker, mocker, db_session, authorized_admin ): templates_count = faker.pyint(min_value=21, max_value=30) templates = TemplateFactory.build_batch(templates_count) for template in templates: db_session.add(template) db_session.commit() mocker.spy(views, "render_template") page = 2 per_page = 20 order = "id" with app.test_client() as client: resp = client.get( flask.url_for( self.ENDPOINT, page=page, per_page=per_page, order=order, ) ) expected_ordering = QueryOrdering( Template.query.with_relations(), [Template.id, Template.name, Template.created_by_name], order, default_is_desc=True, ) expected_pagination = QueryPagination( page=page, per_page=per_page, query=expected_ordering.query, ) expected_templates = list(sorted(templates, key=lambda v: (v.id)))[ per_page: ] render_expects = mocker.call( "templating/list_templates.html", templates=expected_templates, ordering=expected_ordering, pagination=expected_pagination, ) assert resp.status_code == 200 assert views.render_template.call_args_list == [render_expects] class TestCreateTemplateViewTest(UrlForThisMixin): ENDPOINT = "templating.create_template" def test_failure_authentication(self, app): with app.test_client() as client: resp = client.get(flask.url_for(self.ENDPOINT)) assert resp.status_code == 302 assert ( resp.location == f"{app.config.get('ATLAS_LOGIN_URL')}?" f"next={flask.url_for(self.ENDPOINT)}" ) def test_failure_authorization(self, app, authenticated): with app.test_client() as client: resp = client.get(flask.url_for(self.ENDPOINT)) assert resp.status_code == 401 def test_get_success( self, app, mocker, db_session, faker, authorized_admin ): image = ImageFactory.build() db_session.add(image) db_session.commit() expected_form = TemplateForm(email=authorized_admin.email) expected_subject_preview = "" expected_preview = "" mocker.spy(views, "render_template") with app.test_client() as client: resp = client.get(self.url_for_this(app)) render_expects = mocker.call( "templating/edit_template.html", form=expected_form, subject_preview=expected_subject_preview, preview=expected_preview, images=Image.query.desc().all(), ) assert resp.status_code == 200 assert views.render_template.call_args_list == [render_expects] def test_post_success( self, app, mocker, db_session, faker, authorized_admin ): dna_account = DNAAccount.from_user(authorized_admin) db_session.add(dna_account) expected_data = { "action": TemplateForm.Actions.SAVE.name, "name": "test_name", "subject": "Test {{ test_var }}", "source": "{{ test_var }}", "context": '{"test_var": "test_var_value"}', "email": "", } form = TemplateForm(data=expected_data) assert form.validate(), "Initial form should be valid" mocker.spy(views.CreateTemplateService, "execute") with app.test_client() as client: resp = client.post(self.url_for_this(app), data=form.dump()) expected_location = flask.url_for("templating.list_templates") service_expects = mocker.call( **form.data, dna_account=DNAAccount.from_user(authorized_admin) ) assert resp.status_code == 302 assert resp.location == expected_location assert views.CreateTemplateService.execute.call_args_list == [ service_expects ] def test_post_send_success( self, app, mocker, db_session, faker, authorized_admin ): dna_account = DNAAccount.from_user(authorized_admin) db_session.add(dna_account) expected_data = { "action": TemplateForm.Actions.SEND.name, "name": "test_name", "subject": "Test {{ test_var }}", "source": "{{ test_var }}", "context": '{"test_var": "test_var_value"}', "email": "", } form = TemplateForm(data=expected_data) assert form.validate(), "Initial form should be valid" mocker.spy(views.CreateTemplateService, "execute") mocker.spy(views.SendTestEmailService, "execute") with app.test_client() as client: resp = client.post(self.url_for_this(app), data=form.dump()) template = views.CreateTemplateService.execute.spy_return expected_location = flask.url_for( "templating.update_template", template_id=template.id ) assert resp.status_code == 302 assert resp.location == expected_location assert views.CreateTemplateService.execute.call_args_list == [ mocker.call( **form.data, dna_account=DNAAccount.from_user(authorized_admin) ) ] assert views.SendTestEmailService.execute.call_args_list == [ mocker.call(template, authorized_admin.email) ] def test_post_failure_validation( self, app, mocker, db_session, faker, authorized_admin ): image = ImageFactory.build() db_session.add(image) db_session.commit() expected_data = { "action": TemplateForm.Actions.SAVE.name, "name": "test_name", "subject": "Test {{ test_var", "source": "{{ test_var ", "context": '{"test_var": "test_var_value"', "email": "", } form = TemplateForm(data=expected_data) assert not form.validate(), "Initial form should not be valid" subject_preview = RenderLiquidService.execute( form.data.get("subject"), form.data.get("context"), ) preview = RenderLiquidService.execute( form.data.get("source"), form.data.get("context"), ) mocker.spy(views, "render_template") with app.test_client() as client: resp = client.post(self.url_for_this(app), data=form.dump()) render_expects = mocker.call( "templating/edit_template.html", form=form, subject_preview=subject_preview, preview=preview, images=Image.query.desc().all(), ) assert resp.status_code == 200 assert views.render_template.call_args_list == [render_expects] class TestUpdateTemplateViewTest(UrlForThisMixin): ENDPOINT = "templating.update_template" def test_failure_authentication(self, app, db_session): template = TemplateFactory.build() db_session.add(template) db_session.commit() with app.test_client() as client: resp = client.get( flask.url_for(self.ENDPOINT, template_id=template.id) ) assert resp.status_code == 302 assert ( resp.location == f"{app.config.get('ATLAS_LOGIN_URL')}?" f"next={flask.url_for(self.ENDPOINT, template_id=template.id)}" ) def test_failure_authorization(self, app, db_session, authenticated): template = TemplateFactory.build() db_session.add(template) db_session.commit() with app.test_client() as client: resp = client.get( flask.url_for(self.ENDPOINT, template_id=template.id) ) assert resp.status_code == 401 def test_get_success( self, app, mocker, db_session, faker, authorized_admin ): template = TemplateFactory.build() db_session.add(template) image = ImageFactory.build() db_session.add(image) db_session.commit() mocker.spy(views, "render_template") with app.test_client() as client: resp = client.get(self.url_for_this(app, template_id=template.id)) template = Template.query.get(template.id) expected_form = TemplateForm( obj=template, email=authorized_admin.email ) expected_subject_preview = RenderLiquidService.execute( expected_form.data.get("subject"), expected_form.data.get("context"), ) expected_preview = RenderLiquidService.execute( expected_form.data.get("source"), expected_form.data.get("context"), ) render_expects = mocker.call( "templating/edit_template.html", form=expected_form, subject_preview=expected_subject_preview, preview=expected_preview, images=Image.query.desc().all(), ) assert resp.status_code == 200 assert views.render_template.call_args_list == [render_expects] def test_post_success( self, app, mocker, db_session, faker, authorized_admin ): dna_account = DNAAccount.from_user(authorized_admin) db_session.add(dna_account) template = TemplateFactory.build() db_session.add(template) db_session.commit() expected_data = { "action": TemplateForm.Actions.SAVE.name, "name": "test_name2", "subject": "test subject {{ test_var2 }}", "source": "{{ test_var2 }}", "context": '{"test_var2": "test_var_value2"}', "email": "", } form = TemplateForm(data=expected_data) assert form.validate(), "Initial form should be valid" mocker.spy(views.UpdateTemplateService, "execute") with app.test_client() as client: resp = client.post( self.url_for_this(app, template_id=template.id), data=form.dump(), ) template = Template.query.get(template.id) expected_location = flask.url_for("templating.list_templates") service_expects = mocker.call( template, dna_account=DNAAccount.from_user(authorized_admin), **form.data, ) assert resp.status_code == 302 assert resp.location == expected_location assert views.UpdateTemplateService.execute.call_args_list == [ service_expects ] def test_post_send_success( self, app, mocker, db_session, faker, authorized_admin ): dna_account = DNAAccount.from_user(authorized_admin) db_session.add(dna_account) template = TemplateFactory.build() db_session.add(template) db_session.commit() expected_data = { "action": TemplateForm.Actions.SEND.name, "name": "test_name2", "subject": "test subject {{ test_var2 }}", "source": "{{ test_var2 }}", "context": '{"test_var2": "test_var_value2"}', "email": "", } form = TemplateForm(data=expected_data) assert form.validate(), "Initial form should be valid" mocker.spy(views.UpdateTemplateService, "execute") mocker.spy(views.SendTestEmailService, "execute") with app.test_client() as client: resp = client.post( self.url_for_this(app, template_id=template.id), data=form.dump(), ) template = Template.query.get(template.id) assert resp.status_code == 200 assert views.UpdateTemplateService.execute.call_args_list == [ mocker.call( template, dna_account=DNAAccount.from_user(authorized_admin), **form.data, ) ] assert views.SendTestEmailService.execute.call_args_list == [ mocker.call(template, authorized_admin.email) ] def test_post_failure_validation( self, app, mocker, db_session, faker, authorized_admin ): template = TemplateFactory.build() db_session.add(template) image = ImageFactory.build() db_session.add(image) db_session.commit() expected_data = { "action": TemplateForm.Actions.SAVE.name, "name": "test_name", "subject": "test subject {{ test_var2 ", "source": "{{ test_var ", "context": '{"test_var": "test_var_value"', "email": "", } mocker.spy(views, "render_template") with app.test_client() as client: resp = client.post( self.url_for_this(app, template_id=template.id), data=ImmutableMultiDict(expected_data), ) template = Template.query.get(template.id) form = TemplateForm(data=expected_data, obj=template) assert not form.validate(), "Initial form should not be valid" subject_preview = RenderLiquidService.execute( form.data.get("subject"), form.data.get("context"), ) preview = RenderLiquidService.execute( form.data.get("source"), form.data.get("context"), ) render_expects = mocker.call( "templating/edit_template.html", form=form, subject_preview=subject_preview, preview=preview, images=Image.query.desc().all(), ) assert resp.status_code == 200 assert views.render_template.call_args_list == [render_expects] class TestTemplateVersionViewTest(UrlForThisMixin): ENDPOINT = "templating.template_version" def test_failure_authentication(self, app, db_session): template = TemplateFactory.build() db_session.add(template) version = TemplateVersion.from_template(template) db_session.add(version) db_session.commit() with app.test_client() as client: resp = client.get( flask.url_for(self.ENDPOINT, version_id=version.id) ) assert resp.status_code == 302 assert ( resp.location == f"{app.config.get('ATLAS_LOGIN_URL')}?" f"next={flask.url_for(self.ENDPOINT, version_id=version.id)}" ) def test_failure_authorization(self, app, authenticated, db_session): template = TemplateFactory.build() db_session.add(template) version = TemplateVersion.from_template(template) db_session.add(version) db_session.commit() with app.test_client() as client: resp = client.get( flask.url_for(self.ENDPOINT, version_id=version.id) ) assert resp.status_code == 401 def test_get_success( self, app, mocker, db_session, faker, authorized_admin ): template = TemplateFactory.build() db_session.add(template) version = TemplateVersion.from_template(template) template.versions.append(version) db_session.add(version) db_session.commit() mocker.spy(views, "render_template") with app.test_client() as client: resp = client.get(self.url_for_this(app, version_id=version.id)) version = TemplateVersion.query.get(version.id) render_expects = mocker.call( "templating/template_version.html", version=version, ) assert resp.status_code == 200 assert views.render_template.call_args_list == [render_expects] class TestEnableDisableTemplateViewTest(UrlForThisMixin): ENDPOINT = "templating.template_enable_disable" def test_failure_authentication(self, app, db_session): template = TemplateFactory.build() db_session.add(template) db_session.commit() with app.test_client() as client: resp = client.post( flask.url_for(self.ENDPOINT, template_id=template.id) ) assert resp.status_code == 302 assert ( resp.location == f"{app.config.get('ATLAS_LOGIN_URL')}?" f"next={flask.url_for(self.ENDPOINT, template_id=template.id)}" ) def test_failure_authorization(self, app, db_session, authenticated): template = TemplateFactory.build() db_session.add(template) db_session.commit() with app.test_client() as client: resp = client.post( flask.url_for(self.ENDPOINT, template_id=template.id) ) assert resp.status_code == 401 def test_get_failure( self, app, mocker, db_session, faker, authorized_admin ): template = TemplateFactory.build() db_session.add(template) image = ImageFactory.build() db_session.add(image) db_session.commit() mocker.spy(views, "render_template") with app.test_client() as client: resp = client.get(self.url_for_this(app, template_id=template.id)) assert resp.status_code == 405 def test_post_success( self, app, mocker, db_session, faker, authorized_admin ): dna_account = DNAAccount.from_user(authorized_admin) db_session.add(dna_account) template = TemplateFactory.build() db_session.add(template) db_session.commit() assert not template.is_active mocker.spy(views.EnableDisableTemplateService, "execute") with app.test_client() as client: resp = client.post( self.url_for_this(app, template_id=template.id), data={"is_active": True}, ) template = Template.query.get(template.id) expected_location = flask.url_for("templating.list_templates") service_expects = mocker.call( template, dna_account=DNAAccount.from_user(authorized_admin), is_active=True, ) assert resp.status_code == 302 assert resp.location == expected_location assert views.EnableDisableTemplateService.execute.call_args_list == [ service_expects ]