"""Tests for main handlers. /upload-token base.""" import json from typing import Any import pytest from flask.testing import FlaskClient from flexmock import flexmock from owsresponse import response from assets import config from assets.constants import asset_upload as asset_upload_constants, error, field_const from assets.exceptions import AssetUploadError from assets.logic import generate_upload_data, ownership OA_HEADERS = {"Orchard-User-Id": "oa:12345"} ALW_HEADERS = { "Orchard-User-Id": "alw:12345", "Grass-Account-Id": "234234", "Grass-Account-Type": "vendor", } def test_get_upload_token_success_for_entity_vendor( fixture_client: FlaskClient, fixture_upload_token_data_for_entity: dict[str, Any], fixture_is_valid_vendor: str, ) -> None: """Test route that gets the upload token.""" grass_headers = { "Orchard-User-Id": "alw:12345", "Grass-Account-Id": "1234", "Grass-Account-Type": "vendor", } entity = "vendor" entity_id = 1234 mock_logic_response = fixture_upload_token_data_for_entity mock_is_valid_vendor_response = response.Response(fixture_is_valid_vendor) flexmock(ownership).should_receive("check_vendor_ownership").with_args( entity_id, entity, grass_headers["Grass-Account-Id"] ).and_return(mock_is_valid_vendor_response) flexmock(generate_upload_data).should_receive( "get_entity_upload_permission" ).with_args(config.STS_TOKEN_DURATION, entity, entity_id).and_return( mock_logic_response ) result = fixture_client.open( "/upload-token/{entity}/{entity_id}".format(entity=entity, entity_id=entity_id), method="GET", headers=grass_headers, ) assert result.status_code == 200 assert result.headers.get("Correlation-Id") upload_token = json.loads(result.data.decode("utf-8")) assert upload_token.get("credentials") assert upload_token.get("bucket") assert upload_token.get("path") def test_get_upload_token_success_for_entity_artist( fixture_client: FlaskClient, fixture_upload_token_data_for_entity: dict[str, Any], fixture_is_valid_artist: str, ) -> None: """Test route that gets the upload token.""" grass_headers = { "Orchard-User-Id": "alw:12345", "Grass-Account-Id": "1234", "Grass-Account-Type": "vendor", } entity = "artist" entity_id = 1234 artist_photo_id = "12345" mock_logic_response = fixture_upload_token_data_for_entity mock_is_valid_artist_response = fixture_is_valid_artist flexmock(ownership).should_receive("is_valid_artist").with_args( entity_id, grass_headers["Grass-Account-Type"], grass_headers["Grass-Account-Id"], ).and_return(mock_is_valid_artist_response) flexmock(generate_upload_data).should_receive( "get_entity_upload_permission" ).with_args(config.STS_TOKEN_DURATION, entity, int(artist_photo_id)).and_return( mock_logic_response ) result = fixture_client.open( "/upload-token/{entity}/{entity_id}?artist_photo_id={artist_photo_id}".format( entity=entity, entity_id=entity_id, artist_photo_id=artist_photo_id ), method="GET", headers=grass_headers, ) assert result.status_code == 200 assert result.headers.get("Correlation-Id") upload_token = json.loads(result.data.decode("utf-8")) assert upload_token.get("credentials") assert upload_token.get("bucket") assert upload_token.get("path") def test_get_upload_token_invalid_entity(fixture_client: FlaskClient) -> None: """Test upload-token if entity is invalid.""" entity = "test" entity_id = 12345 result = fixture_client.open( "/upload-token/{entity}/{entity_id}".format(entity=entity, entity_id=entity_id), method="GET", headers=ALW_HEADERS, ) error_response = json.loads(result.data.decode("utf-8")) assert result.status_code == 404 assert error_response["code"] == error.ERROR_CODE_INVALID_ENTITY_TYPE @pytest.mark.parametrize( "headers", [ OA_HEADERS, ALW_HEADERS, ], ) @pytest.mark.parametrize("method", ["GET", "POST"]) def test_get_upload_token_success( fixture_client: FlaskClient, headers: dict[str, str], method: str, fixture_upload_token_data: dict[str, Any], ) -> None: """Test route that gets the upload token.""" mock_logic_response = fixture_upload_token_data ( flexmock(generate_upload_data) .should_receive("get_upload_permission") .with_args( headers.get("Orchard-User-Id"), config.STS_TOKEN_DURATION, asset_upload_constants.ASSET_TYPE_AUDIO, ) .and_return(mock_logic_response) ) request_url = "/upload-token?asset_type={}".format( asset_upload_constants.ASSET_TYPE_AUDIO ) data = None if method == "POST": request_url = "/upload-token" data = json.dumps( {field_const.ASSET_TYPE: asset_upload_constants.ASSET_TYPE_AUDIO} ) result = fixture_client.open(request_url, method=method, headers=headers, data=data) assert result.status_code == 200 assert result.headers.get("Correlation-Id") upload_token = json.loads(result.data.decode("utf-8")) assert upload_token.get("credentials") assert upload_token.get("bucket") assert upload_token.get("filename") @pytest.mark.parametrize("headers", [OA_HEADERS, ALW_HEADERS]) @pytest.mark.parametrize( "duration,expected_duration", [(900, 900), (1800, 1800), (700, 900)] ) @pytest.mark.parametrize("method", ["GET", "POST"]) def test_get_upload_token_uses_duration( fixture_client: FlaskClient, headers: dict[str, str], duration: int, expected_duration: int, method: str, fixture_upload_token_data: dict[str, Any], ) -> None: """Test route uses the duration parameter to get upload permission.""" mock_logic_response = fixture_upload_token_data ( flexmock(generate_upload_data) .should_receive("get_upload_permission") .with_args( headers.get("Orchard-User-Id"), expected_duration, asset_upload_constants.ASSET_TYPE_AUDIO, ) .and_return(mock_logic_response) ) request_url = "/upload-token?duration={}&asset_type={}".format( duration, asset_upload_constants.ASSET_TYPE_AUDIO, ) data = None if method == "POST": request_url = "/upload-token" data = json.dumps( { field_const.DURATION: duration, field_const.ASSET_TYPE: asset_upload_constants.ASSET_TYPE_AUDIO, } ) result = fixture_client.open(request_url, method=method, data=data, headers=headers) assert result.status_code == 200 assert result.headers.get("Correlation-Id") upload_token = json.loads(result.data.decode("utf-8")) assert upload_token.get("credentials") assert upload_token.get("bucket") assert upload_token.get("filename") @pytest.mark.parametrize("headers", [OA_HEADERS, ALW_HEADERS]) @pytest.mark.parametrize("method", ["GET", "POST"]) def test_get_upload_token_uses_asset_type( fixture_client: FlaskClient, headers: dict[str, str], method: str, fixture_upload_token_data: dict[str, Any], ) -> None: """Test route uses the asset_type parameter to get upload permission.""" mock_logic_response = fixture_upload_token_data ( flexmock(generate_upload_data) .should_receive("get_upload_permission") .with_args( headers.get("Orchard-User-Id"), config.STS_TOKEN_DURATION, asset_upload_constants.ASSET_TYPE_AUDIO, ) .and_return(mock_logic_response) ) request_url = "/upload-token?asset_type={}".format( asset_upload_constants.ASSET_TYPE_AUDIO ) data = None if method == "POST": request_url = "/upload-token" data = json.dumps( {field_const.ASSET_TYPE: asset_upload_constants.ASSET_TYPE_AUDIO} ) result = fixture_client.open(request_url, method=method, headers=headers, data=data) assert result.status_code == 200 assert result.headers.get("Correlation-Id") upload_token = json.loads(result.data.decode("utf-8")) assert upload_token.get("credentials") assert upload_token.get("bucket") assert upload_token.get("filename") @pytest.mark.parametrize("headers", [OA_HEADERS, ALW_HEADERS]) @pytest.mark.parametrize("method", ["GET", "POST"]) def test_get_upload_token_no_asset_type_failure( fixture_client: FlaskClient, headers: dict[str, str], method: str, ) -> None: """Test route fails with no asset_type parameter provided.""" request_url = "/upload-token?asset_type=" data = None if method == "POST": request_url = "/upload-token" data = json.dumps({}) result = fixture_client.open(request_url, method=method, headers=headers, data=data) assert result.status_code == 400 response_message = json.loads(result.data.decode()) assert response_message["code"] == error.ERROR_CODE_BAD_REQUEST assert response_message["message"] == "Asset Type is required" @pytest.mark.parametrize("headers", [OA_HEADERS, ALW_HEADERS]) @pytest.mark.parametrize("method", ["GET", "POST"]) def test_get_upload_token_invalid_asset_type_failure( fixture_client: FlaskClient, headers: dict[str, str], method: str, ) -> None: """Test route fails with invalid asset_type parameter provided.""" request_url = "/upload-token?asset_type=invalid_asset_type" data = None if method == "POST": request_url = "/upload-token" data = json.dumps({field_const.ASSET_TYPE: "invalid_asset_type"}) result = fixture_client.open(request_url, method=method, headers=headers, data=data) assert result.status_code == 400 response_message = json.loads(result.data.decode()) assert response_message["code"] == error.ERROR_CODE_BAD_REQUEST assert response_message["message"] == "Unsupported Asset Type" @pytest.mark.parametrize("headers", [OA_HEADERS, ALW_HEADERS]) @pytest.mark.parametrize("method", ["GET", "POST"]) def test_get_upload_token_logic_layer_failure( fixture_client: FlaskClient, headers: dict[str, str], method: str ) -> None: """Test route fails when there is logic layer failure.""" error_message = "failed to create asset upload" ( flexmock(generate_upload_data) .should_receive("get_upload_permission") .with_args( headers.get("Orchard-User-Id"), config.STS_TOKEN_DURATION, asset_upload_constants.ASSET_TYPE_AUDIO, ) .and_raise(AssetUploadError(error_message)) ) headers = headers.copy() headers["Correlation-id"] = "correlation_id" request_url = "/upload-token?asset_type={}".format( asset_upload_constants.ASSET_TYPE_AUDIO ) data = None if method == "POST": request_url = "/upload-token" data = json.dumps( {field_const.ASSET_TYPE: asset_upload_constants.ASSET_TYPE_AUDIO} ) result = fixture_client.open(request_url, method=method, headers=headers, data=data) assert result.status_code == 500 response_message = json.loads(result.data.decode()) assert response_message["code"] == "internal_error" assert response_message["message"] == error_message assert result.headers.get("Correlation-Id") == "correlation_id"