ForcePilot/test/test_kb_utils_changes.py

247 lines
8.6 KiB
Python
Raw Normal View History

"""
测试知识库工具函数的异步变更
文件: src/knowledge/utils/kb_utils.py
"""
import os
import sys
from pathlib import Path
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))
import pytest
import hashlib
from src.knowledge.utils.kb_utils import calculate_content_hash, prepare_item_metadata
class TestCalculateContentHashChanges:
"""测试 calculate_content_hash 改为异步后的功能"""
@pytest.mark.asyncio
async def test_hash_bytes_data(self):
"""测试计算字节数据的哈希"""
test_data = b"Test content for hashing"
# 异步计算哈希
hash_result = await calculate_content_hash(test_data)
# 验证结果
expected_hash = hashlib.sha256(test_data).hexdigest()
assert hash_result == expected_hash
assert len(hash_result) == 64 # SHA-256 长度
print(f"✓ 字节数据哈希计算正确: {hash_result}")
@pytest.mark.asyncio
async def test_hash_bytearray_data(self):
"""测试计算 bytearray 数据的哈希"""
test_data = bytearray(b"Bytearray test content")
hash_result = await calculate_content_hash(test_data)
expected_hash = hashlib.sha256(test_data).hexdigest()
assert hash_result == expected_hash
print(f"✓ bytearray 数据哈希计算正确: {hash_result}")
@pytest.mark.asyncio
async def test_hash_file_path_str(self, tmp_path):
"""测试计算文件路径(字符串)的哈希"""
# 创建测试文件
test_file = tmp_path / "test_hash_str.txt"
test_content = b"File content for hashing test"
test_file.write_bytes(test_content)
# 异步计算文件哈希
hash_result = await calculate_content_hash(str(test_file))
# 验证结果
expected_hash = hashlib.sha256(test_content).hexdigest()
assert hash_result == expected_hash
print(f"✓ 文件路径(字符串)哈希计算正确: {hash_result}")
@pytest.mark.asyncio
async def test_hash_file_path_object(self, tmp_path):
"""测试计算文件路径Path对象的哈希"""
# 创建测试文件
test_file = tmp_path / "test_hash_path.txt"
test_content = b"Path object hashing test"
test_file.write_bytes(test_content)
# 异步计算文件哈希
hash_result = await calculate_content_hash(test_file)
# 验证结果
expected_hash = hashlib.sha256(test_content).hexdigest()
assert hash_result == expected_hash
print(f"✓ 文件路径Path对象哈希计算正确: {hash_result}")
@pytest.mark.asyncio
async def test_hash_large_file(self, tmp_path):
"""测试计算大文件的哈希(验证分块读取)"""
# 创建大于 8192 字节的测试文件
test_file = tmp_path / "large_file.bin"
test_content = b"A" * 20000 # 20KB
test_file.write_bytes(test_content)
# 异步计算哈希
hash_result = await calculate_content_hash(test_file)
# 验证结果
expected_hash = hashlib.sha256(test_content).hexdigest()
assert hash_result == expected_hash
print(f"✓ 大文件哈希计算正确(文件大小: {len(test_content)} 字节)")
@pytest.mark.asyncio
async def test_hash_empty_file(self, tmp_path):
"""测试计算空文件的哈希"""
test_file = tmp_path / "empty_file.txt"
test_file.write_bytes(b"")
hash_result = await calculate_content_hash(test_file)
expected_hash = hashlib.sha256(b"").hexdigest()
assert hash_result == expected_hash
print(f"✓ 空文件哈希计算正确: {hash_result}")
@pytest.mark.asyncio
async def test_hash_consistency(self):
"""测试相同数据的哈希一致性"""
test_data = b"Consistency test data"
# 多次计算哈希
hash1 = await calculate_content_hash(test_data)
hash2 = await calculate_content_hash(test_data)
hash3 = await calculate_content_hash(test_data)
# 验证一致性
assert hash1 == hash2 == hash3
print(f"✓ 哈希计算一致性验证通过: {hash1}")
class TestPrepareItemMetadataChanges:
"""测试 prepare_item_metadata 改为异步后的功能"""
@pytest.mark.asyncio
async def test_prepare_file_metadata(self, tmp_path):
"""测试准备文件元数据"""
# 创建测试文件
test_file = tmp_path / "test_metadata.txt"
test_content = b"Test file content for metadata"
test_file.write_bytes(test_content)
# 异步准备元数据
metadata = await prepare_item_metadata(
item=str(test_file),
content_type="file",
db_id="test_database",
params={"custom_key": "custom_value"},
)
# 验证元数据
assert metadata is not None
assert metadata["filename"] == "test_metadata.txt"
assert metadata["database_id"] == "test_database"
assert "file_id" in metadata
assert "content_hash" in metadata
assert len(metadata["content_hash"]) == 64
print(f"✓ 文件元数据准备成功:")
print(f" - filename: {metadata['filename']}")
print(f" - file_id: {metadata['file_id']}")
print(f" - content_hash: {metadata['content_hash'][:16]}...")
@pytest.mark.asyncio
async def test_prepare_url_metadata(self):
"""测试准备 URL 元数据"""
test_url = "https://example.com/document.pdf"
# 异步准备元数据
metadata = await prepare_item_metadata(
item=test_url, content_type="url", db_id="test_db_url", params=None
)
# 验证元数据
assert metadata is not None
assert metadata["filename"] == "document.pdf"
assert metadata["database_id"] == "test_db_url"
assert "file_id" in metadata
assert metadata.get("content_hash") is None # URL 不计算 hash
print(f"✓ URL 元数据准备成功:")
print(f" - filename: {metadata['filename']}")
print(f" - file_id: {metadata['file_id']}")
@pytest.mark.asyncio
async def test_prepare_metadata_with_params(self, tmp_path):
"""测试带参数的元数据准备"""
test_file = tmp_path / "params_test.txt"
test_file.write_bytes(b"Params test content")
params = {
"chunk_size": 500,
"chunk_overlap": 50,
"separator": "\n\n",
"custom_field": "test_value",
}
metadata = await prepare_item_metadata(
item=str(test_file), content_type="file", db_id="params_test_db", params=params
)
assert metadata is not None
assert "file_id" in metadata
print(f"✓ 带参数的元数据准备成功")
@pytest.mark.asyncio
async def test_prepare_metadata_nonexistent_file(self, tmp_path):
"""测试不存在文件的元数据准备"""
nonexistent_file = tmp_path / "nonexistent.txt"
# 即使文件不存在也应该能准备元数据hash 为 None
metadata = await prepare_item_metadata(
item=str(nonexistent_file),
content_type="file",
db_id="test_db",
params=None,
)
assert metadata is not None
assert metadata["filename"] == "nonexistent.txt"
assert metadata.get("content_hash") is None
print(f"✓ 不存在文件的元数据准备成功hash 为 None")
@pytest.mark.asyncio
async def test_concurrent_metadata_preparation(self, tmp_path):
"""测试并发准备多个文件的元数据"""
import asyncio
# 创建多个测试文件
files = []
for i in range(5):
test_file = tmp_path / f"concurrent_{i}.txt"
test_file.write_bytes(f"Content {i}".encode())
files.append(str(test_file))
# 并发准备元数据
tasks = [
prepare_item_metadata(item=f, content_type="file", db_id=f"db_{i}", params=None)
for i, f in enumerate(files)
]
results = await asyncio.gather(*tasks)
# 验证结果
assert len(results) == 5
for i, metadata in enumerate(results):
assert metadata["filename"] == f"concurrent_{i}.txt"
assert metadata["database_id"] == f"db_{i}"
assert len(metadata["content_hash"]) == 64
print(f"✓ 并发元数据准备测试通过,处理了 {len(results)} 个文件")
if __name__ == "__main__":
exit_code = pytest.main([__file__, "-v", "-s", "--tb=short", "--color=yes"])
sys.exit(exit_code)