test_util.py 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. """Tests for config-field normalisation.
  2. Station and device IDs are copy-pasted out of the browser's Network tab, so they
  3. routinely arrive with a stray leading or trailing space. The upstream API
  4. treats " 4235…" as a different, invalid value and the user only sees a generic
  5. "cannot connect".
  6. """
  7. from __future__ import annotations
  8. import pytest
  9. from custom_components.solar_of_things.const import WHITESPACE_SENSITIVE_FIELDS
  10. from custom_components.solar_of_things.util import is_valid_time_zone, normalise_config_fields
  11. def test_trims_pasted_station_and_device_ids() -> None:
  12. cleaned = normalise_config_fields(
  13. {"station_id": "486217907720650752 ", "device_id": " 486217907745816576"}
  14. )
  15. assert cleaned["station_id"] == "486217907720650752"
  16. assert cleaned["device_id"] == "486217907745816576"
  17. def test_password_is_never_trimmed() -> None:
  18. """Whitespace in a password may be intentional, so it must survive."""
  19. cleaned = normalise_config_fields({"password": " hunter2 ", "user_id": " bob "})
  20. assert cleaned["password"] == " hunter2 "
  21. assert cleaned["user_id"] == "bob"
  22. def test_password_is_excluded_from_the_field_list() -> None:
  23. assert "password" not in WHITESPACE_SENSITIVE_FIELDS
  24. def test_trims_a_pasted_iot_token() -> None:
  25. """Tokens are copied out of DevTools and often carry a newline or tab."""
  26. cleaned = normalise_config_fields({"iot_token": "\n eyJhbGciOi.token.value \t"})
  27. assert cleaned["iot_token"] == "eyJhbGciOi.token.value"
  28. @pytest.mark.parametrize(
  29. ("key", "value"),
  30. [
  31. ("access_token_expires", "2026-01-01T00:00:00+00:00"),
  32. ("refresh_token", None),
  33. ("some_number", 5),
  34. ],
  35. )
  36. def test_unlisted_and_non_string_values_pass_through(key, value) -> None:
  37. assert normalise_config_fields({key: value})[key] == value
  38. def test_returns_a_copy_without_mutating_the_input() -> None:
  39. """entry.data is a read-only mapping in HA; never write through it."""
  40. source = {"station_id": " 123 "}
  41. cleaned = normalise_config_fields(source)
  42. assert source["station_id"] == " 123 "
  43. assert cleaned["station_id"] == "123"
  44. assert cleaned is not source
  45. @pytest.mark.parametrize(
  46. ("payload", "expected"),
  47. [
  48. ({}, {}),
  49. ({"unrelated": "x"}, {"unrelated": "x"}),
  50. ({"station_id": " "}, {"station_id": ""}),
  51. ],
  52. )
  53. def test_edge_cases(payload, expected) -> None:
  54. assert normalise_config_fields(payload) == expected
  55. @pytest.mark.parametrize(
  56. "value",
  57. ["Asia/Manila", "Europe/Warsaw", "America/New_York", "UTC"],
  58. )
  59. def test_valid_iana_zone_ids(value) -> None:
  60. assert is_valid_time_zone(value) is True
  61. @pytest.mark.parametrize(
  62. "value",
  63. [
  64. "Europe", # issue #21 — a continent, not a zone
  65. "America",
  66. "GMT+2",
  67. "PST",
  68. "",
  69. None,
  70. ],
  71. )
  72. def test_rejects_non_iana_values(value) -> None:
  73. assert is_valid_time_zone(value) is False