mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-29 04:26:20 +00:00
* fix(pipeline): resolve broken import and missing run() in PipelineWithProvenance Fix two bugs in pipeline_provenance.py: 1. Wrong import path: `from .pipeline import Pipeline` fails because `semantica/pipeline/pipeline.py` does not exist. Pipeline lives in `pipeline_builder.py`. Fixed to `from .pipeline_builder import Pipeline`. 2. Pipeline dataclass has no run() method. PipelineWithProvenance.run() now delegates to ExecutionEngine.execute_pipeline(), which is the intended execution path for built pipelines. Additional changes: - Constructor now accepts a built Pipeline instance (breaking the previous unusable API that tried to instantiate a dataclass with **config). - Replace deprecated datetime.utcnow() with datetime.now(timezone.utc). - Add test suite covering import, instantiation, execution, attribute delegation, and provenance graceful degradation. Fixes #858 * test: address Qodo review findings - Remove redundant test_import_succeeds (module-level import already guards against import regression at collection time). - Fix test_provenance_disabled_when_import_fails to deterministically simulate ImportError via sys.modules patch and assert provenance is actually toggled off (runner.provenance is False). * fix(pipeline): update provenance callers for Pipeline API --------- Co-authored-by: Sameer Kadam <sskadam6305@gmail.com> Co-authored-by: Russell Jurney <russell.jurney@gmail.com>
357 lines
13 KiB
Python
357 lines
13 KiB
Python
"""
|
|
Comprehensive edge case tests for all provenance integration modules.
|
|
|
|
Tests boundary conditions, error handling, and unusual scenarios.
|
|
"""
|
|
|
|
import pytest
|
|
from semantica.provenance import ProvenanceManager
|
|
|
|
|
|
class TestProvenanceManagerEdgeCases:
|
|
"""Test edge cases for ProvenanceManager."""
|
|
|
|
def test_empty_entity_id(self):
|
|
"""Test tracking with empty entity ID."""
|
|
manager = ProvenanceManager()
|
|
# Should handle empty IDs gracefully
|
|
try:
|
|
manager.track_entity(entity_id="", source="test")
|
|
except ValueError:
|
|
pass # Expected to raise ValueError
|
|
|
|
def test_none_entity_id(self):
|
|
"""Test tracking with None entity ID."""
|
|
manager = ProvenanceManager()
|
|
with pytest.raises((ValueError, TypeError)):
|
|
manager.track_entity(entity_id=None, source="test")
|
|
|
|
def test_very_long_entity_id(self):
|
|
"""Test tracking with very long entity ID."""
|
|
manager = ProvenanceManager()
|
|
long_id = "entity_" + "x" * 10000
|
|
manager.track_entity(entity_id=long_id, source="test")
|
|
# Should handle long IDs
|
|
|
|
def test_special_characters_in_entity_id(self):
|
|
"""Test entity IDs with special characters."""
|
|
manager = ProvenanceManager()
|
|
special_id = "entity_@#$%^&*()_中文_émoji🎉"
|
|
manager.track_entity(entity_id=special_id, source="test")
|
|
# Should handle special characters
|
|
|
|
def test_duplicate_entity_tracking(self):
|
|
"""Test tracking same entity multiple times."""
|
|
manager = ProvenanceManager()
|
|
manager.track_entity(entity_id="ent1", source="src1")
|
|
manager.track_entity(entity_id="ent1", source="src2")
|
|
# Should handle duplicates appropriately
|
|
|
|
def test_circular_lineage(self):
|
|
"""Test circular lineage relationships."""
|
|
manager = ProvenanceManager()
|
|
manager.track_entity(entity_id="a", source="b")
|
|
manager.track_entity(entity_id="b", source="c")
|
|
manager.track_entity(entity_id="c", source="a")
|
|
# Should handle circular references
|
|
|
|
def test_very_deep_lineage(self):
|
|
"""Test very deep lineage chains."""
|
|
manager = ProvenanceManager()
|
|
for i in range(100):
|
|
manager.track_entity(
|
|
entity_id=f"entity_{i}",
|
|
source=f"entity_{i-1}" if i > 0 else "root"
|
|
)
|
|
lineage = manager.get_lineage("entity_99")
|
|
# Should handle deep chains
|
|
assert lineage is not None
|
|
|
|
def test_large_metadata(self):
|
|
"""Test tracking with very large metadata."""
|
|
manager = ProvenanceManager()
|
|
large_metadata = {f"key_{i}": f"value_{i}" * 100 for i in range(100)}
|
|
manager.track_entity(
|
|
entity_id="test",
|
|
source="test",
|
|
metadata=large_metadata
|
|
)
|
|
# Should handle large metadata
|
|
|
|
def test_none_metadata_values(self):
|
|
"""Test metadata with None values."""
|
|
manager = ProvenanceManager()
|
|
manager.track_entity(
|
|
entity_id="test",
|
|
source="test",
|
|
metadata={"key1": None, "key2": "value"}
|
|
)
|
|
# Should handle None values in metadata
|
|
|
|
def test_nested_metadata(self):
|
|
"""Test deeply nested metadata structures."""
|
|
manager = ProvenanceManager()
|
|
nested = {"level1": {"level2": {"level3": {"level4": "deep"}}}}
|
|
manager.track_entity(
|
|
entity_id="test",
|
|
source="test",
|
|
metadata=nested
|
|
)
|
|
# Should handle nested structures
|
|
|
|
|
|
class TestConcurrencyEdgeCases:
|
|
"""Test concurrent operations edge cases."""
|
|
|
|
def test_concurrent_entity_tracking(self):
|
|
"""Test concurrent entity tracking operations."""
|
|
manager = ProvenanceManager()
|
|
# Simulate concurrent tracking
|
|
for i in range(100):
|
|
manager.track_entity(entity_id=f"entity_{i}", source="test")
|
|
# Should handle concurrent operations
|
|
|
|
def test_concurrent_lineage_queries(self):
|
|
"""Test concurrent lineage queries."""
|
|
manager = ProvenanceManager()
|
|
manager.track_entity(entity_id="test", source="src")
|
|
# Multiple concurrent queries
|
|
for _ in range(50):
|
|
lineage = manager.get_lineage("test")
|
|
assert lineage is not None
|
|
|
|
|
|
class TestStorageEdgeCases:
|
|
"""Test storage backend edge cases."""
|
|
|
|
def test_storage_with_empty_database(self):
|
|
"""Test querying empty storage."""
|
|
manager = ProvenanceManager()
|
|
lineage = manager.get_lineage("nonexistent")
|
|
# Should handle nonexistent entities gracefully
|
|
assert lineage is not None
|
|
|
|
def test_storage_with_corrupted_data(self):
|
|
"""Test handling of corrupted data."""
|
|
manager = ProvenanceManager()
|
|
# Should handle data issues gracefully
|
|
assert manager is not None
|
|
|
|
|
|
class TestModuleSpecificEdgeCases:
|
|
"""Test edge cases for specific module integrations."""
|
|
|
|
def test_context_with_none_context(self):
|
|
"""Test context manager with None context."""
|
|
try:
|
|
from semantica.context.context_provenance import ContextManagerWithProvenance
|
|
ctx = ContextManagerWithProvenance(provenance=True)
|
|
# Should handle None context
|
|
assert ctx is not None
|
|
except ImportError:
|
|
pytest.skip("ContextManager not available")
|
|
|
|
def test_pipeline_with_empty_data(self):
|
|
"""Test pipeline with empty data."""
|
|
try:
|
|
from semantica.pipeline.pipeline_builder import Pipeline
|
|
from semantica.pipeline.pipeline_provenance import PipelineWithProvenance
|
|
|
|
pipeline = Pipeline(name="edge_case_test")
|
|
runner = PipelineWithProvenance(pipeline, provenance=True)
|
|
# Should handle empty data
|
|
assert runner is not None
|
|
except ImportError:
|
|
pytest.skip("Pipeline not available")
|
|
|
|
def test_embeddings_with_empty_list(self):
|
|
"""Test embeddings with empty text list."""
|
|
try:
|
|
from semantica.embeddings.embeddings_provenance import EmbeddingGeneratorWithProvenance
|
|
embedder = EmbeddingGeneratorWithProvenance(provenance=True)
|
|
# Should handle empty list
|
|
assert embedder is not None
|
|
except ImportError:
|
|
pytest.skip("EmbeddingGenerator not available")
|
|
|
|
def test_deduplication_with_single_item(self):
|
|
"""Test deduplication with single item."""
|
|
try:
|
|
from semantica.deduplication.deduplication_provenance import DeduplicatorWithProvenance
|
|
dedup = DeduplicatorWithProvenance(provenance=True)
|
|
# Should handle single item
|
|
assert dedup is not None
|
|
except ImportError:
|
|
pytest.skip("Deduplicator not available")
|
|
|
|
def test_graph_store_with_duplicate_nodes(self):
|
|
"""Test graph store with duplicate nodes."""
|
|
try:
|
|
from semantica.graph_store.graph_store_provenance import GraphStoreWithProvenance
|
|
store = GraphStoreWithProvenance(provenance=True)
|
|
# Should handle duplicate nodes
|
|
assert store is not None
|
|
except ImportError:
|
|
pytest.skip("GraphStore not available")
|
|
|
|
def test_vector_store_with_mismatched_dimensions(self):
|
|
"""Test vector store with mismatched dimensions."""
|
|
try:
|
|
from semantica.vector_store.vector_store_provenance import VectorStoreWithProvenance
|
|
store = VectorStoreWithProvenance(provenance=True)
|
|
# Should handle dimension mismatches
|
|
assert store is not None
|
|
except ImportError:
|
|
pytest.skip("VectorStore not available")
|
|
|
|
|
|
class TestMemoryAndPerformanceEdgeCases:
|
|
"""Test memory and performance edge cases."""
|
|
|
|
def test_large_number_of_entities(self):
|
|
"""Test tracking large number of entities."""
|
|
manager = ProvenanceManager()
|
|
# Track 1000 entities
|
|
for i in range(1000):
|
|
manager.track_entity(entity_id=f"entity_{i}", source="test")
|
|
# Should handle large volumes
|
|
assert manager is not None
|
|
|
|
def test_large_number_of_relationships(self):
|
|
"""Test tracking large number of relationships."""
|
|
manager = ProvenanceManager()
|
|
# Track 1000 relationships
|
|
for i in range(1000):
|
|
manager.track_relationship(
|
|
relationship_id=f"rel_{i}",
|
|
source="test",
|
|
subject=f"subj_{i}",
|
|
predicate="relates_to",
|
|
obj=f"obj_{i}"
|
|
)
|
|
# Should handle large volumes
|
|
assert manager is not None
|
|
|
|
def test_memory_cleanup(self):
|
|
"""Test memory cleanup after operations."""
|
|
manager = ProvenanceManager()
|
|
# Create and track many entities
|
|
for i in range(100):
|
|
manager.track_entity(entity_id=f"temp_{i}", source="test")
|
|
# Memory should be managed appropriately
|
|
assert manager is not None
|
|
|
|
|
|
class TestErrorHandlingEdgeCases:
|
|
"""Test error handling edge cases."""
|
|
|
|
def test_invalid_source_type(self):
|
|
"""Test tracking with invalid source type."""
|
|
manager = ProvenanceManager()
|
|
# Should handle various source types
|
|
manager.track_entity(entity_id="test", source=123)
|
|
manager.track_entity(entity_id="test2", source=["list", "source"])
|
|
|
|
def test_invalid_metadata_type(self):
|
|
"""Test tracking with invalid metadata type."""
|
|
manager = ProvenanceManager()
|
|
# Should handle or reject invalid metadata
|
|
try:
|
|
manager.track_entity(
|
|
entity_id="test",
|
|
source="test",
|
|
metadata="not_a_dict"
|
|
)
|
|
except (TypeError, ValueError):
|
|
pass # Expected to raise error
|
|
|
|
def test_provenance_disabled_operations(self):
|
|
"""Test that operations work when provenance is disabled."""
|
|
try:
|
|
from semantica.context.context_provenance import ContextManagerWithProvenance
|
|
ctx = ContextManagerWithProvenance(provenance=False)
|
|
# All operations should work without provenance
|
|
assert ctx.provenance is False
|
|
assert ctx._prov_manager is None
|
|
except ImportError:
|
|
pytest.skip("ContextManager not available")
|
|
|
|
|
|
class TestUnicodeAndEncodingEdgeCases:
|
|
"""Test unicode and encoding edge cases."""
|
|
|
|
def test_unicode_entity_ids(self):
|
|
"""Test entity IDs with unicode characters."""
|
|
manager = ProvenanceManager()
|
|
unicode_ids = [
|
|
"entity_中文",
|
|
"entity_العربية",
|
|
"entity_हिन्दी",
|
|
"entity_日本語",
|
|
"entity_한국어"
|
|
]
|
|
for uid in unicode_ids:
|
|
manager.track_entity(entity_id=uid, source="test")
|
|
# Should handle all unicode
|
|
|
|
def test_emoji_in_metadata(self):
|
|
"""Test emoji characters in metadata."""
|
|
manager = ProvenanceManager()
|
|
manager.track_entity(
|
|
entity_id="test",
|
|
source="test",
|
|
metadata={"emoji": "🎉🚀💡🔥✨"}
|
|
)
|
|
# Should handle emoji
|
|
|
|
def test_mixed_encoding_sources(self):
|
|
"""Test sources with mixed encoding."""
|
|
manager = ProvenanceManager()
|
|
sources = [
|
|
"file_中文.pdf",
|
|
"document_العربية.docx",
|
|
"data_émoji🎉.json"
|
|
]
|
|
for i, src in enumerate(sources):
|
|
manager.track_entity(entity_id=f"entity_{i}", source=src)
|
|
# Should handle mixed encodings
|
|
|
|
|
|
class TestBoundaryConditions:
|
|
"""Test boundary conditions."""
|
|
|
|
def test_zero_length_strings(self):
|
|
"""Test with zero-length strings."""
|
|
manager = ProvenanceManager()
|
|
try:
|
|
manager.track_entity(entity_id="", source="")
|
|
except ValueError:
|
|
pass # Expected
|
|
|
|
def test_maximum_string_length(self):
|
|
"""Test with maximum string lengths."""
|
|
manager = ProvenanceManager()
|
|
max_string = "x" * 100000
|
|
manager.track_entity(entity_id="test", source=max_string)
|
|
# Should handle very long strings
|
|
|
|
def test_negative_numbers_in_metadata(self):
|
|
"""Test negative numbers in metadata."""
|
|
manager = ProvenanceManager()
|
|
manager.track_entity(
|
|
entity_id="test",
|
|
source="test",
|
|
metadata={"confidence": -1.0, "count": -100}
|
|
)
|
|
# Should handle negative values
|
|
|
|
def test_infinity_in_metadata(self):
|
|
"""Test infinity values in metadata."""
|
|
manager = ProvenanceManager()
|
|
manager.track_entity(
|
|
entity_id="test",
|
|
source="test",
|
|
metadata={"value": float('inf')}
|
|
)
|
|
# Should handle infinity
|