Fix bulk-pantry-add crash on null unit (found in live MCP testing)

End-to-end testing (MCP client -> FastMCP -> Django) surfaced a 500:
adding an item with an explicit `unit: null` (as add_to_pantry sends
for items without a unit) hit a NOT NULL violation, because
`item_data.get("unit", "items")` returns None when the key is present.

- views.bulk_pantry_add: `item_data.get("unit") or "items"` — tolerate
  null/empty unit.
- mcp_server add_to_pantry: omit quantity/unit from the payload when
  unset, so the API applies its own defaults.
- test: bulk-add with unit/quantity = null returns 201 (25 pass).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom Flux
2026-06-23 21:24:09 +01:00
co-authored by Claude Opus 4.8
parent 44de784f70
commit 9efc4adcd4
3 changed files with 23 additions and 7 deletions
+13
View File
@@ -224,6 +224,19 @@ class BulkAddApiTests(_ApiTestCase):
self.assertEqual(rows.count(), 1)
self.assertEqual(rows.first().state, "in")
def test_tolerates_null_unit_and_quantity(self):
# An MCP client may send unit/quantity as null for unknown items.
r = self.client.post(
"/api/bulk-pantry-add/",
{"items": [{"ingredient_name": "pork mince", "location": "fridge",
"unit": None, "quantity": None}]},
format="json",
)
self.assertEqual(r.status_code, 201)
self.assertTrue(
PantryItem.objects.filter(ingredient__name="pork mince", location="fridge").exists()
)
class WhatCanICookApiTests(_ApiTestCase):
def setUp(self):
+1 -1
View File
@@ -576,7 +576,7 @@ def bulk_pantry_add(request):
name = item_data.get("ingredient_name", "").strip()
qty_raw = item_data.get("quantity")
quantity = int(qty_raw) if qty_raw not in (None, "") else None
unit = item_data.get("unit", "items")
unit = item_data.get("unit") or "items" # tolerate null/empty
location = item_data.get("location", "fridge")
expiry_days = item_data.get("expiry_days") # optional override
+9 -6
View File
@@ -73,15 +73,18 @@ def add_to_pantry(items: list[dict]) -> dict:
"""Add items the user bought. Each item is {name, location?, quantity?,
unit?} — e.g. {"name": "pork mince", "location": "fridge"}. Existing items
are restocked (set back to In) rather than duplicated."""
payload = [
{
payload = []
for it in items:
entry = {
"ingredient_name": it.get("name") or it.get("ingredient_name"),
"location": it.get("location", "fridge"),
"quantity": it.get("quantity"),
"unit": it.get("unit"),
}
for it in items
]
# Only forward optional fields when set, so the API applies its defaults.
if it.get("quantity") is not None:
entry["quantity"] = it["quantity"]
if it.get("unit"):
entry["unit"] = it["unit"]
payload.append(entry)
try:
return _api.bulk_add(payload)
except FoodApiError as e: