| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576 |
- """Schema tests for the spool storage_location field (#1291).
- Reporter @needo37: the `storage_location` column existed on the Spool ORM model
- but was missing from SpoolBase, SpoolUpdate, and SpoolResponse. Pydantic
- silently drops unknown fields, so PATCH writes never reached the DB and reads
- omitted the field entirely. The fix is purely additive on the schema layer.
- """
- import pytest
- from pydantic import ValidationError
- from backend.app.schemas.spool import SpoolCreate, SpoolResponse, SpoolUpdate
- class TestStorageLocationRoundtrips:
- """The bug was that storage_location wasn't on the schemas at all — pin
- the round-trip so a future refactor can't quietly drop it again."""
- def test_create_accepts_storage_location(self):
- spool = SpoolCreate(material="PLA", storage_location="Drybox #1")
- assert spool.storage_location == "Drybox #1"
- def test_create_storage_location_optional(self):
- spool = SpoolCreate(material="PLA")
- assert spool.storage_location is None
- def test_update_accepts_storage_location(self):
- update = SpoolUpdate(storage_location="Top shelf")
- assert update.storage_location == "Top shelf"
- def test_update_omits_unset_storage_location(self):
- """A PATCH that doesn't mention storage_location must NOT clear it —
- model_dump(exclude_unset=True) keeps the field out of the update dict
- so the route's setattr loop skips it."""
- update = SpoolUpdate.model_validate({})
- dumped = update.model_dump(exclude_unset=True)
- assert "storage_location" not in dumped
- def test_update_explicit_null_clears_storage_location(self):
- """A PATCH that explicitly sends storage_location=null must reach
- the route's update_data dict as None, so setattr writes NULL to the
- DB — that's how the UI clears the field."""
- update = SpoolUpdate.model_validate({"storage_location": None})
- dumped = update.model_dump(exclude_unset=True)
- assert "storage_location" in dumped
- assert dumped["storage_location"] is None
- def test_response_carries_storage_location(self):
- """SpoolResponse inherits from SpoolBase, so the field must surface
- on read too — otherwise the inventory table silently always shows '-'."""
- from datetime import datetime, timezone
- now = datetime.now(timezone.utc)
- response = SpoolResponse.model_validate(
- {
- "id": 1,
- "material": "PLA",
- "storage_location": "Drybox #1",
- "created_at": now,
- "updated_at": now,
- }
- )
- assert response.storage_location == "Drybox #1"
- class TestStorageLocationLength:
- """The DB column is String(255). Schema must enforce the same cap so the
- API rejects too-long input cleanly instead of letting SQLAlchemy raise."""
- def test_accepts_max_length(self):
- update = SpoolUpdate(storage_location="x" * 255)
- assert len(update.storage_location) == 255
- def test_rejects_over_max_length(self):
- with pytest.raises(ValidationError, match="storage_location"):
- SpoolUpdate(storage_location="x" * 256)
|