Files
zeekay ea3116aac9 Fix all test issues and achieve 100% test pass rate
- Remove all unconditional pytest.skip() calls
- Fix file content tests to handle both strict/non-strict modes
- Fix missing device_capabilities module in hanzo-network
- Fix HanzoNetProvider imports and exports
- Fix local_llm.py import errors
- Create test helper classes for pagination tests
- Fix FastMCP paginator test constructor args
- Configure all API keys from ~/.zsh/after.zsh
- All tests now pass or skip conditionally based on actual requirements
2025-08-12 16:19:34 -05:00

254 lines
9.2 KiB
Python

"""Unit tests for the encoding module."""
import os
import tempfile
import time
from pathlib import Path
from unittest.mock import patch
import pytest
from cachetools import LRUCache
from dev_aci.editor.encoding import EncodingManager, with_encoding
@pytest.fixture
def temp_file():
"""Create a temporary file for testing."""
fd, path = tempfile.mkstemp()
os.close(fd)
yield Path(path)
try:
os.unlink(path)
except FileNotFoundError:
pass
@pytest.fixture
def encoding_manager():
"""Create an EncodingManager instance for testing."""
return EncodingManager()
def test_init(encoding_manager):
"""Test initialization of EncodingManager."""
assert isinstance(encoding_manager, EncodingManager)
assert isinstance(encoding_manager._encoding_cache, LRUCache)
assert encoding_manager.default_encoding == "utf-8"
assert encoding_manager.confidence_threshold == 0.9
def test_detect_encoding_nonexistent_file(encoding_manager):
"""Test detecting encoding for a nonexistent file."""
nonexistent_path = Path("/nonexistent/file.txt")
encoding = encoding_manager.detect_encoding(nonexistent_path)
assert encoding == encoding_manager.default_encoding
def test_detect_encoding_utf8(encoding_manager, temp_file):
"""Test detecting UTF-8 encoding."""
# Create a UTF-8 encoded file
with open(temp_file, "w", encoding="utf-8") as f:
f.write("Hello, world! UTF-8 encoded text.")
encoding = encoding_manager.detect_encoding(temp_file)
assert encoding.lower() in ("utf-8", "ascii")
def test_detect_encoding_utf8_with_icon(encoding_manager, temp_file):
"""Test detecting UTF-8 encoding with a word and an emoji."""
# Create a UTF-8 encoded file with a single word and an emoji
with open(temp_file, "w", encoding="utf-8") as f:
f.write("Hello 😊")
encoding = encoding_manager.detect_encoding(temp_file)
assert encoding.lower() == "utf-8"
def test_detect_encoding_cp1251(encoding_manager, temp_file):
"""Test detecting CP1251 encoding."""
# Create a CP1251 encoded file with Cyrillic characters
with open(temp_file, "wb") as f:
f.write("Привет, мир! Текст в кодировке CP1251.".encode("cp1251"))
encoding = encoding_manager.detect_encoding(temp_file)
assert encoding.lower() in ("windows-1251", "cp1251")
def test_detect_encoding_low_confidence(encoding_manager, temp_file):
"""Test fallback to default encoding when confidence is low."""
# Create a file with mixed encodings to confuse the detector
with open(temp_file, "wb") as f:
f.write(b"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f")
# Mock chardet.detect to return low confidence
with patch(
"charset_normalizer.detect",
return_value={"encoding": "ascii", "confidence": 0.3},
):
encoding = encoding_manager.detect_encoding(temp_file)
assert encoding == encoding_manager.default_encoding
def test_detect_encoding_none_result(encoding_manager, temp_file):
"""Test fallback to default encoding when chardet returns None for encoding."""
with open(temp_file, "wb") as f:
f.write(b"\x00\x01\x02\x03") # Binary data
# Mock chardet.detect to return None for encoding
with patch(
"charset_normalizer.detect", return_value={"encoding": None, "confidence": 0.0}
):
encoding = encoding_manager.detect_encoding(temp_file)
assert encoding == encoding_manager.default_encoding
def test_get_encoding_cache_hit(encoding_manager, temp_file):
"""Test that get_encoding uses cached values when available."""
# Create a file
with open(temp_file, "w", encoding="utf-8") as f:
f.write("Hello, world!")
# First call should detect encoding
with patch.object(
encoding_manager, "detect_encoding", return_value="utf-8"
) as mock_detect:
encoding1 = encoding_manager.get_encoding(temp_file)
assert encoding1 == "utf-8"
mock_detect.assert_called_once()
# Second call should use cache
with patch.object(
encoding_manager, "detect_encoding", return_value="utf-8"
) as mock_detect:
encoding2 = encoding_manager.get_encoding(temp_file)
assert encoding2 == "utf-8"
mock_detect.assert_not_called()
def test_get_encoding_cache_invalidation(encoding_manager, temp_file):
"""Test that cache is invalidated when file is modified."""
# Create a file
with open(temp_file, "w", encoding="utf-8") as f:
f.write("Hello, world!")
# First call should detect encoding
encoding1 = encoding_manager.get_encoding(temp_file)
assert encoding1.lower() in ("utf-8", "ascii")
# Wait a moment to ensure modification time will be different
time.sleep(0.1)
# Modify the file
with open(temp_file, "w", encoding="utf-8") as f:
f.write("Modified content")
# Mock detect_encoding to verify it's called again
with patch.object(
encoding_manager, "detect_encoding", return_value="utf-8"
) as mock_detect:
encoding2 = encoding_manager.get_encoding(temp_file)
assert encoding2 == "utf-8"
mock_detect.assert_called_once()
def test_with_encoding_decorator():
"""Test the with_encoding decorator."""
# Create a mock class with a method that will be decorated
class MockEditor:
def __init__(self):
self._encoding_manager = EncodingManager()
@with_encoding
def read_file(self, path, encoding="utf-8"):
return f"Reading file with encoding: {encoding}"
editor = MockEditor()
# Test with a directory
with patch.object(Path, "is_dir", return_value=True):
with patch.object(
editor._encoding_manager, "get_encoding"
) as mock_get_encoding:
result = editor.read_file(Path("/some/dir"))
assert result == "Reading file with encoding: utf-8"
mock_get_encoding.assert_not_called()
# Test with a nonexistent file
with patch.object(Path, "is_dir", return_value=False):
with patch.object(Path, "exists", return_value=False):
result = editor.read_file(Path("/nonexistent/file.txt"))
assert (
result
== f"Reading file with encoding: {editor._encoding_manager.default_encoding}"
)
# Test with an existing file
with patch.object(Path, "is_dir", return_value=False):
with patch.object(Path, "exists", return_value=True):
with patch.object(
editor._encoding_manager, "get_encoding", return_value="latin-1"
):
result = editor.read_file(Path("/existing/file.txt"))
assert result == "Reading file with encoding: latin-1"
def test_with_encoding_respects_provided_encoding():
"""Test that the with_encoding decorator respects explicitly provided encoding."""
# The current implementation of with_encoding always calls get_encoding
# but doesn't override the provided encoding if it exists in kwargs
class MockEditor:
def __init__(self):
self._encoding_manager = EncodingManager()
@with_encoding
def read_file(self, path, encoding="utf-8"):
return f"Reading file with encoding: {encoding}"
editor = MockEditor()
# Test with explicitly provided encoding
with patch.object(Path, "is_dir", return_value=False):
with patch.object(Path, "exists", return_value=True):
with patch.object(
editor._encoding_manager,
"get_encoding",
return_value="detected-encoding",
):
result = editor.read_file(Path("/some/file.txt"), encoding="iso-8859-1")
# The provided encoding should be used, not the detected one
assert result == "Reading file with encoding: iso-8859-1"
def test_cache_size_limit(encoding_manager, temp_file):
"""Test that the cache size is limited and LRU entries are evicted."""
# Create a small cache for testing
encoding_manager = EncodingManager(max_cache_size=3)
# Create a file
with open(temp_file, "w", encoding="utf-8") as f:
f.write("Test file")
# Create 4 different paths (using the same file but with different paths)
paths = [Path(f"{temp_file}.{i}") for i in range(4)]
# Mock exists and getmtime to return consistent values
with patch.object(Path, "exists", return_value=True):
with patch.object(os.path, "getmtime", return_value=123456):
with patch.object(
encoding_manager, "detect_encoding", return_value="utf-8"
):
# Access paths in order 0, 1, 2, 3
for i, path in enumerate(paths):
encoding_manager.get_encoding(path)
# After adding 4th item, the cache should still have 3 items
assert len(encoding_manager._encoding_cache) == 3
# Path 0 should have been evicted (LRU)
assert str(paths[0]) not in encoding_manager._encoding_cache
# Paths 1, 2, 3 should still be in the cache
for j in range(1, 4):
assert str(paths[j]) in encoding_manager._encoding_cache