"""Tests for image location logic layer.""" from typing import Any import pytest from flexmock import flexmock from sqlalchemy.exc import SQLAlchemyError from assets import config from assets.constants import ( api, asset_status as asset_status_constants, error, ) from assets.exceptions import AssetFinalNotFound, AssetUploadNotFound, WrongAssetStatus from assets.logic import image_location from assets.logic.legacy import image_location as image_location_legacy from assets.models import ( asset_final, asset_status, asset_upload, ows_permissions, s3_file, ) @pytest.fixture def fixture_asset_upload() -> list[dict[str, int]]: """Fixture for asset_upload records.""" return [ {"id": 11, "product_id": 555, "track_unique_id": 0, "is_correction": 0}, {"id": 10, "product_id": 555, "track_unique_id": 0, "is_correction": 0}, ] @pytest.fixture def fixture_asset_status_ok() -> dict[str, Any]: """Fixture for encoding completed asset_status record.""" return {"asset_upload_id": 11, "status": "encoding_completed"} @pytest.fixture def fixture_asset_status_wrong() -> dict[str, Any]: """Fixture for encoding error asset_status record.""" return {"asset_upload_id": 11, "status": "encoding_error"} @pytest.fixture def fixture_asset_final() -> dict[str, Any]: """Fixture for asset_final record.""" return { "asset_upload_id": 11, "filename": "images/v2/product/cover/some_filename.jpeg", "asset_subtype": "cover", } @pytest.fixture def fixture_filename_cover(fixture_asset_final: dict[str, Any]) -> str: """Fixture for cover filename.""" return "v2/product/cover/some_filename.jpeg" @pytest.fixture def product_id() -> int: """Return a product id for testing.""" return 123 @pytest.fixture def profile_id() -> int: """Return a profile id for testing.""" return 68548 @pytest.fixture def label_id() -> int: """Return a label id for testing.""" return 32387 @pytest.fixture def profile_type() -> str: """Return a profile type for testing.""" return "LabelProfile" def test_get_image_location_success( fixture_asset_upload: list[dict[str, int]], fixture_asset_status_ok: dict[str, Any], fixture_asset_final: dict[str, Any], fixture_filename_cover: str, ) -> None: """Test for get_image_location success.""" expected_image_format = "cover" config.ASSET_STORAGE_BUCKET_NAME = "dev-asset-storage" config.CDN_URL = "https://fakesite.cloudfront.net" ( flexmock(asset_upload) .should_receive("get_asset_uploads") .with_args( track_id=0, product_id=fixture_asset_upload[0]["product_id"], api_version=api.API_VERSION_V2, limit=1, order="DESC", is_correction=None, ) .and_return(fixture_asset_upload) ) ( flexmock(asset_status) .should_receive("get_last_asset_status") .with_args(fixture_asset_upload[0]["id"]) .and_return(fixture_asset_status_ok) ) ( flexmock(asset_final) .should_receive("get_asset_final_by_asset_upload_id_and_subtype") .with_args(fixture_asset_upload[0]["id"], expected_image_format) .and_return(fixture_asset_final) ) ( flexmock(s3_file) .should_receive("check_s3_file_exists") .with_args(config.ASSET_STORAGE_BUCKET_NAME, fixture_asset_final["filename"]) .and_return(True) ) assert image_location.get_image_location( fixture_asset_upload[0]["product_id"], expected_image_format ) == "{cdn}/{filename}".format(cdn=config.CDN_URL, filename=fixture_filename_cover) def test_get_image_location_no_asset_upload() -> None: """Test for get_image_location with non-existent asset_upload.""" expected_product_id = 10 expected_image_format = "cover" ( flexmock(asset_upload) .should_receive("get_asset_uploads") .with_args( track_id=0, product_id=expected_product_id, api_version=api.API_VERSION_V2, limit=1, order="DESC", is_correction=None, ) .and_raise(AssetUploadNotFound(error.ERROR_ASSET_UPLOAD_NOT_FOUND)) ) ( flexmock(image_location_legacy) .should_receive("get_image_location") .with_args(expected_product_id, "product", expected_image_format) .and_return("legacy_image_location") ) assert ( image_location.get_image_location(expected_product_id, expected_image_format) == "legacy_image_location" ) def test_get_image_location_asset_upload_error() -> None: """Test for get_image_location with asset_upload error.""" expected_product_id = 10 expected_image_format = "cover" error_message = "Query error" ( flexmock(asset_upload) .should_receive("get_asset_uploads") .with_args( track_id=0, product_id=expected_product_id, api_version=api.API_VERSION_V2, limit=1, order="DESC", is_correction=None, ) .and_raise(SQLAlchemyError(error_message)) ) with pytest.raises(SQLAlchemyError) as exc: image_location.get_image_location(expected_product_id, expected_image_format) assert str(exc.value) == error_message def test_get_image_location_with_wrong_status( fixture_asset_upload: list[dict[str, int]], fixture_asset_status_wrong: dict[str, Any], ) -> None: """Test for get_image_location with wrong asset_status.""" expected_image_format = "cover" ( flexmock(asset_upload) .should_receive("get_asset_uploads") .with_args( track_id=0, product_id=fixture_asset_upload[0]["product_id"], api_version=api.API_VERSION_V2, limit=1, order="DESC", is_correction=None, ) .and_return(fixture_asset_upload) ) ( flexmock(asset_final) .should_receive("get_asset_final_by_asset_upload_id_and_subtype") .with_args(fixture_asset_upload[0]["id"], expected_image_format) .and_raise(AssetFinalNotFound("message")) ) ( flexmock(asset_status) .should_receive("get_last_asset_status") .with_args(fixture_asset_upload[0]["id"]) .and_return(fixture_asset_status_wrong) ) flexmock(image_location_legacy).should_receive("get_image_location").never() with pytest.raises(WrongAssetStatus) as exc: image_location.get_image_location( fixture_asset_upload[0]["product_id"], expected_image_format ) status = fixture_asset_status_wrong["status"] assert exc.value.description == error.ERROR_ASSET_WRONG_STATUS.format(status=status) def test_get_image_location_with_correction( fixture_asset_upload: list[dict[str, int]], fixture_asset_status_ok: dict[str, Any], fixture_asset_final: dict[str, Any], fixture_filename_cover: str, ) -> None: """Test for get_image_location with asset_upload correction.""" expected_image_format = "cover_correction" config.ASSET_STORAGE_BUCKET_NAME = "dev-asset-storage" config.CDN_URL = "https://fakesite.cloudfront.net" fixture_asset_upload[0]["is_correction"] = 1 ( flexmock(asset_upload) .should_receive("get_asset_uploads") .with_args( track_id=0, product_id=fixture_asset_upload[0]["product_id"], api_version=api.API_VERSION_V2, limit=1, order="DESC", is_correction=None, ) .and_return(fixture_asset_upload) ) ( flexmock(asset_status) .should_receive("get_last_asset_status") .with_args(fixture_asset_upload[0]["id"]) .and_return(fixture_asset_status_ok) ) ( flexmock(asset_final) .should_receive("get_asset_final_by_asset_upload_id_and_subtype") .with_args(fixture_asset_upload[0]["id"], "cover") .and_return(fixture_asset_final) ) ( flexmock(s3_file) .should_receive("check_s3_file_exists") .with_args(config.ASSET_STORAGE_BUCKET_NAME, fixture_asset_final["filename"]) .and_return(True) ) assert image_location.get_image_location( fixture_asset_upload[0]["product_id"], expected_image_format ) == "{cdn}/{filename}".format(cdn=config.CDN_URL, filename=fixture_filename_cover) def test_get_image_location_with_asset_status_error( fixture_asset_upload: list[dict[str, int]], fixture_asset_status_wrong: dict[str, Any], ) -> None: """Test for get_image_location with asset_status error.""" expected_image_format = "cover" ( flexmock(asset_upload) .should_receive("get_asset_uploads") .with_args( track_id=0, product_id=fixture_asset_upload[0]["product_id"], api_version=api.API_VERSION_V2, limit=1, order="DESC", is_correction=None, ) .and_return(fixture_asset_upload) ) ( flexmock(asset_final) .should_receive("get_asset_final_by_asset_upload_id_and_subtype") .with_args(fixture_asset_upload[0]["id"], expected_image_format) .and_raise(AssetFinalNotFound("message")) ) ( flexmock(asset_status) .should_receive("get_last_asset_status") .with_args(fixture_asset_upload[0]["id"]) .and_raise(SQLAlchemyError("error_message")) ) with pytest.raises(SQLAlchemyError) as exc: image_location.get_image_location( fixture_asset_upload[0]["product_id"], expected_image_format ) assert str(exc.value) == "error_message" def test_get_image_location_no_asset_final( fixture_asset_upload: list[dict[str, int]], fixture_asset_status_ok: dict[str, Any] ) -> None: """Test for get_image_location with nonexistent asset_final.""" expected_image_format = "cover" ( flexmock(asset_upload) .should_receive("get_asset_uploads") .with_args( track_id=0, product_id=fixture_asset_upload[0]["product_id"], api_version=api.API_VERSION_V2, limit=1, order="DESC", is_correction=None, ) .and_return(fixture_asset_upload) ) ( flexmock(asset_final) .should_receive("get_asset_final_by_asset_upload_id_and_subtype") .with_args(fixture_asset_upload[0]["id"], expected_image_format) .and_raise(AssetFinalNotFound("message")) ) ( flexmock(asset_status) .should_receive("get_last_asset_status") .with_args(fixture_asset_upload[0]["id"]) .and_return(fixture_asset_status_ok) ) with pytest.raises(WrongAssetStatus) as exc: image_location.get_image_location( fixture_asset_upload[0]["product_id"], expected_image_format ) assert ( exc.value.description == asset_status_constants.ASSET_ENCODING_IN_PROGRESS.format( status=fixture_asset_status_ok["status"] ) ) def test_get_image_location_asset_final_error( fixture_asset_upload: list[dict[str, int]], fixture_asset_status_ok: dict[str, Any] ) -> None: """Test for get_image_location with asset_final error.""" expected_image_format = "cover" ( flexmock(asset_upload) .should_receive("get_asset_uploads") .with_args( track_id=0, product_id=fixture_asset_upload[0]["product_id"], api_version=api.API_VERSION_V2, limit=1, order="DESC", is_correction=None, ) .and_return(fixture_asset_upload) ) error_message = "query error" ( flexmock(asset_final) .should_receive("get_asset_final_by_asset_upload_id_and_subtype") .with_args(fixture_asset_upload[0]["id"], expected_image_format) .and_raise(SQLAlchemyError(error_message)) ) with pytest.raises(SQLAlchemyError) as exc: image_location.get_image_location( fixture_asset_upload[0]["product_id"], expected_image_format ) assert str(exc.value) == error_message def test_get_image_location_no_s3_file( fixture_asset_upload: list[dict[str, int]], fixture_asset_status_ok: dict[str, Any], fixture_asset_final: dict[str, Any], fixture_filename_cover: str, ) -> None: """Test for get_image_location with non-existent s3 file.""" expected_image_format = "cover" config.ASSET_STORAGE_BUCKET_NAME = "dev-asset-storage" config.CDN_URL = "https://fakesite.cloudfront.net" ( flexmock(asset_upload) .should_receive("get_asset_uploads") .with_args( track_id=0, product_id=fixture_asset_upload[0]["product_id"], api_version=api.API_VERSION_V2, limit=1, order="DESC", is_correction=None, ) .and_return(fixture_asset_upload) ) ( flexmock(asset_status) .should_receive("get_last_asset_status") .with_args(fixture_asset_upload[0]["id"]) .and_return(fixture_asset_status_ok) ) ( flexmock(asset_final) .should_receive("get_asset_final_by_asset_upload_id_and_subtype") .with_args(fixture_asset_upload[0]["id"], expected_image_format) .and_return(fixture_asset_final) ) ( flexmock(s3_file) .should_receive("check_s3_file_exists") .with_args(config.ASSET_STORAGE_BUCKET_NAME, fixture_asset_final["filename"]) .and_return(False) ) ( flexmock(image_location_legacy) .should_receive("get_image_location") .with_args( fixture_asset_upload[0]["product_id"], "product", expected_image_format ) .and_return("legacy_image_location") ) assert ( image_location.get_image_location( fixture_asset_upload[0]["product_id"], expected_image_format ) == "legacy_image_location" ) def test_get_profile_image(profile_id: int, profile_type: str, label_id: int) -> None: """Test for a successful response.""" filename = "filename.jpg" image_format = "logo" image_type = "vendor" mock_ows_permission_response = {"items": [{"id": label_id, "type": "Vendor"}]} ( flexmock(ows_permissions) .should_receive("get_label_resources") .with_args(profile_type, profile_id) .and_return(mock_ows_permission_response) ) flexmock(image_location_legacy).should_receive("get_image_location").with_args( label_id, image_type, image_format ).and_return(f"{config.CDN_URL}/{filename}") assert ( image_location.get_profile_image(profile_id, profile_type) == f"{config.CDN_URL}/{filename}" ) def test_validate_product_artwork_success( fixture_asset_status_ok: dict[str, Any], fixture_asset_upload: list[dict[str, int]], product_id: int, ) -> None: """Test for a successful response when v2 artwork is present and valid.""" ( flexmock(asset_upload) .should_receive("get_asset_uploads") .once() .with_args( api_version=api.API_VERSION_V2, track_id=0, product_id=product_id, order=asset_upload.ORDER_DESC, limit=1, ) .and_return([fixture_asset_upload[0]]) ) ( flexmock(asset_status) .should_receive("get_last_asset_status") .once() .with_args(fixture_asset_upload[0]["id"]) .and_return(fixture_asset_status_ok) ) image_location.validate_product_artwork(product_id) def test_validate_product_artwork_status_error( fixture_asset_upload: list[dict[str, int]], product_id: int ) -> None: """Test that the status response is returned if it is not successful.""" error_message = "status error" ( flexmock(asset_upload) .should_receive("get_asset_uploads") .and_return([fixture_asset_upload[0]]) ) ( flexmock(asset_status) .should_receive("get_last_asset_status") .and_raise(SQLAlchemyError(error_message)) ) with pytest.raises(SQLAlchemyError) as exc: image_location.validate_product_artwork(product_id) assert str(exc.value) == error_message def test_validate_product_artwork_v2_incomplete( fixture_asset_upload: list[dict[str, int]], product_id: int ) -> None: """Test that for 404 status if v2 artwork is present but not complete.""" ( flexmock(asset_upload) .should_receive("get_asset_uploads") .and_return([fixture_asset_upload[0]]) ) incomplete_status = asset_status.AssetStatus( asset_upload_id=fixture_asset_upload[0]["id"], status=asset_status_constants.STATUS_ENCODING_ERROR, message='{"foo": "bar"}', ) ( flexmock(asset_status) .should_receive("get_last_asset_status") .and_return(incomplete_status.as_dict()) ) with pytest.raises(WrongAssetStatus) as exc: image_location.validate_product_artwork(product_id) assert exc.value.description == error.ERROR_ASSET_WRONG_STATUS.format( status=incomplete_status.status ) def test_validate_product_artwork_v2_error(product_id: int) -> None: """Test that an error is returned if fetching asset uploads fails.""" error_message = "Query error" ( flexmock(asset_upload) .should_receive("get_asset_uploads") .and_raise(SQLAlchemyError(error_message)) ) with pytest.raises(SQLAlchemyError) as exc: image_location.validate_product_artwork(product_id) assert str(exc.value) == error_message