US01-01 follow-up: replace donor characterization iteration with YAML ledger + full suite (#46)
This commit was merged in pull request #46.
This commit is contained in:
105
tests/characterization/test_webapp_query.py
Normal file
105
tests/characterization/test_webapp_query.py
Normal file
@@ -0,0 +1,105 @@
|
||||
"""Characterize webapp read layer (donor: query._fts_query, _where, search,
|
||||
facets, stats, photo, all_paths) against a seeded temp DB."""
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
import photo_analyzer as pa
|
||||
from webapp import query
|
||||
from conftest import seed_analyzed
|
||||
|
||||
|
||||
def test_fts_query_sanitization_goldens():
|
||||
assert query._fts_query("beach sun") == '"beach"* "sun"*'
|
||||
assert query._fts_query('AND OR "quoted"') == '"AND"* "OR"* "quoted"*'
|
||||
assert query._fts_query("!!! ???") is None, "pure punctuation can't crash MATCH"
|
||||
assert query._fts_query("") is None
|
||||
assert query._fts_query("Straße") == '"Straße"*', "unicode words survive"
|
||||
|
||||
|
||||
def test_where_clause_goldens():
|
||||
clauses, params = query._where({
|
||||
"setting": "indoor", "people": "3+", "year_min": "2010",
|
||||
"has_location": "1"})
|
||||
assert clauses == [
|
||||
"p.setting = ?", "p.people_count >= 3", "p.approx_year >= ?",
|
||||
"p.location_hint IS NOT NULL AND TRIM(p.location_hint) != '' "
|
||||
"AND LOWER(p.location_hint) != 'null'"]
|
||||
assert params == ["indoor", 2010]
|
||||
assert query._where({}) == ([], [])
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def seeded(db):
|
||||
seed_analyzed(db, "/lib/album_a/beach.jpg",
|
||||
description="A sunny beach with palm trees.",
|
||||
tags=["beach", "palm"], setting="outdoor", people_count=2,
|
||||
approx_year=2015, location_hint="Rome, Italy")
|
||||
seed_analyzed(db, "/lib/album_a/city.jpg",
|
||||
description="A night city street.", tags=["city"],
|
||||
setting="outdoor", time_of_day="night", people_count=0,
|
||||
approx_year=2011)
|
||||
seed_analyzed(db, "/lib/album_b/dinner.jpg",
|
||||
description="Family dinner at home.", tags=["family", "dinner"],
|
||||
setting="indoor", people_count=4)
|
||||
pa.mark_exif_written(db, "/lib/album_b/dinner.jpg")
|
||||
pa.upsert_pending(db, "/lib/album_b/err.jpg")
|
||||
pa.mark_error(db, "/lib/album_b/err.jpg", "timeout")
|
||||
return db
|
||||
|
||||
|
||||
def test_search_fts_prefix_match(seeded):
|
||||
out = query.search(seeded, q="beach")
|
||||
assert out["total"] == 1
|
||||
assert out["rows"][0]["path"] == "/lib/album_a/beach.jpg"
|
||||
assert out["rows"][0]["tags"] == ["beach", "palm"], "tags decoded from JSON"
|
||||
assert query.search(seeded, q="bea")["total"] == 1, "prefix term"
|
||||
|
||||
|
||||
def test_search_filters_and_browse(seeded):
|
||||
assert query.search(seeded)["total"] == 4, "empty query = browse all"
|
||||
assert query.search(seeded, filters={"setting": "indoor"})["total"] == 1
|
||||
assert query.search(seeded, filters={"people": "3+"})["total"] == 1
|
||||
assert query.search(seeded, filters={"year_min": 2012})["total"] == 1
|
||||
assert query.search(seeded, filters={"has_location": "1"})["total"] == 1
|
||||
assert query.search(seeded, q="city", filters={"setting": "indoor"})["total"] == 0, \
|
||||
"search ANDs with filters"
|
||||
|
||||
|
||||
def test_search_paging(seeded):
|
||||
page = query.search(seeded, limit=2, offset=2)
|
||||
assert page["total"] == 4
|
||||
assert len(page["rows"]) == 2
|
||||
assert page["offset"] == 2
|
||||
|
||||
|
||||
def test_photo_omits_raw_response(seeded):
|
||||
d = query.photo(seeded, "/lib/album_a/beach.jpg")
|
||||
assert d["description"] == "A sunny beach with palm trees."
|
||||
assert "raw_response" not in d
|
||||
assert query.photo(seeded, "/nope.jpg") is None
|
||||
|
||||
|
||||
def test_facets_and_albums(seeded):
|
||||
f = query.facets(seeded, Path("/lib"))
|
||||
assert (f["year_min"], f["year_max"]) == (2011, 2015)
|
||||
assert {a["album"]: a["count"] for a in f["albums"]} == {
|
||||
"album_a": 2, "album_b": 2}
|
||||
assert {v["value"]: v["count"] for v in f["setting"]} == {
|
||||
"outdoor": 2, "indoor": 1}
|
||||
|
||||
|
||||
def test_stats_album_progress_and_errors(seeded):
|
||||
s = query.stats(seeded, Path("/lib"))
|
||||
assert s["total"] == 4
|
||||
assert s["status"] == {"analyzed": 2, "exif_written": 1, "error": 1}
|
||||
albums = {a["album"]: (a["done"], a["total"]) for a in s["albums"]}
|
||||
assert albums == {"album_a": (2, 2), "album_b": (1, 2)}, \
|
||||
"done counts analyzed + exif_written"
|
||||
assert s["errors"] == [{"path": "/lib/album_b/err.jpg", "error": "timeout"}]
|
||||
|
||||
|
||||
def test_all_paths_is_the_image_endpoint_allowlist(seeded):
|
||||
assert query.all_paths(seeded) == {
|
||||
"/lib/album_a/beach.jpg", "/lib/album_a/city.jpg",
|
||||
"/lib/album_b/dinner.jpg", "/lib/album_b/err.jpg"}
|
||||
Reference in New Issue
Block a user