"""Tests for the search logic.""" from typing import Any from unittest import mock from unittest.mock import MagicMock import pytest from flask.ctx import AppContext from vectororder.constants import search as search_const from vectororder.logic import search @pytest.mark.parametrize( "search_kwargs, expected_matched_kwargs, expected_unmatched_kwargs", ( # Only expected args. ( {"meta_update": "Y", "status": ["delivering"]}, { "single": {"meta_update": "Y"}, "multiple": {"status": ["delivering"]}, }, {}, ), # Expected and unknown args. ( {"meta_update": "Y", "status": ["delivering"], "unknown1": "foo"}, { "single": {"meta_update": "Y"}, "multiple": {"status": ["delivering"]}, }, {"unknown1": "foo"}, ), # Only unknown args. ( {"unknown1": "foo", "unknown2": "bar"}, {}, {"unknown1": "foo", "unknown2": "bar"}, ), # Inner disjunction args to test recursive matching. ( { search_const.VO_STATUSES: ( { "status": ["delivering"], "encoding_started": "fake dates", "unknown_nested": "bar", }, ), "meta_update": "Y", "unknown_top_level": "foo", }, { search_const.VO_FIELD_CATEGORY_INNER_DISJUNCTION: { search_const.VO_STATUSES: ( { "multiple": {"status": ["delivering"]}, "date_range": {"encoding_started": "fake dates"}, }, ) }, "single": {"meta_update": "Y"}, }, {"unknown_top_level": "foo", "unknown_nested": "bar"}, ), ), ) def test_search_kwargs_by_type( search_kwargs: dict[str, Any], expected_matched_kwargs: dict[str, Any], expected_unmatched_kwargs: dict[str, Any], ) -> None: """Test search kwargs categorisation.""" matched, unmatched = search.search_kwargs_by_type(search_kwargs) assert matched == expected_matched_kwargs assert unmatched == expected_unmatched_kwargs @pytest.mark.parametrize("offset", (None, 10)) @pytest.mark.parametrize("limit", (None, 5)) @pytest.mark.parametrize("order_by", (None, "foo")) @pytest.mark.parametrize( "unknown_kwargs, expected_log_calls, expected_logged_unmatched_fields", (({}, 0, None), ({"f1": "foo", "f2": "bar"}, 1, "f1, f2")), ) @mock.patch("vectororder.logic.search.g", spec=["ows"]) @mock.patch("vectororder.logic.search.search_kwargs_by_type") @mock.patch("vectororder.logic.search.search.search_vector_order_details") def test_search_vector_orders( search_vo_details_mock: MagicMock, search_kwargs_by_type_mock: MagicMock, g_mock: MagicMock, offset: int | None, limit: int | None, order_by: list[str] | None, unknown_kwargs: dict[str, Any], expected_log_calls: int, expected_logged_unmatched_fields: str, app_context: AppContext, ) -> None: """Test search Vector order logic is correct.""" fake_details = [{"foo": "bar"}], 0 search_kwargs = {"statuses": ["encoding"]} kwargs_by_type = {"multiple": {"status": ["encoding"]}} search_kwargs_by_type_mock.return_value = kwargs_by_type, unknown_kwargs search_vo_details_mock.return_value = fake_details result = search.search_vector_orders( offset=offset, limit=limit, order_by=order_by, search_kwargs=search_kwargs ) assert g_mock.ows.log.warning.call_count == expected_log_calls if expected_logged_unmatched_fields: assert ( expected_logged_unmatched_fields == (g_mock.ows.log.warning.call_args[0][1]) ) expected_models_call_kwargs = dict( limit=limit, offset=offset, order_by=order_by, **kwargs_by_type ) assert search_vo_details_mock.call_args[1] == expected_models_call_kwargs assert result == fake_details @mock.patch("vectororder.logic.search.search.save_search_request") def test_save_search_request( mock_save_search_request: MagicMock, ) -> None: """Test search request saving logic calls model layer.""" expected_return_value = "abc123" mock_save_search_request.return_value = expected_return_value assert search.save_search_request({"field": 42}) == expected_return_value @mock.patch("vectororder.logic.search.search.get_search_request") def test_get_search_request(mock_get_search_request: MagicMock) -> None: """Test search request retrieving logic calls model layer.""" expected_return_value = {"field": 42} mock_get_search_request.return_value = expected_return_value assert search.get_search_request("abc123") == expected_return_value