/
/
/
1"""Tests for collection type handling in API argument parsing."""
2
3from __future__ import annotations
4
5from typing import Any
6
7import pytest
8from music_assistant_models.enums import DashboardType
9
10from music_assistant.helpers.api import parse_value
11
12
13def test_parse_value_none_for_optional_dict() -> None:
14 """A None value is returned as-is when the annotation is dict[str, Any] | None."""
15 result = parse_value("supported_types", None, dict[str, Any] | None)
16 assert result is None
17
18
19def test_parse_value_none_for_optional_list() -> None:
20 """A None value is returned as-is when the annotation is list[str] | None."""
21 result = parse_value("values", None, list[str] | None)
22 assert result is None
23
24
25def test_parse_value_dict_for_optional_dict() -> None:
26 """A real dict is still parsed correctly when the annotation is dict[str, Any] | None."""
27 result = parse_value("supported_types", {"a": 1}, dict[str, Any] | None)
28 assert result == {"a": 1}
29
30
31def test_parse_value_set_of_enum() -> None:
32 """A json list is parsed into a set when the annotation is set[Enum]."""
33 result = parse_value("supported_types", ["party"], set[DashboardType])
34 assert result == {DashboardType.PARTY}
35
36
37def test_parse_value_optional_set_of_enum() -> None:
38 """A json list is parsed into a set for an optional set[Enum] annotation."""
39 result = parse_value("supported_types", ["party", "now_playing"], set[DashboardType] | None)
40 assert result == {DashboardType.PARTY, DashboardType.NOW_PLAYING}
41
42
43def test_parse_value_frozenset_of_str() -> None:
44 """A json list is parsed into a frozenset when the annotation is frozenset[str]."""
45 result = parse_value("values", ["a", "b"], frozenset[str])
46 assert result == frozenset({"a", "b"})
47
48
49def test_parse_value_none_members_dropped_from_list() -> None:
50 """None members are currently dropped from a homogeneous list annotation."""
51 result = parse_value("values", ["a", None, "b"], list[str])
52 assert result == ["a", "b"]
53
54
55def test_parse_value_fixed_length_tuple_per_position_types() -> None:
56 """Each member of a fixed-length tuple is parsed against the type of its own position."""
57 result = parse_value("values", ["a", 1, "party"], tuple[str, int, DashboardType])
58 assert result == ("a", 1, DashboardType.PARTY)
59
60
61def test_parse_value_fixed_length_tuple_keeps_none_members() -> None:
62 """A None member of a fixed-length tuple is kept, so the tuple keeps its length."""
63 result = parse_value("values", ["a", "name", None], tuple[str, str, str | None])
64 assert result == ("a", "name", None)
65
66
67def test_parse_value_list_of_fixed_length_tuples() -> None:
68 """A json list of lists is parsed into tuples that each keep their None members."""
69 result = parse_value(
70 "stations",
71 [["t1", "Name A", "http://img"], ["t2", "Name B", None]],
72 list[tuple[str, str, str | None]],
73 )
74 assert result == [("t1", "Name A", "http://img"), ("t2", "Name B", None)]
75
76
77def test_parse_value_variadic_tuple() -> None:
78 """A json list is parsed into a tuple of arbitrary length for a variadic annotation."""
79 result = parse_value("values", ["a", "b", "c"], tuple[str, ...])
80 assert result == ("a", "b", "c")
81
82
83def test_parse_value_fixed_length_tuple_wrong_length() -> None:
84 """A value with the wrong number of members is rejected for a fixed-length tuple."""
85 with pytest.raises(TypeError, match="is invalid for values"):
86 parse_value("values", ["a"], tuple[str, str])
87
88
89def test_parse_value_fixed_length_tuple_names_the_failing_member() -> None:
90 """A member that does not fit its type is reported with its position in the tuple."""
91 with pytest.raises(TypeError, match=r"invalid for values\[1\]"):
92 parse_value("values", ["a", {}], tuple[str, str])
93
94
95def test_parse_value_fixed_length_tuple_in_union() -> None:
96 """A union of fixed-length tuples falls through to the member that fits the value."""
97 result = parse_value("values", ["a", "b", "c"], tuple[str, str] | tuple[str, str, str])
98 assert result == ("a", "b", "c")
99