"""Tests for image location logic layer.""" from typing import Any, Generator from unittest.mock import patch import pytest from flexmock import flexmock from assets.config import ASSET_STORAGE_BUCKET_NAME, CDN_URL, XLARGE_COVER_CDN_URL from assets.constants import error, s3 from assets.exceptions import ImageAssetNotFound, ProductNotFound from assets.logic.legacy import image_location from assets.models import image_location as image_location_model, ows_product, s3_file from assets.models.legacy import image_asset @pytest.fixture def fixture_file_path_cover() -> str: """Fixture for a s3 file_path.""" return "images/product/large_cover/e10adc3949ba59abbe56e057f20f883e.jpg" @pytest.fixture def fixture_file_path_thumbnail_correction() -> str: """Fixture for a s3 file_path.""" return "images/product/cover_correction/e10adc3949ba59abbe56e057f20f883e.jpg" @pytest.fixture def fixture_file_path_cover_correction() -> str: """Fixture for a s3 file_path.""" return "images/product/large_cover_correction/e10adc3949ba59abbe56e057f20f883e.jpg" @pytest.fixture def fixture_file_path_thumbnail() -> str: """Fixture for a s3 file_path.""" return "images/product/cover/e10adc3949ba59abbe56e057f20f883e.jpg" @pytest.fixture def image_location_data() -> dict[str, Any]: """Data for use in the image location call.""" return {"entity_id": 123456, "image_type": "product", "image_format": "large_cover"} @pytest.fixture def product_data() -> dict[str, Any]: """Data for getting product call.""" return {"product_id": 111, "upc": 123456789} @pytest.fixture def artwork_correction_data() -> list[Any]: """Data for getting artwork correction call.""" return [{"asset_location_detail": {"filename": "123456789.tif"}}] @pytest.fixture def import_asset_finished() -> dict[str, Any]: """Fixture for a successful upload.""" return { "id": 123, "import_asset_batch_id": 456, "foldername": "qa-orcd-raw-assets", "filename": "04c88d03_bab0_41e7_8b72_ecccefd894c8.jpg", "asset_type": "image", "result": "success", "status": "finished", } @pytest.fixture def import_asset_error() -> dict[str, Any]: """Fixture for a unsuccessful upload.""" return { "id": 123, "import_asset_batch_id": 456, "foldername": "qa-orcd-raw-assets", "filename": "04c88d03_bab0_41e7_8b72_ecccefd894c8.jpg", "asset_type": "image", "result": "image dimension mismatch", "status": "error", } @pytest.fixture def vendor_id() -> int: """Return a vendor id for testing.""" return 25824 @pytest.fixture def asset_id() -> int: """Return a asset id for testing.""" return 3614947 class TestGetImageLocationCoverImages(object): """Test get_image_location for cover images.""" entity_id = 123 image_filename = "filename" @pytest.fixture def fixture_s3_filename(self) -> str: """Fixture for S3 filename.""" return "{bucket}/{filename}".format( bucket=s3.IMAGE_ASSET_BUCKET, filename=self.image_filename ) @pytest.fixture def fixture_get_image_filename(self) -> None: """Fixture to mock out get_image_filename.""" ( flexmock(image_location_model) .should_receive("get_image_filename") .with_args(self.entity_id, "product", "cover") .and_return(self.image_filename) .once() ) @pytest.fixture def fixture_check_s3_file_exists_success(self, fixture_s3_filename: str) -> None: """Fixture to mock successful response for check_s3_file_exists.""" ( flexmock(s3_file) .should_receive("check_s3_file_exists") .with_args(ASSET_STORAGE_BUCKET_NAME, fixture_s3_filename) .and_return(True) .once() ) @pytest.fixture def fixture_check_s3_file_exists_error(self, fixture_s3_filename: str) -> None: """Fixture to mock error response for check_s3_file_exists.""" ( flexmock(s3_file) .should_receive("check_s3_file_exists") .with_args(ASSET_STORAGE_BUCKET_NAME, fixture_s3_filename) .and_return(False) .once() ) @pytest.fixture def get_image_location_success( self, fixture_get_image_filename: None, fixture_check_s3_file_exists_success: None, ) -> str: """Run get_image_location when file exists on S3.""" return image_location.get_image_location(self.entity_id, "product", "cover") def test_get_image_location_success(self, get_image_location_success: str) -> None: """Test for successful image location call.""" assert get_image_location_success == "{cdn}/{filename}".format( cdn=CDN_URL, filename=self.image_filename ) def test_get_image_location_error( self, fixture_get_image_filename: None, fixture_check_s3_file_exists_error: None ) -> None: """Test for error on image location call.""" with pytest.raises(ImageAssetNotFound) as exc: image_location.get_image_location(self.entity_id, "product", "cover") assert exc.value.description == error.ERROR_IMAGE_NOT_FOUND class TestGetImageLocationXLargeCoverImages(object): """Test get_image_location for x-large cover images.""" entity_id = 123 image_upc = "123" @pytest.fixture def fixture_s3_filename(self) -> str: """Fixture for S3 filename.""" return "{bucket}/{upc}.jpg".format( bucket=s3.XLARGE_COVER_BUCKET, upc=self.image_upc ) @pytest.fixture def fixture_get_product_id_success(self) -> None: """Fixture to mock successful response for get_product_by_id.""" ( flexmock(ows_product) .should_receive("get_product_by_id") .with_args(self.entity_id) .and_return({"upc": self.image_upc}) .once() ) @pytest.fixture def fixture_get_product_id_error(self) -> None: """Fixture to mock error response for get_product_by_id.""" ( flexmock(ows_product) .should_receive("get_product_by_id") .with_args(self.entity_id) .and_raise(ProductNotFound("product not found")) .once() ) @pytest.fixture def fixture_check_s3_file_exists_success(self, fixture_s3_filename: str) -> None: """Fixture to mock successful response for check_s3_file_exists.""" ( flexmock(s3_file) .should_receive("check_s3_file_exists") .with_args(ASSET_STORAGE_BUCKET_NAME, fixture_s3_filename) .and_return(True) .once() ) @pytest.fixture def fixture_check_s3_file_exists_error(self, fixture_s3_filename: str) -> None: """Fixture to mock error response for check_s3_file_exists.""" ( flexmock(s3_file) .should_receive("check_s3_file_exists") .with_args(ASSET_STORAGE_BUCKET_NAME, fixture_s3_filename) .and_return(False) .once() ) @pytest.fixture def get_image_location_success( self, fixture_get_product_id_success: None, fixture_check_s3_file_exists_success: None, ) -> str: """Run get_image_location when UPC found and file exists on S3.""" return image_location.get_image_location( self.entity_id, "product", "xlarge_cover" ) def test_get_image_location_success(self, get_image_location_success: str) -> None: """Test for successful image location call.""" assert get_image_location_success == "{cdn}/{upc}.jpg".format( cdn=XLARGE_COVER_CDN_URL, upc=self.image_upc ) def test_get_image_location_upc_not_found( self, fixture_get_product_id_error: None ) -> None: """Test for error on image location call when UPC not found.""" with pytest.raises(ProductNotFound) as exc: image_location.get_image_location(self.entity_id, "product", "xlarge_cover") assert exc.value.description == "product not found" def test_get_image_location_s3_file_not_found( self, fixture_get_product_id_success: None, fixture_check_s3_file_exists_error: None, ) -> None: """Test for error on image location call when S3 file doesn't exist.""" with pytest.raises(ImageAssetNotFound) as exc: image_location.get_image_location(self.entity_id, "product", "xlarge_cover") assert exc.value.description == error.ERROR_IMAGE_NOT_FOUND class TestGetImageLocations(object): """Test get_image_locations.""" entity_ids = [123, 789] image_filenames = ["filename1", "filename2"] cdn_url = "cdn_url" @pytest.fixture def fixture_cdn_url(self) -> Generator[None, None, None]: """Fixture to mock CDN url.""" with patch( "assets.logic.legacy.image_location.config.CDN_URL", new=self.cdn_url ): yield @pytest.fixture def fixture_get_image_filenames(self) -> None: """Fixture to mock out get_image_filenames.""" ( flexmock(image_location_model) .should_receive("get_image_filenames") .with_args(self.entity_ids, "product", "cover") .and_return(self.image_filenames) .once() ) def test_get_image_locations_success( self, fixture_cdn_url: None, fixture_get_image_filenames: None ) -> None: """Test for successful image locations call.""" assert image_location.get_image_locations( self.entity_ids, "product", "cover" ) == { 123: "cdn_url/filename1", 789: "cdn_url/filename2", } def test_get_vendor_icon_s3_success(vendor_id: int, asset_id: int) -> None: """Fixture to mock out get_vendor_icon.""" image_filename = "filename" s3_filename = "{bucket}/{filename}".format( bucket=s3.IMAGE_ASSET_BUCKET, filename=image_filename ) ( flexmock(image_location_model) .should_receive("get_image_filename") .with_args(vendor_id, "vendor", "icon") .and_return(image_filename) .once() ) ( flexmock(s3_file) .should_receive("check_s3_file_exists") .with_args(ASSET_STORAGE_BUCKET_NAME, s3_filename) .and_return(True) .once() ) assert ( image_location.get_vendor_icon(vendor_id, asset_id) == f"{CDN_URL}/{image_filename}" ) def test_get_vendor_icon_legacy_fallback(vendor_id: int, asset_id: int) -> None: """Fixture to mock out get_vendor_icon.""" url = "https://images.qaorch.com/vendor/vendor_icon/25824/25824/3614947.jpg" image_filename = "filename" s3_filename = "{bucket}/{filename}".format( bucket=s3.IMAGE_ASSET_BUCKET, filename=image_filename ) ( flexmock(image_location_model) .should_receive("get_image_filename") .with_args(vendor_id, "vendor", "icon") .and_return(image_filename) .once() ) ( flexmock(s3_file) .should_receive("check_s3_file_exists") .with_args(ASSET_STORAGE_BUCKET_NAME, s3_filename) .and_return(False) .once() ) ( flexmock(image_asset) .should_receive("get_image_asset_url_by_id") .with_args(asset_id) .and_return(url) .once() ) assert image_location.get_vendor_icon(vendor_id, asset_id) == url