test_temperature_fan_presets.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. """Validator tests for the user-configurable temperature / fan presets."""
  2. import pytest
  3. from pydantic import ValidationError
  4. from backend.app.schemas.settings import AppSettingsUpdate
  5. # (field_name, valid_payload, range_lo, range_hi)
  6. PRESET_FIELDS = [
  7. ("nozzle_temp_presets", "[120, 220, 260]", 0, 320),
  8. ("bed_temp_presets", "[55, 75, 90]", 0, 140),
  9. ("chamber_temp_presets", "[35, 45, 60]", 0, 60),
  10. ("fan_speed_presets", "[50, 75, 100]", 0, 100),
  11. ]
  12. @pytest.mark.parametrize("field,valid,lo,hi", PRESET_FIELDS)
  13. def test_valid_triple_round_trips(field, valid, lo, hi):
  14. update = AppSettingsUpdate(**{field: valid})
  15. assert getattr(update, field) == valid
  16. @pytest.mark.parametrize("field,_valid,_lo,_hi", PRESET_FIELDS)
  17. def test_empty_string_means_use_defaults(field, _valid, _lo, _hi):
  18. update = AppSettingsUpdate(**{field: ""})
  19. assert getattr(update, field) == ""
  20. @pytest.mark.parametrize("field,_valid,_lo,_hi", PRESET_FIELDS)
  21. def test_missing_field_is_optional(field, _valid, _lo, _hi):
  22. # Updates are PATCH-style; omitting the field shouldn't trigger the validator.
  23. update = AppSettingsUpdate()
  24. assert getattr(update, field) is None
  25. @pytest.mark.parametrize("field,_valid,_lo,_hi", PRESET_FIELDS)
  26. def test_malformed_json_rejected(field, _valid, _lo, _hi):
  27. with pytest.raises(ValidationError) as exc:
  28. AppSettingsUpdate(**{field: "not json"})
  29. assert field in str(exc.value)
  30. @pytest.mark.parametrize("field,_valid,_lo,_hi", PRESET_FIELDS)
  31. def test_non_array_rejected(field, _valid, _lo, _hi):
  32. with pytest.raises(ValidationError) as exc:
  33. AppSettingsUpdate(**{field: '{"a": 1}'})
  34. assert "array of exactly 3 integers" in str(exc.value)
  35. @pytest.mark.parametrize("field,_valid,_lo,_hi", PRESET_FIELDS)
  36. @pytest.mark.parametrize("bad_payload", ["[]", "[100]", "[100, 200]", "[100, 200, 300, 400]"])
  37. def test_wrong_length_rejected(field, _valid, _lo, _hi, bad_payload):
  38. with pytest.raises(ValidationError) as exc:
  39. AppSettingsUpdate(**{field: bad_payload})
  40. assert "array of exactly 3 integers" in str(exc.value)
  41. @pytest.mark.parametrize("field,_valid,_lo,_hi", PRESET_FIELDS)
  42. def test_float_entries_rejected(field, _valid, _lo, _hi):
  43. with pytest.raises(ValidationError) as exc:
  44. AppSettingsUpdate(**{field: "[1.5, 2.0, 3.0]"})
  45. assert "must all be integers" in str(exc.value)
  46. @pytest.mark.parametrize("field,_valid,_lo,_hi", PRESET_FIELDS)
  47. def test_string_entries_rejected(field, _valid, _lo, _hi):
  48. with pytest.raises(ValidationError) as exc:
  49. AppSettingsUpdate(**{field: '["120", "220", "260"]'})
  50. assert "must all be integers" in str(exc.value)
  51. @pytest.mark.parametrize("field,_valid,lo,hi", PRESET_FIELDS)
  52. def test_below_range_rejected(field, _valid, lo, hi):
  53. bad = f"[{lo - 1}, {lo}, {lo}]"
  54. with pytest.raises(ValidationError) as exc:
  55. AppSettingsUpdate(**{field: bad})
  56. assert f"[{lo}, {hi}]" in str(exc.value)
  57. @pytest.mark.parametrize("field,_valid,lo,hi", PRESET_FIELDS)
  58. def test_above_range_rejected(field, _valid, lo, hi):
  59. bad = f"[{hi}, {hi}, {hi + 1}]"
  60. with pytest.raises(ValidationError) as exc:
  61. AppSettingsUpdate(**{field: bad})
  62. assert f"[{lo}, {hi}]" in str(exc.value)
  63. @pytest.mark.parametrize("field,_valid,lo,hi", PRESET_FIELDS)
  64. def test_range_bounds_inclusive(field, _valid, lo, hi):
  65. """Both endpoints lo and hi must be accepted (inclusive bounds)."""
  66. update = AppSettingsUpdate(**{field: f"[{lo}, {hi}, {hi}]"})
  67. assert getattr(update, field) == f"[{lo}, {hi}, {hi}]"
  68. def test_booleans_rejected_even_though_python_treats_them_as_ints():
  69. """`isinstance(True, int)` is True in Python — explicit guard required."""
  70. with pytest.raises(ValidationError) as exc:
  71. AppSettingsUpdate(nozzle_temp_presets="[true, 220, 260]")
  72. assert "must all be integers" in str(exc.value)