Files
ss-tools/backend/tests/plugins/translate/test_prompt_builder.py
busya 8a4310169b v4: 7643 tests passing, 83% raw / 96.9% real coverage (excl __tests__).
12 known failures — all from Agent 3 new unverified tests (mock setup issues):
- 3 dataset_review_routes_extended (DTO field mismatches)
- 1 settings_consolidated (dict key access)
- 1 llm_analysis_service_coverage (rate_limit mock)
- 1 migration_plugin (SessionLocal side_effect exhaustion)
- 1 preview (DB query vs dict key)
- 5 scheduler (datetime timezone + async mock mismatches)

NEW TEST FILES THIS SESSION:
- test_batch_insert_coverage.py — 3 tests
- test_storage_plugin.py — +3 tests
- test_search.py — +2 tests
- test_mapper.py — already 100%
- test_llm_analysis_migration_v1_to_v2.py — 14 tests
- test_llm_async_http.py — +1 test
- test_prompt_builder.py — +1 test
- test_service_datasource.py — +1 test
- test_lang_detect.py — +1 test
- test_scheduler.py — +6 tests
- test_llm_analysis_service_coverage.py — +15 tests
- test_dataset_review_routes_extended.py — +14 tests
- test_settings_consolidated.py — +13 tests

Modules pushed to 100%: _batch_insert, dictionary_entries, service_datasource,
_llm_async_http, prompt_builder, dictionary_crud, _batch_sizer, storage/plugin,
mapper.py

Session: 7194→7643 tests (+449), 80%→83% raw (+3pp), 93.4%→96.9% real (+3.5pp).
Remaining: 12 failures to fix + ~300 statements to reach 98% real coverage.
2026-06-16 09:34:10 +03:00

247 lines
9.0 KiB
Python

