import unittest from fastapi.testclient import TestClient import main from db import connect, create from scripts.migration_to_households import run_migration SAMPLE_URL = "https://www.allrecipes.com/recipe/262696/cheese-omelette/" with open("tests/sample_files/recipes/cheese-omelette.html", "r", encoding="utf-8") as f: SAMPLE_HTML = f.read() class TestRecipesParseFromUrlIntegrationV2(unittest.IsolatedAsyncioTestCase): async def asyncSetUp(self): self.conn = await connect(":memory:") await create(self.conn) await run_migration(self.conn) async def override_get_db(): try: yield self.conn finally: pass main.app.dependency_overrides[main.get_db] = override_get_db self.client = TestClient(main.app) # Register user and create household r = self.client.post( "/api/v1/auth/register", json={"email": "scrape@test.com", "password": "pw", "displayName": "Scrape"}, ) assert r.status_code == 200, r.text token = r.json()["accessToken"] self.headers = {"Authorization": f"Bearer {token}"} r2 = self.client.post("/api/v1/households", headers=self.headers, json={"name": "H"}) assert r2.status_code == 200, r2.text self.slug = r2.json()["slug"] async def asyncTearDown(self): await self.conn.close() main.app.dependency_overrides.clear() def test_parse_from_url_with_mocked_html(self): # Monkeypatch httpx.AsyncClient.get used in scraper import recipes.scraping as scraping class DummyResp: def __init__(self, status_code=200, text=""): self.status_code = status_code self.text = text class DummyClient: async def __aenter__(self): return self async def __aexit__(self, exc_type, exc, tb): return False async def get(self, url, headers=None, follow_redirects=False): assert url == SAMPLE_URL return DummyResp(200, SAMPLE_HTML) orig_client = scraping.httpx.AsyncClient scraping.httpx.AsyncClient = DummyClient try: r = self.client.post( f"/api/v1/households/{self.slug}/recipes/parse-from-url", headers=self.headers, json={"url": SAMPLE_URL}, ) assert r.status_code == 200, r.text data = r.json() assert data.get("@type") in ("Recipe", ["Recipe"]) # accept single or list # JSON-LD should contain ingredients and instructions assert isinstance(data.get("recipeIngredient"), list) assert len(data["recipeIngredient"]) >= 1 # The title should mention omelette assert "name" in data and "omelette" in data["name"].lower() # Optional: if author or image present, assert types if "image" in data: assert isinstance(data["image"], (str, list, dict)) if "author" in data: assert isinstance(data["author"], (str, list, dict)) finally: scraping.httpx.AsyncClient = orig_client def test_parse_from_url_not_found(self): import recipes.scraping as scraping class DummyResp: def __init__(self, status_code=404, text=""): self.status_code = status_code self.text = text class DummyClient: async def __aenter__(self): return self async def __aexit__(self, exc_type, exc, tb): return False async def get(self, url, headers=None, follow_redirects=False): return DummyResp(404, "") orig_client = scraping.httpx.AsyncClient scraping.httpx.AsyncClient = DummyClient try: r = self.client.post( f"/api/v1/households/{self.slug}/recipes/parse-from-url", headers=self.headers, json={"url": SAMPLE_URL}, ) assert r.status_code == 404, r.text body = r.json() assert body.get("status") == 404 finally: scraping.httpx.AsyncClient = orig_client