smodusermc commited on
Commit
e4b9b5d
·
verified ·
1 Parent(s): b211831

Update tests/test_e2e.py

Browse files
Files changed (1) hide show
  1. tests/test_e2e.py +86 -7
tests/test_e2e.py CHANGED
@@ -208,16 +208,31 @@ async def main():
208
  check(alice.ws.state is WsState.OPEN, "alice socket STILL OPEN after delete (bug fixed)")
209
  check(bob.ws.state is WsState.OPEN, "bob socket STILL OPEN after delete (bug fixed)")
210
 
211
- # ---- private chats: create, cap of 3, scoped delivery ----
212
  await alice.send({"type": "create_dm", "user_id": bob.user["id"]})
213
  dm1_a = await alice.expect("dm_created")
214
  dm1 = dm1_a["conversation"]
215
  check(dm1["type"] == "dm" and dm1["peer"]["id"] == bob.user["id"], "dm created for alice")
216
- dm1_b = await bob.expect("dm_created", 6)
217
- check(dm1_b["conversation"]["id"] == dm1["id"] and dm1_b["conversation"]["peer"]["id"] == alice.user["id"],
218
- "bob sees same dm")
 
219
  dmid = dm1["id"]
220
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
  # send dm message -> global watchers must NOT see it
222
  await alice.send({"type": "send_message", "conversation_id": dmid, "content": "psst secret",
223
  "client_id": "dm1"})
@@ -237,11 +252,12 @@ async def main():
237
  ty = await wait_events([bob], "typing_indicator")
238
  check(ty["bob"] and ty["bob"][0]["conversation_id"] == dmid, "typing scoped to dm")
239
 
240
- # two more dms, 4th must be rejected
 
241
  for i in range(2):
242
  await alice.send({"type": "create_dm", "user_id": bob.user["id"]})
243
- await alice.expect("dm_created")
244
- await bob.expect("dm_created", 6)
245
  await alice.send({"type": "create_dm", "user_id": bob.user["id"]})
246
  err = await alice.expect("error")
247
  check(err["code"] == "DM_FAILED" and "3" in err["message"], "4th dm rejected (cap 3)", err["message"])
@@ -289,6 +305,28 @@ async def main():
289
  rr = r.json()
290
  check(rr["is_dm"] and rr["reader_count"] == 1 and rr["not_read"] == [], "dm receipts exact")
291
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
  # ---- password change ----
293
  r = await http.post(f"{BASE}/api/profile/password?current_password=password123&new_password=password456",
294
  headers={"X-Auth-Token": bob.token})
@@ -466,6 +504,47 @@ async def main():
466
  check(any(c["id"] == dmid for c in after.json()["conversations"]),
467
  "hidden dm returns after unblock")
468
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
469
  # ---- auth edge ----
470
  r = await http.get(BASE + "/api/auth/verify", headers={"X-Auth-Token": "bogus"})
471
  check(r.status_code == 401, "bad token rejected")
 
208
  check(alice.ws.state is WsState.OPEN, "alice socket STILL OPEN after delete (bug fixed)")
209
  check(bob.ws.state is WsState.OPEN, "bob socket STILL OPEN after delete (bug fixed)")
210
 
211
+ # ---- private chats: create, require acceptance, cap of 3, scoped delivery ----
212
  await alice.send({"type": "create_dm", "user_id": bob.user["id"]})
213
  dm1_a = await alice.expect("dm_created")
214
  dm1 = dm1_a["conversation"]
215
  check(dm1["type"] == "dm" and dm1["peer"]["id"] == bob.user["id"], "dm created for alice")
216
+ dm1_b = await bob.expect("conversation_updated", 6)
217
+ check(dm1_b["conversation"]["id"] == dm1["id"] and dm1_b["conversation"]["peer"]["id"] == alice.user["id"]
218
+ and dm1_b["conversation"]["is_pending"],
219
+ "bob sees pending private chat invite")
220
  dmid = dm1["id"]
221
 
222
+ # A pending DM invitee cannot send or load until accepting.
223
+ await bob.send({"type": "send_message", "conversation_id": dmid,
224
+ "content": "before accept", "client_id": "bob-pre-accept"})
225
+ pre_err = await bob.expect("error")
226
+ check(pre_err.get("code") == "FORBIDDEN", "pending invitee cannot send", pre_err.get("message", ""))
227
+ r = await http.post(f"{BASE}/api/conversations/{dmid}/accept", headers={"X-Auth-Token": bob.token})
228
+ check(r.status_code == 200 and r.json()["conversation"]["is_pending"] is False,
229
+ "bob accepts private chat invite")
230
+ accept_a = await alice.expect("conversation_updated", 6)
231
+ check(accept_a["conversation"]["id"] == dmid and accept_a["conversation"]["is_pending"] is False,
232
+ "alice is notified bob accepted")
233
+ await alice.drain(0.2)
234
+ await bob.drain(0.2)
235
+
236
  # send dm message -> global watchers must NOT see it
237
  await alice.send({"type": "send_message", "conversation_id": dmid, "content": "psst secret",
238
  "client_id": "dm1"})
 
252
  ty = await wait_events([bob], "typing_indicator")
253
  check(ty["bob"] and ty["bob"][0]["conversation_id"] == dmid, "typing scoped to dm")
254
 
255
+ # two more dms (pending for bob), 4th must be rejected
256
+ extra_dm_ids = []
257
  for i in range(2):
258
  await alice.send({"type": "create_dm", "user_id": bob.user["id"]})
