"""Behavioural tests for the relationship deriver. A relationship claim asserts that a person matters to the user, supported by repeated co-occurrence in calendar evidence. """ from __future__ import annotations from datetime import timedelta from mnema.derive.derivers.relationships import RelationshipDeriver MEETING_TITLES = [ "Lunch", "Coffee", "1:1", "Walk", "Dinner", "Catch-up", "Project sync", "Movie night", "Brunch", "Climbing", "Call", "Planning", ] def _events_with(kit, name, count, start_offset_days=0): return [ kit.make_event( MEETING_TITLES[i % len(MEETING_TITLES)], (kit.T0 + timedelta(days=start_offset_days + 3 * i)).replace( hour=12 + (i % 3), minute=0 ), attendees=[name], ) for i in range(count) ] def _relationships_run(kit, evidence): engine = kit.new_engine([RelationshipDeriver()]) claims = kit.run_claims(engine, evidence) return engine, kit.by_predicate(claims, "relationship") def test_frequent_attendee_yields_relationship_claim(kit): events = _events_with(kit, "Sam Rivera", 6) _, rels = _relationships_run(kit, events) assert rels, "six meetings with the same person must yield a relationship claim" claim = kit.best(rels) assert "sam" in kit.vdump(claim) assert 0.0 < claim.confidence <= 1.0 def test_one_off_attendee_is_not_claimed(kit): events = _events_with(kit, "Casey Oneoff", 1) _, rels = _relationships_run(kit, events) assert rels == [], "a single shared event is not evidence of a relationship" def test_relationship_inputs_cite_only_relevant_evidence(kit): sam_events = _events_with(kit, "Sam Rivera", 6) noise_events = [ kit.make_event( "Solo focus block", (kit.T0 + timedelta(days=2 * i)).replace(hour=9, minute=0), ) for i in range(5) ] _, rels = _relationships_run(kit, sam_events + noise_events) sam_claims = [c for c in rels if "sam" in kit.vdump(c)] assert sam_claims claim = kit.best(sam_claims) sam_ids = {e.evidence_id for e in sam_events} cited = set(claim.inputs) assert cited, "relationship claim must carry provenance inputs" assert cited <= sam_ids, ( "a relationship claim about Sam must only cite events Sam attended, " f"but cited {cited - sam_ids}" ) assert len(cited) >= 3 def test_confidence_orders_by_contact_frequency(kit): sam_events = _events_with(kit, "Sam Rivera", 12) alex_events = _events_with(kit, "Alex Chen", 5, start_offset_days=1) _, rels = _relationships_run(kit, sam_events + alex_events) sam_claims = [c for c in rels if "sam" in kit.vdump(c)] alex_claims = [c for c in rels if "alex" in kit.vdump(c)] assert sam_claims, "12 meetings must produce a claim about Sam" assert alex_claims, "5 meetings must produce a claim about Alex" c_sam = kit.best(sam_claims).confidence c_alex = kit.best(alex_claims).confidence assert c_sam >= c_alex, ( f"the person seen far more often must not score lower: " f"sam={c_sam} alex={c_alex}" ) def test_relationship_claim_is_explainable(kit): events = _events_with(kit, "Sam Rivera", 6) engine, rels = _relationships_run(kit, events) claim = kit.best([c for c in rels if "sam" in kit.vdump(c)]) text = kit.explanation_text(engine, claim).lower() assert text, "every claim must have a retrievable explanation" assert "sam" in text, "the explanation should name the person it concerns"