Skip to content

Commit 4be9d5b

Browse files
committed
feat: add update validation tests
1 parent 94ba954 commit 4be9d5b

6 files changed

Lines changed: 563 additions & 6 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""Shared fixture wiring for update validation integration tests."""
2+
3+
from tests.operations._shared.integration_db_fixtures import * # noqa: F401,F403
Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
1+
"""Integrity contracts for update-path validation."""
2+
3+
from collections.abc import Callable
4+
5+
from app.core.contracts.requests import MemoryUpdateRequest
6+
from app.core.entities.memory import MemoryKind, MemoryScope
7+
from app.core.use_cases.update_memory import execute_update_memory
8+
from app.periphery.db.uow import PostgresUnitOfWork
9+
10+
11+
def test_update_requires_visible_target_memory(
12+
uow_factory: Callable[[], PostgresUnitOfWork],
13+
seed_memory: Callable[..., object],
14+
) -> None:
15+
"""update requests should always require memory_id to reference a visible memory."""
16+
17+
missing_request = _make_update_request(
18+
repo_id="repo-a",
19+
memory_id="missing-memory",
20+
update={"type": "archive_state", "archived": True},
21+
)
22+
23+
with uow_factory() as uow:
24+
missing_result = execute_update_memory(missing_request, uow)
25+
26+
assert missing_result.status == "error"
27+
assert any(error.code.value == "not_found" and error.field == "memory_id" for error in missing_result.errors)
28+
29+
seed_memory(
30+
memory_id="hidden-memory",
31+
repo_id="repo-b",
32+
scope=MemoryScope.REPO,
33+
kind=MemoryKind.FACT,
34+
text_value="Hidden target memory.",
35+
)
36+
hidden_request = _make_update_request(
37+
repo_id="repo-a",
38+
memory_id="hidden-memory",
39+
update={"type": "archive_state", "archived": True},
40+
)
41+
42+
with uow_factory() as uow:
43+
hidden_result = execute_update_memory(hidden_request, uow)
44+
45+
assert hidden_result.status == "error"
46+
assert any(
47+
error.code.value == "integrity_error" and error.field == "memory_id" for error in hidden_result.errors
48+
)
49+
50+
51+
def test_update_utility_vote_requires_visible_problem_memory(
52+
uow_factory: Callable[[], PostgresUnitOfWork],
53+
seed_memory: Callable[..., object],
54+
) -> None:
55+
"""utility_vote updates should always require problem_id to reference a visible problem memory."""
56+
57+
seed_memory(
58+
memory_id="target-memory",
59+
repo_id="repo-a",
60+
scope=MemoryScope.REPO,
61+
kind=MemoryKind.FACT,
62+
text_value="Target memory.",
63+
)
64+
65+
missing_problem_request = _make_update_request(
66+
repo_id="repo-a",
67+
memory_id="target-memory",
68+
update={
69+
"type": "utility_vote",
70+
"problem_id": "problem-missing",
71+
"vote": 0.7,
72+
},
73+
)
74+
with uow_factory() as uow:
75+
missing_problem_result = execute_update_memory(missing_problem_request, uow)
76+
77+
assert missing_problem_result.status == "error"
78+
assert any(
79+
error.code.value == "not_found" and error.field == "update.problem_id"
80+
for error in missing_problem_result.errors
81+
)
82+
83+
seed_memory(
84+
memory_id="problem-hidden",
85+
repo_id="repo-b",
86+
scope=MemoryScope.REPO,
87+
kind=MemoryKind.PROBLEM,
88+
text_value="Hidden problem memory.",
89+
)
90+
hidden_problem_request = _make_update_request(
91+
repo_id="repo-a",
92+
memory_id="target-memory",
93+
update={
94+
"type": "utility_vote",
95+
"problem_id": "problem-hidden",
96+
"vote": 0.7,
97+
},
98+
)
99+
with uow_factory() as uow:
100+
hidden_problem_result = execute_update_memory(hidden_problem_request, uow)
101+
102+
assert hidden_problem_result.status == "error"
103+
assert any(
104+
error.code.value == "integrity_error" and error.field == "update.problem_id"
105+
for error in hidden_problem_result.errors
106+
)
107+
108+
seed_memory(
109+
memory_id="not-a-problem",
110+
repo_id="repo-a",
111+
scope=MemoryScope.REPO,
112+
kind=MemoryKind.FACT,
113+
text_value="Wrong kind.",
114+
)
115+
wrong_kind_request = _make_update_request(
116+
repo_id="repo-a",
117+
memory_id="target-memory",
118+
update={
119+
"type": "utility_vote",
120+
"problem_id": "not-a-problem",
121+
"vote": 0.7,
122+
},
123+
)
124+
with uow_factory() as uow:
125+
wrong_kind_result = execute_update_memory(wrong_kind_request, uow)
126+
127+
assert wrong_kind_result.status == "error"
128+
assert any(
129+
error.code.value == "integrity_error" and error.field == "update.problem_id"
130+
for error in wrong_kind_result.errors
131+
)
132+
133+
134+
def test_update_fact_update_requires_visible_fact_endpoints_and_visible_change_memory(
135+
uow_factory: Callable[[], PostgresUnitOfWork],
136+
seed_memory: Callable[..., object],
137+
) -> None:
138+
"""fact_update_link updates should always require visible fact endpoints and memory_id to reference a visible change memory."""
139+
140+
seed_memory(
141+
memory_id="change-1",
142+
repo_id="repo-a",
143+
scope=MemoryScope.REPO,
144+
kind=MemoryKind.CHANGE,
145+
text_value="Visible change memory.",
146+
)
147+
seed_memory(
148+
memory_id="old-fact-1",
149+
repo_id="repo-a",
150+
scope=MemoryScope.REPO,
151+
kind=MemoryKind.FACT,
152+
text_value="Visible old fact.",
153+
)
154+
seed_memory(
155+
memory_id="new-fact-1",
156+
repo_id="repo-a",
157+
scope=MemoryScope.REPO,
158+
kind=MemoryKind.FACT,
159+
text_value="Visible new fact.",
160+
)
161+
162+
missing_old_fact_request = _make_update_request(
163+
repo_id="repo-a",
164+
memory_id="change-1",
165+
update={
166+
"type": "fact_update_link",
167+
"old_fact_id": "old-fact-missing",
168+
"new_fact_id": "new-fact-1",
169+
},
170+
)
171+
with uow_factory() as uow:
172+
missing_old_fact_result = execute_update_memory(missing_old_fact_request, uow)
173+
174+
assert missing_old_fact_result.status == "error"
175+
assert any(
176+
error.code.value == "not_found" and error.field == "update.old_fact_id"
177+
for error in missing_old_fact_result.errors
178+
)
179+
180+
seed_memory(
181+
memory_id="old-fact-hidden",
182+
repo_id="repo-b",
183+
scope=MemoryScope.REPO,
184+
kind=MemoryKind.FACT,
185+
text_value="Hidden old fact.",
186+
)
187+
hidden_old_fact_request = _make_update_request(
188+
repo_id="repo-a",
189+
memory_id="change-1",
190+
update={
191+
"type": "fact_update_link",
192+
"old_fact_id": "old-fact-hidden",
193+
"new_fact_id": "new-fact-1",
194+
},
195+
)
196+
with uow_factory() as uow:
197+
hidden_old_fact_result = execute_update_memory(hidden_old_fact_request, uow)
198+
199+
assert hidden_old_fact_result.status == "error"
200+
assert any(
201+
error.code.value == "integrity_error" and error.field == "update.old_fact_id"
202+
for error in hidden_old_fact_result.errors
203+
)
204+
205+
missing_new_fact_request = _make_update_request(
206+
repo_id="repo-a",
207+
memory_id="change-1",
208+
update={
209+
"type": "fact_update_link",
210+
"old_fact_id": "old-fact-1",
211+
"new_fact_id": "new-fact-missing",
212+
},
213+
)
214+
with uow_factory() as uow:
215+
missing_new_fact_result = execute_update_memory(missing_new_fact_request, uow)
216+
217+
assert missing_new_fact_result.status == "error"
218+
assert any(
219+
error.code.value == "not_found" and error.field == "update.new_fact_id"
220+
for error in missing_new_fact_result.errors
221+
)
222+
223+
seed_memory(
224+
memory_id="new-fact-hidden",
225+
repo_id="repo-b",
226+
scope=MemoryScope.REPO,
227+
kind=MemoryKind.FACT,
228+
text_value="Hidden new fact.",
229+
)
230+
hidden_new_fact_request = _make_update_request(
231+
repo_id="repo-a",
232+
memory_id="change-1",
233+
update={
234+
"type": "fact_update_link",
235+
"old_fact_id": "old-fact-1",
236+
"new_fact_id": "new-fact-hidden",
237+
},
238+
)
239+
with uow_factory() as uow:
240+
hidden_new_fact_result = execute_update_memory(hidden_new_fact_request, uow)
241+
242+
assert hidden_new_fact_result.status == "error"
243+
assert any(
244+
error.code.value == "integrity_error" and error.field == "update.new_fact_id"
245+
for error in hidden_new_fact_result.errors
246+
)
247+
248+
seed_memory(
249+
memory_id="not-a-change",
250+
repo_id="repo-a",
251+
scope=MemoryScope.REPO,
252+
kind=MemoryKind.PROBLEM,
253+
text_value="Wrong target kind.",
254+
)
255+
wrong_target_kind_request = _make_update_request(
256+
repo_id="repo-a",
257+
memory_id="not-a-change",
258+
update={
259+
"type": "fact_update_link",
260+
"old_fact_id": "old-fact-1",
261+
"new_fact_id": "new-fact-1",
262+
},
263+
)
264+
with uow_factory() as uow:
265+
wrong_target_kind_result = execute_update_memory(wrong_target_kind_request, uow)
266+
267+
assert wrong_target_kind_result.status == "error"
268+
assert any(
269+
error.code.value == "integrity_error" and error.field == "memory_id"
270+
for error in wrong_target_kind_result.errors
271+
)
272+
273+
274+
def test_update_association_link_requires_visible_target_memory(
275+
uow_factory: Callable[[], PostgresUnitOfWork],
276+
seed_memory: Callable[..., object],
277+
) -> None:
278+
"""association_link updates should always require to_memory_id to reference a visible memory."""
279+
280+
seed_memory(
281+
memory_id="source-memory",
282+
repo_id="repo-a",
283+
scope=MemoryScope.REPO,
284+
kind=MemoryKind.FACT,
285+
text_value="Source memory.",
286+
)
287+
288+
missing_target_request = _make_update_request(
289+
repo_id="repo-a",
290+
memory_id="source-memory",
291+
update={
292+
"type": "association_link",
293+
"to_memory_id": "target-missing",
294+
"relation_type": "depends_on",
295+
"evidence_refs": ["session://1"],
296+
},
297+
)
298+
with uow_factory() as uow:
299+
missing_target_result = execute_update_memory(missing_target_request, uow)
300+
301+
assert missing_target_result.status == "error"
302+
assert any(
303+
error.code.value == "not_found" and error.field == "update.to_memory_id"
304+
for error in missing_target_result.errors
305+
)
306+
307+
seed_memory(
308+
memory_id="target-hidden",
309+
repo_id="repo-b",
310+
scope=MemoryScope.REPO,
311+
kind=MemoryKind.FACT,
312+
text_value="Hidden target memory.",
313+
)
314+
hidden_target_request = _make_update_request(
315+
repo_id="repo-a",
316+
memory_id="source-memory",
317+
update={
318+
"type": "association_link",
319+
"to_memory_id": "target-hidden",
320+
"relation_type": "depends_on",
321+
"evidence_refs": ["session://1"],
322+
},
323+
)
324+
with uow_factory() as uow:
325+
hidden_target_result = execute_update_memory(hidden_target_request, uow)
326+
327+
assert hidden_target_result.status == "error"
328+
assert any(
329+
error.code.value == "integrity_error" and error.field == "update.to_memory_id"
330+
for error in hidden_target_result.errors
331+
)
332+
333+
334+
def _make_update_request(*, repo_id: str, memory_id: str, update: dict[str, object]) -> MemoryUpdateRequest:
335+
"""Build a valid update request with caller-provided target and update payload."""
336+
337+
return MemoryUpdateRequest.model_validate(
338+
{
339+
"op": "update",
340+
"repo_id": repo_id,
341+
"memory_id": memory_id,
342+
"mode": "commit",
343+
"update": update,
344+
}
345+
)

0 commit comments

Comments
 (0)