"""Integration: migration 0002 (hybrid retrieval) schema contract. Asserts the state the migration must leave on the live schema: ``chunks.tsv`` as a stored generated tsvector, its GIN index, and the nullable ``query_log.fts_hits`` column (pre-0002 rows stay NULL, so it must accept NULL and an int). Requires ``podman compose up -d db``. """ from __future__ import annotations from sqlalchemy import text def test_migration_0002_schema_contract(db) -> None: tsv_col = db.execute( text( "SELECT count(*) FROM information_schema.columns" " WHERE table_name = 'chunks' AND column_name = 'tsv'" ) ).scalar() assert tsv_col == 1, "chunks.tsv (stored tsvector) is missing" gin = db.execute( text( "SELECT count(*) FROM pg_indexes" " WHERE tablename = 'chunks' AND indexdef ILIKE '%USING gin%'" " AND indexdef ILIKE '%tsv%'" ) ).scalar() assert gin == 1, "GIN index on chunks.tsv is missing" fts = db.execute( text( "SELECT is_nullable = 'YES' FROM information_schema.columns" " WHERE table_name = 'query_log' AND column_name = 'fts_hits'" ) ).scalar() assert fts is True, "query_log.fts_hits must exist and be nullable (pre-0002 rows)" def test_tsv_is_generated_and_lexically_queryable(db) -> None: """The tsvector is generated from ``content`` (not maintained by app code) and answers a tsquery — the retrieval path's lexical branch.""" doc_id = db.execute(text("SELECT gen_random_uuid()")).scalar() try: db.execute( text( "INSERT INTO documents (id, source, path, full_path, title, content," " content_hash, indexed_at) VALUES" " (:id, 'mig_test', 't.md', '/t.md', 'T', 'kafkabridge routes here'," " repeat('0', 64), now())" ), {"id": doc_id}, ) db.execute( text( "INSERT INTO chunks (id, document_id, position, content) VALUES" " (gen_random_uuid(), :id, 0, 'kafkabridge routes here')" ), {"id": doc_id}, ) db.commit() hit = db.execute( text( "SELECT count(*) FROM chunks" " WHERE tsv @@ to_tsquery('english', 'kafkabridge')" ) ).scalar() assert hit == 1 finally: db.execute(text("DELETE FROM chunks WHERE document_id = :id"), {"id": doc_id}) db.execute(text("DELETE FROM documents WHERE id = :id"), {"id": doc_id}) db.commit()