""" Integration tests for the FTS5 search index. Confirms that materials_list and skills_developed are indexed by FTS5 and kept in sync by the insert / update / delete triggers (plan §6, §7). """ import os import sys import json import pytest # Make the project root importable when pytest is run from anywhere. PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) if PROJECT_ROOT not in sys.path: sys.path.insert(0, PROJECT_ROOT) from app.models.activity import Activity # noqa: E402 from app.models.database import DatabaseManager # noqa: E402 @pytest.fixture def db(tmp_path): """A fresh DatabaseManager backed by a temporary SQLite file.""" return DatabaseManager(str(tmp_path / "test_activities.db")) def _make_activity(**overrides): base = dict( name="Vânătoarea de comori", description="O activitate de echipă în aer liber.", category="camp-outdoor", content_type="joc", source_file="test.txt", language="ro", ) base.update(overrides) return Activity(**base) def test_search_by_materials_list(db): """A term that only appears in materials_list returns the activity.""" activity = _make_activity(materials_list="frânghie, eșarfă, busolă") db.insert_activity(activity) results = db.search_activities(search_text="busolă") assert len(results) == 1 assert results[0]["name"] == "Vânătoarea de comori" def test_search_by_skills_developed(db): """A term that only appears in skills_developed returns the activity.""" activity = _make_activity(skills_developed="comunicare, leadership, rabdare") db.insert_activity(activity) results = db.search_activities(search_text="leadership") assert len(results) == 1 assert results[0]["name"] == "Vânătoarea de comori" def test_term_absent_from_indexed_columns_no_hit(db): """A term present in no indexed column yields no hit (control).""" db.insert_activity(_make_activity(materials_list="frânghie")) assert db.search_activities(search_text="zzzunlikelyterm") == [] def test_delete_trigger_removes_from_fts(db): """Deleting an activity removes it from the FTS index (delete trigger).""" activity = _make_activity(materials_list="catalige") activity_id = db.insert_activity(activity) assert len(db.search_activities(search_text="catalige")) == 1 with db._get_connection() as conn: conn.execute("DELETE FROM activities WHERE id = ?", (activity_id,)) conn.commit() assert db.search_activities(search_text="catalige") == [] def test_update_trigger_resyncs_fts(db): """Updating materials_list re-syncs the FTS index (update trigger).""" activity = _make_activity(materials_list="creioane") activity_id = db.insert_activity(activity) assert len(db.search_activities(search_text="creioane")) == 1 with db._get_connection() as conn: conn.execute( "UPDATE activities SET materials_list = ? WHERE id = ?", ("acuarele", activity_id), ) conn.commit() # Old term gone, new term found. assert db.search_activities(search_text="creioane") == [] assert len(db.search_activities(search_text="acuarele")) == 1 def test_rebuild_fts_index(db): """rebuild_fts_index keeps materials_list / skills_developed searchable.""" db.insert_activity(_make_activity(skills_developed="orientare")) db.rebuild_fts_index() assert len(db.search_activities(search_text="orientare")) == 1 def test_new_schema_columns_round_trip(db): """New activity columns persist and load back via from_dict.""" activity = _make_activity( source_files=["a.txt", "b.txt"], source_excerpt="Citat scurt din sursă.", extraction_confidence="high", needs_review=1, normalized_name="vanatoarea de comori", ) activity_id = db.insert_activity(activity) row = db.get_activity_by_id(activity_id) assert row["content_type"] == "joc" assert row["language"] == "ro" assert row["extraction_confidence"] == "high" assert row["needs_review"] == 1 assert row["normalized_name"] == "vanatoarea de comori" assert json.loads(row["source_files"]) == ["a.txt", "b.txt"] assert row["source_excerpt"] == "Citat scurt din sursă." loaded = Activity.from_dict(row) assert loaded.source_files == ["a.txt", "b.txt"] assert loaded.content_type == "joc" def test_normalized_name_auto_derived(db): """normalized_name is auto-derived from name when not provided.""" activity = Activity( name="Ștafetă cu Obstacole", description="desc", category="sports-active", source_file="t.txt", ) assert activity.normalized_name == "stafeta cu obstacole"