"""Behavioural tests for the preference deriver. Preference claims capture likes and dislikes expressed in the user's notes, with polarity (positive/negative) surfaced in the predicate or value. """ from __future__ import annotations from datetime import timedelta from mnema.derive.derivers.preferences import PreferenceDeriver def _preferences_run(kit, evidence): engine = kit.new_engine([PreferenceDeriver()]) claims = kit.run_claims(engine, evidence) return engine, kit.by_predicate(claims, "preference") def _espresso_notes(kit, count): texts = [ "I love espresso. Best part of my morning.", "That double espresso at the new cafe was fantastic, I love it.", "Espresso again today -- I really love how it kicks off the day.", "My favorite drink is still a straight espresso.", "Great espresso at the market stall. Love that place's roast.", ] return [ kit.make_note(texts[i % len(texts)], kit.T0 + timedelta(days=i)) for i in range(count) ] def test_positive_note_yields_liking_claim(kit): notes = _espresso_notes(kit, 2) _, prefs = _preferences_run(kit, notes) espresso = [c for c in prefs if "espresso" in kit.vdump(c)] assert espresso, "repeated positive notes about espresso must yield a preference claim" claim = kit.best(espresso) assert kit.polarity(claim) == "positive", ( f"polarity must be recognisably positive; predicate={claim.predicate!r} " f"value={claim.value!r}" ) assert 0.0 < claim.confidence <= 1.0 def test_negative_note_yields_disliking_claim(kit): notes = [ kit.make_note( "I really hate cilantro. Ask them to leave it out.", kit.T0 ), kit.make_note( "Soup ruined by cilantro again. I hate that herb.", kit.T0 + timedelta(days=4), ), ] _, prefs = _preferences_run(kit, notes) cilantro = [c for c in prefs if "cilantro" in kit.vdump(c)] assert cilantro, "repeated negative notes about cilantro must yield a preference claim" claim = kit.best(cilantro) assert kit.polarity(claim) == "negative", ( f"polarity must be recognisably negative; predicate={claim.predicate!r} " f"value={claim.value!r}" ) def test_neutral_notes_yield_no_preference(kit): notes = [ kit.make_note("Picked up the dry cleaning and groceries.", kit.T0), kit.make_note( "Meeting moved to Thursday; remember to send the slides.", kit.T0 + timedelta(days=1), ), ] _, prefs = _preferences_run(kit, notes) assert prefs == [], "neutral logistics notes must not produce preference claims" def test_repetition_strengthens_preference_confidence(kit): _, few = _preferences_run(kit, _espresso_notes(kit, 2)) _, many = _preferences_run(kit, _espresso_notes(kit, 5)) few_claims = [c for c in few if "espresso" in kit.vdump(c)] many_claims = [c for c in many if "espresso" in kit.vdump(c)] assert few_claims and many_claims c_few = kit.best(few_claims).confidence c_many = kit.best(many_claims).confidence assert c_many >= c_few, ( f"confidence must be monotone in supporting mentions: " f"{c_many} (5 notes) < {c_few} (2 notes)" ) def test_preference_claim_cites_its_notes(kit): notes = _espresso_notes(kit, 3) noise = [kit.make_note("Renewed the car insurance.", kit.T0 + timedelta(days=9))] _, prefs = _preferences_run(kit, notes + noise) espresso = [c for c in prefs if "espresso" in kit.vdump(c)] assert espresso claim = kit.best(espresso) note_ids = {n.evidence_id for n in notes} cited = set(claim.inputs) assert cited, "preference claim must carry provenance inputs" assert cited <= note_ids, ( "an espresso preference must only cite notes mentioning espresso, " f"but cited {cited - note_ids}" )