259
+ extra_dm_ids.append((await alice.expect("dm_created"))["conversation"]["id"])
260
+ await bob.expect("conversation_updated", 6)
261
  await alice.send({"type": "create_dm", "user_id": bob.user["id"]})
262
  err = await alice.expect("error")
263
  check(err["code"] == "DM_FAILED" and "3" in err["message"], "4th dm rejected (cap 3)", err["message"])
 
305
  rr = r.json()
306
  check(rr["is_dm"] and rr["reader_count"] == 1 and rr["not_read"] == [], "dm receipts exact")
307
 
308
+ # ---- DM invites can be declined and re-invites require acceptance again ----
309
+ reinv_id = extra_dm_ids[0]
310
+ r = await http.post(f"{BASE}/api/conversations/{reinv_id}/reject",
311
+ headers={"X-Auth-Token": bob.token})
312
+ check(r.status_code == 200 and r.json()["status"] == "rejected", "pending invitee declines dm")
313
+ await alice.drain(0.3) # alice is told the invite was declined
314
+ await bob.drain(0.2)
315
+ await alice.send({"type": "create_dm", "user_id": bob.user["id"]})
316
+ re_created = await alice.expect("dm_created")
317
+ check(re_created["conversation"]["id"] == reinv_id,
318
+ "dm re-invite reuses the existing conversation")
319
+ re_pend = await bob.expect("conversation_updated", 6)
320
+ check(re_pend["conversation"]["id"] == reinv_id and re_pend["conversation"]["is_pending"],
321
+ "dm re-invite requires acceptance again")
322
+ r = await http.post(f"{BASE}/api/conversations/{reinv_id}/accept",
323
+ headers={"X-Auth-Token": bob.token})
324
+ check(r.status_code == 200 and r.json()["conversation"]["is_pending"] is False,
325
+ "bob accepts the dm re-invite")
326
+ await alice.expect("conversation_updated", 6)
327
+ await alice.drain(0.2)
328
+ await bob.drain(0.2)
329
+
330
  # ---- password change ----
331
  r = await http.post(f"{BASE}/api/profile/password?current_password=password123&new_password=password456",
332
  headers={"X-Auth-Token": bob.token})
 
504
  check(any(c["id"] == dmid for c in after.json()["conversations"]),
505
  "hidden dm returns after unblock")
506
 
507
+ # ---- privacy: deleting a private chat hides it for that user ----
508
+ await alice.drain(0.3)
509
+ await bob.drain(0.3)
510
+ r = await http.delete(f"{BASE}/api/conversations/{dmid}", headers={"X-Auth-Token": alice.token})
511
+ check(r.status_code == 200 and r.json()["status"] == "hidden", "alice hides dm", r.text[:160])
512
+ left_a = await alice.expect("conversation_left", 6)
513
+ check(left_a["user_id"] == alice.user["id"] and left_a["reason"] == "deleted",
514
+ "alice gets conversation_left for own hide")
515
+ left_b = await bob.expect("conversation_left", 6)
516
+ check(left_b["user_id"] == alice.user["id"] and left_b["reason"] == "deleted"
517
+ and left_b["conversation"]["id"] == dmid
518
+ and left_b["conversation"]["peer_removed"] is True,
519
+ "bob is told alice no longer wants to chat")
520
+ r = await http.get(f"{BASE}/api/conversations", headers={"X-Auth-Token": alice.token})
521
+ check(all(c["id"] != dmid for c in r.json()["conversations"]),
522
+ "hidden dm omitted from alice's list")
523
+ r = await http.get(f"{BASE}/api/conversations", headers={"X-Auth-Token": bob.token})
524
+ check(any(c["id"] == dmid for c in r.json()["conversations"]),
525
+ "hidden dm still present for bob")
526
+
527
+ # bob can still send, but the hidden user never receives it
528
+ await bob.send({"type": "send_message", "conversation_id": dmid,
529
+ "content": "after alice hid", "client_id": "after-hide"})
530
+ echo = await bob.expect("new_message")
531
+ check(echo["message"]["content"] == "after alice hid", "bob can still send into the dm")
532
+ leak = await alice.drain(0.5)
533
+ check(not any(e.get("type") == "new_message" and e.get("message", {}).get("conversation_id") == dmid
534
+ for e in leak), "hidden user does not receive dm messages")
535
+
536
+ # bob deletes too -> chat is removed for everyone
537
+ r = await http.delete(f"{BASE}/api/conversations/{dmid}", headers={"X-Auth-Token": bob.token})
538
+ check(r.status_code == 200 and r.json()["status"] == "deleted", "bob deletes dm too", r.text[:160])
539
+ del_b = await bob.expect("conversation_deleted", 6)
540
+ del_a = await alice.expect("conversation_deleted", 6)
541
+ check(del_b["conversation_id"] == dmid and del_a["conversation_id"] == dmid,
542
+ "both users are notified when the dm is fully deleted")
543
+ r = await http.get(f"{BASE}/api/conversations", headers={"X-Auth-Token": alice.token})
544
+ check(all(c["id"] != dmid for c in r.json()["conversations"]), "deleted dm gone for alice")
545
+ r = await http.get(f"{BASE}/api/conversations", headers={"X-Auth-Token": bob.token})
546
+ check(all(c["id"] != dmid for c in r.json()["conversations"]), "deleted dm gone for bob")
547
+
548
  # ---- auth edge ----
549
  r = await http.get(BASE + "/api/auth/verify", headers={"X-Auth-Token": "bogus"})
550
  check(r.status_code == 401, "bad token rejected")