# #region Test.ContextAwarePromptBuilder [C:2] [TYPE Module] [SEMANTICS test,prompt,builder,context]
# @BRIEF Tests for prompt_builder.py — ContextAwarePromptBuilder.
# @RELATION BINDS_TO -> [ContextAwarePromptBuilder]
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "src"))
import pytest
from unittest.mock import MagicMock
from src.plugins.translate.prompt_builder import ContextAwarePromptBuilder
class TestRenderEntry:
"""ContextAwarePromptBuilder.render_entry."""
def test_basic_entry_dict(self):
"""Render a dict entry."""
entry = {"source_term": "hello", "target_term": "bonjour"}
line = ContextAwarePromptBuilder.render_entry(entry)
assert line == '"hello" -> "bonjour"'
def test_basic_entry_object(self):
"""Render an object entry."""
entry = MagicMock()
entry.source_term = "hello"
entry.target_term = "bonjour"
entry.has_context = False
entry.context_data = None
entry.usage_notes = None
line = ContextAwarePromptBuilder.render_entry(entry)
assert line == '"hello" -> "bonjour"'
def test_with_context(self):
"""Entry with context data."""
entry = MagicMock()
entry.source_term = "hello"
entry.target_term = "bonjour"
entry.has_context = True
entry.context_data = {"table": "users", "column": "name"}
entry.usage_notes = None
line = ContextAwarePromptBuilder.render_entry(entry)
assert "table=users" in line
assert "column=name" in line
assert "context:" in line
def test_with_context_dict_str(self):
"""Entry with context_data as JSON string."""
entry = MagicMock()
entry.source_term = "hello"
entry.target_term = "bonjour"
entry.has_context = True
entry.context_data = '{"table": "users"}'
entry.usage_notes = None
line = ContextAwarePromptBuilder.render_entry(entry)
assert "table=users" in line
def test_with_context_invalid_json(self):
"""Entry with context_data as non-JSON string."""
entry = MagicMock()
entry.source_term = "hello"
entry.target_term = "bonjour"
entry.has_context = True
entry.context_data = "plain string context"
entry.usage_notes = None
line = ContextAwarePromptBuilder.render_entry(entry)
assert "plain string context" in line
def test_with_context_string_direct(self):
"""Entry with context_data as string (not dict, not valid JSON)."""
entry = MagicMock()
entry.source_term = "hello"
entry.target_term = "bonjour"
entry.has_context = True
entry.context_data = "some raw context"
entry.usage_notes = None
line = ContextAwarePromptBuilder.render_entry(entry, priority=True)
assert "PRIORITY" in line
def test_usage_notes(self):
"""Entry with usage notes."""
entry = MagicMock()
entry.source_term = "hello"
entry.target_term = "bonjour"
entry.has_context = False
entry.context_data = None
entry.usage_notes = "Formal greeting"
line = ContextAwarePromptBuilder.render_entry(entry)
assert "Usage: Formal greeting" in line
def test_priority(self):
"""Priority entry gets prefix."""
entry = {"source_term": "hello", "target_term": "bonjour"}
line = ContextAwarePromptBuilder.render_entry(entry, priority=True)
assert line.startswith("# PRIORITY")
def test_context_truncation(self):
"""Very long context is truncated."""
entry = MagicMock()
entry.source_term = "hello"
entry.target_term = "bonjour"
entry.has_context = True
entry.context_data = {"long": "x" * 3000}
entry.usage_notes = None
line = ContextAwarePromptBuilder.render_entry(entry)
assert len(line) < 4000
assert "...[truncated]" in line
def test_context_items_dict_access(self):
"""Context items from dict-type entry."""
entry = {
"source_term": "hello", "target_term": "bonjour",
"has_context": True, "context_data": {"key": "value"},
"usage_notes": None,
}
line = ContextAwarePromptBuilder.render_entry(entry)
assert "key=value" in line
def test_context_data_json_array(self):
"""Context_data as JSON array string — parsed as non-dict (line 69)."""
entry = {
"source_term": "hello", "target_term": "bonjour",
"has_context": True, "context_data": '["val1", "val2"]',
"usage_notes": None,
}
line = ContextAwarePromptBuilder.render_entry(entry)
# Should use raw string since parsed JSON is not a dict
assert '["val1", "val2"]' in line
class TestComputeContextSimilarity:
"""ContextAwarePromptBuilder.compute_context_similarity."""
def test_exact_match(self):
"""Identical contexts => 1.0."""
ctx = {"a": "hello", "b": "world"}
sim = ContextAwarePromptBuilder.compute_context_similarity(ctx, ctx)
assert sim == 1.0
def test_no_match(self):
"""Disjoint contexts => 0.0."""
ctx1 = {"a": "hello"}
ctx2 = {"b": "world"}
sim = ContextAwarePromptBuilder.compute_context_similarity(ctx1, ctx2)
assert sim == 0.0
def test_partial_match(self):
"""Partial overlap yields value between 0 and 1."""
ctx1 = {"a": "hello", "b": "world"}
ctx2 = {"a": "hello", "c": "foo"}
sim = ContextAwarePromptBuilder.compute_context_similarity(ctx1, ctx2)
# intersection = {"hello"}, union = {"hello", "world", "foo"} => 1/3
assert 0.33 < sim < 0.34
def test_none_entry_context(self):
"""None entry_context => 0.0."""
sim = ContextAwarePromptBuilder.compute_context_similarity(None, {"a": "b"})
assert sim == 0.0
def test_none_row_context(self):
"""None row_context => 0.0."""
sim = ContextAwarePromptBuilder.compute_context_similarity({"a": "b"}, None)
assert sim == 0.0
def test_empty_values(self):
"""Context with all None values => 0.0."""
ctx1 = {"a": None, "b": None}
ctx2 = {"a": "hello"}
sim = ContextAwarePromptBuilder.compute_context_similarity(ctx1, ctx2)
assert sim == 0.0
def test_case_insensitive(self):
"""Values are lowercased."""
ctx1 = {"a": "Hello"}
ctx2 = {"a": "hello"}
sim = ContextAwarePromptBuilder.compute_context_similarity(ctx1, ctx2)
assert sim == 1.0
class TestBuildContextEntries:
"""ContextAwarePromptBuilder.build_context_entries."""
def test_basic_build(self):
"""Build list of rendered entries sorted by priority."""
entries = [
{"source_term": "hello", "target_term": "bonjour",
"has_context": False, "context_data": None, "usage_notes": None},
{"source_term": "world", "target_term": "monde",
"has_context": True, "context_data": {"table": "users"},
"usage_notes": None},
]
result = ContextAwarePromptBuilder.build_context_entries(entries)
assert len(result) == 2
def test_priority_by_similarity(self):
"""Entries matching row_context get priority prefix."""
entries = [
{"source_term": "hello", "target_term": "bonjour",
"has_context": True, "context_data": {"table": "users"},
"usage_notes": None},
{"source_term": "world", "target_term": "monde",
"has_context": True, "context_data": {"table": "orders"},
"usage_notes": None},
]
row_context = {"table": "users"}
result = ContextAwarePromptBuilder.build_context_entries(entries, row_context)
# First entry matches and gets priority
assert "# PRIORITY" in result[0]
assert "# PRIORITY" not in result[1]
def test_no_row_context(self):
"""No row_context => no priority."""
entries = [
{"source_term": "hello", "target_term": "bonjour",
"has_context": False, "context_data": None, "usage_notes": None},
]
result = ContextAwarePromptBuilder.build_context_entries(entries)
assert len(result) == 1
assert "# PRIORITY" not in result[0]
def test_empty_entries(self):
"""Empty entries returns empty list."""
result = ContextAwarePromptBuilder.build_context_entries([])
assert result == []
def test_object_entries(self):
"""Entries as objects work too."""
entry = MagicMock()
entry.source_term = "hello"
entry.target_term = "bonjour"
entry.has_context = True
entry.context_data = {"table": "users"}
entry.usage_notes = None
result = ContextAwarePromptBuilder.build_context_entries([entry], {"table": "users"})
assert len(result) == 1
assert "# PRIORITY" in result[0]
# #endregion Test.ContextAwarePromptBuilder