Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
299 lines
12 KiB
Python
299 lines
12 KiB
Python
"""Unit tests for chapter10/book-translation/consistency_auditor.py."""
|
|
|
|
from pathlib import Path
|
|
import sys
|
|
|
|
# Ensure chapter10/book-translation is in sys.path
|
|
ch10_dir = (Path(__file__).resolve().parent.parent / "chapter10" / "book-translation").resolve()
|
|
if str(ch10_dir) not in sys.path:
|
|
sys.path.insert(0, str(ch10_dir))
|
|
|
|
from consistency_auditor import (
|
|
AuditReport,
|
|
BilingualConsistencyAuditor,
|
|
audit_translation,
|
|
)
|
|
|
|
|
|
def test_bilingual_consistency_auditor_perfect_match():
|
|
"""Test auditing a perfectly translated markdown document."""
|
|
source_md = """# Transformer Model Overview
|
|
|
|
The transformer model relies on attention mechanisms and token embedding.
|
|
Fine-tuning reduces latency during inference.
|
|
|
|
```python
|
|
def forward(x):
|
|
return x * 2
|
|
```
|
|
|
|
The energy formula is $E = mc^2$.
|
|
For details, see [Documentation](https://example.com/docs).
|
|
"""
|
|
|
|
target_md = """# Transformer 模型概述
|
|
|
|
Transformer 模型依赖注意力机制和词元嵌入。
|
|
微调可以在推理过程中降低时延。
|
|
|
|
```python
|
|
def forward(x):
|
|
return x * 2
|
|
```
|
|
|
|
能量公式为 $E = mc^2$。
|
|
更多细节参见 [文档](https://example.com/docs).
|
|
"""
|
|
|
|
report = audit_translation(source_md, target_md, lang="zh")
|
|
|
|
assert isinstance(report, AuditReport)
|
|
assert report.is_consistent is True
|
|
assert report.overall_score == 1.0
|
|
assert report.scores["terminology"] == 1.0
|
|
assert report.scores["code_blocks"] == 1.0
|
|
assert report.scores["latex_formulas"] == 1.0
|
|
assert report.scores["link_targets"] == 1.0
|
|
assert len(report.findings) == 0
|
|
|
|
|
|
def test_bilingual_consistency_auditor_terminology_drift():
|
|
"""Test auditing when terminology is missing or translated inconsistently."""
|
|
source_md = "The transformer uses token embedding and attention for inference."
|
|
target_md = "该模型使用未知处理和关注度。" # Missing 'token' (词元) and 'inference' (推理)
|
|
|
|
auditor = BilingualConsistencyAuditor()
|
|
report = auditor.run_audit(source_md, target_md, lang="zh")
|
|
|
|
assert report.scores["terminology"] < 1.0
|
|
term_findings = [f for f in report.findings if f["category"] == "terminology"]
|
|
assert len(term_findings) > 0
|
|
|
|
|
|
def test_bilingual_consistency_auditor_code_block_mismatch():
|
|
"""Test auditing code block synchronization errors."""
|
|
source_md = """
|
|
```python
|
|
x = 10
|
|
print(x)
|
|
```
|
|
"""
|
|
target_md = """
|
|
```python
|
|
x = 999
|
|
print(x)
|
|
```
|
|
"""
|
|
|
|
auditor = BilingualConsistencyAuditor()
|
|
report = auditor.run_audit(source_md, target_md, lang="zh")
|
|
|
|
assert report.scores["code_blocks"] < 1.0
|
|
code_findings = [f for f in report.findings if f["category"] == "code_blocks"]
|
|
assert len(code_findings) > 0
|
|
assert any("desynchronized" in f["message"] for f in code_findings)
|
|
|
|
|
|
def test_bilingual_consistency_auditor_latex_formula_corruption():
|
|
"""Test auditing LaTeX formula syntax and content preservation errors."""
|
|
source_md = "Formula: $E = mc^2$ and block $$\\\\alpha + \\\\beta = 1$$"
|
|
target_md = "公式: $E = mc^3$ 且块 $$"
|
|
|
|
auditor = BilingualConsistencyAuditor()
|
|
report = auditor.run_audit(source_md, target_md, lang="zh")
|
|
|
|
assert report.scores["latex_formulas"] < 1.0
|
|
latex_findings = [f for f in report.findings if f["category"] == "latex_formulas"]
|
|
assert len(latex_findings) > 0
|
|
|
|
|
|
def test_bilingual_consistency_auditor_link_target_mismatch():
|
|
"""Test auditing link target mismatches."""
|
|
source_md = "Check [API Guide](https://api.example.com/v1)."
|
|
target_md = "查看 [API 指南](https://api.wrong-domain.com/v1)."
|
|
|
|
auditor = BilingualConsistencyAuditor()
|
|
report = auditor.run_audit(source_md, target_md, lang="zh")
|
|
|
|
assert report.scores["link_targets"] < 1.0
|
|
link_findings = [f for f in report.findings if f["category"] == "link_targets"]
|
|
assert len(link_findings) > 0
|
|
assert "https://api.example.com/v1" in link_findings[0]["message"]
|
|
|
|
|
|
def test_bilingual_consistency_auditor_file_path_inputs(tmp_path):
|
|
"""Test auditing with actual file path inputs on disk."""
|
|
src_file = tmp_path / "source.md"
|
|
tgt_file = tmp_path / "target.md"
|
|
|
|
src_file.write_text("The prompt improves fine-tuning.", encoding="utf-8")
|
|
tgt_file.write_text("提示词可以改进微调。", encoding="utf-8")
|
|
|
|
report = audit_translation(src_file, tgt_file, lang="zh")
|
|
|
|
assert report["is_consistent"] is True
|
|
assert report["scores"]["terminology"] == 1.0
|
|
assert report.overall_score == 1.0
|
|
|
|
|
|
def test_bilingual_consistency_auditor_custom_glossary():
|
|
"""Test auditing with a custom terminology glossary."""
|
|
custom_glossary = {
|
|
"es": {
|
|
"agent": {"canonical": "agente", "variants": ["agente"]},
|
|
"prompt": {"canonical": "indicación", "variants": ["indicación", "prompt"]},
|
|
}
|
|
}
|
|
|
|
auditor = BilingualConsistencyAuditor(glossary=custom_glossary)
|
|
report = auditor.run_audit(
|
|
"An agent processes the prompt.",
|
|
"Un agente procesa la indicación.",
|
|
lang="es",
|
|
)
|
|
|
|
assert report.scores["terminology"] == 1.0
|
|
assert report.is_consistent is True
|
|
|
|
|
|
def test_bilingual_consistency_auditor_nonexistent_path_raises_error(tmp_path):
|
|
"""Test that passing a non-existent Path object raises FileNotFoundError."""
|
|
non_existent = tmp_path / "does_not_exist.md"
|
|
auditor = BilingualConsistencyAuditor()
|
|
try:
|
|
auditor.run_audit(non_existent, "Some content", lang="zh")
|
|
assert False, "Expected FileNotFoundError"
|
|
except FileNotFoundError:
|
|
pass
|
|
|
|
|
|
def test_bilingual_consistency_auditor_latex_formula_in_code_block():
|
|
"""Test that dollar signs in code blocks do not trigger false formula errors."""
|
|
source_md = "Run `$ pip install pkg` for setup."
|
|
target_md = "运行 `$ pip install pkg` 进行设置。"
|
|
report = audit_translation(source_md, target_md, lang="zh")
|
|
assert report.scores["latex_formulas"] == 1.0
|
|
assert report.is_consistent is True
|
|
|
|
|
|
def test_bilingual_consistency_auditor_independent_substring_variants():
|
|
"""Test that independent usage of canonical term alongside longer variant triggers warning."""
|
|
source_md = "The embedding concept."
|
|
target_md = "嵌入 和 嵌入向量。"
|
|
report = audit_translation(source_md, target_md, lang="zh")
|
|
assert report.scores["terminology"] == 0.5
|
|
term_findings = [f for f in report.findings if f["category"] == "terminology"]
|
|
assert any(f["severity"] == "warning" for f in term_findings)
|
|
|
|
|
|
def test_bilingual_consistency_auditor_case_insensitive_target_matching():
|
|
"""Test that capital letters in target text (e.g. sentence start) match terms case-insensitively."""
|
|
custom_glossary = {
|
|
"es": {
|
|
"agent": {"canonical": "agente", "variants": ["agente"]},
|
|
}
|
|
}
|
|
auditor = BilingualConsistencyAuditor(glossary=custom_glossary)
|
|
report = auditor.run_audit("An agent works.", "Agente trabaja.", lang="es")
|
|
assert report.scores["terminology"] == 1.0
|
|
assert report.is_consistent is True
|
|
|
|
|
|
def test_bilingual_consistency_auditor_unbalanced_dollars_without_source_formulas():
|
|
"""Test that unbalanced dollar signs in target document trigger error even if source has no formulas."""
|
|
source_md = "Simple text without formula."
|
|
target_md = "简单文本 带着 $ 不匹配定界符。"
|
|
report = audit_translation(source_md, target_md, lang="zh")
|
|
assert report.scores["latex_formulas"] == 0.0
|
|
assert report.is_consistent is False
|
|
latex_findings = [f for f in report.findings if f["category"] == "latex_formulas"]
|
|
assert len(latex_findings) > 0
|
|
assert "Unbalanced" in latex_findings[0]["message"]
|
|
|
|
def test_bilingual_consistency_auditor_non_overlapping_position_matching():
|
|
"""Test that variant matching uses non-overlapping text position match (longest match first)."""
|
|
custom_glossary = {
|
|
"zh": {
|
|
"embedding": {
|
|
"canonical": "嵌入向量",
|
|
"variants": ["嵌入向量", "嵌入"],
|
|
}
|
|
}
|
|
}
|
|
auditor = BilingualConsistencyAuditor(glossary=custom_glossary)
|
|
# Target has "嵌入向量" twice (index positions 0..4 and 7..11)
|
|
# Shorter variant "嵌入" overlaps with both (positions 0..2 and 7..9), so it should NOT be matched
|
|
report = auditor.run_audit("The embedding is good.", "嵌入向量 和 嵌入向量。", lang="zh")
|
|
assert report.scores["terminology"] == 1.0
|
|
term_findings = [f for f in report.findings if f["category"] == "terminology"]
|
|
assert len(term_findings) == 0
|
|
|
|
|
|
def test_bilingual_consistency_auditor_fenced_code_block_dollar_signs_ignored_in_latex_audit():
|
|
"""Test that dollar signs in fenced code blocks are stripped before checking LaTeX formula balance."""
|
|
source_md = "Here is script:\n```bash\necho $VAR1 $VAR2\n```\nFormula: $x = y$."
|
|
target_md = "这里是脚本:\n```bash\necho $VAR1 $VAR2 $VAR3\n```\n公式: $x = y$."
|
|
report = audit_translation(source_md, target_md, lang="zh")
|
|
assert report.scores["latex_formulas"] == 1.0
|
|
latex_findings = [f for f in report.findings if f["category"] == "latex_formulas"]
|
|
assert len(latex_findings) == 0
|
|
|
|
|
|
def test_bilingual_consistency_auditor_nonexistent_string_path_raises_error(tmp_path):
|
|
"""Test that passing a non-existent string file path raises FileNotFoundError."""
|
|
non_existent = str(tmp_path / "does_not_exist.md")
|
|
auditor = BilingualConsistencyAuditor()
|
|
try:
|
|
auditor.run_audit(non_existent, "Some content", lang="zh")
|
|
assert False, "Expected FileNotFoundError"
|
|
except FileNotFoundError:
|
|
pass
|
|
|
|
|
|
def test_bilingual_consistency_auditor_currency_dollar_not_formula_error():
|
|
"""Regression: dollar signs in prose (e.g. 'costs $5') must not be
|
|
misjudged as unbalanced LaTeX formula delimiters.
|
|
Old code counted all '$' in target text, including currency symbols.
|
|
"""
|
|
source = "The cost is $5 per request.\n\nSome text here."
|
|
target = "The cost is $5 per request.\n\nSome translated text here."
|
|
auditor = BilingualConsistencyAuditor()
|
|
report = auditor.run_audit(source, target, lang="zh")
|
|
latex_findings = [f for f in report.findings if f["category"] == "latex_formulas"]
|
|
assert len(latex_findings) == 0
|
|
|
|
|
|
def test_bilingual_consistency_auditor_canonical_not_suppressed_by_longer_variant():
|
|
"""Regression: canonical term must not be suppressed by a longer variant
|
|
that contains it, causing false 'non-canonical' flagging.
|
|
Old code sorted variants by length (longest first), so a longer variant
|
|
could occupy the span where the canonical appears, hiding the canonical match.
|
|
"""
|
|
glossary = {
|
|
"zh": {
|
|
"model": {
|
|
"canonical": "模型",
|
|
"variants": ["模型", "大语言模型"],
|
|
}
|
|
}
|
|
}
|
|
# Target uses only the canonical "模型", not the longer "大语言模型"
|
|
source = "The model processes input."
|
|
target = "模型处理输入。"
|
|
auditor = BilingualConsistencyAuditor(glossary=glossary)
|
|
report = auditor.run_audit(source, target, lang="zh")
|
|
term_findings = [f for f in report.findings if f["category"] == "terminology"]
|
|
# Should not flag as non-canonical since canonical "模型" is present
|
|
assert not any("non-canonical" in f["message"] for f in term_findings)
|
|
|
|
|
|
def test_bilingual_consistency_auditor_text_ending_in_md_not_treated_as_path():
|
|
"""Regression: single-line text ending in '.md' that is not an actual file
|
|
must be treated as content, not raise FileNotFoundError.
|
|
Old code treated any string ending in '.md' as a file path.
|
|
"""
|
|
auditor = BilingualConsistencyAuditor()
|
|
# This is content text, not a file path — should not raise
|
|
report = auditor.run_audit("Some source content", "This is a note about file.md", lang="zh")
|
|
assert report is not None
|