test_slice_request_schema.py 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. """Tests for `SliceRequest` validator — covers both the legacy bare-int
  2. shape and the new source-aware shape, plus the backwards-compat
  3. normalisation that lets the route handler ignore the difference.
  4. """
  5. import pytest
  6. from pydantic import ValidationError
  7. from backend.app.schemas.slicer import PresetRef, SliceRequest
  8. class TestLegacyBareIntegerShape:
  9. """Existing clients (and stale browser tabs after upgrade) keep
  10. sending bare integer ids. They must continue working unchanged."""
  11. def test_bare_int_ids_normalise_to_local_preset_ref(self):
  12. req = SliceRequest(printer_preset_id=1, process_preset_id=2, filament_preset_id=3)
  13. assert req.printer_preset == PresetRef(source="local", id="1")
  14. assert req.process_preset == PresetRef(source="local", id="2")
  15. assert req.filament_preset == PresetRef(source="local", id="3")
  16. def test_legacy_ids_unchanged_in_payload(self):
  17. """The legacy fields stay populated — no behaviour change for
  18. clients that read them back from the model."""
  19. req = SliceRequest(printer_preset_id=10, process_preset_id=20, filament_preset_id=30)
  20. assert req.printer_preset_id == 10
  21. assert req.process_preset_id == 20
  22. assert req.filament_preset_id == 30
  23. class TestNewSourceAwareShape:
  24. """The new modal sends source-aware refs explicitly."""
  25. def test_cloud_refs_pass_through(self):
  26. req = SliceRequest(
  27. printer_preset=PresetRef(source="cloud", id="PFUprinter"),
  28. process_preset=PresetRef(source="cloud", id="PFUprocess"),
  29. filament_preset=PresetRef(source="cloud", id="PFUfilament"),
  30. )
  31. assert req.printer_preset.source == "cloud"
  32. assert req.printer_preset.id == "PFUprinter"
  33. def test_mixed_sources_per_slot(self):
  34. """A user may pick cloud for printer, local for process, standard
  35. for filament — the modal is per-slot."""
  36. req = SliceRequest(
  37. printer_preset=PresetRef(source="cloud", id="PFU123"),
  38. process_preset=PresetRef(source="local", id="42"),
  39. filament_preset=PresetRef(source="standard", id="Bambu PLA Basic"),
  40. )
  41. assert req.printer_preset.source == "cloud"
  42. assert req.process_preset.source == "local"
  43. assert req.filament_preset.source == "standard"
  44. class TestValidationErrors:
  45. def test_missing_printer_slot_raises(self):
  46. with pytest.raises(ValidationError) as exc:
  47. SliceRequest(process_preset_id=2, filament_preset_id=3)
  48. assert "printer" in str(exc.value)
  49. def test_invalid_source_rejected(self):
  50. with pytest.raises(ValidationError):
  51. SliceRequest(
  52. printer_preset={"source": "made_up", "id": "x"},
  53. process_preset_id=2,
  54. filament_preset_id=3,
  55. )
  56. class TestPriorityWhenBothSet:
  57. """If a client sends BOTH the legacy id AND the new ref for the same
  58. slot (unlikely in practice, but ambiguous), the new ref wins. Tests
  59. pin the resolution order so a future schema change can't silently
  60. flip it."""
  61. def test_explicit_ref_wins_over_legacy_id(self):
  62. req = SliceRequest(
  63. printer_preset_id=999, # would resolve to local:999
  64. printer_preset=PresetRef(source="cloud", id="PFU"),
  65. process_preset_id=2,
  66. filament_preset_id=3,
  67. )
  68. # Validator only fills the ref when it's None — the explicit cloud
  69. # ref stays untouched.
  70. assert req.printer_preset == PresetRef(source="cloud", id="PFU")