"""Tests for Handlers.""" import json from typing import Any from unittest.mock import MagicMock, patch import pytest from flask.ctx import AppContext from flask.testing import FlaskClient from flexmock import flexmock from owsresponse import response from transcoding import handlers from transcoding.logic import ( create_transcoding, preset, transcoding_job as transcoding_job_logic, transcoding_order, transcoding_status, ) from tests.testutils import seed @pytest.fixture def header_fixture() -> dict[str, str]: """Return default header for test API call.""" return {"Content-Type": "application/json"} @pytest.fixture def create_preset_transcoding_input_data() -> dict[str, Any]: """Return data for use in create preset transcoding API call.""" return { "input": { "key": "test_key", "bucket": "test_bucket", "metadata": { "channels": 2, "codec": "pcm", "bit_rate": 1411200, "sample_rate": 44100, "bit_depth": 16, "container": "wave", }, }, "status_topic_alias": "sns_spotify", } @pytest.fixture def create_transcoding_input_data() -> dict[str, Any]: """Return data for use in create custom transcoding API call.""" return { "input": {"bucket": "bucket_name", "key": "track.wav"}, "output": { "bucket": "output_bucket_name", "transcoding": [ { "container": "flac", "codec": None, "sample_rate": None, "bit_rate": None, "channels": None, "bit_depth": None, }, { "container": "wave", "codec": "alac", "sample_rate": 44100, "bit_rate": None, "channels": 2, "bit_depth": 24, }, ], }, "status_topic_alias": "assets_topic", } @pytest.fixture def get_status_response_fixture() -> dict[str, Any]: """Fixtures for transcoding_order.get_status function response.""" return { "transcoding_order_status": "completed", "transcoding_order_id": 1, "transcoding_jobs": [ { "status": "completed", "transcoding_job_id": 10, "description": "", }, { "status": "completed", "transcoding_job_id": 11, "description": "", }, ], } @pytest.fixture def valid_transcoding_job_fixture() -> dict[str, Any]: """Fixtures for transcoding_job.get_transcoding_job_by_id response.""" return { "transcoding_job_id": 4, "transcoding_order_id": 2, "status": "requested", "description": None, "attempt": 0, "output_bucket": "ows-transcoding-raw", "output_key": "4e6e4670-03f4-4a34-8d92-123123dc.wav", "container": "m4a", "channels": None, "codec": None, "sample_rate": None, "bit_rate": None, "bit_depth": None, "preset_id": None, } @patch("transcoding.handlers.g", spec=["log"]) def test_exception_handler(mock_g: MagicMock, app_context: AppContext) -> None: """Verify exception_Handler returns 500 status code and json payload.""" message = ( "The server encountered an internal error " "and was unable to complete your request." ) mock_error = MagicMock() server_response = handlers.exception_handler(mock_error) mock_g.log.exception.assert_called_with(mock_error) # assert status code is 500 assert server_response.status_code == 500 # assert json payload response_message = json.loads(server_response.data.decode()) assert response_message["message"] == message assert response_message["code"] == response.error.ERROR_CODE_INTERNAL_ERROR def test_get_all_presets( fixture_client: FlaskClient, header_fixture: dict[str, str] ) -> None: """Test route that gets all existing presets.""" # data preparation request_url = "/transcoding/presets" # mocking ( flexmock(preset) .should_receive("get_all_presets") .with_args() .and_return(response.Response(seed.preset_seed_data)) ) # test function call result_response = fixture_client.get(request_url, headers=header_fixture) # checking assert result_response.status_code == 200 assert result_response.headers.get("Correlation-Id") result_presets = json.loads(result_response.data.decode("utf-8")) assert result_presets == seed.preset_seed_data def test_create_preset_transcoding_success_with_metadata( fixture_client: FlaskClient, create_preset_transcoding_input_data: dict[str, Any], header_fixture: dict[str, str], ) -> None: """Test route that start transcoding workflow by preset with metadata.""" # data preparation preset_alias = "spotify" request_url = "/transcoding/preset/{preset_alias}".format(preset_alias=preset_alias) response_dict = {"transcoding_order_id": 1, "status": "processing"} successful_response = response.Response(response_dict) # mocking ( flexmock(create_transcoding) .should_receive("create_transcoding_by_preset") .with_args( input_bucket=create_preset_transcoding_input_data["input"]["bucket"], input_key=create_preset_transcoding_input_data["input"]["key"], preset_alias=preset_alias, status_topic_alias=create_preset_transcoding_input_data[ "status_topic_alias" ], source_asset_metadata=create_preset_transcoding_input_data["input"][ "metadata" ], ) .and_return(successful_response) ) # test function call data = json.dumps(create_preset_transcoding_input_data) result_response = fixture_client.post( request_url, data=data, headers=header_fixture ) # checking assert result_response.status_code == 200 assert result_response.headers.get("Correlation-Id") assert response_dict == json.loads(result_response.data.decode("utf-8")) def test_create_preset_transcoding_failure( fixture_client: FlaskClient, create_preset_transcoding_input_data: dict[str, Any], header_fixture: dict[str, str], ) -> None: """Test route that start transcoding workflow by preset failure.""" # data preparation preset_alias = "spotify" request_url = "/transcoding/preset/{preset_alias}".format(preset_alias=preset_alias) expected_error = "error" invalid_response = response.create_fatal_response(expected_error) # mocking ( flexmock(create_transcoding) .should_receive("create_transcoding_by_preset") .with_args( input_bucket=create_preset_transcoding_input_data["input"]["bucket"], input_key=create_preset_transcoding_input_data["input"]["key"], preset_alias=preset_alias, status_topic_alias=create_preset_transcoding_input_data[ "status_topic_alias" ], source_asset_metadata=create_preset_transcoding_input_data["input"][ "metadata" ], ) .and_return(invalid_response) ) # test function call data = json.dumps(create_preset_transcoding_input_data) result_response = fixture_client.post( request_url, data=data, headers=header_fixture ) # checking assert result_response.status_code == 500 assert result_response.headers.get("Correlation-Id") error_message = json.loads(result_response.data.decode("utf-8"))["message"] assert expected_error == error_message def test_transcoding_status( fixture_client: FlaskClient, header_fixture: dict[str, str], get_status_response_fixture: dict[str, Any], ) -> None: """Test route that gets status existing transcoding order with jobs.""" # data preparation transcoding_id = 1 request_url = "/transcoding-order/{}/status".format(transcoding_id) # mocking ( flexmock(transcoding_order) .should_receive("get_status") .with_args(transcoding_order_id=transcoding_id) .and_return(response.Response(get_status_response_fixture)) ) # test function call result_response = fixture_client.get(request_url, headers=header_fixture) # checking assert result_response.status_code == 200 assert result_response.headers.get("Correlation-Id") result_presets = json.loads(result_response.data.decode("utf-8")) assert result_presets == get_status_response_fixture @pytest.fixture def post_transcoding_job_status() -> dict[str, Any]: """Return data for use in post transcoding by preset API call.""" return { "status": "completed", "status_description": "completed_description", "metadata": { "@type": "Audio", "Format": "PCM", "Format_Settings_Endianness": "Little", "Format_Settings_Sign": "Signed", "CodecID": "1", "Duration": "2.963", "BitRate_Mode": "CBR", "BitRate": "1411200", "Channels": "2", "SamplingRate": "44100", "SamplingCount": "130688", "BitDepth": "16", "StreamSize": "522752", }, } def test_set_transcoding_job_status_success( fixture_client: FlaskClient, post_transcoding_job_status: dict[str, Any], header_fixture: dict[str, str], ) -> None: """Test route that save transcoding status.""" transcoding_job_id = 10 status_url = "/transcoding-job/{transcoding_job_id}/status".format( transcoding_job_id=transcoding_job_id ) successful_response = response.Response("status_response") ( flexmock(transcoding_status) .should_receive("process_transcoding_status") .with_args( transcoding_job_id=transcoding_job_id, status=post_transcoding_job_status["status"], status_description=post_transcoding_job_status["status_description"], metadata=post_transcoding_job_status["metadata"], ) .and_return(successful_response) ) data = json.dumps(post_transcoding_job_status) result_response = fixture_client.post(status_url, data=data, headers=header_fixture) assert result_response.status_code == 200 assert result_response.headers.get("Correlation-Id") def test_set_transcoding_job_status_missing_duration_success( fixture_client: FlaskClient, post_transcoding_job_status: dict[str, Any], header_fixture: dict[str, str], ) -> None: """Test route that save transcoding status.""" transcoding_job_id = 10 status_url = "/transcoding-job/{transcoding_job_id}/status".format( transcoding_job_id=transcoding_job_id ) successful_response = response.Response("status_response") post_transcoding_job_status["metadata"].pop("Duration") ( flexmock(transcoding_status) .should_receive("process_transcoding_status") .with_args( transcoding_job_id=transcoding_job_id, status=post_transcoding_job_status["status"], status_description=post_transcoding_job_status["status_description"], metadata=post_transcoding_job_status["metadata"], ) .and_return(successful_response) ) data = json.dumps(post_transcoding_job_status) result_response = fixture_client.post(status_url, data=data, headers=header_fixture) assert result_response.status_code == 200 assert result_response.headers.get("Correlation-Id") def test_set_transcoding_job_status_failure( fixture_client: FlaskClient, post_transcoding_job_status: dict[str, Any], header_fixture: dict[str, str], ) -> None: """Test route that save transcoding status return error.""" transcoding_job_id = 10 status_url = "/transcoding-job/{transcoding_job_id}/status".format( transcoding_job_id=transcoding_job_id ) ( flexmock(transcoding_status) .should_receive("process_transcoding_status") .and_return(response.create_fatal_response("fatal_error")) ) data = json.dumps(post_transcoding_job_status) result_response = fixture_client.post(status_url, data=data, headers=header_fixture) assert result_response.status_code == 500 assert result_response.headers.get("Correlation-Id") def test_get_transcoding_job( fixture_client: FlaskClient, header_fixture: dict[str, str], valid_transcoding_job_fixture: dict[str, Any], ) -> None: """Test route that gets existing transcoding job.""" # data preparation transcoding_job_id = 4 request_url = "/transcoding-job/{}".format(transcoding_job_id) # mocking ( flexmock(transcoding_job_logic) .should_receive("get_transcoding_job_by_id") .with_args(transcoding_job_id=transcoding_job_id) .and_return(response.Response(valid_transcoding_job_fixture)) ) # test function call result_response = fixture_client.get(request_url, headers=header_fixture) # checking assert result_response.status_code == 200 assert result_response.headers.get("Correlation-Id") result_presets = json.loads(result_response.data.decode("utf-8")) assert result_presets == valid_transcoding_job_fixture