Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion app/api/schemas/profiles.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from pydantic import BaseModel, Field, ConfigDict
from pydantic import BaseModel, Field, ConfigDict, field_validator
from uuid import UUID
from datetime import datetime

Expand All @@ -17,6 +17,15 @@ class ProfileUpdate(ProfileBase):
profession: str | None = None
role: str | None = None

# Leaving a field out keeps it, but these columns are NOT NULL, so an
# explicit null is rejected here instead of failing on commit.
@field_validator("name", "profession", "role")
@classmethod
def reject_null(cls, value):
if value is None:
raise ValueError("cannot be null")
return value

class ProfileResponse(ProfileBase):
profile_id: UUID
created_at: datetime
Expand Down
6 changes: 2 additions & 4 deletions contracts/schemas/profile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,15 @@ CreateProfileRequest:

UpdateProfileRequest:
type: object
description: Only the keys sent are changed.
description: Only the keys sent are changed. name, profession and role
can be left out but not set to null.
properties:
name:
type: string
nullable: true
profession:
type: string
nullable: true
role:
type: string
nullable: true
description:
type: string
nullable: true
Expand Down
32 changes: 32 additions & 0 deletions tests/test_v1_profiles.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import pytest
from fastapi.testclient import TestClient
from sqlmodel import Session, select
from uuid import UUID
Expand Down Expand Up @@ -46,6 +47,37 @@ def test_profile_crud(client: TestClient, db: Session):
assert db_profile.role == "Battalion Chief"



@pytest.mark.parametrize("field", ["name", "profession", "role"])
def test_profile_patch_rejects_null_required_field(client: TestClient, field):
"""An explicit null for a NOT NULL column is a 422, not a 500, and changes nothing."""
payload = {"name": "Jane Doe", "profession": "Firefighter", "role": "Captain"}
profile_id = client.post("/api/v1/profiles/", json=payload).json()["profile_id"]

response = client.patch(f"/api/v1/profiles/{profile_id}", json={field: None})
assert response.status_code == 422
body = response.json()
assert body["error_code"] == "VALIDATION_ERROR"
assert body["validation_errors"][0]["field"] == field

assert client.get(f"/api/v1/profiles/{profile_id}").json()[field] == payload[field]


def test_profile_patch_allows_null_optional_fields(client: TestClient):
"""description and custom_fields are nullable, and omitted fields are left alone."""
payload = {"name": "Jane Doe", "profession": "Firefighter", "role": "Captain", "description": "Station 42"}
profile_id = client.post("/api/v1/profiles/", json=payload).json()["profile_id"]

response = client.patch(
f"/api/v1/profiles/{profile_id}", json={"description": None, "custom_fields": None}
)
assert response.status_code == 200
data = response.json()
assert data["description"] is None
assert data["custom_fields"] is None
assert data["name"] == "Jane Doe"


def test_append_profile_context():
"""Unit test to ensure the LLM prompt is correctly formatted with profile data."""
base_prompt = "Extract the incident details."
Expand Down
Loading