"""/web/images and /web/drivers — the HTMX fragments behind the search UIs. `websearch.search_images` / `search_pages` are monkeypatched throughout: these tests are about routing, ownership, and rendering, not about the scrape. """ from __future__ import annotations import io import json import pytest from PIL import Image from imptune.services.websearch import ImageHit, PageHit @pytest.fixture def stub_images(monkeypatch): """Record the query the route asked for, return two canned hits.""" calls: list[tuple[str, int]] = [] def fake(query, limit=12): calls.append((query, limit)) return [ ImageHit( url="https://cdn.example.com/a.png", thumbnail="https://tn.example.com/a.png", title="Printer A", width=800, height=600, source="cdn.example.com", ), ImageHit( url="https://cdn.example.com/b.jpg", thumbnail="https://tn.example.com/b.jpg", title="Printer B", width=400, height=400, source="cdn.example.com", ), ] from imptune.api import web monkeypatch.setattr(web.websearch, "search_images", fake) return calls @pytest.fixture def stub_pages(monkeypatch): calls: list[tuple[str, int]] = [] def fake(query, limit=8): calls.append((query, limit)) return [ PageHit( url="https://support.hp.com/upd", title="HP Universal Print Driver", snippet="Download the UPD for Windows.", host="support.hp.com", ) ] from imptune.api import web monkeypatch.setattr(web.websearch, "search_pages", fake) return calls def _printer(owner): from imptune.db.models import Printer return Printer.create( name="HP LaserJet M404", ip_address="10.0.0.7", port_name="IP_10.0.0.7", owner=owner, ) def _driver(): from imptune.db.models import Driver return Driver.create( sha256="c" * 64, original_filename="hp_upd.zip", size_bytes=1024, driver_desc=json.dumps(["HP Universal Printing PCL 6"]), ) class TestImageSearchRoute: def test_renders_pickable_results_for_a_printer(self, client, owner, tmp_data_dir, stub_images): printer = _printer(owner) response = client.get( "/web/images", params={"q": "hp m404 printer", "target": "printer", "id": printer.id} ) assert response.status_code == 200 assert stub_images == [("hp m404 printer", 12)] assert f'hx-post="/printers/{printer.id}/icon/from-web"' in response.text assert 'hx-target="#icon-status"' in response.text assert "https://cdn.example.com/a.png" in response.text # Thumbnails load from the engine's CDN — no referrer leak. assert 'referrerpolicy="no-referrer"' in response.text def test_renders_pickable_results_for_a_driver(self, client, owner, tmp_data_dir, stub_images): driver = _driver() response = client.get( "/web/images", params={"q": "hp upd", "target": "driver", "id": driver.id} ) assert response.status_code == 200 assert f'hx-post="/drivers/{driver.id}/icon/from-web"' in response.text assert f'hx-target="#driver-icon-status-{driver.id}"' in response.text def test_another_owners_printer_reads_as_missing(self, client, owner, tmp_data_dir, stub_images): """404, not 403 — and no search is spent on it.""" from imptune.db.models import Owner, Printer from imptune.services.session import generate_key stranger = Owner.create(key=generate_key()) theirs = Printer.create( name="Theirs", ip_address="10.0.0.9", port_name="IP_10_0_0_9", owner=stranger ) response = client.get( "/web/images", params={"q": "x", "target": "printer", "id": theirs.id} ) assert response.status_code == 404 assert stub_images == [] def test_unknown_driver_is_404(self, client, tmp_data_dir, stub_images): response = client.get( "/web/images", params={"q": "x", "target": "driver", "id": 99999} ) assert response.status_code == 404 def test_unknown_target_is_400(self, client, tmp_data_dir, stub_images): response = client.get("/web/images", params={"q": "x", "target": "wat", "id": 1}) assert response.status_code == 400 def test_blank_query_does_not_search(self, client, owner, tmp_data_dir, stub_images): printer = _printer(owner) response = client.get( "/web/images", params={"q": " ", "target": "printer", "id": printer.id} ) assert response.status_code == 200 assert stub_images == [] def test_no_results_renders_a_hint(self, client, owner, tmp_data_dir, monkeypatch): from imptune.api import web monkeypatch.setattr(web.websearch, "search_images", lambda q, limit=12: []) printer = _printer(owner) response = client.get( "/web/images", params={"q": "zzz", "target": "printer", "id": printer.id} ) assert response.status_code == 200 assert "No results" in response.text def test_disabled_deployment_says_so(self, client, owner, tmp_data_dir, monkeypatch): import imptune.config as cfg monkeypatch.setattr(cfg, "WEB_SEARCH", False) printer = _printer(owner) response = client.get( "/web/images", params={"q": "x", "target": "printer", "id": printer.id} ) assert response.status_code == 200 assert "disabled" in response.text class TestDriverSearchRoute: def test_generic_mode_rewrites_the_query(self, client, tmp_data_dir, stub_pages): response = client.get( "/web/drivers", params={"q": "HP LaserJet M404dn", "mode": "generic"} ) assert response.status_code == 200 assert stub_pages == [("HP Universal Print Driver PCL6 download", 12)] assert "support.hp.com/upd" in response.text def test_exact_mode_searches_the_text_as_typed(self, client, tmp_data_dir, stub_pages): response = client.get( "/web/drivers", params={"q": "HP LaserJet M404dn", "mode": "exact"} ) assert stub_pages == [("HP LaserJet M404dn", 12)] def test_results_carry_the_unvetted_warning(self, client, tmp_data_dir, stub_pages): """The links are raw search results — the fragment must say so.""" response = client.get("/web/drivers", params={"q": "hp", "mode": "generic"}) assert "driver_search_warning" in response.text assert 'rel="noopener noreferrer nofollow"' in response.text assert 'target="_blank"' in response.text def test_blank_query_does_not_search(self, client, tmp_data_dir, stub_pages): response = client.get("/web/drivers", params={"q": " "}) assert response.status_code == 200 assert stub_pages == [] def test_disabled_deployment_says_so(self, client, tmp_data_dir, monkeypatch): import imptune.config as cfg monkeypatch.setattr(cfg, "WEB_SEARCH", False) response = client.get("/web/drivers", params={"q": "hp"}) assert "disabled" in response.text class TestSearchUiVisibility: def test_drivers_page_offers_the_search_card(self, client, tmp_data_dir): page = client.get("/drivers") assert 'hx-get="/web/drivers"' in page.text assert "Konica Minolta" in page.text # brand datalist def test_printer_detail_offers_the_image_picker(self, client, owner, tmp_data_dir): printer = _printer(owner) page = client.get(f"/printers/{printer.id}") assert 'hx-get="/web/images"' in page.text # Prefilled from the printer name, so one click is enough. assert 'value="HP LaserJet M404 printer"' in page.text def test_every_search_control_disappears_when_disabled( self, client, owner, tmp_data_dir, monkeypatch ): import imptune.config as cfg monkeypatch.setattr(cfg, "WEB_SEARCH", False) printer = _printer(owner) assert 'hx-get="/web/drivers"' not in client.get("/drivers").text assert 'hx-get="/web/images"' not in client.get(f"/printers/{printer.id}").text def test_picked_image_becomes_the_printer_icon(client, owner, tmp_data_dir, monkeypatch): """End to end through the picker's POST: download, normalize, store, preview.""" from imptune.db.models import Icon from imptune.services import websearch printer = _printer(owner) buf = io.BytesIO() Image.new("RGB", (500, 300), color="orange").save(buf, format="JPEG") monkeypatch.setattr(websearch, "fetch_image", lambda url: buf.getvalue()) response = client.post( f"/printers/{printer.id}/icon/from-web", data={"url": "https://cdn.example.com/photos/m404.jpg"}, ) assert response.status_code == 200 assert f'src="/printers/{printer.id}/icon?v=' in response.text icon = Icon.get(Icon.printer == printer.id) with Image.open(tmp_data_dir / "icons" / icon.sha256) as img: assert img.format == "PNG" assert img.size == (256, 256) def test_from_web_rejects_an_unowned_printer(client, tmp_data_dir, monkeypatch): from imptune.db.models import Owner, Printer from imptune.services import websearch from imptune.services.session import generate_key monkeypatch.setattr(websearch, "fetch_image", lambda url: b"unused") stranger = Owner.create(key=generate_key()) theirs = Printer.create( name="Theirs", ip_address="10.0.0.9", port_name="IP_10_0_0_9", owner=stranger ) response = client.post( f"/printers/{theirs.id}/icon/from-web", data={"url": "https://example.com/x.png"} ) assert response.status_code == 404