Skip to content

Commit 988b0a7

Browse files
committed
test(crud): add IDOR cross-user authorization tests for PUT/DELETE
1 parent 0002a57 commit 988b0a7

1 file changed

Lines changed: 87 additions & 1 deletion

File tree

backend/tests/test_crud.py

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,27 @@ def test_update_cycle_future_date(self, client, auth_headers):
7272
}, headers=auth_headers)
7373
assert resp.status_code == 400
7474

75+
def test_update_cycle_cross_user_denied(self, client):
76+
user_a_headers = {"Content-Type": "application/json", "X-User-Id": "user_a"}
77+
user_b_headers = {"Content-Type": "application/json", "X-User-Id": "user_b"}
78+
79+
create = client.post("/add-cycle", json={
80+
"startDate": "2026-03-01",
81+
"endDate": "2026-03-05",
82+
}, headers=user_a_headers)
83+
assert create.status_code == 201
84+
cycle_id = create.get_json()["cycle"]["id"]
85+
86+
resp = client.put(f"/cycles/{cycle_id}", json={
87+
"startDate": "2026-03-02",
88+
"endDate": "2026-03-06",
89+
}, headers=user_b_headers)
90+
assert resp.status_code in [403, 404]
91+
92+
get_resp = client.get(f"/cycles/{cycle_id}", headers=user_a_headers)
93+
assert get_resp.status_code == 200
94+
assert get_resp.get_json()["cycle"]["startDate"] == "2026-03-01"
95+
7596

7697
class TestDeleteCycle:
7798
"""DELETE /cycles/:id"""
@@ -103,6 +124,23 @@ def test_delete_cycle_removes_from_list(self, client, auth_headers):
103124
cycle_ids = [c["id"] for c in cycles_resp.get_json()["cycles"]]
104125
assert cycle_id not in cycle_ids
105126

127+
def test_delete_cycle_cross_user_denied(self, client):
128+
user_a_headers = {"Content-Type": "application/json", "X-User-Id": "user_a"}
129+
user_b_headers = {"Content-Type": "application/json", "X-User-Id": "user_b"}
130+
131+
create = client.post("/add-cycle", json={
132+
"startDate": "2026-05-01",
133+
"endDate": "2026-05-05",
134+
}, headers=user_a_headers)
135+
cycle_id = create.get_json()["cycle"]["id"]
136+
137+
resp = client.delete(f"/cycles/{cycle_id}", headers=user_b_headers)
138+
assert resp.status_code in [403, 404]
139+
140+
get_resp = client.get(f"/cycles/{cycle_id}", headers=user_a_headers)
141+
assert get_resp.status_code == 200
142+
assert get_resp.get_json()["cycle"]["id"] == cycle_id
143+
106144

107145
class TestUpdateSymptom:
108146
"""PUT /symptoms/:id"""
@@ -154,4 +192,52 @@ def test_update_symptom_invalid_severity(self, client, auth_headers):
154192
uid = "test_user_001"
155193
mock_symptoms[uid] = [{"id": "sym_sev_1", "type": "Cramps", "severity": "Low", "date": "2026-05-20"}]
156194

157-
resp = client.put("/symptoms/sym
195+
resp = client.put("/symptoms/sym_sev_1", json={
196+
"type": "Cramps",
197+
"severity": "InvalidSeverity",
198+
"date": "2026-05-20",
199+
}, headers=auth_headers)
200+
assert resp.status_code == 400
201+
202+
def test_update_symptom_cross_user_denied(self, client):
203+
from app import mock_symptoms
204+
user_b_headers = {"Content-Type": "application/json", "X-User-Id": "user_b"}
205+
mock_symptoms["user_a"] = [{"id": "sym_user_a", "type": "Cramps", "severity": "Low", "date": "2026-05-20"}]
206+
207+
resp = client.put("/symptoms/sym_user_a", json={
208+
"type": "Headache",
209+
"severity": "High",
210+
"date": "2026-05-21",
211+
}, headers=user_b_headers)
212+
assert resp.status_code in [403, 404]
213+
214+
assert mock_symptoms["user_a"][0]["type"] == "Cramps"
215+
assert mock_symptoms["user_a"][0]["severity"] == "Low"
216+
217+
218+
class TestDeleteSymptom:
219+
"""DELETE /symptoms/:id"""
220+
221+
def test_delete_symptom_success(self, client, auth_headers):
222+
from app import mock_symptoms
223+
uid = "test_user_001"
224+
mock_symptoms[uid] = [{"id": "sym_del_1", "type": "Cramps", "severity": "Low", "date": "2026-05-20"}]
225+
226+
resp = client.delete("/symptoms/sym_del_1", headers=auth_headers)
227+
assert resp.status_code == 200
228+
assert mock_symptoms[uid] == []
229+
230+
def test_delete_symptom_not_found(self, client, auth_headers):
231+
resp = client.delete("/symptoms/nonexistent_id", headers=auth_headers)
232+
assert resp.status_code == 404
233+
234+
def test_delete_symptom_cross_user_denied(self, client):
235+
from app import mock_symptoms
236+
user_b_headers = {"Content-Type": "application/json", "X-User-Id": "user_b"}
237+
mock_symptoms["user_a"] = [{"id": "sym_del_user_a", "type": "Fatigue", "severity": "Medium", "date": "2026-05-22"}]
238+
239+
resp = client.delete("/symptoms/sym_del_user_a", headers=user_b_headers)
240+
assert resp.status_code in [403, 404]
241+
242+
assert len(mock_symptoms["user_a"]) == 1
243+
assert mock_symptoms["user_a"][0]["id"] == "sym_del_user_a"

0 commit comments

Comments
 (0)