- 10 translate plugin test files (100% coverage on 12 modules) - assistant/handler tools: 85+ tests covering dispatch, registry, resolvers, routes, llm_planner, 13 tool handlers - clean release: artifact_catalog_loader, mappers, approval, publication tests - API routes: translate_helpers, validation_service extensions, datasets to 100% - notifications: providers/service tests - services: profile_preference_service - docs/orthogonal-test-report.md — full speckit.tests audit - Fixes: 3 git_base async mock failures, 4 assistant handler permission-check patches - .gitignore: coverage artifacts
126 lines
4.3 KiB
Python
126 lines
4.3 KiB
Python
# #region Test.TextCleaner [C:3] [TYPE Module] [SEMANTICS test,translate,text,cleaner,whitespace,truncation]
|
|
# @BRIEF Tests for _text_cleaner.py — normalize_whitespace, truncate_text, clean_text.
|
|
# @RELATION BINDS_TO -> [TextCleaner]
|
|
# @TEST_CONTRACT: normalize_whitespace -> str | whitespace collapsed + trimmed
|
|
# @TEST_CONTRACT: truncate_text -> str | truncated with "..." or unchanged
|
|
# @TEST_CONTRACT: clean_text -> str | normalized then truncated
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "src"))
|
|
|
|
import os
|
|
os.environ.setdefault("SECRET_KEY", "test-secret-key-for-testing")
|
|
os.environ.setdefault("DATABASE_URL", "sqlite:///:memory:")
|
|
os.environ.setdefault("AUTH_DATABASE_URL", "sqlite:///:memory:")
|
|
|
|
import pytest
|
|
|
|
from src.plugins.translate._text_cleaner import (
|
|
normalize_whitespace,
|
|
truncate_text,
|
|
clean_text,
|
|
)
|
|
|
|
|
|
class TestNormalizeWhitespace:
|
|
"""normalize_whitespace — collapse whitespace, trim."""
|
|
|
|
def test_basic_trim(self):
|
|
"""Leading/trailing spaces are removed."""
|
|
assert normalize_whitespace(" hello world ") == "hello world"
|
|
|
|
def test_collapse_multi_spaces(self):
|
|
"""Multiple internal spaces collapsed to single."""
|
|
assert normalize_whitespace("hello world") == "hello world"
|
|
|
|
def test_tabs_newlines(self):
|
|
"""Tabs and newlines collapse to single space."""
|
|
assert normalize_whitespace("hello\t\tworld\n\nfoo") == "hello world foo"
|
|
|
|
def test_empty_string(self):
|
|
"""Empty string returns empty string."""
|
|
assert normalize_whitespace("") == ""
|
|
|
|
def test_whitespace_only(self):
|
|
"""Whitespace-only string returns empty string."""
|
|
assert normalize_whitespace(" \t\n ") == ""
|
|
|
|
def test_no_change(self):
|
|
"""Already clean text unchanged."""
|
|
assert normalize_whitespace("hello world") == "hello world"
|
|
|
|
def test_single_word(self):
|
|
"""Single word is trimmed."""
|
|
assert normalize_whitespace(" hello ") == "hello"
|
|
|
|
|
|
class TestTruncateText:
|
|
"""truncate_text — truncate with '...' suffix."""
|
|
|
|
def test_no_truncation_needed(self):
|
|
"""Short text returned unchanged."""
|
|
assert truncate_text("hello", 10) == "hello"
|
|
|
|
def test_exact_length(self):
|
|
"""Text equal to max_length returned unchanged."""
|
|
assert truncate_text("hello", 5) == "hello"
|
|
|
|
def test_truncation_applied(self):
|
|
"""Text longer than max_length is truncated with '...'."""
|
|
assert truncate_text("hello world", 5) == "hello..."
|
|
|
|
def test_empty_string(self):
|
|
"""Empty string with any max_length returns empty."""
|
|
assert truncate_text("", 100) == ""
|
|
|
|
def test_default_max_length(self):
|
|
"""Default max_length is 500."""
|
|
short = "a" * 400
|
|
long_str = "a" * 600
|
|
assert truncate_text(short) == short
|
|
assert truncate_text(long_str) == "a" * 500 + "..."
|
|
|
|
def test_zero_max_length(self):
|
|
"""max_length=0 truncates everything."""
|
|
assert truncate_text("hello", 0) == "..."
|
|
|
|
def test_unicode_preserved(self):
|
|
"""Unicode characters preserved through truncation."""
|
|
text = "héllo wörld"
|
|
assert truncate_text(text, 5) == "héllo..."
|
|
|
|
def test_cjk_characters(self):
|
|
"""CJK characters counted as single chars."""
|
|
text = "你好世界"
|
|
assert truncate_text(text, 2) == "你好..."
|
|
|
|
|
|
class TestCleanText:
|
|
"""clean_text — normalize then truncate."""
|
|
|
|
def test_normalize_and_truncate(self):
|
|
"""Input is normalized then truncated."""
|
|
result = clean_text(" hello world ", 5)
|
|
assert result == "hello..."
|
|
|
|
def test_no_truncation_needed(self):
|
|
"""Normalized text within max_length unchanged."""
|
|
result = clean_text(" hello world ", 50)
|
|
assert result == "hello world"
|
|
|
|
def test_empty_string(self):
|
|
"""Empty string stays empty."""
|
|
assert clean_text("") == ""
|
|
|
|
def test_default_max_length(self):
|
|
"""Default max_length of 500 used."""
|
|
text = " " + "a" * 600 + " "
|
|
result = clean_text(text)
|
|
assert result == "a" * 500 + "..."
|
|
|
|
def test_whitespace_only(self):
|
|
"""Whitespace-only input returns empty string."""
|
|
assert clean_text(" \t\n ") == ""
|
|
# #endregion Test.TextCleaner
|