"""Functional tests for the create_asset_upload endpoint.""" import datetime from unittest.mock import MagicMock, patch from uuid import UUID import boto3 import freezegun from flask.testing import FlaskClient from moto import mock_aws from owsrequest.utils import mock_request from owsresponse.response import Response from pytest_mock import MockerFixture from assets import config, rate_limiting from assets.connectors import s3 as s3_connector from assets.constants import asset_upload_types as asset_upload_types_constants from assets.models import asset_upload as asset_upload_model from assets.rate_limiting import RateLimitedResource @freezegun.freeze_time("2024-02-27 21:58:08.183726") @patch("uuid.uuid4", return_value=UUID("37bc7fa6-e316-43e3-b9d3-65b59931b545")) @mock_aws def test_create_asset_upload( _mocked_uuid4: MagicMock, db_fixture: None, fixture_client: FlaskClient, mocker: MockerFixture, mock_raise_for_status: MagicMock, ) -> None: """Test create_asset_upload success.""" def _fake_response( product_id: int, account_type: str | None = None, account_id: int | None = None ) -> Response: return Response() product_id = 123 upc = 123456789012 track_unique_id = 456 grass_account_type = "vendor" grass_account_id = 234234 orchard_user_id = "alw:12345" mocker.patch("assets.handlers.ownership", check_ownership=_fake_response) mock_resolve_asset_upload_type_id = mocker.patch( "assets.models.asset_upload_type.resolve_asset_upload_type_id", return_value=1 ) mock_request.head( service="ows-product", path=f"/{grass_account_type}/{grass_account_id}/product/{product_id}", status=200, ) mock_request.get( service="ows-product", path=f"/product/{product_id}", response={ "upc": upc, "status": "in_content", "context_type": "digital", }, status=200, ) mock_request.get( service="ows-product-digital", path=f"/product/audio/{product_id}", response={ "release_status": "error_correction", "is_error_correction_and_action_required": True, }, status=200, ) mock_request.get( service="ows-track", path=f"/track/{track_unique_id}", response={ "product_id": product_id, }, status=200, ) create_multipart_upload_spy = mocker.spy( s3_connector.get_s3_client(), "create_multipart_upload" ) s3_client = boto3.client("s3") s3_client.create_bucket(Bucket=config.RAW_ASSETS_BUCKET_NAME) create_asset_upload_with_context_spy = mocker.spy( asset_upload_model, "create_asset_upload_with_context" ) request_body = { "product_id": product_id, "track_unique_id": track_unique_id, "original_filename": "test.tif", } result = fixture_client.post( "/v2/assets/upload", json=request_body, headers={ "Orchard-User-Id": orchard_user_id, "Grass-Account-Type": grass_account_type, "Grass-Account-Id": grass_account_id, }, ) create_multipart_upload_spy.assert_called_once_with( Bucket=config.RAW_ASSETS_BUCKET_NAME, Key="37bc7fa6_e316_43e3_b9d3_65b59931b545", ContentType="application/octet-stream", ) mock_resolve_asset_upload_type_id.assert_called_once_with( asset_upload_types_constants.STEREO ) assert create_asset_upload_with_context_spy.spy_return == { "asset_upload": { "id": 1, "user_id": orchard_user_id, "asset_upload_type_id": 1, "token": create_multipart_upload_spy.spy_return["UploadId"], "filename": "37bc7fa6_e316_43e3_b9d3_65b59931b545", "api_version": 2, "product_id": product_id, "upc": str(upc), "track_unique_id": track_unique_id, "bucket_name": "test-orcd-raw-assets", "original_filename": "test.tif", "deleted": False, "is_correction": True, }, "asset_status": { "id": 1, "asset_upload_id": 1, "status": "uploading", "description": None, "status_time": datetime.datetime( 2024, 2, 27, 21, 58, 8, 183726, tzinfo=datetime.timezone.utc ), "message": None, }, "deleted_asset_uploads": [], } assert result.status_code == 200 assert result.json == { "filename": "37bc7fa6_e316_43e3_b9d3_65b59931b545", } @freezegun.freeze_time("2024-02-27 21:58:08.183726") @patch("uuid.uuid4", return_value=UUID("37bc7fa6-e316-43e3-b9d3-65b59931b545")) @mock_aws def test_create_asset_upload_digital_booklet( _mocked_uuid4: MagicMock, db_fixture: None, fixture_client: FlaskClient, mocker: MockerFixture, mock_raise_for_status: MagicMock, ) -> None: """Test create_asset_upload success for the digital_booklet image upload type.""" def _fake_response( product_id: int, account_type: str | None = None, account_id: int | None = None ) -> Response: return Response() product_id = 123 upc = 123456789012 track_unique_id = 0 grass_account_type = "vendor" grass_account_id = 234234 orchard_user_id = "alw:12345" mocker.patch("assets.handlers.ownership", check_ownership=_fake_response) mock_resolve_asset_upload_type_id = mocker.patch( "assets.models.asset_upload_type.resolve_asset_upload_type_id", return_value=2 ) mock_request.head( service="ows-product", path=f"/{grass_account_type}/{grass_account_id}/product/{product_id}", status=200, ) mock_request.get( service="ows-product", path=f"/product/{product_id}", response={ "upc": upc, "status": "in_content", "context_type": "digital", }, status=200, ) mock_request.get( service="ows-product-digital", path=f"/product/audio/{product_id}", response={ "release_status": "error_correction", "is_error_correction_and_action_required": True, }, status=200, ) create_multipart_upload_spy = mocker.spy( s3_connector.get_s3_client(), "create_multipart_upload" ) s3_client = boto3.client("s3") s3_client.create_bucket(Bucket=config.RAW_ASSETS_BUCKET_NAME) create_asset_upload_with_context_spy = mocker.spy( asset_upload_model, "create_asset_upload_with_context" ) request_body = { "product_id": product_id, "track_unique_id": track_unique_id, "original_filename": "booklet.pdf", "asset_upload_type": asset_upload_types_constants.DIGITAL_BOOKLET, } result = fixture_client.post( "/v2/assets/upload", json=request_body, headers={ "Orchard-User-Id": orchard_user_id, "Grass-Account-Type": grass_account_type, "Grass-Account-Id": grass_account_id, }, ) create_multipart_upload_spy.assert_called_once_with( Bucket=config.RAW_ASSETS_BUCKET_NAME, Key="37bc7fa6_e316_43e3_b9d3_65b59931b545", ContentType="application/octet-stream", ) mock_resolve_asset_upload_type_id.assert_called_once_with( asset_upload_types_constants.DIGITAL_BOOKLET ) assert create_asset_upload_with_context_spy.spy_return == { "asset_upload": { "id": 1, "user_id": orchard_user_id, "asset_upload_type_id": 2, "token": create_multipart_upload_spy.spy_return["UploadId"], "filename": "37bc7fa6_e316_43e3_b9d3_65b59931b545", "api_version": 2, "product_id": product_id, "upc": str(upc), "track_unique_id": track_unique_id, "bucket_name": "test-orcd-raw-assets", "original_filename": "booklet.pdf", "deleted": False, "is_correction": True, }, "asset_status": { "id": 1, "asset_upload_id": 1, "status": "uploading", "description": None, "status_time": datetime.datetime( 2024, 2, 27, 21, 58, 8, 183726, tzinfo=datetime.timezone.utc ), "message": None, }, "deleted_asset_uploads": [], } assert result.status_code == 200 assert result.json == { "filename": "37bc7fa6_e316_43e3_b9d3_65b59931b545", } def test_create_asset_upload_over_limit(fixture_client: FlaskClient) -> None: limit = rate_limiting.RATE_LIMITS[ (rate_limiting.RateLimitedResource.UPLOAD, "oa:179") ] product_id = 123 track_unique_id = 456 grass_account_type = "vendor" grass_account_id = 234234 orchard_user_id = "oa:179" for _ in range(limit.amount): rate_limiting.get_rate_limiter().hit( RateLimitedResource.UPLOAD, orchard_user_id ) request_body = { "product_id": product_id, "track_unique_id": track_unique_id, "original_filename": "test.tif", } result = fixture_client.post( "/v2/assets/upload", json=request_body, headers={ "Orchard-User-Id": orchard_user_id, "Grass-Account-Type": grass_account_type, "Grass-Account-Id": grass_account_id, }, ) assert result.status_code == 429 assert result.json == { "code": "rate_limit_exceeded", "message": f"Rate limit exceeded. Limit: {limit}.", }