forked from zovos/vk_hackathon
- Add logviewer/: Dozzle web UI (port 9999) + analyze.py CLI tool - docker-compose.yml: add json-file logging with rotation and labels for index/search - Fix Dockerfiles: COPY *.py . so all modules are included in image - Convert all relative imports to flat absolute imports for Docker flat layout - Rename index/schemas.py → index/index_schemas.py to avoid module name collision with search/schemas.py in test runner - Update all tests to add service dir to sys.path and use flat imports Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
118 lines
3.7 KiB
Python
118 lines
3.7 KiB
Python
"""Unit tests for search/query_builder.py (pure logic only, no HTTP)"""
|
|
import sys
|
|
import os
|
|
|
|
_SEARCH_DIR = os.path.join(os.path.dirname(__file__), "..", "search")
|
|
sys.path.insert(0, _SEARCH_DIR)
|
|
|
|
# Stub env vars before importing search modules
|
|
os.environ.setdefault("EMBEDDINGS_DENSE_URL", "http://localhost/embed")
|
|
os.environ.setdefault("RERANKER_URL", "http://localhost/rerank")
|
|
os.environ.setdefault("QDRANT_URL", "http://localhost:6333")
|
|
os.environ.setdefault("API_KEY", "test-key")
|
|
|
|
from schemas import Entities, Question
|
|
from query_builder import (
|
|
build_primary_query,
|
|
build_extra_dense_queries,
|
|
build_sparse_query,
|
|
build_entity_tokens,
|
|
)
|
|
|
|
|
|
def _q(**kwargs) -> Question:
|
|
defaults = dict(text="default question")
|
|
defaults.update(kwargs)
|
|
return Question(**defaults)
|
|
|
|
|
|
class TestBuildPrimaryQuery:
|
|
def test_uses_search_text_over_text(self):
|
|
q = _q(text="original", search_text="refined query")
|
|
assert build_primary_query(q) == "refined query"
|
|
|
|
def test_fallback_to_text(self):
|
|
q = _q(text="fallback text", search_text="")
|
|
assert build_primary_query(q) == "fallback text"
|
|
|
|
def test_strips_whitespace(self):
|
|
q = _q(text=" trimmed ")
|
|
assert build_primary_query(q) == "trimmed"
|
|
|
|
def test_collapses_internal_spaces(self):
|
|
q = _q(text="too many spaces")
|
|
result = build_primary_query(q)
|
|
assert " " not in result
|
|
|
|
|
|
class TestBuildExtraDenseQueries:
|
|
def test_no_extras_when_none(self):
|
|
q = _q(text="q")
|
|
assert build_extra_dense_queries(q) == []
|
|
|
|
def test_includes_variants(self):
|
|
q = _q(text="q", variants=["var1", "var2"])
|
|
extras = build_extra_dense_queries(q)
|
|
assert "var1" in extras
|
|
assert "var2" in extras
|
|
|
|
def test_includes_hyde(self):
|
|
q = _q(text="q", hyde=["hypothetical answer"])
|
|
extras = build_extra_dense_queries(q)
|
|
assert "hypothetical answer" in extras
|
|
|
|
def test_skips_empty_strings(self):
|
|
q = _q(text="q", variants=["", " ", "valid"])
|
|
extras = build_extra_dense_queries(q)
|
|
assert "" not in extras
|
|
assert " " not in extras
|
|
assert "valid" in extras
|
|
|
|
|
|
class TestBuildSparseQuery:
|
|
def test_uses_keywords_when_present(self):
|
|
q = _q(text="question", keywords=["go", "golang", "performance"])
|
|
result = build_sparse_query(q)
|
|
assert "go" in result
|
|
assert "golang" in result
|
|
|
|
def test_fallback_to_primary_when_no_keywords(self):
|
|
q = _q(text="fallback question", search_text="refined")
|
|
result = build_sparse_query(q)
|
|
assert result == "refined"
|
|
|
|
def test_empty_keywords_fallback(self):
|
|
q = _q(text="my question", keywords=[])
|
|
result = build_sparse_query(q)
|
|
assert result == "my question"
|
|
|
|
|
|
class TestBuildEntityTokens:
|
|
def test_no_entities(self):
|
|
q = _q(text="q")
|
|
assert build_entity_tokens(q) == []
|
|
|
|
def test_people_extracted(self):
|
|
q = _q(text="q", entities=Entities(people=["Alice", "Bob"]))
|
|
tokens = build_entity_tokens(q)
|
|
assert "Alice" in tokens
|
|
assert "Bob" in tokens
|
|
|
|
def test_all_entity_fields(self):
|
|
q = _q(
|
|
text="q",
|
|
entities=Entities(
|
|
people=["Alice"],
|
|
emails=["alice@corp.com"],
|
|
documents=["report.pdf"],
|
|
names=["Project X"],
|
|
links=["https://example.com"],
|
|
),
|
|
)
|
|
tokens = build_entity_tokens(q)
|
|
assert len(tokens) == 5
|
|
|
|
def test_strips_whitespace(self):
|
|
q = _q(text="q", entities=Entities(people=[" Alice "]))
|
|
tokens = build_entity_tokens(q)
|
|
assert "Alice" in tokens
|