"""Behavioural tests for the routine deriver. A routine is a recurring temporal pattern in the user's calendar evidence: same activity, same weekday, roughly the same time, over several weeks. """ from __future__ import annotations from datetime import timedelta from mnema.derive.derivers.routines import RoutineDeriver def _routines_run(kit, evidence): engine = kit.new_engine([RoutineDeriver()]) claims = kit.run_claims(engine, evidence) return engine, kit.by_predicate(claims, "routine") def test_weekly_event_yields_routine_claim(kit): events = kit.weekly_events( "Yoga class", weekday=1, hour=18, count=6, location="Riverside Yoga Studio" ) _, routines = _routines_run(kit, events) assert routines, "six identical weekly events must yield a routine claim" claim = kit.best(routines) assert "yoga" in kit.vdump(claim) assert isinstance(claim.value, dict) and claim.value assert 0.0 < claim.confidence <= 1.0 def test_routine_claim_cites_supporting_events(kit): events = kit.weekly_events("Yoga class", weekday=1, hour=18, count=6) _, routines = _routines_run(kit, events) claim = kit.best(routines) event_ids = {e.evidence_id for e in events} cited = set(claim.inputs) assert cited, "routine claim must carry provenance inputs" assert cited <= event_ids, ( "a routines-only run can only cite the supplied calendar evidence, " f"but cited {cited - event_ids}" ) assert len(cited) >= 3, "a routine should cite several supporting occurrences" def test_single_event_is_not_a_routine(kit): events = kit.weekly_events("Yoga class", weekday=1, hour=18, count=1) _, routines = _routines_run(kit, events) assert routines == [], "one occurrence is not a routine" def test_irregular_events_do_not_form_a_routine(kit): # Same title, but aperiodic days and wandering times of day. day_offsets = [0, 4, 9, 17, 26] hours = [9, 13, 16, 8, 11] events = [ kit.make_event( "Dentist follow-up", (kit.T0 + timedelta(days=d)).replace(hour=h, minute=0), ) for d, h in zip(day_offsets, hours) ] _, routines = _routines_run(kit, events) assert routines == [], "aperiodic events at scattered times must not become a routine" def test_more_occurrences_do_not_lower_confidence(kit): _, few = _routines_run( kit, kit.weekly_events("Morning run", weekday=3, hour=7, count=3) ) _, many = _routines_run( kit, kit.weekly_events("Morning run", weekday=3, hour=7, count=10) ) assert few and many c_few = kit.best(few).confidence c_many = kit.best(many).confidence assert 0.0 < c_few <= 1.0 assert 0.0 < c_many <= 1.0 assert c_many >= c_few, ( f"confidence must be monotone in supporting evidence: " f"{c_many} (10 weeks) < {c_few} (3 weeks)" ) def test_routine_claim_is_explainable(kit): events = kit.weekly_events("Yoga class", weekday=1, hour=18, count=6) engine, routines = _routines_run(kit, events) claim = kit.best(routines) text = kit.explanation_text(engine, claim).lower() assert text, "every claim must have a retrievable explanation" assert "yoga" in text, "the explanation should name the routine it explains"