"""Tests for OWS Assets connector.""" from typing import Any from unittest.mock import MagicMock, Mock, patch import pytest from flexmock import flexmock from transcoding import config from transcoding.connectors import ows_assets @pytest.fixture(autouse=True) def _clear_get_asset_owner_cache() -> None: ows_assets.get_asset_owner.cache_clear() @pytest.fixture def mock_response() -> Mock: """Return a mock response object.""" response = Mock() response.status_code = 200 response.text = "Success" return response @pytest.fixture def post_asset_final_status_args() -> dict[str, Any]: """Return arguments for post_asset_final_status.""" return { "filename": "test_file.wav", "status": "completed", "description": "Processing completed successfully", "final_assets": [ { "key": "output_file.wav", "bucket": "output-bucket", "container": "wav", "duration": 1000, "channels": 2, "codec": "pcm", "sample_rate": 44100, "bit_rate": 1411200, "bit_depth": 16, } ], "message": { "status": "completed", "result_assets": [], "input": {"bucket": "input-bucket", "key": "test_file.wav"}, }, } @patch("transcoding.connectors.ows_assets.datetime") def test_post_asset_final_status_success_200( mock_datetime: MagicMock, post_asset_final_status_args: dict[str, Any], mock_response: Mock, ) -> None: """Test successful post to ows-assets with 200 response.""" fixed_timestamp = "2024-01-15T10:30:45.123456Z" mock_now = Mock() mock_now.strftime.return_value = fixed_timestamp mock_datetime.now.return_value = mock_now expected_data = { "filename": post_asset_final_status_args["filename"], "status": post_asset_final_status_args["status"], "description": post_asset_final_status_args["description"], "message": post_asset_final_status_args["message"], "timestamp": fixed_timestamp, "final_assets": post_asset_final_status_args["final_assets"], } ( flexmock(ows_assets.client) .should_receive("post") .with_args( service_name=config.OWS_ASSETS_SERVICE_NAME, path=config.POST_ASSET_FINAL_PATH, json=expected_data, ) .and_return(mock_response) .once() ) result = ows_assets.post_asset_final_status(**post_asset_final_status_args) assert result @patch("transcoding.connectors.ows_assets.datetime") def test_post_asset_final_status_404_response( mock_datetime: MagicMock, post_asset_final_status_args: dict[str, Any], mock_response: Mock, ) -> None: """Test post to ows-assets with 404 response (asset deleted).""" fixed_timestamp = "2024-01-15T10:30:45.123456Z" mock_now = Mock() mock_now.strftime.return_value = fixed_timestamp mock_datetime.now.return_value = mock_now mock_response.status_code = 404 ( flexmock(ows_assets.client) .should_receive("post") .and_return(mock_response) .once() ) # Should not raise exception, just pass silently result = ows_assets.post_asset_final_status(**post_asset_final_status_args) assert result @patch("transcoding.connectors.ows_assets.datetime") def test_post_asset_final_status_409_response( mock_datetime: MagicMock, post_asset_final_status_args: dict[str, Any], mock_response: Mock, ) -> None: """Test post to ows-assets with 409 response (duplicate processing).""" fixed_timestamp = "2024-01-15T10:30:45.123456Z" mock_now = Mock() mock_now.strftime.return_value = fixed_timestamp mock_datetime.now.return_value = mock_now mock_response.status_code = 409 ( flexmock(ows_assets.client) .should_receive("post") .and_return(mock_response) .once() ) # Should not raise exception, just pass silently result = ows_assets.post_asset_final_status(**post_asset_final_status_args) assert result @patch("transcoding.connectors.ows_assets.datetime") def test_post_asset_final_status_500_error( mock_datetime: MagicMock, post_asset_final_status_args: dict[str, Any], mock_response: Mock, ) -> None: """Test post to ows-assets with 500 error response.""" fixed_timestamp = "2024-01-15T10:30:45.123456Z" mock_now = Mock() mock_now.strftime.return_value = fixed_timestamp mock_datetime.now.return_value = mock_now mock_response.status_code = 500 mock_response.text = "Internal Server Error" ( flexmock(ows_assets.client) .should_receive("post") .and_return(mock_response) .once() ) # Should raise exception with formatted error message with pytest.raises(Exception) as exc_info: ows_assets.post_asset_final_status(**post_asset_final_status_args) error_message = str(exc_info.value) assert "Failed to post to ows-assets" in error_message assert config.SERVICE_NAME in error_message assert "500" in error_message assert "Internal Server Error" in error_message @patch("transcoding.connectors.ows_assets.datetime") def test_post_asset_final_status_with_empty_final_assets( mock_datetime: MagicMock, mock_response: Mock ) -> None: """Test post to ows-assets with empty final assets.""" fixed_timestamp = "2024-01-15T10:30:45.123456Z" mock_now = Mock() mock_now.strftime.return_value = fixed_timestamp mock_datetime.now.return_value = mock_now ( flexmock(ows_assets.client) .should_receive("post") .and_return(mock_response) .once() ) result = ows_assets.post_asset_final_status( "test_file.wav", "error", "Processing failed", [], { "status": "error", "input": {"bucket": "input-bucket", "key": "test_file.wav"}, }, ) assert result @patch("transcoding.connectors.ows_assets.datetime") def test_post_asset_final_status_timestamp_format( mock_datetime: MagicMock, post_asset_final_status_args: dict[str, Any], mock_response: Mock, ) -> None: """Test that timestamp is correctly formatted.""" fixed_timestamp = "2024-12-31T23:59:59.999999Z" mock_now = Mock() mock_now.strftime.return_value = fixed_timestamp mock_datetime.now.return_value = mock_now expected_data = { "filename": post_asset_final_status_args["filename"], "status": post_asset_final_status_args["status"], "description": post_asset_final_status_args["description"], "message": post_asset_final_status_args["message"], "timestamp": fixed_timestamp, "final_assets": post_asset_final_status_args["final_assets"], } ( flexmock(ows_assets.client) .should_receive("post") .with_args( service_name=config.OWS_ASSETS_SERVICE_NAME, path=config.POST_ASSET_FINAL_PATH, json=expected_data, ) .and_return(mock_response) .once() ) result = ows_assets.post_asset_final_status(**post_asset_final_status_args) assert result @patch("transcoding.connectors.ows_assets.datetime") def test_post_asset_final_status_calls_correct_endpoint( mock_datetime: MagicMock, post_asset_final_status_args: dict[str, Any], mock_response: Mock, ) -> None: """Test that correct service name and path are used.""" fixed_timestamp = "2024-01-15T10:30:45.123456Z" mock_now = Mock() mock_now.strftime.return_value = fixed_timestamp mock_datetime.now.return_value = mock_now ( flexmock(ows_assets.client) .should_receive("post") .with_args(service_name="ows-assets", path="/v2/asset/final", json=dict) .and_return(mock_response) .once() ) result = ows_assets.post_asset_final_status(**post_asset_final_status_args) assert result @patch("transcoding.connectors.ows_assets.datetime") def test_post_asset_final_status_403_error( mock_datetime: MagicMock, post_asset_final_status_args: dict[str, Any], mock_response: Mock, ) -> None: """Test post to ows-assets with 403 forbidden response.""" fixed_timestamp = "2024-01-15T10:30:45.123456Z" mock_now = Mock() mock_now.strftime.return_value = fixed_timestamp mock_datetime.now.return_value = mock_now mock_response.status_code = 403 mock_response.text = "Forbidden" ( flexmock(ows_assets.client) .should_receive("post") .and_return(mock_response) .once() ) with pytest.raises(Exception) as exc_info: ows_assets.post_asset_final_status(**post_asset_final_status_args) error_message = str(exc_info.value) assert "Failed to post to ows-assets" in error_message assert "403" in error_message assert "Forbidden" in error_message def test_get_asset_owner_success(mock_response: Mock) -> None: """Test successful get asset owner by filename.""" filename = "test_file.wav" expected_payload = {"vendor_id": 100, "subaccount_id": 200} mock_response.status_code = 200 mock_response.json.return_value = expected_payload ( flexmock(ows_assets.client) .should_receive("get") .with_args( service_name=config.OWS_ASSETS_SERVICE_NAME, path=config.GET_ASSET_OWNER_PATH_TEMPLATE.format(filename=filename), ) .and_return(mock_response) .once() ) result = ows_assets.get_asset_owner(filename) assert result == expected_payload def test_get_asset_owner_not_found_404(mock_response: Mock) -> None: """Test get asset owner returns specific exception for 404.""" filename = "missing_file.wav" mock_response.status_code = 404 (flexmock(ows_assets.client).should_receive("get").and_return(mock_response).once()) with pytest.raises(ows_assets.AssetOwnerNotDeterminableException) as exc_info: ows_assets.get_asset_owner(filename) assert filename in str(exc_info.value) def test_get_asset_owner_500_error(mock_response: Mock) -> None: """Test get asset owner raises AssetOwnerLookupException for non-200 and non-404 response.""" filename = "test_file.wav" mock_response.status_code = 500 mock_response.text = "Internal Server Error" (flexmock(ows_assets.client).should_receive("get").and_return(mock_response).once()) with pytest.raises(ows_assets.AssetOwnerLookupException) as exc_info: ows_assets.get_asset_owner(filename) error_message = str(exc_info.value) assert "Failed to get from ows-assets" in error_message assert config.SERVICE_NAME in error_message assert "500" in error_message assert "Internal Server Error" in error_message