Spiritual_Health_Project / tests /verification_mode /test_properties_persistence.py
DocUA's picture
Add per-session prompt override functionality across interfaces and AI client
2e0e95f
# test_properties_persistence.py
"""
Property-based tests for verification data persistence.
Tests that verification records and sessions persist correctly.
"""
import pytest
from hypothesis import given, strategies as st, settings, HealthCheck
from datetime import datetime
from src.core.verification_models import (
VerificationRecord,
VerificationSession,
)
from src.core.verification_store import JSONVerificationStore
# Strategies for generating test data
def valid_id_strategy():
"""Generate valid IDs for use as filenames."""
return st.text(
alphabet="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-",
min_size=1,
max_size=20,
)
def verification_record_strategy():
"""Generate random verification records with consistent is_correct field."""
# Generate classifier_decision and ground_truth_label together to ensure is_correct is consistent
@st.composite
def build_record(draw):
message_id = draw(valid_id_strategy())
original_message = draw(st.text(min_size=1, max_size=500))
classifier_decision = draw(st.sampled_from(["green", "yellow", "red"]))
classifier_confidence = draw(st.floats(min_value=0.0, max_value=1.0))
classifier_indicators = draw(st.lists(st.text(min_size=1, max_size=50), max_size=5))
verifier_notes = draw(st.text(max_size=200))
# Decide if this should be correct or incorrect
is_correct = draw(st.booleans())
# Set ground_truth_label based on is_correct
if is_correct:
ground_truth_label = classifier_decision
else:
# Pick a different label
other_labels = [l for l in ["green", "yellow", "red"] if l != classifier_decision]
ground_truth_label = draw(st.sampled_from(other_labels))
return VerificationRecord(
message_id=message_id,
original_message=original_message,
classifier_decision=classifier_decision,
classifier_confidence=classifier_confidence,
classifier_indicators=classifier_indicators,
ground_truth_label=ground_truth_label,
verifier_notes=verifier_notes,
is_correct=is_correct,
timestamp=datetime.now(),
)
return build_record()
def verification_session_strategy():
"""Generate random verification sessions."""
return st.builds(
VerificationSession,
session_id=valid_id_strategy(),
verifier_name=st.text(min_size=1, max_size=50),
dataset_id=valid_id_strategy(),
dataset_name=st.text(min_size=1, max_size=100),
created_at=st.just(datetime.now()),
completed_at=st.none(),
total_messages=st.integers(min_value=1, max_value=100),
verified_count=st.integers(min_value=0, max_value=100),
correct_count=st.integers(min_value=0, max_value=100),
incorrect_count=st.integers(min_value=0, max_value=100),
verifications=st.just([]),
is_complete=st.booleans(),
)
class TestVerificationRecordPersistence:
"""
**Feature: verification-mode, Property 1: Feedback Saves Correctly**
Tests that verification records save and load correctly with all fields intact.
"""
@given(verification_record_strategy())
@settings(suppress_health_check=[HealthCheck.function_scoped_fixture])
def test_record_saves_and_loads_correctly(self, verification_store, record):
"""
**Feature: verification-mode, Property 1: Feedback Saves Correctly**
**Validates: Requirements 3.2, 3.5, 8.1**
For any verification record, when saved to storage and then loaded,
all fields should be preserved exactly.
"""
# Create a session to hold the record
session = VerificationSession(
session_id="test_session",
verifier_name="Test Verifier",
dataset_id="test_dataset",
dataset_name="Test Dataset",
total_messages=1,
)
verification_store.save_session(session)
# Save the verification record
verification_store.save_verification("test_session", record)
# Load the session and verify the record
loaded_session = verification_store.load_session("test_session")
assert loaded_session is not None
assert len(loaded_session.verifications) == 1
loaded_record = loaded_session.verifications[0]
# Verify all fields are preserved
assert loaded_record.message_id == record.message_id
assert loaded_record.original_message == record.original_message
assert loaded_record.classifier_decision == record.classifier_decision
assert loaded_record.classifier_confidence == record.classifier_confidence
assert loaded_record.classifier_indicators == record.classifier_indicators
assert loaded_record.ground_truth_label == record.ground_truth_label
assert loaded_record.verifier_notes == record.verifier_notes
assert loaded_record.is_correct == record.is_correct
@given(verification_record_strategy())
def test_record_to_dict_and_back(self, record):
"""
**Feature: verification-mode, Property 1: Feedback Saves Correctly**
**Validates: Requirements 3.2, 3.5, 8.1**
For any verification record, converting to dict and back should
preserve all fields.
"""
# Convert to dict and back
record_dict = record.to_dict()
restored_record = VerificationRecord.from_dict(record_dict)
# Verify all fields match
assert restored_record.message_id == record.message_id
assert restored_record.original_message == record.original_message
assert restored_record.classifier_decision == record.classifier_decision
assert restored_record.classifier_confidence == record.classifier_confidence
assert restored_record.classifier_indicators == record.classifier_indicators
assert restored_record.ground_truth_label == record.ground_truth_label
assert restored_record.verifier_notes == record.verifier_notes
assert restored_record.is_correct == record.is_correct
class TestSessionStatePersistence:
"""
**Feature: verification-mode, Property 3: Session State Persists**
Tests that verification sessions persist and can be resumed with state intact.
"""
@given(verification_session_strategy())
@settings(suppress_health_check=[HealthCheck.function_scoped_fixture])
def test_session_saves_and_loads_correctly(self, verification_store, session):
"""
**Feature: verification-mode, Property 3: Session State Persists**
**Validates: Requirements 8.2, 8.3**
For any verification session, when saved and then loaded,
all session state should be preserved exactly.
"""
# Save the session
verification_store.save_session(session)
# Load the session
loaded_session = verification_store.load_session(session.session_id)
# Verify all fields are preserved
assert loaded_session is not None
assert loaded_session.session_id == session.session_id
assert loaded_session.verifier_name == session.verifier_name
assert loaded_session.dataset_id == session.dataset_id
assert loaded_session.dataset_name == session.dataset_name
assert loaded_session.total_messages == session.total_messages
assert loaded_session.verified_count == session.verified_count
assert loaded_session.correct_count == session.correct_count
assert loaded_session.incorrect_count == session.incorrect_count
assert loaded_session.is_complete == session.is_complete
@given(verification_session_strategy())
def test_session_to_dict_and_back(self, session):
"""
**Feature: verification-mode, Property 3: Session State Persists**
**Validates: Requirements 8.2, 8.3**
For any verification session, converting to dict and back should
preserve all session state.
"""
# Convert to dict and back
session_dict = session.to_dict()
restored_session = VerificationSession.from_dict(session_dict)
# Verify all fields match
assert restored_session.session_id == session.session_id
assert restored_session.verifier_name == session.verifier_name
assert restored_session.dataset_id == session.dataset_id
assert restored_session.dataset_name == session.dataset_name
assert restored_session.total_messages == session.total_messages
assert restored_session.verified_count == session.verified_count
assert restored_session.correct_count == session.correct_count
assert restored_session.incorrect_count == session.incorrect_count
assert restored_session.is_complete == session.is_complete
@given(verification_session_strategy())
@settings(suppress_health_check=[HealthCheck.function_scoped_fixture], deadline=None)
def test_session_with_multiple_records_persists(
self, verification_store, session
):
"""
**Feature: verification-mode, Property 3: Session State Persists**
**Validates: Requirements 8.2, 8.3**
For any session with multiple verification records, when saved and loaded,
all records and session state should be preserved.
"""
# Ensure session is not already marked complete
session.is_complete = False
session.completed_at = None
# Generate records with unique message IDs
records = []
for i in range(5):
record = VerificationRecord(
message_id=f"msg_{i}",
original_message=f"Test message {i}",
classifier_decision="green",
classifier_confidence=0.9,
classifier_indicators=["test"],
ground_truth_label="green",
verifier_notes="",
is_correct=True,
timestamp=datetime.now(),
)
records.append(record)
# Save the session
verification_store.save_session(session)
# Add records to the session
for record in records:
verification_store.save_verification(session.session_id, record)
# Load the session
loaded_session = verification_store.load_session(session.session_id)
# Verify session state
assert loaded_session is not None
assert loaded_session.session_id == session.session_id
assert len(loaded_session.verifications) == len(records)
# Verify all records are preserved
for i, original_record in enumerate(records):
loaded_record = loaded_session.verifications[i]
assert loaded_record.message_id == original_record.message_id
assert loaded_record.original_message == original_record.original_message
assert (
loaded_record.classifier_decision
== original_record.classifier_decision
)
class TestCompletedSessionImmutability:
"""
**Feature: verification-mode, Property 13: Completed Sessions Cannot be Modified**
Tests that completed sessions cannot be modified after completion.
"""
@given(verification_session_strategy(), verification_record_strategy())
@settings(suppress_health_check=[HealthCheck.function_scoped_fixture])
def test_completed_session_cannot_be_modified(
self, verification_store, session, record
):
"""
**Feature: verification-mode, Property 13: Completed Sessions Cannot be Modified**
**Validates: Requirements 8.4**
For any completed verification session, attempting to add new verifications
should raise an error and the session should remain unchanged.
"""
# Save the session
verification_store.save_session(session)
# Mark session as complete
verification_store.mark_session_complete(session.session_id)
# Verify session is marked complete
loaded_session = verification_store.load_session(session.session_id)
assert loaded_session.is_complete is True
assert loaded_session.completed_at is not None
# Attempt to add a verification record to completed session
with pytest.raises(ValueError, match="Cannot modify completed session"):
verification_store.save_verification(session.session_id, record)
@given(verification_session_strategy())
@settings(suppress_health_check=[HealthCheck.function_scoped_fixture])
def test_can_modify_session_returns_false_for_completed(
self, verification_store, session
):
"""
**Feature: verification-mode, Property 13: Completed Sessions Cannot be Modified**
**Validates: Requirements 8.4**
For any completed session, can_modify_session should return False.
"""
# Ensure session is not already marked complete
session.is_complete = False
session.completed_at = None
# Save the session
verification_store.save_session(session)
# Initially should be modifiable
assert verification_store.can_modify_session(session.session_id) is True
# Mark session as complete
verification_store.mark_session_complete(session.session_id)
# Now should not be modifiable
assert verification_store.can_modify_session(session.session_id) is False
@given(verification_session_strategy())
@settings(suppress_health_check=[HealthCheck.function_scoped_fixture])
def test_completed_session_persists_completion_state(
self, verification_store, session
):
"""
**Feature: verification-mode, Property 13: Completed Sessions Cannot be Modified**
**Validates: Requirements 8.4**
For any completed session, when saved and reloaded, the completion state
should be preserved.
"""
# Save the session
verification_store.save_session(session)
# Mark session as complete
verification_store.mark_session_complete(session.session_id)
# Load the session
loaded_session = verification_store.load_session(session.session_id)
# Verify completion state is preserved
assert loaded_session.is_complete is True
assert loaded_session.completed_at is not None
# Verify it still cannot be modified
assert verification_store.can_modify_session(session.session_id) is False