from io import BytesIO import flask from werkzeug.datastructures import FileStorage from werkzeug.utils import secure_filename from core_notifications.helpers.either import Right from core_notifications.helpers.ordering import QueryOrdering from core_notifications.helpers.pagination import QueryPagination from core_notifications.images import views from core_notifications.images.forms import ImageForm from core_notifications.images.services import CreateImageService from core_notifications.models import DNAAccount, Image from core_notifications.settings import Settings from tests.core_notifications.factories import ImageFactory from tests.core_notifications.pytest_helpers import UrlForThisMixin class TestListImagesView(UrlForThisMixin): ENDPOINT = "images.list_images" 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_images_no_params_success( self, app, faker, mocker, db_session, authorized_admin ): images_count = faker.pyint(min_value=2, max_value=10) images = ImageFactory.build_batch(images_count) for image in images: db_session.add(image) 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( Image.query.with_relations(), [Image.id, Image.name, Image.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( "images/list_images.html", images=list(reversed(images)), ordering=expected_ordering, pagination=expected_pagination, ) assert resp.status_code == 200 assert views.render_template.call_args_list == [render_expects] def test_list_images_with_params_success( self, app, faker, mocker, db_session, authorized_admin ): images_count = faker.pyint(min_value=20, max_value=30) images = ImageFactory.build_batch(images_count) for image in images: db_session.add(image) 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( Image.query.with_relations(), [Image.id, Image.name, Image.created_by_name], order, default_is_desc=True, ) expected_pagination = QueryPagination( page=page, per_page=per_page, query=expected_ordering.query, ) expected_images = list(sorted(images, key=lambda v: (v.id)))[ per_page: ] render_expects = mocker.call( "images/list_images.html", images=expected_images, ordering=expected_ordering, pagination=expected_pagination, ) assert resp.status_code == 200 assert views.render_template.call_args_list == [render_expects] class TestUploadImageViewTest(UrlForThisMixin): ENDPOINT = "images.upload_image" 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_post_success( self, app, mocker, db_session, faker, authorized_admin ): dna_account = DNAAccount.from_user(authorized_admin) db_session.add(dna_account) source = BytesIO() file = FileStorage(stream=source, filename="test.png") form = ImageForm(file=file) assert form.validate() mocker.spy(CreateImageService, "execute") mocker.patch( "core_notifications.images.views.upload_file_to_s3" ).return_value = Right(secure_filename(file.filename)) with app.test_client() as client: resp = client.post(self.url_for_this(app), data={"file": file}) expected_location = flask.url_for("images.list_images") service_expects = mocker.call( name=secure_filename(form.file.data.filename), dna_account=DNAAccount.from_user(authorized_admin), ) assert resp.status_code == 302 assert resp.location == expected_location assert CreateImageService.execute.call_args_list == [service_expects] def test_post_success_ajax( self, app, mocker, db_session, faker, authorized_admin ): dna_account = DNAAccount.from_user(authorized_admin) db_session.add(dna_account) source = BytesIO() file = FileStorage(stream=source, filename="test.png") form = ImageForm(file=file) assert form.validate() mocker.spy(CreateImageService, "execute") mocker.patch( "core_notifications.images.views.upload_file_to_s3" ).return_value = Right(secure_filename(file.filename)) with app.test_client() as client: resp = client.post( self.url_for_this(app, is_ajax=True), data={"file": file} ) image = CreateImageService.execute.spy_return expected_json = { "success": True, "name": image.name, "url": image.url, "fallbackbase64": flask.url_for( "images.base64_image", image_id=image.id ), } service_expects = mocker.call( name=secure_filename(form.file.data.filename), dna_account=DNAAccount.from_user(authorized_admin), ) assert resp.status_code == 200 assert resp.json == expected_json assert CreateImageService.execute.call_args_list == [service_expects] class TestViewImageViewTest(UrlForThisMixin): ENDPOINT = "images.view_image" def test_failure_authentication(self, app, db_session): image = ImageFactory.build() db_session.add(image) db_session.commit() with app.test_client() as client: resp = client.get(self.url_for_this(app, image_id=image.id)) assert resp.status_code == 302 assert resp.location == ( f"{app.config.get('ATLAS_LOGIN_URL')}?" f"next={self.url_for_this(app, image_id=image.id)}" ) def test_failure_authorization(self, app, db_session, authenticated): image = ImageFactory.build() db_session.add(image) db_session.commit() with app.test_client() as client: resp = client.get(self.url_for_this(app, image_id=image.id)) assert resp.status_code == 401 def test_success(self, app, mocker, db_session, faker, authorized_admin): 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, image_id=image.id)) image = Image.query.get(image.id) render_expects = mocker.call( "images/view_image.html", image=image, ) assert resp.status_code == 200 assert views.render_template.call_args_list == [render_expects]