""" 测试知识库工具函数的异步变更 文件: 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)