fix: POST /api/contacts/add crashes on JSON null name/email (None.strip()) (#1544)
This commit is contained in:
@@ -676,8 +676,8 @@ def setup_contacts_routes():
|
|||||||
@router.post("/add")
|
@router.post("/add")
|
||||||
async def add_contact(data: dict, _admin: str = Depends(require_admin)):
|
async def add_contact(data: dict, _admin: str = Depends(require_admin)):
|
||||||
"""Add a new contact."""
|
"""Add a new contact."""
|
||||||
name = data.get("name", "").strip()
|
name = (data.get("name") or "").strip()
|
||||||
email = data.get("email", "").strip()
|
email = (data.get("email") or "").strip()
|
||||||
if not email:
|
if not email:
|
||||||
return {"success": False, "error": "Email required"}
|
return {"success": False, "error": "Email required"}
|
||||||
# Check if already exists
|
# Check if already exists
|
||||||
|
|||||||
42
tests/test_contacts_add_null_name.py
Normal file
42
tests/test_contacts_add_null_name.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
"""Regression: POST /api/contacts/add must not crash when name/email is JSON null.
|
||||||
|
|
||||||
|
The handler did `data.get("name", "").strip()`. dict.get returns the default
|
||||||
|
only when the key is ABSENT; a body like {"name": null, "email": "x@y.com"}
|
||||||
|
gives name=None, so None.strip() raised AttributeError -> 500. Now guarded with
|
||||||
|
`(data.get("name") or "")`.
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import routes.contacts_routes as cr
|
||||||
|
|
||||||
|
|
||||||
|
def _add_handler():
|
||||||
|
router = cr.setup_contacts_routes()
|
||||||
|
for r in router.routes:
|
||||||
|
if getattr(r, "path", "").endswith("/add") and "POST" in getattr(r, "methods", set()):
|
||||||
|
return r.endpoint
|
||||||
|
raise AssertionError("add_contact route not found")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def _stub_store(monkeypatch):
|
||||||
|
created = []
|
||||||
|
monkeypatch.setattr(cr, "_fetch_contacts", lambda *a, **k: [])
|
||||||
|
monkeypatch.setattr(cr, "_create_contact", lambda name, email: created.append((name, email)) or True)
|
||||||
|
return created
|
||||||
|
|
||||||
|
|
||||||
|
def test_null_name_does_not_crash(_stub_store):
|
||||||
|
handler = _add_handler()
|
||||||
|
result = asyncio.run(handler({"name": None, "email": "x@y.com"}, _admin="admin"))
|
||||||
|
assert result["success"] is True
|
||||||
|
# name fell back to the email local-part instead of crashing.
|
||||||
|
assert _stub_store == [("x", "x@y.com")]
|
||||||
|
|
||||||
|
|
||||||
|
def test_null_email_is_rejected_cleanly(_stub_store):
|
||||||
|
handler = _add_handler()
|
||||||
|
result = asyncio.run(handler({"name": "Bob", "email": None}, _admin="admin"))
|
||||||
|
assert result == {"success": False, "error": "Email required"}
|
||||||
Reference in New Issue
Block a user