File: test_box.py

package info (click to toggle)
blebox-uniapi 2.5.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 520 kB
  • sloc: python: 4,994; makefile: 85; sh: 5
file content (169 lines) | stat: -rw-r--r-- 5,526 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
import pytest
from unittest import mock
from blebox_uniapi.box import Box
from blebox_uniapi import error
from blebox_uniapi.jfollow import follow

pytestmark = pytest.mark.asyncio


@pytest.fixture
def mock_session():
    return mock.MagicMock(host="172.1.2.3", port=80)


@pytest.fixture
def sample_data():
    return {
        "id": "abcd1234ef",
        "type": "airSensor",
        "deviceName": "foobar",
        "fv": "1.23",
        "hv": "4.56",
        "apiLevel": "20180403",
    }


@pytest.fixture
def config(sample_data):
    return Box._match_device_config(sample_data)


async def test_without_type(mock_session, sample_data, config):
    del sample_data["type"]

    with pytest.raises(error.UnsupportedBoxResponse, match="has no type"):
        Box(mock_session, sample_data, config, None)


async def test_with_unknown_type(mock_session, sample_data):
    sample_data["type"] = "unknownBox"

    with pytest.raises(error.UnsupportedBoxResponse, match="not a supported type"):
        Box._match_device_config(sample_data)


async def test_without_name(mock_session, sample_data, config):
    del sample_data["deviceName"]

    with pytest.raises(error.UnsupportedBoxResponse, match="has no name"):
        Box(mock_session, sample_data, config, None)


async def test_without_firmware_version(mock_session, sample_data, config):
    del sample_data["fv"]

    with pytest.raises(error.UnsupportedBoxResponse, match="has no firmware version"):
        Box(mock_session, sample_data, config, None)


async def test_without_hardware_version(mock_session, sample_data, config):
    del sample_data["hv"]

    with pytest.raises(error.UnsupportedBoxResponse, match="has no hardware version"):
        Box(mock_session, sample_data, config, None)


async def test_without_api_level(mock_session, sample_data, config):
    del sample_data["apiLevel"]

    with pytest.raises(error.UnsupportedBoxVersion, match=r"unsupported version"):
        Box._match_device_config(sample_data)


async def test_json_path_extraction(mock_session, sample_data, config):
    # note: follow is thin wrapper over jmespath so there's no real reason to test it.
    # However, this tests makes sure we understand the syntax and know how it works.
    # We can also use it as a canary for any unexpected change in syntax that may come
    # in newer version.

    # succesfull extraction
    assert follow(["foo"], "[0]") == "foo"
    assert follow([{"foo": "3", "value": 4}], "[?foo=='3'].value") == [4]

    # "dud" extraction
    assert follow([{"foo": "ab", "value": 4}], "[?foo=='bc'].value") == []
    assert follow([{"value": 4}], "[1].value") is None
    assert follow({"value": 4}, "foo") is None
    assert follow({"foo": [4]}, "[?bar==`0`].value") is None


async def test_missing_device_id(mock_session, sample_data, config):
    del sample_data["id"]
    with pytest.raises(error.UnsupportedBoxResponse, match="has no id"):
        Box(mock_session, sample_data, config, None)


# Add more test cases for other missing fields (type, name, versions, etc.)


async def test_invalid_init(mock_session, sample_data, config):
    with mock.patch(
        "blebox_uniapi.sensor.SensorFactory.many_from_config",
        spec_set=True,
        autospec=True,
    ) as mock_sensor:
        mock_sensor.side_effect = KeyError
        with pytest.raises(
            error.UnsupportedBoxResponse, match=r"Failed to initialize:"
        ):
            Box(mock_session, sample_data, config, None)


async def test_properties(mock_session, sample_data, config):
    box = Box(mock_session, sample_data, config, None)
    assert box.name == "foobar"
    assert box.last_data is None
    assert box.type == "airSensor"
    assert box.model == "airSensor"
    assert box.unique_id == "abcd1234ef"
    assert box.firmware_version == "1.23"
    assert box.hardware_version == "4.56"
    assert box.brand == "BleBox"
    assert box.api_version == 20180403
    assert box.address == "172.1.2.3:80"


async def test_field_validations(mock_session, sample_data, config):
    box = Box(mock_session, sample_data, config, None)

    with pytest.raises(
        error.BadFieldExceedsMax,
        match=r"foobar.field1 is 123 which exceeds max \(100\)",
    ):
        box.check_int_range(123, "field1", 100, 0)

    with pytest.raises(
        error.BadFieldLessThanMin,
        match=r"foobar.field1 is 123 which is less than minimum \(200\)",
    ):
        box.check_int_range(123, "field1", 300, 200)

    with pytest.raises(error.BadFieldMissing, match=r"foobar.field1 is missing"):
        box.check_int(None, "field1", 300, 200)

    with pytest.raises(
        error.BadFieldNotANumber, match=r"foobar.field1 is '123' which is not a number"
    ):
        box.check_int("123", "field1", 300, 200)

    with pytest.raises(error.BadFieldMissing, match=r"foobar.field1 is missing"):
        box.check_hex_str(None, "field1", 300, 200)

    with pytest.raises(
        error.BadFieldNotAString, match=r"foobar.field1 is 123 which is not a string"
    ):
        box.check_hex_str(123, "field1", 300, 200)

    with pytest.raises(error.BadFieldMissing, match=r"foobar.field1 is missing"):
        box.check_rgbw(None, "field1")

    with pytest.raises(
        error.BadFieldNotAString, match=r"foobar.field1 is 123 which is not a string"
    ):
        box.check_rgbw(123, "field1")

    with pytest.raises(
        error.BadFieldNotRGBW, match=r"foobar.field1 is 123 which is not a rgbw string"
    ):
        box.check_rgbw("123", "field1")