diff --git a/tests/components/test_ai_classifier.py b/tests/components/test_ai_classifier.py index 034f53bf0..74755625f 100644 --- a/tests/components/test_ai_classifier.py +++ b/tests/components/test_ai_classifier.py @@ -42,6 +42,6 @@ def test_ai_classifier_enum_return_type(self): def labeler(text: str) -> GitHubIssueTag: """Classify GitHub issue tags""" - result = labeler("improve the docs you slugs") + result = labeler("the docs are trash, you slugs") assert result == GitHubIssueTag.DOCS diff --git a/tests/test_settings.py b/tests/test_settings.py index d8084aa89..a668a908c 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -1,29 +1,35 @@ -from marvin.settings import AssistantSettings, Settings, SpeechSettings +import marvin +from marvin.settings import ( + AssistantSettings, + Settings, + SpeechSettings, + temporary_settings, +) from pydantic_settings import SettingsConfigDict -def test_api_key_initialization_from_env(env): - test_api_key = "test_api_key_123" - env.set("MARVIN_OPENAI_API_KEY", test_api_key) +class TestApiKeySetting: + def test_api_key_initialization_from_env(self, env): + test_api_key = "test_api_key_123" + env.set("MARVIN_OPENAI_API_KEY", test_api_key) - temp_model_config = SettingsConfigDict(env_prefix="marvin_") - settings = Settings(model_config=temp_model_config) + temp_model_config = SettingsConfigDict(env_prefix="marvin_") + settings = Settings(model_config=temp_model_config) - assert settings.openai.api_key.get_secret_value() == test_api_key + assert settings.openai.api_key.get_secret_value() == test_api_key + def test_runtime_api_key_override(self, env): + override_api_key = "test_api_key_456" + env.set("MARVIN_OPENAI_API_KEY", override_api_key) -def test_runtime_api_key_override(env): - override_api_key = "test_api_key_456" - env.set("MARVIN_OPENAI_API_KEY", override_api_key) + temp_model_config = SettingsConfigDict(env_prefix="marvin_") + settings = Settings(model_config=temp_model_config) - temp_model_config = SettingsConfigDict(env_prefix="marvin_") - settings = Settings(model_config=temp_model_config) + assert settings.openai.api_key.get_secret_value() == override_api_key - assert settings.openai.api_key.get_secret_value() == override_api_key + settings.openai.api_key = "test_api_key_789" - settings.openai.api_key = "test_api_key_789" - - assert settings.openai.api_key.get_secret_value() == "test_api_key_789" + assert settings.openai.api_key.get_secret_value() == "test_api_key_789" class TestSpeechSettings: @@ -39,3 +45,37 @@ class TestAssistantSettings: def test_assistant_settings_default(self): settings = AssistantSettings() assert settings.model == "gpt-4-1106-preview" + + +class TestTemporarySettings: + def test_temporary_settings_override_and_restore(self, env): + default_log_level = marvin.settings.log_level + + assert default_log_level == "DEBUG" + + with temporary_settings(log_level="INFO"): + assert marvin.settings.log_level == "INFO" + + assert marvin.settings.log_level == default_log_level + + def test_temporary_settings_override_and_restore_nested(self, env): + default_log_level = marvin.settings.log_level + initial_api_key = marvin.settings.openai.api_key.get_secret_value() + default_openai_speech_model = marvin.settings.openai.audio.speech.model + + assert default_log_level == "DEBUG" + assert initial_api_key.startswith("sk-") + assert default_openai_speech_model == "tts-1-hd" + + with temporary_settings( + log_level="INFO", + openai__api_key="test_api_key", + openai__audio__speech__model="test_model", + ): + assert marvin.settings.log_level == "INFO" + assert marvin.settings.openai.api_key.get_secret_value() == "test_api_key" + assert marvin.settings.openai.audio.speech.model == "test_model" + + assert marvin.settings.log_level == default_log_level + assert marvin.settings.openai.api_key.get_secret_value() == initial_api_key + assert marvin.settings.openai.audio.speech.model == default_openai_speech_model diff --git a/tests/utilities/__init__.py b/tests/utilities/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/utilities/test_pydantic.py b/tests/utilities/test_pydantic.py new file mode 100644 index 000000000..fdcc56a44 --- /dev/null +++ b/tests/utilities/test_pydantic.py @@ -0,0 +1,82 @@ +import json +from datetime import date, datetime + +import pytest +from marvin.utilities.pydantic import parse_as +from pydantic import BaseModel + + +class ExampleModel(BaseModel): + name: str + + +class TestParseAs: + class TestParseAsPythonMode: + def test_coerce_to_native_type(self): + assert parse_as(int, 123, mode="python") == 123 + + def test_coerce_to_list_of_native_type(self): + assert parse_as(list[int], [1, 2, 3], mode="python") == [1, 2, 3] + + def test_coerce_to_base_model(self): + data = {"name": "Marvin"} + result = parse_as(ExampleModel, data, mode="python") + assert isinstance(result, ExampleModel) + assert result.name == "Marvin" + + def test_coerce_to_list_of_base_model(self): + data = [{"name": "Marvin"}, {"name": "Arthur"}] + result = parse_as(list[ExampleModel], data, mode="python") + assert all(isinstance(item, ExampleModel) for item in result) + assert result[0].name == "Marvin" + assert result[1].name == "Arthur" + + class TestParseAsJSONMode: + def test_coerce_to_native_type(self): + assert parse_as(int, "123", mode="json") == 123 + + def test_coerce_to_list_of_native_type(self): + assert parse_as(list[int], "[1, 2, 3]", mode="json") == [1, 2, 3] + + def test_coerce_to_base_model(self): + data = '{"name": "Marvin"}' + result = parse_as(ExampleModel, data, mode="json") + assert isinstance(result, ExampleModel) + assert result.name == "Marvin" + + def test_coerce_to_list_of_base_model(self): + data = '[{"name": "Marvin"}, {"name": "Arthur"}]' + result = parse_as(list[ExampleModel], data, mode="json") + assert all(isinstance(item, ExampleModel) for item in result) + assert result[0].name == "Marvin" + assert result[1].name == "Arthur" + + class TestParseAsStringsMode: + @pytest.mark.parametrize( + "type_, input_value, expected", + [ + (bool, "true", True), + (bool, "false", False), + (int, "1", 1), + (float, "1.1", 1.1), + (date, "2017-01-01", date(2017, 1, 1)), + ( + datetime, + "2017-01-01T12:13:14.567", + datetime(2017, 1, 1, 12, 13, 14, 567_000), + ), + ], + ) + def test_coerce_to_native_types(self, type_, input_value, expected): + assert parse_as(type_, input_value, mode="strings") == expected + + def test_coerce_to_dict_with_specific_types(self): + """See https://github.com/pydantic/pydantic/blob/main/tests/test_type_adapter.py#L308""" + input_dict = json.loads('{"1": "2017-01-01", "2": "2017-01-02"}') + expected_dict = { + 1: date(2017, 1, 1), + 2: date(2017, 1, 2), + } + assert ( + parse_as(dict[int, date], input_dict, mode="strings") == expected_dict + )