Files
semantica/tests/provenance/test_backward_compat.py
T
KaifAhmad1 6391dcdf72 Add comprehensive W3C PROV-O compliant provenance tracking module
- Implemented provenance tracking across all 17 Semantica modules
- Added W3C PROV-O compliant schemas (prov:Entity, prov:Activity, prov:Agent, prov:wasDerivedFrom)
- Created ProvenanceManager with InMemory and SQLite storage backends
- Implemented SHA-256 integrity verification for tamper detection
- Added bridge axiom support for domain transformations (L1→L2→L3)
- Created provenance-enabled versions of all modules (opt-in with provenance=True)
- Added comprehensive test suite (237 tests covering edge cases and real scenarios)
- Updated README with accurate claims and compliance disclaimers
- Added complete documentation (usage guide and API reference)
- Zero breaking changes - fully backward compatible
2026-01-31 20:11:38 +05:30

253 lines
8.5 KiB
Python

"""
Backward Compatibility Tests
Critical tests to ensure all existing Semantica code works unchanged
with the new unified provenance module.
"""
import pytest
from semantica.kg import ProvenanceTracker as KGProvenanceTracker
from semantica.split import ProvenanceTracker as SplitProvenanceTracker
from semantica.split.semantic_chunker import Chunk
class TestKGProvenanceBackwardCompat:
"""Test kg.ProvenanceTracker backward compatibility."""
def test_existing_code_unchanged(self):
"""Test that existing kg.ProvenanceTracker code works unchanged."""
# Existing code pattern
tracker = KGProvenanceTracker()
# Track entity (existing API)
tracker.track_entity("entity_1", source="doc_1", metadata={"confidence": 0.9})
# Get lineage (existing API)
lineage = tracker.get_lineage("entity_1")
# Verify existing return format
assert "sources" in lineage
assert "first_seen" in lineage
assert "last_updated" in lineage
assert "metadata" in lineage
def test_track_relationship_unchanged(self):
"""Test relationship tracking works unchanged."""
tracker = KGProvenanceTracker()
tracker.track_relationship("rel_1", source="doc_1", metadata={"type": "founded"})
lineage = tracker.get_lineage("rel_1")
assert lineage is not None
def test_get_all_sources_unchanged(self):
"""Test get_all_sources returns expected format."""
tracker = KGProvenanceTracker()
tracker.track_entity("entity_1", source="doc_1")
tracker.track_entity("entity_1", source="doc_2")
sources = tracker.get_all_sources("entity_1")
assert isinstance(sources, list)
assert len(sources) >= 2
for source in sources:
assert "source" in source
assert "timestamp" in source
def test_batch_operations_unchanged(self):
"""Test batch operations work unchanged."""
tracker = KGProvenanceTracker()
entities = [
{"id": "entity_1", "confidence": 0.9},
{"id": "entity_2", "confidence": 0.85}
]
count = tracker.track_entities_batch(entities, "doc_1")
assert count == 2
assert tracker.get_lineage("entity_1") is not None
assert tracker.get_lineage("entity_2") is not None
class TestSplitProvenanceBackwardCompat:
"""Test split.ProvenanceTracker backward compatibility."""
def test_existing_code_unchanged(self):
"""Test that existing split.ProvenanceTracker code works unchanged."""
tracker = SplitProvenanceTracker()
# Create chunk
chunk = Chunk(
text="Test chunk",
start_index=0,
end_index=10,
metadata={}
)
# Track chunk (existing API)
prov_id = tracker.track_chunk(
chunk,
source_document="doc_1",
source_path="/path/to/doc.pdf"
)
assert prov_id is not None
def test_get_provenance_unchanged(self):
"""Test get_provenance returns ProvenanceInfo."""
tracker = SplitProvenanceTracker()
chunk = Chunk(text="Test", start_index=0, end_index=4, metadata={})
chunk.id = "chunk_1"
tracker.track_chunk(chunk, source_document="doc_1")
prov = tracker.get_provenance("chunk_1")
# Verify ProvenanceInfo format
assert prov is not None
assert hasattr(prov, "chunk_id")
assert hasattr(prov, "source_document")
assert hasattr(prov, "start_index")
assert hasattr(prov, "end_index")
def test_get_chunk_lineage_unchanged(self):
"""Test get_chunk_lineage returns list of ProvenanceInfo."""
tracker = SplitProvenanceTracker()
chunk1 = Chunk(text="Test1", start_index=0, end_index=5, metadata={})
chunk1.id = "chunk_1"
chunk2 = Chunk(text="Test2", start_index=5, end_index=10, metadata={})
chunk2.id = "chunk_2"
tracker.track_chunk(chunk1, source_document="doc_1")
tracker.track_chunk(chunk2, source_document="doc_1", parent_chunk_id="chunk_1")
lineage = tracker.get_chunk_lineage("chunk_2")
assert isinstance(lineage, list)
assert len(lineage) >= 1
for prov in lineage:
assert hasattr(prov, "chunk_id")
assert hasattr(prov, "source_document")
def test_batch_tracking_unchanged(self):
"""Test batch chunk tracking works unchanged."""
tracker = SplitProvenanceTracker()
chunks = [
Chunk(text=f"Chunk {i}", start_index=i*10, end_index=(i+1)*10, metadata={})
for i in range(3)
]
prov_ids = tracker.track_chunks(chunks, source_document="doc_1")
assert len(prov_ids) == 3
class TestNoProvenanceOverhead:
"""Test that provenance has zero overhead when not used."""
def test_kg_tracker_no_overhead(self):
"""Test kg.ProvenanceTracker has no overhead."""
import time
# Measure without provenance
tracker = KGProvenanceTracker()
start = time.time()
for i in range(100):
tracker.track_entity(f"entity_{i}", source="doc_1")
elapsed = time.time() - start
# Should complete quickly (< 1 second for 100 entities)
assert elapsed < 1.0
def test_split_tracker_no_overhead(self):
"""Test split.ProvenanceTracker has no overhead."""
import time
tracker = SplitProvenanceTracker()
start = time.time()
for i in range(100):
chunk = Chunk(
text=f"Chunk {i}",
start_index=i*10,
end_index=(i+1)*10,
metadata={}
)
tracker.track_chunk(chunk, source_document="doc_1")
elapsed = time.time() - start
# Should complete quickly
assert elapsed < 1.0
class TestGracefulDegradation:
"""Test graceful degradation when unified backend fails."""
def test_kg_tracker_fallback(self):
"""Test kg.ProvenanceTracker falls back to legacy on error."""
tracker = KGProvenanceTracker()
# Should work even if unified backend has issues
tracker.track_entity("entity_1", source="doc_1")
lineage = tracker.get_lineage("entity_1")
assert lineage is not None
assert "sources" in lineage
def test_split_tracker_fallback(self):
"""Test split.ProvenanceTracker falls back to legacy on error."""
tracker = SplitProvenanceTracker()
chunk = Chunk(text="Test", start_index=0, end_index=4, metadata={})
# Should work even if unified backend has issues
prov_id = tracker.track_chunk(chunk, source_document="doc_1")
assert prov_id is not None
class TestExistingTestsPass:
"""Verify that all existing Semantica tests still pass."""
def test_kg_provenance_existing_behavior(self):
"""Test existing kg.ProvenanceTracker behavior is preserved."""
tracker = KGProvenanceTracker()
# Test 1: Basic tracking
tracker.track_entity("e1", "src1")
assert tracker.get_provenance("e1") is not None
# Test 2: Multiple sources
tracker.track_entity("e1", "src2")
sources = tracker.get_all_sources("e1")
assert len(sources) >= 2
# Test 3: Metadata
tracker.track_entity("e2", "src1", metadata={"key": "value"})
lineage = tracker.get_lineage("e2")
assert "metadata" in lineage
def test_split_provenance_existing_behavior(self):
"""Test existing split.ProvenanceTracker behavior is preserved."""
tracker = SplitProvenanceTracker()
# Test 1: Basic tracking
chunk = Chunk(text="Test", start_index=0, end_index=4, metadata={})
chunk.id = "c1"
tracker.track_chunk(chunk, "doc1")
assert tracker.get_provenance("c1") is not None
# Test 2: Parent-child relationship
chunk2 = Chunk(text="Test2", start_index=4, end_index=9, metadata={})
chunk2.id = "c2"
tracker.track_chunk(chunk2, "doc1", parent_chunk_id="c1")
lineage = tracker.get_chunk_lineage("c2")
assert len(lineage) >= 1