| """
|
| Tests for Phase 5.0 Confidence Calibration Module.
|
| Tests ConfidenceEnvelopeGenerator for all confidence levels and edge cases.
|
| """
|
|
|
| import pytest
|
| from datetime import datetime, timezone, timedelta
|
| from unittest.mock import MagicMock
|
|
|
| from mnemocore.core.confidence import (
|
| ConfidenceEnvelopeGenerator,
|
| build_confidence_envelope,
|
| LEVEL_HIGH,
|
| LEVEL_MEDIUM,
|
| LEVEL_LOW,
|
| LEVEL_CONTRADICTED,
|
| LEVEL_STALE,
|
| )
|
| from mnemocore.core.provenance import ProvenanceRecord
|
|
|
|
|
|
|
|
|
|
|
|
|
| def _make_node(
|
| ltp_strength: float = 0.9,
|
| access_count: int = 10,
|
| days_old: float = 5.0,
|
| bayes_mean: float | None = None,
|
| ):
|
| """Create a minimal mock MemoryNode."""
|
| node = MagicMock()
|
| node.ltp_strength = ltp_strength
|
| node.access_count = access_count
|
| now = datetime.now(timezone.utc)
|
| node.last_accessed = now - timedelta(days=days_old)
|
|
|
| if bayes_mean is not None:
|
| bayes = MagicMock()
|
| bayes.mean = bayes_mean
|
| node._bayes = bayes
|
| else:
|
|
|
| del node._bayes
|
| return node
|
|
|
|
|
|
|
|
|
|
|
|
|
| class TestConfidenceEnvelopeGenerator:
|
| def test_high_confidence(self):
|
| node = _make_node(ltp_strength=0.92, access_count=8, days_old=3)
|
| prov = ProvenanceRecord.new(origin_type="observation")
|
| env = ConfidenceEnvelopeGenerator.build(node, prov)
|
| assert env["level"] == LEVEL_HIGH
|
| assert env["reliability"] >= 0.80
|
|
|
| def test_medium_confidence_low_reliability(self):
|
| node = _make_node(ltp_strength=0.65, access_count=3, days_old=5)
|
| env = ConfidenceEnvelopeGenerator.build(node)
|
| assert env["level"] == LEVEL_MEDIUM
|
|
|
| def test_low_confidence_insufficient_access(self):
|
| node = _make_node(ltp_strength=0.75, access_count=1, days_old=2)
|
| env = ConfidenceEnvelopeGenerator.build(node)
|
| assert env["level"] == LEVEL_LOW
|
|
|
| def test_low_confidence_poor_reliability(self):
|
| node = _make_node(ltp_strength=0.35, access_count=10, days_old=2)
|
| env = ConfidenceEnvelopeGenerator.build(node)
|
| assert env["level"] == LEVEL_LOW
|
|
|
| def test_stale_overrides_high_reliability(self):
|
| """A memory last verified 40 days ago should be STALE even if reliable."""
|
| node = _make_node(ltp_strength=0.98, access_count=20, days_old=40)
|
| env = ConfidenceEnvelopeGenerator.build(node)
|
| assert env["level"] == LEVEL_STALE
|
| assert env["staleness_days"] >= 30
|
|
|
| def test_contradicted_overrides_everything(self):
|
| """Contradicted memories take priority over any reliability score."""
|
| node = _make_node(ltp_strength=0.99, access_count=100, days_old=1)
|
| prov = ProvenanceRecord.new(origin_type="observation")
|
| prov.mark_contradicted("cg_999")
|
| env = ConfidenceEnvelopeGenerator.build(node, prov)
|
| assert env["level"] == LEVEL_CONTRADICTED
|
| assert env["is_contradicted"] is True
|
|
|
| def test_source_type_observation(self):
|
| node = _make_node(ltp_strength=0.9, access_count=6)
|
| prov = ProvenanceRecord.new(origin_type="observation")
|
| env = ConfidenceEnvelopeGenerator.build(node, prov)
|
| assert env["source_type"] == "observation"
|
| assert env["source_trust"] == 1.0
|
|
|
| def test_source_type_dream_lower_trust(self):
|
| node = _make_node(ltp_strength=0.9, access_count=6)
|
| prov = ProvenanceRecord.new(origin_type="dream")
|
| env = ConfidenceEnvelopeGenerator.build(node, prov)
|
| assert env["source_type"] == "dream"
|
|
|
| assert env["level"] != LEVEL_HIGH
|
|
|
| def test_verified_event_resets_staleness(self):
|
| """A fresh verification event should make staleness very short."""
|
| node = _make_node(ltp_strength=0.9, access_count=8, days_old=50)
|
| prov = ProvenanceRecord.new(origin_type="observation")
|
| prov.mark_verified(success=True)
|
| env = ConfidenceEnvelopeGenerator.build(node, prov)
|
|
|
| assert env["staleness_days"] < 1.0
|
| assert env["level"] != LEVEL_STALE
|
|
|
| def test_bayesian_state_used_over_ltp(self):
|
| """If node has _bayes, reliability = bayes.mean, not ltp_strength."""
|
| node = _make_node(ltp_strength=0.3, access_count=6, bayes_mean=0.95)
|
| env = ConfidenceEnvelopeGenerator.build(node)
|
| assert env["reliability"] == pytest.approx(0.95, abs=0.01)
|
|
|
| def test_no_provenance_uses_last_accessed(self):
|
| node = _make_node(ltp_strength=0.85, access_count=7, days_old=5)
|
| env = ConfidenceEnvelopeGenerator.build(node, provenance=None)
|
| assert env["source_type"] == "unknown"
|
| assert "level" in env
|
|
|
| def test_envelope_keys_present(self):
|
| node = _make_node()
|
| env = build_confidence_envelope(node)
|
| expected_keys = {
|
| "level", "reliability", "access_count",
|
| "staleness_days", "source_type", "source_trust", "is_contradicted",
|
| }
|
| assert expected_keys.issubset(env.keys())
|
|
|
| def test_module_shortcut_same_result(self):
|
| node = _make_node(ltp_strength=0.88, access_count=6)
|
| prov = ProvenanceRecord.new()
|
| r1 = ConfidenceEnvelopeGenerator.build(node, prov)
|
| r2 = build_confidence_envelope(node, prov)
|
| assert r1["level"] == r2["level"]
|
| assert r1["reliability"] == r2["reliability"]
|
|
|