fix: harden catch report idempotency
This commit is contained in:
@@ -538,6 +538,47 @@ def test_pending_report_accepts_one_validated_screenshot(monkeypatch) -> None:
|
||||
assert reused.status_code == 401
|
||||
|
||||
|
||||
def test_idempotency_replay_survives_completed_screenshot(monkeypatch) -> None:
|
||||
import uuid
|
||||
|
||||
monkeypatch.setattr("app.routers.submissions.check_rate_limit", lambda *args, **kwargs: None)
|
||||
key = f"idem-upload-{uuid.uuid4().hex}"
|
||||
payload = {"fish_slug": "pike", "waterbody_slug": "test-lake", "x": 94, "y": 95, "weight_g": 4300}
|
||||
created = client.post("/api/v1/catch-reports", json=payload, headers={"Idempotency-Key": key}).json()
|
||||
monkeypatch.setattr("app.routers.submissions.upload_screenshot", lambda raw, **metadata: "reports/idempotent.jpg")
|
||||
upload = client.post(
|
||||
f"/api/v1/catch-reports/{created['id']}/screenshot",
|
||||
headers={"X-Upload-Token": created["screenshot_upload_token"]},
|
||||
files={"screenshot": ("catch.jpg", b"image-bytes", "image/jpeg")},
|
||||
)
|
||||
assert upload.status_code == 204
|
||||
|
||||
replay = client.post("/api/v1/catch-reports", json=payload, headers={"Idempotency-Key": key})
|
||||
assert replay.status_code == 200
|
||||
assert replay.json()["id"] == created["id"]
|
||||
assert replay.json()["screenshot_upload_token"] == created["screenshot_upload_token"]
|
||||
assert client.post("/api/v1/catch-reports", json=dict(payload, weight_g=4301), headers={"Idempotency-Key": key}).status_code == 409
|
||||
|
||||
|
||||
def test_expired_idempotency_key_integrity_winner_is_not_replayed(monkeypatch) -> None:
|
||||
import uuid
|
||||
|
||||
monkeypatch.setattr("app.routers.submissions.check_rate_limit", lambda *args, **kwargs: None)
|
||||
key = f"idem-expired-{uuid.uuid4().hex}"
|
||||
payload = {"fish_slug": "pike", "waterbody_slug": "test-lake", "x": 96, "y": 97, "weight_g": 4400}
|
||||
created = client.post("/api/v1/catch-reports", json=payload, headers={"Idempotency-Key": key})
|
||||
assert created.status_code == 201
|
||||
with Session(engine) as db:
|
||||
attempt = db.scalar(select(SubmissionAttempt).where(SubmissionAttempt.catch_report_id == UUID(created.json()["id"])))
|
||||
assert attempt is not None
|
||||
attempt.created_at = datetime.now(timezone.utc) - timedelta(minutes=6)
|
||||
db.commit()
|
||||
|
||||
expired = client.post("/api/v1/catch-reports", json=payload, headers={"Idempotency-Key": key})
|
||||
assert expired.status_code == 409
|
||||
assert "expired" in expired.json()["detail"]
|
||||
|
||||
|
||||
def test_admin_delete_anonymizes_report_removes_screenshot_and_keeps_audit(monkeypatch) -> None:
|
||||
created = client.post("/api/v1/catch-reports", json={"fish_slug": "pike", "waterbody_slug": "test-lake", "x": 93, "y": 94, "weight_g": 4300, "player_name": "Private Player", "source_url": "https://example.test/private", "comment": "private comment"}).json()
|
||||
with Session(engine) as db:
|
||||
|
||||
Reference in New Issue
Block a user