mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-05-25 19:20:26 -04:00
feat: In-app AI Provider Configuration (#7650)
This commit is contained in:
@@ -3,6 +3,7 @@ import json
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from mealie.schema.group.ai_providers import AIProviderCreate, AIProviderSettingsUpdate
|
||||
from mealie.schema.openai.recipe import (
|
||||
OpenAIRecipe,
|
||||
OpenAIRecipeIngredient,
|
||||
@@ -15,6 +16,22 @@ from tests.utils.factories import random_int, random_string
|
||||
from tests.utils.fixture_schemas import TestUser
|
||||
|
||||
|
||||
@pytest.fixture(scope="module", autouse=True)
|
||||
def setup_ai_providers(unique_user: TestUser):
|
||||
"""Create AI providers for the test group so image-based OpenAI routes are enabled."""
|
||||
provider = unique_user.repos.group_ai_providers.create(
|
||||
AIProviderCreate(name="test-provider", model="gpt-4o", api_key="test-key")
|
||||
)
|
||||
unique_user.repos.group_ai_provider_settings.update(
|
||||
unique_user.repos.group_id,
|
||||
AIProviderSettingsUpdate(
|
||||
default_provider_id=provider.id,
|
||||
audio_provider_id=None,
|
||||
image_provider_id=provider.id,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def test_openai_create_recipe_from_image(
|
||||
api_client: TestClient,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
|
||||
@@ -4,7 +4,7 @@ import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
import mealie.services.scraper.recipe_scraper as recipe_scraper_module
|
||||
import mealie.services.scraper.scraper_strategies as scraper_strategies_module
|
||||
from mealie.schema.group.ai_providers import AIProviderCreate, AIProviderSettingsUpdate
|
||||
from mealie.schema.openai.general import OpenAIText
|
||||
from mealie.services.openai import OpenAIService
|
||||
from mealie.services.recipe.recipe_data_service import RecipeDataService
|
||||
@@ -47,12 +47,17 @@ def recipe_url() -> str:
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def openai_scraper_setup(monkeypatch: pytest.MonkeyPatch, bare_html: str):
|
||||
"""Restrict to only RecipeScraperOpenAI, enable it unconditionally, and prevent real HTTP calls."""
|
||||
def openai_scraper_setup(monkeypatch: pytest.MonkeyPatch, bare_html: str, unique_user: TestUser):
|
||||
"""Restrict to only RecipeScraperOpenAI, create real DB provider data, and prevent real HTTP calls."""
|
||||
monkeypatch.setattr(recipe_scraper_module, "DEFAULT_SCRAPER_STRATEGIES", [RecipeScraperOpenAI])
|
||||
|
||||
settings_stub = type("_Settings", (), {"OPENAI_ENABLED": True})()
|
||||
monkeypatch.setattr(scraper_strategies_module, "get_app_settings", lambda: settings_stub)
|
||||
provider = unique_user.repos.group_ai_providers.create(
|
||||
AIProviderCreate(name=random_string(), model="gpt-4o", api_key="test-key")
|
||||
)
|
||||
unique_user.repos.group_ai_provider_settings.update(
|
||||
unique_user.repos.group_id,
|
||||
AIProviderSettingsUpdate(default_provider_id=provider.id, audio_provider_id=None, image_provider_id=None),
|
||||
)
|
||||
|
||||
async def mock_safe_scrape_html(url: str) -> str:
|
||||
return bare_html
|
||||
@@ -171,9 +176,11 @@ def test_create_by_url_openai_disabled(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
recipe_url: str,
|
||||
):
|
||||
"""When OPENAI_ENABLED is False, can_scrape() returns False and the endpoint returns 400."""
|
||||
disabled_settings = type("_Settings", (), {"OPENAI_ENABLED": False})()
|
||||
monkeypatch.setattr(scraper_strategies_module, "get_app_settings", lambda: disabled_settings)
|
||||
"""When no default provider is set, can_scrape() returns False and the endpoint returns 400."""
|
||||
unique_user.repos.group_ai_provider_settings.update(
|
||||
unique_user.repos.group_id,
|
||||
AIProviderSettingsUpdate(default_provider_id=None, audio_provider_id=None, image_provider_id=None),
|
||||
)
|
||||
|
||||
response = api_client.post(
|
||||
api_routes.recipes_create_url,
|
||||
|
||||
@@ -6,7 +6,7 @@ from fastapi.testclient import TestClient
|
||||
|
||||
import mealie.services.scraper.recipe_scraper as recipe_scraper_module
|
||||
from mealie.core import exceptions
|
||||
from mealie.core.config import get_app_settings
|
||||
from mealie.schema.group.ai_providers import AIProviderCreate, AIProviderSettingsUpdate
|
||||
from mealie.schema.openai.recipe import OpenAIRecipe, OpenAIRecipeIngredient, OpenAIRecipeInstruction
|
||||
from mealie.services.openai import OpenAIService
|
||||
from mealie.services.scraper.scraper_strategies import RecipeScraperOpenAITranscription
|
||||
@@ -27,10 +27,22 @@ def _make_openai_recipe() -> OpenAIRecipe:
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def video_scraper_setup(monkeypatch: pytest.MonkeyPatch):
|
||||
def video_scraper_setup(monkeypatch: pytest.MonkeyPatch, unique_user: TestUser):
|
||||
# Restrict to only the video scraper so other strategies don't interfere
|
||||
monkeypatch.setattr(recipe_scraper_module, "DEFAULT_SCRAPER_STRATEGIES", [RecipeScraperOpenAITranscription])
|
||||
|
||||
provider = unique_user.repos.group_ai_providers.create(
|
||||
AIProviderCreate(name=random_string(), model="gpt-4o", api_key="test-key")
|
||||
)
|
||||
unique_user.repos.group_ai_provider_settings.update(
|
||||
unique_user.repos.group_id,
|
||||
AIProviderSettingsUpdate(
|
||||
default_provider_id=provider.id,
|
||||
audio_provider_id=provider.id,
|
||||
image_provider_id=None,
|
||||
),
|
||||
)
|
||||
|
||||
# Prevent any real HTTP calls during scraping
|
||||
async def mock_safe_scrape_html(url: str) -> str:
|
||||
return "<html></html>"
|
||||
@@ -117,8 +129,10 @@ def test_create_recipe_from_video_transcription_disabled(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
unique_user: TestUser,
|
||||
):
|
||||
settings = get_app_settings()
|
||||
monkeypatch.setattr(settings, "OPENAI_ENABLE_TRANSCRIPTION_SERVICES", False)
|
||||
unique_user.repos.group_ai_provider_settings.update(
|
||||
unique_user.repos.group_id,
|
||||
AIProviderSettingsUpdate(default_provider_id=None, audio_provider_id=None, image_provider_id=None),
|
||||
)
|
||||
|
||||
r = api_client.post(api_routes.recipes_create_url, json={"url": VIDEO_URL}, headers=unique_user.token)
|
||||
assert r.status_code == 400
|
||||
|
||||
Reference in New Issue
Block a user