"""Integration API tests for ows-assets.""" from time import sleep from typing import Any import pytest import requests from assets.constants.asset_upload import ASSET_TYPE_AUDIO, ASSET_TYPE_IMAGE from tests.integration.conftest import big_image, valid_files from tests.testutils.api_client.api_client import APIClient from tests.testutils.api_client.product_digital_api_client import ( ProductDigitalAPIClient, ) from tests.testutils.api_client.track_api_client import TrackAPIClient from tests.testutils.file_helper.file_helper import FileHelper, FileType from tests.testutils.mysql.query_helper import QueryHelper from tests.testutils.s3_helper.s3_helper import S3Helper from tests.testutils.test_helper.test_helper import TestHelper @pytest.mark.parametrize("file_status", ["raw", "final"]) def test_get_asset( workstation_api_client: APIClient, file_status: str, workstation_product_digital_api_client: ProductDigitalAPIClient, product_data: dict[str, Any], workstation_track_api_client: TrackAPIClient, ) -> None: """Test GET asset on v2 after securing upload token.""" # uploading of a file to s3 with meta asset_file = "tests/integration/assets/wav_file.wav" file_data = FileHelper.get_file_data_from_asset(asset_file) upload_token = S3Helper.get_upload_token(workstation_api_client, ASSET_TYPE_AUDIO) product_dict = TestHelper.create_product( workstation_product_digital_api_client, product_data ) tuid = TestHelper.create_track_if_audio( workstation_track_api_client, file_data["file_type"], product_dict["product_id"] ) metadata = S3Helper.s3_metadata(product_dict, file_data["file_ext"], tuid) metadata["asset_type"] = file_data["file_ext"] S3Helper.upload_to_s3_check_response( asset_file, metadata, file_data["file_ext"], file_data["content_type"], upload_token, ) def assertions_asset_status(response: requests.Response) -> None: assert response.status_code == 200, ( "Result of GET was {}, expected 200.".format(response.status_code) ) response_content = response.json() assert response_content["status"] == "encoding_completed", ( "Expected status to be encoding_completed, was {}.".format( response_content["status"] ) ) # Execute GETs against asset/status to obtain transcoded assets filenames asset_status_response = TestHelper.assert_with_timeout( workstation_api_client.get_asset_status, upload_token["filename"], assertions_asset_status, ) def assert_response( response: requests.Response, track_id: int | None, product: dict[str, Any], file_extension: str, token: dict[str, Any], ) -> None: assert response.status_code == 200, ( "Result of GET was {}, expected 200.".format(response.status_code) ) res = response.json() assert res["product_id"] == product["product_id"], ( "Expected product_id to be {}, was {}.".format( product["product_id"], res["product_id"] ) ) assert res["upc"] == product["upc"], "Expected upc to be {}, was {}.".format( product["upc"], response.json()["upc"], ) assert res["filename"] == token["filename"], ( "Expected filename to be {}, was {}.".format( upload_token["filename"], res["filename"] ) ) assert res["track_unique_id"] == track_id, ( "Expected tuid to be {}, was {}.".format(track_id, res["track_unique_id"]) ) if file_status == "raw": response_get = workstation_api_client.get_asset( TestHelper.assets_query(upload_token["filename"], file_status) ) assert_response( response_get, tuid, product_dict, file_data["file_ext"], upload_token, ) elif file_status == "final": transcoded_assets = asset_status_response.json()["message"]["result_assets"] for asset in transcoded_assets: response_get = workstation_api_client.get_asset( TestHelper.assets_query(asset["key"], file_status) ) assert_response( response_get, tuid, product_dict, file_data["file_ext"], upload_token, ) else: raise NotImplementedError() @pytest.mark.parametrize("asset_file", big_image() + valid_files()) def test_get_asset_status_main( workstation_api_client: APIClient, workstation_product_digital_api_client: ProductDigitalAPIClient, workstation_track_api_client: TrackAPIClient, product_data: dict[str, Any], asset_file: str, ) -> None: """Test GET asset/status on v2 after uploading to s3.""" file_data = FileHelper.get_file_data_from_asset(asset_file) asset_type = ( ASSET_TYPE_AUDIO if file_data["file_type"] == FileType.AUDIO else ASSET_TYPE_IMAGE ) upload_token = S3Helper.get_upload_token(workstation_api_client, asset_type) product_dict = TestHelper.create_product( workstation_product_digital_api_client, product_data ) tuid = TestHelper.create_track_if_audio( workstation_track_api_client, file_data["file_type"], product_dict["product_id"] ) metadata = S3Helper.s3_metadata(product_dict, file_data["file_ext"], tuid) S3Helper.upload_to_s3_check_response( asset_file, metadata, file_data["file_ext"], file_data["content_type"], upload_token, ) # Execute GETs against asset/status TestHelper.assert_with_timeout( workstation_api_client.get_asset_status, upload_token["filename"], TestHelper.expect_completely_encoded_asset, additional_assert_args=upload_token["filename"], ) def test_get_asset_status_no_metadata(workstation_api_client: APIClient) -> None: """Test GET asset/status on v2 after uploading to s3 with no metadata.""" asset_file = valid_files()[0] file_data = FileHelper.get_file_data_from_asset(asset_file) upload_token = S3Helper.get_upload_token(workstation_api_client, ASSET_TYPE_IMAGE) # upload with no metadata S3Helper.upload_to_s3_check_response( asset_file, {}, file_data["file_ext"], file_data["content_type"], upload_token ) def assertions(response: requests.Response) -> None: assert response.status_code == 200, ( "Result of GET was {}, expected 200.".format(response.status_code) ) response_content = response.json() assert response_content["asset_upload_id"], ( "Expected asset_upload_id to not be null." ) assert response_content["status"] == "upload_error", ( "Expected status to be upload_error, was {}.".format( response_content["status"] ) ) message_content = response_content["message"] assert upload_token["filename"] in message_content["input"]["key"], ( "Expected response message to include filename of {}, got {}.".format( upload_token["filename"], message_content["input"]["key"] ) ) assert message_content["status"] == "upload_error", ( "Status was {}, expected upload_error.".format(message_content["status"]) ) assert message_content["error_code"] == "s3_metadata_not_found", ( "Status was {}, expected s3_metadata_not_found.".format( message_content["error_code"] ) ) assert message_content["function"] == "acknowledge", ( "Expected function to be acknowledge, was {}".format( message_content["function"] ) ) # Execute GETs against asset/status TestHelper.assert_with_timeout( workstation_api_client.get_asset_status, upload_token["filename"], assertions, ) @pytest.mark.parametrize("meta_to_delete", ["original_filename", "product_id", "upc"]) def test_get_asset_status_partial_metadata( workstation_api_client: APIClient, workstation_product_digital_api_client: ProductDigitalAPIClient, workstation_track_api_client: TrackAPIClient, product_data: dict[str, Any], meta_to_delete: str, ) -> None: """Test GET asset/status on v2 after uploading to s3 with partial metadata.""" asset_file = valid_files()[0] file_data = FileHelper.get_file_data_from_asset(asset_file) upload_token = S3Helper.get_upload_token(workstation_api_client, ASSET_TYPE_IMAGE) product_dict = TestHelper.create_product( workstation_product_digital_api_client, product_data ) tuid = TestHelper.create_track_if_audio( workstation_track_api_client, file_data["file_type"], product_dict["product_id"] ) # meta_to_delete parameter deletes metadata metadata = S3Helper.s3_metadata( product_dict, file_data["file_ext"], tuid, meta_to_delete, ) S3Helper.upload_to_s3_check_response( asset_file, metadata, file_data["file_ext"], file_data["content_type"], upload_token, ) def assertions(response: requests.Response) -> None: assert response.status_code == 200, ( "Result of GET was {}, expected 200.".format(response.status_code) ) response_content = response.json() assert response_content["status_time"], "Expected status_time to not be null." assert response_content["message"]["status"] == "acknowledge_error", ( "Expected status to be: acknowledge_error" ) # Execute GETs against asset/status TestHelper.assert_with_timeout( workstation_api_client.get_asset_status, upload_token["filename"], assertions ) @pytest.fixture def expected_errors_fixtures( wrong_dimensions: dict[str, str], too_big: dict[str, str], too_small: dict[str, str], invalid_colors: dict[str, str], ) -> dict[str, dict[str, str]]: """Fixture for expected errors.""" return { "wrong_dimensions": wrong_dimensions, "too_big": too_big, "too_small": too_small, "invalid_colors": invalid_colors, } @pytest.mark.parametrize( "asset_file, expected_errors_index", [ ("tests/integration/assets/wrong_dimensions.jpg", "wrong_dimensions"), ("tests/integration/assets/too_big.jpg", "too_big"), ("tests/integration/assets/too_small.jpg", "too_small"), ("tests/integration/assets/invalid_colors.jpg", "invalid_colors"), ], ) def test_get_asset_status_bad_file( workstation_api_client: APIClient, workstation_product_digital_api_client: ProductDigitalAPIClient, workstation_track_api_client: TrackAPIClient, product_data: dict[str, Any], asset_file: str, expected_errors_index: str, expected_errors_fixtures: dict[str, dict[str, str]], ) -> None: """Test GET asset/status on v2 after uploading bad file to s3.""" expected_errors = expected_errors_fixtures[expected_errors_index] file_data = FileHelper.get_file_data_from_asset(asset_file) upload_token = S3Helper.get_upload_token(workstation_api_client, ASSET_TYPE_IMAGE) product_dict = TestHelper.create_product( workstation_product_digital_api_client, product_data ) tuid = TestHelper.create_track_if_audio( workstation_track_api_client, file_data["file_type"], product_dict["product_id"] ) metadata = S3Helper.s3_metadata(product_dict, file_data["file_ext"], tuid) S3Helper.upload_to_s3_check_response( asset_file, metadata, file_data["file_ext"], file_data["content_type"], upload_token, ) def assertions(response: requests.Response) -> None: assert response.status_code == 200, ( "Result of GET was {}, expected 200.".format(response.status_code) ) response_content = response.json() assert response_content["status"] == "validation_error", ( 'Expected status to be "validation_error", was {}'.format( response_content["status"] ) ) assert expected_errors["description"] in response_content["description"], ( "Expected description to contain {}, was {}".format( expected_errors["description"], response_content["description"] ) ) # Execute GETs against asset/status TestHelper.assert_with_timeout( workstation_api_client.get_asset_status, upload_token["filename"], assertions ) def test_get_asset_status_bad_wav_file( workstation_api_client: APIClient, workstation_product_digital_api_client: ProductDigitalAPIClient, workstation_track_api_client: TrackAPIClient, product_data: dict[str, Any], ) -> None: """Test GET asset/status on v2 after uploading bad wav file to s3.""" asset_file = "tests/integration/assets/bad_wav_file.wav" file_data = FileHelper.get_file_data_from_asset(asset_file) upload_token = S3Helper.get_upload_token(workstation_api_client, ASSET_TYPE_AUDIO) product_dict = TestHelper.create_product( workstation_product_digital_api_client, product_data ) tuid = TestHelper.create_track_if_audio( workstation_track_api_client, file_data["file_type"], product_dict["product_id"] ) metadata = S3Helper.s3_metadata(product_dict, file_data["file_ext"], tuid) S3Helper.upload_to_s3_check_response( asset_file, metadata, file_data["file_ext"], file_data["content_type"], upload_token, ) def assertions(response: requests.Response) -> None: assert response.status_code == 200, ( "Result of GET was {}, expected 200.".format(response.status_code) ) response_content = response.json() assert response_content["status"] == "validation_error", ( 'Expected status to be "validation_error", was {}'.format( response_content["status"] ) ) assert ( "Invalid input value 32000 for sample_rate" in response_content["description"] ), ( "Expected description to contain " '"Invalid input value for sample_rate", was {}'.format( response_content["description"] ) ) # Execute GETs against asset/status TestHelper.assert_with_timeout( workstation_api_client.get_asset_status, upload_token["filename"], assertions ) @pytest.mark.parametrize( "asset_file", [ "tests/integration/assets/jpg_file.jpg", "tests/integration/assets/flac_file.flac", ], ) def test_delete_asset( workstation_api_client: APIClient, workstation_product_digital_api_client: ProductDigitalAPIClient, workstation_track_api_client: TrackAPIClient, product_data: dict[str, Any], asset_file: str, ) -> None: """Test DELETE asset on v2 after uploading to s3.""" file_data = FileHelper.get_file_data_from_asset(asset_file) asset_type = ( ASSET_TYPE_AUDIO if file_data["file_type"] == FileType.AUDIO else ASSET_TYPE_IMAGE ) upload_token = S3Helper.get_upload_token(workstation_api_client, asset_type) product_dict = TestHelper.create_product( workstation_product_digital_api_client, product_data ) tuid = TestHelper.create_track_if_audio( workstation_track_api_client, file_data["file_type"], product_dict["product_id"] ) metadata = S3Helper.s3_metadata(product_dict, file_data["file_ext"], tuid) S3Helper.upload_to_s3_check_response( asset_file, metadata, file_data["file_ext"], file_data["content_type"], upload_token, ) def upload_assertions(response: requests.Response) -> None: assert response.status_code == 200, ( "Result of GET was {}, expected 200.".format(response.status_code) ) response_content = response.json() assert response_content["status"] == "encoding_completed", ( 'Status was {}, expected "encoding_completed".'.format( response_content["status"] ) ) # Execute GETs against asset/status TestHelper.assert_with_timeout( workstation_api_client.get_asset_status, upload_token["filename"], upload_assertions, ) asset_by_product_response = workstation_api_client.get_assets_by_product( product_dict["product_id"] ) assert asset_by_product_response.status_code == 200, ( "Result of GET was {}, expected 200.".format( asset_by_product_response.status_code ) ) # QA database refresh only affect art relations and so product IDs are reused when creating new products on QA. # But the QA RDS is not refreshed and so asset_upload records might already exist for a reused product ID. assets = asset_by_product_response.json()["assets"] filenames = [a["filename"] for a in assets] uploaded_filename = upload_token["filename"] assert uploaded_filename in filenames, ( "Expected {} to exist in product asset list {}".format( upload_token["filename"], filenames ) ) if file_data["file_type"] == FileType.AUDIO: assert tuid is not None delete_response = workstation_api_client.delete_track_asset(tuid) assert delete_response.status_code == 200, ( "Result of DELETE was {}, expected 200.".format(delete_response.status_code) ) elif file_data["file_type"] == FileType.IMAGE: delete_response = workstation_api_client.delete_image_asset( product_dict["product_id"] ) assert delete_response.status_code == 200, ( "Result of DELETE was {}, expected 200.".format(delete_response.status_code) ) asset_by_product_response_2 = workstation_api_client.get_assets_by_product( product_dict["product_id"] ) assert asset_by_product_response_2.status_code == 200, ( "Result of GET was {}, expected 200.".format( asset_by_product_response_2.status_code ) ) assets = asset_by_product_response_2.json()["assets"] filenames = [a["filename"] for a in assets] assert uploaded_filename not in filenames, ( "Expected {} not to be in product asset list {}".format( uploaded_filename, assets ) ) def test_delete_all_assets( workstation_api_client: APIClient, workstation_product_digital_api_client: ProductDigitalAPIClient, workstation_track_api_client: TrackAPIClient, product_data: dict[str, Any], ) -> None: """Test DELETE asset on v2 after uploading an image and audio file for one product to s3.""" product_dict = TestHelper.create_product( workstation_product_digital_api_client, product_data ) assets = [ "tests/integration/assets/jpg_file.jpg", "tests/integration/assets/wav_file.wav", ] for asset in assets: file_data = FileHelper.get_file_data_from_asset(asset) asset_type = ( ASSET_TYPE_AUDIO if file_data["file_type"] == FileType.AUDIO else ASSET_TYPE_IMAGE ) upload_token = S3Helper.get_upload_token(workstation_api_client, asset_type) tuid = TestHelper.create_track_if_audio( workstation_track_api_client, file_data["file_type"], product_dict["product_id"], ) metadata = S3Helper.s3_metadata(product_dict, file_data["file_ext"], tuid) S3Helper.upload_to_s3_check_response( asset, metadata, file_data["file_ext"], file_data["content_type"], upload_token, ) def upload_assertions(response: requests.Response) -> None: assert response.status_code == 200, ( "Result of GET was {}, expected 200.".format(response.status_code) ) response_content = response.json() assert response_content["status"] == "encoding_completed", ( 'Status was {}, expected "encoding_completed".'.format( response_content["status"] ) ) # Execute GETs against asset/status TestHelper.assert_with_timeout( workstation_api_client.get_asset_status, upload_token["filename"], upload_assertions, ) asset_by_product_response = workstation_api_client.get_assets_by_product( product_dict["product_id"] ) assert asset_by_product_response.status_code == 200, ( "Result of GET was {}, expected 200.".format( asset_by_product_response.status_code ) ) asset_by_product_content = asset_by_product_response.json() assert len(asset_by_product_content["assets"]) == 2, ( "Expected length of assets response to be 2, got {}".format( len(asset_by_product_content["assets"]) ) ) delete_response = workstation_api_client.delete_all_assets( product_dict["product_id"] ) assert delete_response.status_code == 200, ( "Result of DELETE was {}, expected 200.".format(delete_response.status_code) ) asset_by_product_response_2 = workstation_api_client.get_assets_by_product( product_dict["product_id"] ) assert asset_by_product_response_2.status_code == 200, ( "Result of GET was {}, expected 200.".format( asset_by_product_response_2.status_code ) ) asset_by_product_response_2 = asset_by_product_response_2.json() assert len(asset_by_product_response_2["assets"]) == 0, ( "Expected length of assets response to be 0, got {}".format( len(asset_by_product_response_2["assets"]) ) ) @pytest.mark.parametrize( "image1,image2", [ ( "tests/integration/assets/jpg_file.jpg", "tests/integration/assets/jpg_file_1.jpg", ), ( "tests/integration/assets/jpg_file.jpg", "tests/integration/assets/tif_file.tif", ), ], ) def test_replace_image_asset( workstation_api_client: APIClient, workstation_product_digital_api_client: ProductDigitalAPIClient, product_data: dict[str, Any], image1: str, image2: str, ) -> None: """Test replace image asset marks old image asset to deleted.""" image_file_1 = image1 image_file_2 = image2 image_data_1 = FileHelper.get_file_data_from_asset(image_file_1) image_data_2 = FileHelper.get_file_data_from_asset(image_file_2) upload_token_image_1 = S3Helper.get_upload_token( workstation_api_client, ASSET_TYPE_IMAGE ) upload_token_image_2 = S3Helper.get_upload_token( workstation_api_client, ASSET_TYPE_IMAGE ) product_dict = TestHelper.create_product( workstation_product_digital_api_client, product_data ) metadata = S3Helper.s3_metadata(product_dict, image_data_1["file_ext"], None) metadata_1 = S3Helper.s3_metadata(product_dict, image_data_2["file_ext"], None) S3Helper.upload_to_s3_check_response( image_file_1, metadata, image_data_1["file_ext"], image_data_1["content_type"], upload_token_image_1, ) # Race condition can possibly be here where the first file doesn't finish uploading # but the second one finishes and thus the first one is actually the the non-deleted one # The sleep should ensure that the upload for the first image should finish before the second one sleep(10) S3Helper.upload_to_s3_check_response( image_file_2, metadata_1, image_data_2["file_ext"], image_data_2["content_type"], upload_token_image_2, ) def replaced_upload_assertions(response: requests.Response) -> None: assert response.status_code == 404, ( "Result of GET was {}, expected 404.".format(response.status_code) ) def upload_assertions(response: requests.Response) -> None: assert response.status_code == 200, ( "Result of GET was {}, expected 200.".format(response.status_code) ) response_content = response.json() assert response_content["status"] == "encoding_completed", ( 'Status was {}, expected "encoding_completed".'.format( response_content["status"] ) ) # Execute GETs against asset/status TestHelper.assert_with_timeout( workstation_api_client.get_asset_status, upload_token_image_1["filename"], replaced_upload_assertions, ) TestHelper.assert_with_timeout( workstation_api_client.get_asset_status, upload_token_image_2["filename"], upload_assertions, ) query_result = QueryHelper.get_image_asset_record_by_filename( upload_token_image_2["filename"], deleted="0" ) assert query_result == upload_token_image_2["filename"] query_result = QueryHelper.get_image_asset_record_by_filename( upload_token_image_1["filename"], deleted="1" ) assert query_result == upload_token_image_1["filename"] asset_by_product_response = workstation_api_client.get_assets_by_product( product_dict["product_id"] ) assert asset_by_product_response.status_code == 200, ( "Result of GET was {}, expected 200.".format( asset_by_product_response.status_code ) ) # QA database refresh only affect art relations and so product IDs are reused when creating new products on QA. # But the QA RDS is not refreshed and so asset_upload records might already exist for a reused product ID. assets = asset_by_product_response.json()["assets"] filenames = [a["filename"] for a in assets] uploaded_filename = upload_token_image_2["filename"] assert uploaded_filename in filenames, ( "Expected {} to exist in product asset list {}".format( upload_token_image_2["filename"], filenames ) ) QueryHelper.delete_asset_records( filename=upload_token_image_1["filename"], product_id=product_dict["product_id"] ) QueryHelper.delete_asset_records( filename=upload_token_image_2["filename"], product_id=product_dict["product_id"] ) def test_replace_correction_image_asset( workstation_api_client: APIClient, workstation_product_digital_api_client: ProductDigitalAPIClient, product_data: dict[str, Any], ) -> None: """Test replace image asset correction marks old image asset correction to deleted.""" image_file_1 = "tests/integration/assets/jpg_file.jpg" image_file_2 = "tests/integration/assets/jpg_file_1.jpg" image_file_3 = "tests/integration/assets/tif_file.tif" image_data_1 = FileHelper.get_file_data_from_asset(image_file_1) image_data_2 = FileHelper.get_file_data_from_asset(image_file_2) image_data_3 = FileHelper.get_file_data_from_asset(image_file_3) upload_token_image_1 = S3Helper.get_upload_token( workstation_api_client, ASSET_TYPE_IMAGE ) upload_token_image_2 = S3Helper.get_upload_token( workstation_api_client, ASSET_TYPE_IMAGE ) upload_token_image_3 = S3Helper.get_upload_token( workstation_api_client, ASSET_TYPE_IMAGE ) product_dict = TestHelper.create_product( workstation_product_digital_api_client, product_data ) metadata_1 = S3Helper.s3_metadata(product_dict, image_data_1["file_ext"], None) metadata_2 = S3Helper.s3_metadata( product_dict, image_data_2["file_ext"], None, is_correction="1" ) metadata_3 = S3Helper.s3_metadata( product_dict, image_data_3["file_ext"], None, is_correction="1" ) S3Helper.upload_to_s3_check_response( image_file_1, metadata_1, image_data_1["file_ext"], image_data_1["content_type"], upload_token_image_1, ) # Race condition can possibly be here where the first file does not finish uploading # but the second one finishes and thus the first one is actually the the non-deleted one # The sleep should ensure that the upload for the first image should finish before the second one sleep(10) S3Helper.upload_to_s3_check_response( image_file_2, metadata_2, image_data_2["file_ext"], image_data_2["content_type"], upload_token_image_2, ) sleep(10) S3Helper.upload_to_s3_check_response( image_file_3, metadata_3, image_data_3["file_ext"], image_data_3["content_type"], upload_token_image_3, ) def replaced_upload_assertions(response: requests.Response) -> None: assert response.status_code == 404, ( "Result of GET was {}, expected 404.".format(response.status_code) ) def upload_assertions(response: requests.Response) -> None: assert response.status_code == 200, ( "Result of GET was {}, expected 200.".format(response.status_code) ) response_content = response.json() assert response_content["status"] == "encoding_completed", ( 'Status was {}, expected "encoding_completed".'.format( response_content["status"] ) ) # Execute GETs against asset/status TestHelper.assert_with_timeout( workstation_api_client.get_asset_status, upload_token_image_1["filename"], upload_assertions, ) TestHelper.assert_with_timeout( workstation_api_client.get_asset_status, upload_token_image_2["filename"], replaced_upload_assertions, ) TestHelper.assert_with_timeout( workstation_api_client.get_asset_status, upload_token_image_3["filename"], upload_assertions, ) # Ensure first image is still valid query_result = QueryHelper.get_image_asset_record_by_filename( upload_token_image_1["filename"], deleted="0", is_correction="0" ) assert query_result == upload_token_image_1["filename"] # Ensure second image is correction and deleted query_result = QueryHelper.get_image_asset_record_by_filename( upload_token_image_2["filename"], deleted="1", is_correction="1" ) assert query_result == upload_token_image_2["filename"] # Ensure third image is correction and not deleted query_result = QueryHelper.get_image_asset_record_by_filename( upload_token_image_3["filename"], deleted="0", is_correction="1" ) assert query_result == upload_token_image_3["filename"] asset_by_product_response = workstation_api_client.get_assets_by_product( product_dict["product_id"] ) assert asset_by_product_response.status_code == 200, ( "Result of GET was {}, expected 200.".format( asset_by_product_response.status_code ) ) # QA database refresh only affect art relations and so product IDs are reused when creating new products on QA. # But the QA RDS is not refreshed and so asset_upload records might already exist for a reused product ID. assets = asset_by_product_response.json()["assets"] filenames = [a["filename"] for a in assets] uploaded_filename = upload_token_image_3["filename"] assert uploaded_filename in filenames, ( "Expected {} to exist in product asset list {}".format( upload_token_image_3["filename"], filenames ) ) QueryHelper.delete_asset_records( filename=upload_token_image_1["filename"], product_id=product_dict["product_id"] ) QueryHelper.delete_asset_records( filename=upload_token_image_2["filename"], product_id=product_dict["product_id"] ) QueryHelper.delete_asset_records( filename=upload_token_image_3["filename"], product_id=product_dict["product_id"] ) def test_discard_image_correction( workstation_api_client: APIClient, workstation_product_digital_api_client: ProductDigitalAPIClient, workstation_track_api_client: TrackAPIClient, product_data: dict[str, Any], ) -> None: """Test discard error correction marks discarded correction as deleted.""" image_file_1 = "tests/integration/assets/jpg_file.jpg" image_file_2 = "tests/integration/assets/jpg_file_1.jpg" image_data_1 = FileHelper.get_file_data_from_asset(image_file_1) image_data_2 = FileHelper.get_file_data_from_asset(image_file_2) upload_token_image_1 = S3Helper.get_upload_token( workstation_api_client, ASSET_TYPE_IMAGE ) upload_token_image_2 = S3Helper.get_upload_token( workstation_api_client, ASSET_TYPE_IMAGE ) product_dict = TestHelper.create_product( workstation_product_digital_api_client, product_data ) metadata_image_1 = S3Helper.s3_metadata( product_dict, image_data_1["file_ext"], None ) metadata_image_2 = S3Helper.s3_metadata( product_dict, image_data_2["file_ext"], None, is_correction="1" ) S3Helper.upload_to_s3_check_response( image_file_1, metadata_image_1, image_data_1["file_ext"], image_data_1["content_type"], upload_token_image_1, ) # Race condition can possibly be here where the first file does not finish uploading # but the second one finishes and thus the first one is actually the the non-deleted one # The sleep should ensure that the upload for the first image should finish before the second one sleep(5) S3Helper.upload_to_s3_check_response( image_file_2, metadata_image_2, image_data_2["file_ext"], image_data_2["content_type"], upload_token_image_2, ) def upload_assertions(response: requests.Response) -> None: assert response.status_code == 200, ( "Result of GET was {}, expected 200.".format(response.status_code) ) response_content = response.json() assertion_error = 'Status was {}, expected "encoding_completed".'.format( response_content["status"] ) assert response_content["status"] == "encoding_completed", assertion_error # Execute GETs against asset/status TestHelper.assert_with_timeout( workstation_api_client.get_asset_status, upload_token_image_1["filename"], upload_assertions, ) TestHelper.assert_with_timeout( workstation_api_client.get_asset_status, upload_token_image_2["filename"], upload_assertions, ) asset_by_product_response = workstation_api_client.get_assets_by_product( product_dict["product_id"] ) assert asset_by_product_response.status_code == 200, ( "Result of GET was {}, expected 200.".format( asset_by_product_response.status_code ) ) # Ensure first image is still valid query_result = QueryHelper.get_image_asset_record_by_filename( upload_token_image_1["filename"], deleted="0", is_correction="0" ) assert query_result == upload_token_image_1["filename"] # Ensure second image is correction and not deleted query_result = QueryHelper.get_image_asset_record_by_filename( upload_token_image_2["filename"], deleted="0", is_correction="1" ) assert query_result == upload_token_image_2["filename"] discard_response = workstation_api_client.discard_correction( product_dict["product_id"] ) assert discard_response.status_code == 200, ( "Result of DELETE was {}, expected 200.".format(discard_response.status_code) ) asset_by_product_response_2 = workstation_api_client.get_assets_by_product( product_dict["product_id"] ) assert asset_by_product_response_2.status_code == 200, ( "Result of GET was {}, expected 200.".format( asset_by_product_response_2.status_code ) ) # Ensure first image is still valid after discarding query_result = QueryHelper.get_image_asset_record_by_filename( upload_token_image_1["filename"], deleted="0", is_correction="0" ) assert query_result == upload_token_image_1["filename"] # Ensure correction image is marked deleted after discarding query_result = QueryHelper.get_image_asset_record_by_filename( upload_token_image_2["filename"], deleted="1", is_correction="1" ) assert query_result == upload_token_image_2["filename"] QueryHelper.delete_asset_records( filename=upload_token_image_1["filename"], product_id=product_dict["product_id"] ) QueryHelper.delete_asset_records( filename=upload_token_image_2["filename"], product_id=product_dict["product_id"] ) def test_delete_missing_image( workstation_api_client: APIClient, invalid_id: int ) -> None: """Attempt to delete a non-existent image asset.""" expected_message = "Product not found for provided product id." delete_response = workstation_api_client.delete_image_asset(invalid_id) assert delete_response.status_code == 404, ( "Result of DELETE was {}, expected 404.".format(delete_response.status_code) ) delete_content = delete_response.json() assert delete_content["message"] == expected_message, ( "Expected message to be {}, got {}".format( expected_message, delete_content["message"] ) ) assert delete_content["code"] == "not_found_error", ( 'Expected code to be "not_found_error", got {}'.format(delete_content["code"]) ) def test_delete_missing_audio( workstation_api_client: APIClient, invalid_id: int ) -> None: """Attempt to delete a non-existent track asset.""" expected_message = f"Track not found for provided track id. {invalid_id}" delete_response = workstation_api_client.delete_track_asset(invalid_id) assert delete_response.status_code == 404, ( "Result of DELETE was {}, expected 404.".format(delete_response.status_code) ) delete_content = delete_response.json() assert delete_content["message"] == expected_message, ( "Expected message to be {}, got {}".format( expected_message, delete_content["message"] ) ) assert delete_content["code"] == "not_found_error", ( 'Expected code to be "not_found_error", got {}'.format(delete_content["code"]) ) def test_delete_missing_product( workstation_api_client: APIClient, invalid_id: int ) -> None: """Attempt to delete a non-existent product's assets.""" expected_message = "Product not found for provided product id." delete_response = workstation_api_client.delete_all_assets(invalid_id) assert delete_response.status_code == 404, ( "Result of DELETE was {}, expected 404.".format(delete_response.status_code) ) delete_content = delete_response.json() assert delete_content["message"] == expected_message, ( "Expected message to be {}, got {}".format( expected_message, delete_content["message"] ) ) assert delete_content["code"] == "not_found_error", ( 'Expected code to be "not_found_error", got {}'.format(delete_content["code"]) ) def test_get_image_location_v2( workstation_api_client: APIClient, workstation_product_digital_api_client: ProductDigitalAPIClient, workstation_track_api_client: TrackAPIClient, product_data: dict[str, Any], ) -> None: """An image URL can be obtained via GET /image/location endpoint for an asset uploaded via v2.""" cover_types = ["cover", "large_cover", "xlarge_cover"] asset_file = "tests/integration/assets/jpg_file.jpg" file_data = FileHelper.get_file_data_from_asset(asset_file) upload_token = S3Helper.get_upload_token(workstation_api_client, ASSET_TYPE_IMAGE) product_dict = TestHelper.create_product( workstation_product_digital_api_client, product_data ) tuid = TestHelper.create_track_if_audio( workstation_track_api_client, file_data["file_type"], product_dict["product_id"] ) s3_meta_dict = S3Helper.s3_metadata(product_dict, file_data["file_ext"], tuid) S3Helper.upload_to_s3_check_response( asset_file, s3_meta_dict, file_data["file_ext"], file_data["content_type"], upload_token, ) def upload_assertions(response: requests.Response) -> None: assert response.status_code == 200, ( "Result of GET was {}, expected 200.".format(response.status_code) ) response_content = response.json() assert response_content["status"] == "encoding_completed", ( 'Status was {}, expected "encoding_completed".'.format( response_content["status"] ) ) # Execute GETs against asset/status TestHelper.assert_with_timeout( workstation_api_client.get_asset_status, upload_token["filename"], upload_assertions, ) for cover_type in cover_types: response_get = workstation_api_client.get_image_location( cover_type, product_dict["product_id"] ) assert response_get.status_code == 200, ( "Result of GET was {}, expected 200.".format(response_get.status_code) ) response_content = response_get.content.decode("utf-8") assert cover_type in response_content, ( "Expected response to contain {}, was {}.".format( cover_type, response_content ) ) assert ".jpeg" in response_content, ( "Expected extension to be {}, response was {}.".format( file_data["file_ext"], response_content ) ) def test_get_image_location_not_owned( workstation_api_client: APIClient, workstation_product_digital_api_client: ProductDigitalAPIClient, workstation_track_api_client: TrackAPIClient, product_data: dict[str, Any], workstation_api_client_d3: APIClient, ) -> None: """Image URL will be blocked via GET /image/location endpoint for a client that does not own product.""" asset_file = "tests/integration/assets/jpg_file.jpg" file_data = FileHelper.get_file_data_from_asset(asset_file) upload_token = S3Helper.get_upload_token(workstation_api_client, ASSET_TYPE_IMAGE) product_dict = TestHelper.create_product( workstation_product_digital_api_client, product_data ) tuid = TestHelper.create_track_if_audio( workstation_track_api_client, file_data["file_type"], product_dict["product_id"] ) s3_meta_dict = S3Helper.s3_metadata(product_dict, file_data["file_ext"], tuid) S3Helper.upload_to_s3_check_response( asset_file, s3_meta_dict, file_data["file_ext"], file_data["content_type"], upload_token, ) def upload_assertions(response: requests.Response) -> None: assert response.status_code == 200, ( "Result of GET was {}, expected 200.".format(response.status_code) ) response_content = response.json() assert response_content["status"] == "encoding_completed", ( 'Status was {}, expected "encoding_completed".'.format( response_content["status"] ) ) # Execute GETs against asset/status TestHelper.assert_with_timeout( workstation_api_client.get_asset_status, upload_token["filename"], upload_assertions, ) response_get = workstation_api_client_d3.get_image_location( "cover", product_dict["product_id"] ) assert response_get.status_code == 403, ( "Result of GET was {}, expected 403.".format(response_get.status_code) ) def test_artwork_submission_v2( workstation_api_client: APIClient, workstation_user: dict[str, Any] ) -> None: """Asset should be processed v2 flows. If uploaded with meta and POST /asset is executed. """ # Get a upload token for an image asset upload_token = S3Helper.post_upload_token(workstation_api_client, "image") # assert no v1 image record us created. query_result = QueryHelper.get_image_asset_record_by_filename( upload_token["filename"], deleted="0", api_version=1 ) assert query_result is None upload_token = S3Helper.post_upload_token(workstation_api_client, "image") # assert no v2 image record us created. query_result = QueryHelper.get_image_asset_record_by_filename( upload_token["filename"], deleted="0", api_version=2 ) assert query_result == upload_token["filename"] # Get a upload token for an audio asset upload_token = S3Helper.post_upload_token(workstation_api_client, "audio") # assert v2 audio record is created. query_result = QueryHelper.get_image_asset_record_by_filename( upload_token["filename"], deleted="0", api_version=2 ) assert query_result == upload_token["filename"]