@@ -36,3 +36,119 @@ def test_memory_add_query_update_delete(tmp_path: Path) -> None:
3636 store .delete_memory (mem_id )
3737 items3 = store .query_memory (subject_type = "user" , subject_id = "u1" , limit = 10 )
3838 assert items3 == []
39+
40+
41+ def test_facts_upsert_and_conflict_policy (tmp_path : Path ) -> None :
42+ db = tmp_path / "memory.sqlite3"
43+ store = MemoryStore (db )
44+
45+ fid1 = store .upsert_fact (
46+ subject_type = "user" ,
47+ subject_id = "u1" ,
48+ key = "preferred_name" ,
49+ value = "Alfredo" ,
50+ confidence = 0.9 ,
51+ source = "explicit" ,
52+ evidence = "запомни, меня зовут Альфредо" ,
53+ tags = ["identity" ],
54+ person_ref = "user:u1" ,
55+ )
56+ assert fid1 > 0
57+
58+ # same value -> should keep same row (id stable)
59+ fid2 = store .upsert_fact (
60+ subject_type = "user" ,
61+ subject_id = "u1" ,
62+ key = "preferred_name" ,
63+ value = "Alfredo" ,
64+ confidence = 0.95 ,
65+ source = "auto" ,
66+ evidence = "(repeat)" ,
67+ tags = ["identity" , "repeat" ],
68+ person_ref = "user:u1" ,
69+ )
70+ assert fid2 == fid1
71+
72+ f = store .get_fact (subject_type = "user" , subject_id = "u1" , key = "preferred_name" )
73+ assert f is not None
74+ assert f .value == "Alfredo"
75+ assert f .confidence >= 0.95
76+ assert "identity" in f .tags
77+
78+ # lower priority overwrite should be blocked
79+ store .upsert_fact (
80+ subject_type = "user" ,
81+ subject_id = "u1" ,
82+ key = "preferred_name" ,
83+ value = "NotAlfredo" ,
84+ confidence = 0.99 ,
85+ source = "auto" ,
86+ evidence = "call me NotAlfredo" ,
87+ )
88+ f2 = store .get_fact (subject_type = "user" , subject_id = "u1" , key = "preferred_name" )
89+ assert f2 is not None
90+ assert f2 .value == "Alfredo"
91+
92+ # higher/equal priority overwrite allowed
93+ store .upsert_fact (
94+ subject_type = "user" ,
95+ subject_id = "u1" ,
96+ key = "preferred_name" ,
97+ value = "Alfredo The Second" ,
98+ confidence = 0.9 ,
99+ source = "explicit" ,
100+ evidence = "зови меня Alfredo The Second" ,
101+ )
102+ f3 = store .get_fact (subject_type = "user" , subject_id = "u1" , key = "preferred_name" )
103+ assert f3 is not None
104+ assert f3 .value == "Alfredo The Second"
105+
106+
107+ def test_facts_set_add_remove_values (tmp_path : Path ) -> None :
108+ db = tmp_path / "memory.sqlite3"
109+ store = MemoryStore (db )
110+
111+ store .add_fact_value (
112+ subject_type = "user" ,
113+ subject_id = "u1" ,
114+ key = "likes" ,
115+ value = "tea" ,
116+ source = "explicit" ,
117+ evidence = "запомни: я люблю чай" ,
118+ )
119+ store .add_fact_value (
120+ subject_type = "user" ,
121+ subject_id = "u1" ,
122+ key = "likes" ,
123+ value = "coffee" ,
124+ source = "explicit" ,
125+ evidence = "и кофе тоже" ,
126+ )
127+
128+ f = store .get_fact (subject_type = "user" , subject_id = "u1" , key = "likes" )
129+ assert f is not None
130+ assert isinstance (f .value , list )
131+ assert set (f .value ) == {"tea" , "coffee" }
132+
133+ store .remove_fact_value (
134+ subject_type = "user" ,
135+ subject_id = "u1" ,
136+ key = "likes" ,
137+ value = "tea" ,
138+ source = "explicit" ,
139+ evidence = "я больше не люблю чай" ,
140+ )
141+ f2 = store .get_fact (subject_type = "user" , subject_id = "u1" , key = "likes" )
142+ assert f2 is not None
143+ assert set (f2 .value ) == {"coffee" }
144+
145+ store .remove_fact_value (
146+ subject_type = "user" ,
147+ subject_id = "u1" ,
148+ key = "likes" ,
149+ value = "coffee" ,
150+ source = "explicit" ,
151+ evidence = "и кофе тоже больше не люблю" ,
152+ )
153+ f3 = store .get_fact (subject_type = "user" , subject_id = "u1" , key = "likes" )
154+ assert f3 is None
0 commit comments