forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_crud.py
More file actions
350 lines (294 loc) · 13.7 KB
/
Copy pathtest_crud.py
File metadata and controls
350 lines (294 loc) · 13.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
"""
Scenario 1: CRUD Golden Path
Tests basic create-read-update-delete round-trips for conversations,
action items, and memories through the real API with fake backend services.
Assertions focus on real route behavior and durable postconditions through the
request → router → database → response cycle.
"""
import json
from fakes.firestore import seed_conversation
class TestConversationCRUD:
"""Conversation read/update/delete lifecycle.
The backend does not expose a generic "create conversation from JSON" API.
POST /v1/conversations processes an existing in-progress conversation, so
these CRUD tests seed Firestore directly and then exercise real read/update/delete routes.
"""
def test_seed_and_read_conversation(self, client, auth_headers, sample_conversation_data):
"""Seed a conversation and read it back via GET."""
seed_conversation("123", sample_conversation_data)
conv_id = sample_conversation_data["id"]
resp = client.get(f"/v1/conversations/{conv_id}", headers=auth_headers)
assert resp.status_code == 200, f"Read failed: {resp.text}"
body = resp.json()
assert body["id"] == conv_id
assert body["source"] == "omi"
assert body["structured"]["title"] == "Test Conversation"
assert isinstance(body["transcript_segments"], list)
assert len(body["transcript_segments"]) >= 1
def test_list_conversations(self, client, auth_headers, sample_conversation_data):
"""Seed two conversations, then list them through the real API."""
data1 = dict(sample_conversation_data, id="conv-list-001")
data2 = dict(
sample_conversation_data,
id="conv-list-002",
structured={
**sample_conversation_data["structured"],
"title": "Second Test Conversation",
},
)
seed_conversation("123", data1)
seed_conversation("123", data2)
resp = client.get("/v1/conversations", headers=auth_headers)
assert resp.status_code == 200
body = resp.json()
assert isinstance(body, list)
ids = [c["id"] for c in body]
assert "conv-list-001" in ids
assert "conv-list-002" in ids
def test_update_conversation_title(self, client, auth_headers, sample_conversation_data):
"""Update a seeded conversation's title via PATCH."""
conv_id = sample_conversation_data["id"]
seed_conversation("123", sample_conversation_data)
new_title = "Updated Title from E2E"
resp = client.patch(
f"/v1/conversations/{conv_id}/title",
params={"title": new_title},
headers=auth_headers,
)
assert resp.status_code == 200, f"Title update failed: {resp.text}"
resp = client.get(f"/v1/conversations/{conv_id}", headers=auth_headers)
assert resp.status_code == 200
assert resp.json()["structured"]["title"] == new_title
def test_delete_conversation(self, client, auth_headers, sample_conversation_data):
"""Delete a seeded conversation and verify it's gone."""
conv_id = sample_conversation_data["id"]
seed_conversation("123", sample_conversation_data)
resp = client.delete(f"/v1/conversations/{conv_id}", headers=auth_headers)
assert resp.status_code in (200, 204), f"Delete failed: {resp.text}"
resp = client.get(f"/v1/conversations/{conv_id}", headers=auth_headers)
assert resp.status_code == 404
class TestActionItemCRUD:
"""Action item lifecycle: create → read → update → delete."""
def test_create_and_read_action_item(self, client, auth_headers, sample_action_item_data):
"""Create an action item and read it back."""
resp = client.post(
"/v1/action-items",
json=sample_action_item_data,
headers=auth_headers,
)
assert resp.status_code == 200, f"Create AI failed: {resp.text}"
body = resp.json()
ai_id = body["id"]
assert body["description"] == sample_action_item_data["description"]
assert body["completed"] is False
assert ai_id is not None
# Read back
resp = client.get(f"/v1/action-items/{ai_id}", headers=auth_headers)
assert resp.status_code == 200
assert resp.json()["description"] == sample_action_item_data["description"]
def test_list_action_items(self, client, auth_headers):
"""Create multiple action items and list them."""
create_a = client.post("/v1/action-items", json={"description": "Task A"}, headers=auth_headers)
create_b = client.post("/v1/action-items", json={"description": "Task B"}, headers=auth_headers)
assert create_a.status_code == 200, create_a.text
assert create_b.status_code == 200, create_b.text
created_ids = {create_a.json()["id"], create_b.json()["id"]}
resp = client.get("/v1/action-items", headers=auth_headers)
assert resp.status_code == 200
body = resp.json()
items = body.get("action_items", [])
by_id = {i["id"]: i for i in items}
assert created_ids.issubset(by_id.keys())
assert {by_id[item_id]["description"] for item_id in created_ids} == {"Task A", "Task B"}
def test_update_action_item(self, client, auth_headers):
"""Update an action item's description."""
create_resp = client.post(
"/v1/action-items",
json={"description": "Original task"},
headers=auth_headers,
)
assert create_resp.status_code == 200, create_resp.text
ai_id = create_resp.json()["id"]
resp = client.patch(
f"/v1/action-items/{ai_id}",
json={"description": "Updated task description"},
headers=auth_headers,
)
assert resp.status_code == 200
assert resp.json()["description"] == "Updated task description"
def test_delete_action_item(self, client, auth_headers):
"""Delete an action item and verify it's gone."""
create_resp = client.post(
"/v1/action-items",
json={"description": "To be deleted"},
headers=auth_headers,
)
assert create_resp.status_code == 200, create_resp.text
ai_id = create_resp.json()["id"]
resp = client.delete(f"/v1/action-items/{ai_id}", headers=auth_headers)
assert resp.status_code in (200, 204)
resp = client.get(f"/v1/action-items/{ai_id}", headers=auth_headers)
assert resp.status_code == 404
def test_complete_action_item(self, client, auth_headers):
"""Mark an action item as completed."""
create_resp = client.post(
"/v1/action-items",
json={"description": "Complete me"},
headers=auth_headers,
)
assert create_resp.status_code == 200, create_resp.text
ai_id = create_resp.json()["id"]
resp = client.patch(
f"/v1/action-items/{ai_id}/completed",
params={"completed": True},
headers=auth_headers,
)
assert resp.status_code == 200
assert resp.json()["completed"] is True
class TestMemoryCRUD:
"""Memory lifecycle: create → read → edit → delete."""
def test_create_and_read_memory(self, client, auth_headers, sample_memory_data):
"""Create a memory and read it back."""
resp = client.post(
"/v3/memories",
json=sample_memory_data,
headers=auth_headers,
)
assert resp.status_code == 200, f"Create memory failed: {resp.text}"
body = resp.json()
mem_id = body["id"]
assert body["content"] == sample_memory_data["content"]
assert body["category"] == sample_memory_data["category"]
# Read back
resp = client.get("/v3/memories", headers=auth_headers)
assert resp.status_code == 200
memories = resp.json()
found = any(m["id"] == mem_id for m in memories)
assert found, f"Memory {mem_id} not found in list"
def test_list_memories(self, client, auth_headers):
"""Create multiple memories and list them."""
create_a = client.post(
"/v3/memories", json={"content": "Memory A", "category": "interesting"}, headers=auth_headers
)
create_b = client.post("/v3/memories", json={"content": "Memory B", "category": "system"}, headers=auth_headers)
assert create_a.status_code == 200, create_a.text
assert create_b.status_code == 200, create_b.text
created_ids = {create_a.json()["id"], create_b.json()["id"]}
resp = client.get("/v3/memories", headers=auth_headers)
assert resp.status_code == 200
body = resp.json()
by_id = {m["id"]: m for m in body}
assert created_ids.issubset(by_id.keys())
assert {by_id[mem_id]["content"] for mem_id in created_ids} == {"Memory A", "Memory B"}
def test_edit_memory(self, client, auth_headers):
"""Edit a memory's content."""
create_resp = client.post(
"/v3/memories",
json={"content": "Original content", "category": "manual"},
headers=auth_headers,
)
assert create_resp.status_code == 200, create_resp.text
mem_id = create_resp.json()["id"]
resp = client.patch(
f"/v3/memories/{mem_id}",
params={"value": "Edited content"},
headers=auth_headers,
)
assert resp.status_code == 200, f"Edit failed: {resp.text}"
list_resp = client.get("/v3/memories", headers=auth_headers)
assert list_resp.status_code == 200, list_resp.text
found = [m for m in list_resp.json() if m["id"] == mem_id]
assert found and found[0]["content"] == "Edited content"
def test_delete_memory(self, client, auth_headers):
"""Delete a memory and verify it's gone."""
create_resp = client.post(
"/v3/memories",
json={"content": "Delete me", "category": "interesting"},
headers=auth_headers,
)
assert create_resp.status_code == 200, create_resp.text
mem_id = create_resp.json()["id"]
resp = client.delete(f"/v3/memories/{mem_id}", headers=auth_headers)
assert resp.status_code == 200, f"Delete failed: {resp.text}"
assert resp.json()["status"] == "ok"
list_resp = client.get("/v3/memories", headers=auth_headers)
assert list_resp.status_code == 200, list_resp.text
assert mem_id not in {m["id"] for m in list_resp.json()}
def test_batch_create_memories(self, client, auth_headers):
"""Create multiple memories in a single batch request."""
resp = client.post(
"/v3/memories/batch",
json={
"memories": [
{"content": "Batch memory 1", "category": "interesting"},
{"content": "Batch memory 2", "category": "system"},
{"content": "Batch memory 3", "category": "manual"},
]
},
headers=auth_headers,
)
assert resp.status_code == 200
body = resp.json()
assert body["created_count"] == 3
assert len(body["memories"]) == 3
created_ids = {m["id"] for m in body["memories"]}
list_resp = client.get("/v3/memories", headers=auth_headers)
assert list_resp.status_code == 200, list_resp.text
assert created_ids.issubset({m["id"] for m in list_resp.json()})
class TestDataShapePreservation:
"""Verify that round-trips preserve all expected fields."""
def test_conversation_fields_preserved(self, client, auth_headers, sample_conversation_data):
"""All conversation fields survive seed→read round-trip."""
seed_conversation("123", sample_conversation_data)
resp = client.get(f"/v1/conversations/{sample_conversation_data['id']}", headers=auth_headers)
assert resp.status_code == 200
body = resp.json()
assert body["id"] == sample_conversation_data["id"]
assert body["source"] == sample_conversation_data["source"]
assert body["structured"]["title"] == sample_conversation_data["structured"]["title"]
assert body["transcript_segments"][0]["text"] == sample_conversation_data["transcript_segments"][0]["text"]
# Check core fields exist
for field in [
"id",
"created_at",
"started_at",
"finished_at",
"source",
"structured",
"transcript_segments",
"status",
"discarded",
]:
assert field in body, f"Missing field: {field}"
# Check structured sub-fields
s = body["structured"]
for sf in ["title", "overview", "emoji", "category", "action_items", "events"]:
assert sf in s, f"Missing structured field: {sf}"
def test_action_item_fields_preserved(self, client, auth_headers):
"""Action item fields survive create→read round-trip."""
resp = client.post(
"/v1/action-items",
json={
"description": "Field check task",
"completed": False,
"due_at": "2025-02-01T00:00:00Z",
},
headers=auth_headers,
)
assert resp.status_code == 200
body = resp.json()
assert body["description"] == "Field check task"
assert body["completed"] is False
assert body.get("due_at") is not None
for field in [
"id",
"description",
"completed",
"created_at",
"updated_at",
"is_locked",
"exported",
"sort_order",
"indent_level",
]:
assert field in body, f"Missing AI field: {field}"