"""Tests for validation module.""" import json from typing import Any, Callable from unittest import mock import pytest from flask import Request, Response from assets.constants import error, field_const from assets.validation import json_schema from assets.validation.schema import body def fixture_function(request: Request) -> Response: """Function to mock real endpoint handler.""" @json_schema.validate_body(request, body.post_asset_schema) def function() -> Response: return Response("test_result") return function() @pytest.fixture def valid_post_asset_body() -> dict[str, Any]: """Function that returns valid post_asset body. Returns: dict: Body dict. """ return { field_const.ASSET_TYPE: "WAV", field_const.PRODUCT_ID: 12345, field_const.UPC: "12321", field_const.TRACK_UNIQUE_ID: 54321, field_const.FILENAME: "filename.jpg", field_const.TOKEN: "unique_token", field_const.ORIGINAL_FILENAME: "original_filename.wav", field_const.IS_CORRECTION: True, } def get_json_mock(response: dict[str, Any]) -> Callable[[bool], dict[str, Any]]: """Mock method for getting json from request body.""" return lambda silent: response def test_post_asset_validate_body_success( valid_post_asset_body: dict[str, Any], ) -> None: """Assert that validate_body succeeds when body is valid.""" request = mock.Mock() # s is silent argument from real request.get_json, stub here request.get_json = get_json_mock(valid_post_asset_body) assert "test_result" == fixture_function(request).data.decode() @pytest.mark.parametrize( "missing_field", [ field_const.ASSET_TYPE, field_const.PRODUCT_ID, field_const.UPC, field_const.TRACK_UNIQUE_ID, field_const.FILENAME, field_const.TOKEN, ], ) def test_post_asset_validate_body_missing_field_fail( valid_post_asset_body: dict[str, Any], missing_field: str ) -> None: """Assert that validate_body fail when field is missing.""" request = mock.Mock() del valid_post_asset_body[missing_field] request.get_json = get_json_mock(valid_post_asset_body) response = fixture_function(request) assert response.status_code == 400 response_json = json.loads(response.data.decode()) assert response_json["code"] == error.ERROR_CODE_BODY_VALIDATION def test_post_asset_validate_body_missing_correction_success( valid_post_asset_body: dict[str, Any], ) -> None: """Assert that validate_body succeed when is_correction is missing.""" request = mock.Mock() del valid_post_asset_body[field_const.IS_CORRECTION] request.get_json = get_json_mock(valid_post_asset_body) assert fixture_function(request) @pytest.mark.parametrize( "integer_field", [field_const.PRODUCT_ID, field_const.TRACK_UNIQUE_ID] ) def test_post_asset_validate_integer_field_fail( valid_post_asset_body: dict[str, Any], integer_field: str ) -> None: """Assert that validate_body fail when int field is string.""" request = mock.Mock() valid_post_asset_body[integer_field] = "some_string" request.get_json = get_json_mock(valid_post_asset_body) response = fixture_function(request) assert response.status_code == 400 response_json = json.loads(response.data.decode()) assert response_json["code"] == error.ERROR_CODE_BODY_VALIDATION def test_post_asset_validate_extra_field_fail( valid_post_asset_body: dict[str, Any], ) -> None: """Assert that validate_body fail unknown field.""" request = mock.Mock() valid_post_asset_body["extra_field"] = "some_string" request.get_json = get_json_mock(valid_post_asset_body) response = fixture_function(request) assert response.status_code == 400 response_json = json.loads(response.data.decode()) assert response_json["code"] == error.ERROR_CODE_BODY_VALIDATION def test_post_asset_validate_upc_leading_zeros_success( valid_post_asset_body: dict[str, Any], ) -> None: """Assert that validate_body success upc leading zeros.""" request = mock.Mock() valid_post_asset_body[field_const.UPC] = "000123" request.get_json = get_json_mock(valid_post_asset_body) assert "test_result" == fixture_function(request).data.decode() def test_post_asset_validate_upc_alpha_failure( valid_post_asset_body: dict[str, Any], ) -> None: """Assert that validate_body fail upc alpha.""" request = mock.Mock() valid_post_asset_body[field_const.UPC] = "a upc" request.get_json = get_json_mock(valid_post_asset_body) response = fixture_function(request) assert response.status_code == 400 def fixture_post_status_function(request: Request) -> Response: """Function to mock real endpoint handler.""" @json_schema.validate_body(request, body.post_asset_status_schema) def function() -> Response: return Response("test_result", status=200) return function() @pytest.fixture def valid_post_asset_status_body() -> dict[str, Any]: """Function that returns valid post_asset_status body. Returns: dict: Body dict. """ return { field_const.FILENAME: "unique_filename.ext", field_const.STATUS: "success", field_const.DESCRIPTION: "Success description", field_const.MESSAGE: { "function": "some_function", "status": "success", "description": "Success description", "input": {"key": "unique_filename.ext"}, }, field_const.TIMESTAMP: "timestamp", } def test_post_asset_status_validate_body_success( valid_post_asset_status_body: dict[str, Any], ) -> None: """Assert that validate_body succeeds when body is valid.""" request = mock.Mock() request.get_json = get_json_mock(valid_post_asset_status_body) assert "test_result" == fixture_post_status_function(request).data.decode() @pytest.mark.parametrize( "missing_field", [field_const.FILENAME, field_const.STATUS, field_const.MESSAGE] ) def test_post_asset_status_validate_body_failure( missing_field: str, valid_post_asset_status_body: dict[str, Any] ) -> None: """Assert that validate_body fails when field is missing.""" request = mock.Mock() valid_post_asset_status_body.pop(missing_field) request.get_json = get_json_mock(valid_post_asset_status_body) result = fixture_post_status_function(request) assert result.status_code == 400 def test_post_asset_status_validate_body_message_failure( valid_post_asset_status_body: dict[str, Any], ) -> None: """Assert that validate_body fails when message is string.""" request = mock.Mock() valid_post_asset_status_body["message"] = 123 request.get_json = get_json_mock(valid_post_asset_status_body) result = fixture_post_status_function(request) assert result.status_code == 400 @pytest.fixture def valid_post_final_status_body() -> dict[str, Any]: """Function that returns valid post_final_status body. Returns: dict: Body dict. """ return { field_const.FILENAME: "unique_filename.ext", field_const.STATUS: "success", field_const.DESCRIPTION: "Success description", field_const.MESSAGE: { "function": "some_function", "status": "success", "description": "Success description", "input": {"key": "unique_filename.ext"}, }, field_const.TIMESTAMP: "timestamp", field_const.FINAL_ASSETS: [], } def fixture_post_final_status_function(request: Request) -> Response: """Function to mock real endpoint handler.""" @json_schema.validate_body(request, body.post_asset_final_status_schema) def function() -> Response: return Response("test_result") return function() def test_post_final_status_validate_body_success( valid_post_final_status_body: dict[str, Any], ) -> None: """Assert that validate_body succeeds when body is valid.""" request = mock.Mock() request.get_json = get_json_mock(valid_post_final_status_body) assert "test_result" == fixture_post_final_status_function(request).data.decode() @pytest.mark.parametrize( "missing_field", [field_const.FILENAME, field_const.STATUS, field_const.MESSAGE] ) def test_post_final_status_validate_body_failure( missing_field: str, valid_post_final_status_body: dict[str, Any], ) -> None: """Assert that validate_body fails when field is missing.""" request = mock.Mock() valid_post_final_status_body.pop(missing_field) request.get_json = get_json_mock(valid_post_final_status_body) result = fixture_post_final_status_function(request) assert result.status_code == 400 def test_post_final_status_validate_body_message_failure( valid_post_asset_status_body: dict[str, Any], ) -> None: """Assert that validate_body fails when message is string.""" request = mock.Mock() valid_post_asset_status_body["message"] = 123 request.get_json = get_json_mock(valid_post_asset_status_body) result = fixture_post_final_status_function(request) assert result.status_code == 400 def test_post_final_status_validate_body_final_assets_failure( valid_post_asset_status_body: dict[str, Any], ) -> None: """Assert that validate_body fails when message is string.""" request = mock.Mock() valid_post_asset_status_body["final_assets"] = ["123"] request.get_json = get_json_mock(valid_post_asset_status_body) result = fixture_post_final_status_function(request) assert result.status_code == 400 @pytest.fixture def valid_get_asset_properties_body() -> dict[str, Any]: """Function that returns valid get_asset_properties body. Returns: dict: Body dict. """ return { field_const.ASSET_TYPE_ID: 10, field_const.PHYSYCAL_LOCATION_ID: 20, field_const.TRACKS: [ {"tuid": 10001, "volume_number": 1, "track_number": 1}, {"tuid": 10002, "volume_number": 1, "track_number": 2}, ], } def fixture_get_asset_properties_function(request: Request) -> Response: """Function to mock real endpoint handler.""" @json_schema.validate_body(request, body.asset_properties_schema) def function() -> Response: return Response("test_result") return function() def test_asset_properties_validate_body_success( valid_get_asset_properties_body: dict[str, Any], ) -> None: """Assert that validate_body succeeds when body is valid.""" request = mock.Mock() request.get_json = get_json_mock(valid_get_asset_properties_body) assert fixture_get_asset_properties_function(request) def test_asset_properties_validate_body_empty_tracks( valid_get_asset_properties_body: dict[str, Any], ) -> None: """Assert that validate_body fails when tracks are empty.""" request = mock.Mock() valid_get_asset_properties_body[field_const.TRACKS] = [] request.get_json = get_json_mock(valid_get_asset_properties_body) result = fixture_get_asset_properties_function(request) assert result.status_code == 400 @pytest.mark.parametrize( "missing_field", [field_const.ASSET_TYPE_ID, field_const.PHYSYCAL_LOCATION_ID, field_const.TRACKS], ) def test_asset_properties_validate_body_missing_field_failure( missing_field: str, valid_get_asset_properties_body: dict[str, Any], valid_post_asset_status_body: dict[str, Any], ) -> None: """Assert that validate_body fails when field is missing.""" request = mock.Mock() valid_get_asset_properties_body.pop(missing_field) request.get_json = get_json_mock(valid_post_asset_status_body) result = fixture_get_asset_properties_function(request) assert result.status_code == 400 @pytest.mark.parametrize( "missing_field", [field_const.TUID, field_const.VOLUME_NUMBER, field_const.TRACK_NUMBER], ) def test_asset_properties_validate_body_missing_track_field_failure( missing_field: str, valid_get_asset_properties_body: dict[str, Any], valid_post_asset_status_body: dict[str, Any], ) -> None: """Assert that validate_body fails when track field is missing.""" request = mock.Mock() del valid_get_asset_properties_body[field_const.TRACKS][0][missing_field] request.get_json = get_json_mock(valid_post_asset_status_body) result = fixture_get_asset_properties_function(request) assert result.status_code == 400 @pytest.fixture def valid_post_hive_segment_properties_body() -> dict[str, Any]: """Function that returns valid post_hive_segment_properties body. Returns: dict: Body dict. """ return { field_const.ASSET_FINAL_ID: 101, field_const.TASK: { field_const.TASK_ID: "a22414c0-0cba-11f1-bc98-c7fc13f309f3", field_const.MODEL: "ai_music_classifier_DORIAN_2025_04_02_v00", field_const.MODEL_VERSION: 1, }, field_const.SEGMENTS: [ { field_const.TIME: 1, field_const.AI_GENERATED_MUSIC: 2.999325161043201e-07, field_const.AI_GENERATED_MUSIC_VOCAL: 2.6973483452557593e-07, field_const.MUBERT: 3.3340098796857813e-06, field_const.MUSICGEN: 1.681489804075235, field_const.RIFFUSION: 2.9261321591240557e-06, field_const.STABLE_AUDIO: 1.0112554402526732e-05, field_const.SUNO: 5.258721193160129e-06, field_const.UDIO: 2.272364926179113e-05, }, { field_const.TIME: 3, field_const.AI_GENERATED_MUSIC: 1.9993255161043201e-07, field_const.AI_GENERATED_MUSIC_VOCAL: 2.6973483452557593e-07, field_const.MUBERT: 3.3340098796857813e-06, field_const.MUSICGEN: 1.681489804075235, field_const.RIFFUSION: 2.9261321591240557e-06, field_const.STABLE_AUDIO: 1.0112554402526732e-05, field_const.SUNO: 5.258721193160129e-06, field_const.UDIO: 2.272364926179113e-05, }, ], } def fixture_post_hive_segment_function(request: Request) -> Response: """Function to mock real endpoint handler.""" @json_schema.validate_body(request, body.post_hive_segment_schema) def function() -> Response: return Response("test_result") return function() def test_post_hive_segment_success( valid_post_hive_segment_properties_body: dict[str, Any], ) -> None: """Assert that post_hive_segment_properties succeeds when body is valid.""" request = mock.Mock() request.get_json = get_json_mock(valid_post_hive_segment_properties_body) assert fixture_post_hive_segment_function(request) @pytest.mark.parametrize( "missing_field", [ field_const.ASSET_FINAL_ID, field_const.TASK, field_const.SEGMENTS, ], ) def test_post_hive_segment_failure( missing_field: str, valid_post_hive_segment_properties_body: dict[str, Any], valid_post_asset_status_body: dict[str, Any], ) -> None: """Assert that validate_body fails when message is string.""" request = mock.Mock() valid_post_hive_segment_properties_body.pop(missing_field) request.get_json = get_json_mock(valid_post_asset_status_body) result = fixture_post_hive_segment_function(request) assert result.status_code == 400 def test_post_hive_segment_overwrite_accepts_bool( valid_post_hive_segment_properties_body: dict[str, Any], ) -> None: """Assert that a boolean overwrite flag is accepted.""" request = mock.Mock() valid_post_hive_segment_properties_body[field_const.OVERWRITE] = True request.get_json = get_json_mock(valid_post_hive_segment_properties_body) assert fixture_post_hive_segment_function(request) def test_post_hive_segment_overwrite_rejects_non_bool( valid_post_hive_segment_properties_body: dict[str, Any], ) -> None: """Assert that a non-boolean overwrite value is rejected with a 400.""" request = mock.Mock() valid_post_hive_segment_properties_body[field_const.OVERWRITE] = "yes" request.get_json = get_json_mock(valid_post_hive_segment_properties_body) result = fixture_post_hive_segment_function(request) assert result.status_code == 400