mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-05-26 03:30:26 -04:00
207 lines
7.6 KiB
Python
207 lines
7.6 KiB
Python
from uuid import uuid4
|
|
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from mealie.schema.group.ai_providers import (
|
|
AIProviderCreate,
|
|
AIProviderSettingsOut,
|
|
AIProviderSummary,
|
|
)
|
|
|
|
|
|
class AIProviderCreateTests:
|
|
def test_valid_create(self):
|
|
provider = AIProviderCreate(name="test", api_key="key", model="gpt-4o")
|
|
assert provider.name == "test"
|
|
assert provider.model == "gpt-4o"
|
|
assert provider.timeout == 300
|
|
assert provider.base_url is None
|
|
|
|
@pytest.mark.parametrize("field", ["name", "api_key", "model"])
|
|
def test_empty_field_raises(self, field: str):
|
|
data: dict = {"name": "test", "api_key": "key", "model": "gpt-4o", field: ""}
|
|
with pytest.raises(ValidationError):
|
|
AIProviderCreate(**data)
|
|
|
|
@pytest.mark.parametrize("timeout", [-1, -100])
|
|
def test_negative_timeout_raises(self, timeout: int):
|
|
with pytest.raises(ValidationError):
|
|
AIProviderCreate(name="test", api_key="key", model="gpt-4o", timeout=timeout)
|
|
|
|
def test_zero_timeout_is_valid(self):
|
|
provider = AIProviderCreate(name="test", api_key="key", model="gpt-4o", timeout=0)
|
|
assert provider.timeout == 0
|
|
|
|
@pytest.mark.parametrize("base_url", ["", None])
|
|
def test_base_url_empty_becomes_none(self, base_url: str | None):
|
|
provider = AIProviderCreate(name="test", api_key="key", model="gpt-4o", base_url=base_url)
|
|
assert provider.base_url is None
|
|
|
|
def test_api_key_excluded_from_serialization(self):
|
|
provider = AIProviderCreate(name="test", api_key="secret", model="gpt-4o")
|
|
dumped = provider.model_dump()
|
|
assert "api_key" not in dumped
|
|
|
|
def test_api_key_excluded_from_json(self):
|
|
provider = AIProviderCreate(name="test", api_key="secret", model="gpt-4o")
|
|
json_str = provider.model_dump_json()
|
|
assert "api_key" not in json_str
|
|
assert "secret" not in json_str
|
|
|
|
|
|
class AIProviderSettingsOutTests:
|
|
def _make_settings(
|
|
self,
|
|
*,
|
|
default_provider_id=None,
|
|
audio_provider_id=None,
|
|
image_provider_id=None,
|
|
providers=None,
|
|
) -> AIProviderSettingsOut:
|
|
if providers is None:
|
|
providers = []
|
|
return AIProviderSettingsOut(
|
|
default_provider_id=default_provider_id,
|
|
audio_provider_id=audio_provider_id,
|
|
image_provider_id=image_provider_id,
|
|
providers=providers,
|
|
)
|
|
|
|
# --- ai_enabled ---
|
|
|
|
def test_ai_enabled_false_when_no_default(self):
|
|
s = self._make_settings()
|
|
assert not s.ai_enabled
|
|
|
|
def test_ai_enabled_true_when_default_set(self):
|
|
pid = uuid4()
|
|
s = self._make_settings(default_provider_id=pid, providers=[AIProviderSummary(id=pid, name="p")])
|
|
assert s.ai_enabled
|
|
|
|
# --- audio_provider_enabled ---
|
|
|
|
def test_audio_provider_disabled_when_no_default(self):
|
|
audio_id = uuid4()
|
|
s = self._make_settings(
|
|
audio_provider_id=audio_id,
|
|
providers=[AIProviderSummary(id=audio_id, name="audio")],
|
|
)
|
|
# audio_provider_id is valid, but validate_providers sets audio_provider_id to None
|
|
# because without default_provider_id, it would be fine; let's test audio_provider_enabled
|
|
# which requires ai_enabled to be True
|
|
assert not s.ai_enabled
|
|
assert not s.audio_provider_enabled
|
|
|
|
def test_audio_provider_disabled_when_only_default_set(self):
|
|
pid = uuid4()
|
|
s = self._make_settings(default_provider_id=pid, providers=[AIProviderSummary(id=pid, name="p")])
|
|
assert s.ai_enabled
|
|
assert not s.audio_provider_enabled
|
|
|
|
def test_audio_provider_enabled_when_both_set(self):
|
|
pid = uuid4()
|
|
audio_id = uuid4()
|
|
s = self._make_settings(
|
|
default_provider_id=pid,
|
|
audio_provider_id=audio_id,
|
|
providers=[AIProviderSummary(id=pid, name="p"), AIProviderSummary(id=audio_id, name="audio")],
|
|
)
|
|
assert s.ai_enabled
|
|
assert s.audio_provider_enabled
|
|
|
|
# --- image_provider_enabled ---
|
|
|
|
def test_image_provider_disabled_when_no_default(self):
|
|
image_id = uuid4()
|
|
s = self._make_settings(
|
|
image_provider_id=image_id,
|
|
providers=[AIProviderSummary(id=image_id, name="img")],
|
|
)
|
|
assert not s.ai_enabled
|
|
assert not s.image_provider_enabled
|
|
|
|
def test_image_provider_disabled_when_only_default_set(self):
|
|
pid = uuid4()
|
|
s = self._make_settings(default_provider_id=pid, providers=[AIProviderSummary(id=pid, name="p")])
|
|
assert s.ai_enabled
|
|
assert not s.image_provider_enabled
|
|
|
|
def test_image_provider_enabled_when_both_set(self):
|
|
pid = uuid4()
|
|
image_id = uuid4()
|
|
s = self._make_settings(
|
|
default_provider_id=pid,
|
|
image_provider_id=image_id,
|
|
providers=[AIProviderSummary(id=pid, name="p"), AIProviderSummary(id=image_id, name="img")],
|
|
)
|
|
assert s.ai_enabled
|
|
assert s.image_provider_enabled
|
|
|
|
# --- validate_providers model validator ---
|
|
|
|
def test_validate_providers_strips_unknown_default(self):
|
|
s = self._make_settings(default_provider_id=uuid4(), providers=[])
|
|
assert s.default_provider_id is None
|
|
assert not s.ai_enabled
|
|
|
|
def test_validate_providers_strips_unknown_audio(self):
|
|
pid = uuid4()
|
|
providers = [AIProviderSummary(id=pid, name="p")]
|
|
s = self._make_settings(default_provider_id=pid, audio_provider_id=uuid4(), providers=providers)
|
|
assert s.default_provider_id == pid
|
|
assert s.audio_provider_id is None
|
|
|
|
def test_validate_providers_strips_unknown_image(self):
|
|
pid = uuid4()
|
|
providers = [AIProviderSummary(id=pid, name="p")]
|
|
s = self._make_settings(default_provider_id=pid, image_provider_id=uuid4(), providers=providers)
|
|
assert s.default_provider_id == pid
|
|
assert s.image_provider_id is None
|
|
|
|
def test_validate_providers_keeps_valid_ids(self):
|
|
pid = uuid4()
|
|
audio_id = uuid4()
|
|
image_id = uuid4()
|
|
providers = [
|
|
AIProviderSummary(id=pid, name="p"),
|
|
AIProviderSummary(id=audio_id, name="audio"),
|
|
AIProviderSummary(id=image_id, name="img"),
|
|
]
|
|
s = self._make_settings(
|
|
default_provider_id=pid,
|
|
audio_provider_id=audio_id,
|
|
image_provider_id=image_id,
|
|
providers=providers,
|
|
)
|
|
assert s.default_provider_id == pid
|
|
assert s.audio_provider_id == audio_id
|
|
assert s.image_provider_id == image_id
|
|
|
|
def test_validate_providers_strips_all_if_empty_list(self):
|
|
pid = uuid4()
|
|
s = self._make_settings(
|
|
default_provider_id=pid,
|
|
audio_provider_id=uuid4(),
|
|
image_provider_id=uuid4(),
|
|
providers=[],
|
|
)
|
|
assert s.default_provider_id is None
|
|
assert s.audio_provider_id is None
|
|
assert s.image_provider_id is None
|
|
|
|
def test_validate_providers_partial_strip(self):
|
|
"""Only the IDs pointing to missing providers are stripped."""
|
|
pid = uuid4()
|
|
audio_id = uuid4()
|
|
providers = [AIProviderSummary(id=pid, name="p"), AIProviderSummary(id=audio_id, name="audio")]
|
|
s = self._make_settings(
|
|
default_provider_id=pid,
|
|
audio_provider_id=audio_id,
|
|
image_provider_id=uuid4(), # not in list → stripped
|
|
providers=providers,
|
|
)
|
|
assert s.default_provider_id == pid
|
|
assert s.audio_provider_id == audio_id
|
|
assert s.image_provider_id is None
|