Skip to content

Commit fd3e697

Browse files
Merge pull request #70 from BottlecapDave/develop
New release
2 parents 3305000 + a479dfa commit fd3e697

File tree

8 files changed

+215
-144
lines changed

8 files changed

+215
-144
lines changed

custom_components/first_bus/api_client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ async def async_get_bus_times(self, stop):
1313
# Disable content type check as sometimes it can report text/html
1414
data = await response.json(content_type=None)
1515

16-
if ("times" in data):
16+
if ("times" in data and "atcocode" in data):
1717
return list(map(lambda time: {
1818
"due": time["Due"],
1919
"service_ref": time["ServiceRef"],
@@ -22,4 +22,4 @@ async def async_get_bus_times(self, stop):
2222
"is_live": True if time["IsLive"] == 'Y' or time["IsLive"] == 'y' else False
2323
}, data["times"]))
2424

25-
return []
25+
return None

custom_components/first_bus/config/__init__.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import logging
22
import re
3-
from ..const import CONFIG_BUSES, REGEX_BUSES
3+
4+
from ..api_client import FirstBusApiClient
5+
from ..const import CONFIG_BUSES, CONFIG_STOP, REGEX_BUSES
46

57
_LOGGER = logging.getLogger(__name__)
68

@@ -19,9 +21,16 @@ def merge_config(data: dict, options: dict, updated_config: dict = None):
1921

2022
return config
2123

22-
def validate_config(config: dict):
24+
async def async_validate_main_config(config: dict):
2325
new_config = dict(config)
2426
errors = {}
27+
client = FirstBusApiClient()
28+
29+
if CONFIG_STOP in new_config:
30+
buses = await client.async_get_bus_times(new_config[CONFIG_STOP])
31+
if buses is None:
32+
errors[CONFIG_STOP] = "invalid_stop"
33+
2534
if CONFIG_BUSES in new_config and new_config[CONFIG_BUSES] is not None and len(new_config[CONFIG_BUSES]) > 0:
2635
matches = re.search(REGEX_BUSES, new_config[CONFIG_BUSES])
2736
if (matches is None):

custom_components/first_bus/config_flow.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
DATA_SCHEMA_STOP,
1414
)
1515

16-
from .config import merge_config, validate_config
16+
from .config import merge_config, async_validate_main_config
1717

1818
_LOGGER = logging.getLogger(__name__)
1919

@@ -27,7 +27,7 @@ async def async_step_user(self, user_input):
2727

2828
errors = {}
2929
if user_input is not None:
30-
(errors, config) = validate_config(user_input)
30+
(errors, config) = await async_validate_main_config(user_input)
3131

3232
# Setup our basic sensors
3333
if len(errors) < 1:
@@ -77,7 +77,7 @@ async def async_step_user(self, user_input):
7777

7878
_LOGGER.debug(f"Update config {config}")
7979

80-
(errors, config) = validate_config(config)
80+
(errors, config) = await async_validate_main_config(config)
8181

8282
if len(errors) < 1:
8383
return self.async_create_entry(title="", data=config)

custom_components/first_bus/translations/en.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
}
1818
},
1919
"error": {
20-
"invalid_buses": "Buses must be alphanumeric and spaces separated by commas"
20+
"invalid_buses": "Buses must be alphanumeric and spaces separated by commas",
21+
"invalid_stop": "Bus stop is not recognised by First Bus. Please check and try again."
2122
},
2223
"abort": {
2324
}
@@ -36,7 +37,8 @@
3637
}
3738
},
3839
"error": {
39-
"invalid_buses": "Buses must be alphanumeric and spaces separated by commas"
40+
"invalid_buses": "Buses must be alphanumeric and spaces separated by commas",
41+
"invalid_stop": "Bus stop is not recognised by First Bus. Please check and try again."
4042
},
4143
"abort": {
4244
}

package-lock.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/integration/api_client/test_get_bus_times.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,13 @@ async def test_when_get_buses_is_called_then_next_bus_is_returned():
3939
# Ignore any thrown exceptions
4040
pass
4141

42-
assert passes > 0
42+
assert passes > 0
43+
44+
@pytest.mark.asyncio
45+
async def test_when_get_buses_is_called_with_invalid_stop_then_none_is_returned():
46+
# Arrange
47+
client = FirstBusApiClient()
48+
49+
# Act
50+
buses = await client.async_get_bus_times("123")
51+
assert buses is None
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
from unittest import mock
2+
import pytest
3+
from custom_components.first_bus.config import async_validate_main_config
4+
from custom_components.first_bus.const import CONFIG_NAME, CONFIG_STOP, CONFIG_BUSES
5+
from custom_components.first_bus.api_client import FirstBusApiClient
6+
7+
@pytest.mark.asyncio
8+
async def test_when_data_valid_then_no_errors_returned():
9+
# Arrange
10+
original_config = {
11+
CONFIG_NAME: "test",
12+
CONFIG_STOP: "123",
13+
CONFIG_BUSES: "12 A,12B,12"
14+
}
15+
16+
async def async_mocked_get_bus_times(*args, **kwargs):
17+
return []
18+
19+
# Act
20+
with mock.patch.multiple(FirstBusApiClient, async_get_bus_times=async_mocked_get_bus_times):
21+
(errors, config) = await async_validate_main_config(original_config)
22+
23+
# Assert
24+
assert CONFIG_NAME not in errors
25+
assert CONFIG_STOP not in errors
26+
assert CONFIG_BUSES not in errors
27+
28+
assert CONFIG_NAME in config
29+
assert config[CONFIG_NAME] == original_config[CONFIG_NAME]
30+
assert CONFIG_STOP in config
31+
assert config[CONFIG_STOP] == original_config[CONFIG_STOP]
32+
assert CONFIG_BUSES in config
33+
assert config[CONFIG_BUSES] == ["12 A", "12B", "12"]
34+
35+
@pytest.mark.asyncio
36+
async def test_when_buses_not_present_then_buses_empty_array():
37+
# Arrange
38+
original_config = {
39+
CONFIG_NAME: "test",
40+
CONFIG_STOP: "123"
41+
}
42+
43+
async def async_mocked_get_bus_times(*args, **kwargs):
44+
return []
45+
46+
# Act
47+
with mock.patch.multiple(FirstBusApiClient, async_get_bus_times=async_mocked_get_bus_times):
48+
(errors, config) = await async_validate_main_config(original_config)
49+
50+
# Assert
51+
assert CONFIG_NAME not in errors
52+
assert CONFIG_STOP not in errors
53+
assert CONFIG_BUSES not in errors
54+
55+
assert CONFIG_NAME in config
56+
assert config[CONFIG_NAME] == original_config[CONFIG_NAME]
57+
assert CONFIG_STOP in config
58+
assert config[CONFIG_STOP] == original_config[CONFIG_STOP]
59+
assert CONFIG_BUSES in config
60+
assert config[CONFIG_BUSES] == []
61+
62+
@pytest.mark.asyncio
63+
async def test_when_buses_none_then_buses_empty_array():
64+
# Arrange
65+
original_config = {
66+
CONFIG_NAME: "test",
67+
CONFIG_STOP: "123",
68+
CONFIG_BUSES: None
69+
}
70+
71+
async def async_mocked_get_bus_times(*args, **kwargs):
72+
return []
73+
74+
# Act
75+
with mock.patch.multiple(FirstBusApiClient, async_get_bus_times=async_mocked_get_bus_times):
76+
(errors, config) = await async_validate_main_config(original_config)
77+
78+
# Assert
79+
assert CONFIG_NAME not in errors
80+
assert CONFIG_STOP not in errors
81+
assert CONFIG_BUSES not in errors
82+
83+
assert CONFIG_NAME in config
84+
assert config[CONFIG_NAME] == original_config[CONFIG_NAME]
85+
assert CONFIG_STOP in config
86+
assert config[CONFIG_STOP] == original_config[CONFIG_STOP]
87+
assert CONFIG_BUSES in config
88+
assert config[CONFIG_BUSES] == []
89+
90+
@pytest.mark.asyncio
91+
async def test_when_buses_empty_then_buses_empty_array():
92+
# Arrange
93+
original_config = {
94+
CONFIG_NAME: "test",
95+
CONFIG_STOP: "123",
96+
CONFIG_BUSES: ""
97+
}
98+
99+
async def async_mocked_get_bus_times(*args, **kwargs):
100+
return []
101+
102+
# Act
103+
with mock.patch.multiple(FirstBusApiClient, async_get_bus_times=async_mocked_get_bus_times):
104+
(errors, config) = await async_validate_main_config(original_config)
105+
106+
# Assert
107+
assert CONFIG_NAME not in errors
108+
assert CONFIG_STOP not in errors
109+
assert CONFIG_BUSES not in errors
110+
111+
assert CONFIG_NAME in config
112+
assert config[CONFIG_NAME] == original_config[CONFIG_NAME]
113+
assert CONFIG_STOP in config
114+
assert config[CONFIG_STOP] == original_config[CONFIG_STOP]
115+
assert CONFIG_BUSES in config
116+
assert config[CONFIG_BUSES] == []
117+
118+
@pytest.mark.asyncio
119+
@pytest.mark.parametrize("bus_value",[
120+
("A-B"),
121+
("12,12B,"),
122+
])
123+
async def test_when_buses_not_valid_then_buses_empty_array(bus_value: str):
124+
# Arrange
125+
original_config = {
126+
CONFIG_NAME: "test",
127+
CONFIG_STOP: "123",
128+
CONFIG_BUSES: bus_value
129+
}
130+
131+
async def async_mocked_get_bus_times(*args, **kwargs):
132+
return []
133+
134+
# Act
135+
with mock.patch.multiple(FirstBusApiClient, async_get_bus_times=async_mocked_get_bus_times):
136+
(errors, config) = await async_validate_main_config(original_config)
137+
138+
# Assert
139+
assert CONFIG_NAME not in errors
140+
assert CONFIG_STOP not in errors
141+
assert CONFIG_BUSES in errors
142+
assert errors[CONFIG_BUSES] == "invalid_buses"
143+
144+
assert CONFIG_NAME in config
145+
assert config[CONFIG_NAME] == original_config[CONFIG_NAME]
146+
assert CONFIG_STOP in config
147+
assert config[CONFIG_STOP] == original_config[CONFIG_STOP]
148+
assert CONFIG_BUSES in config
149+
assert config[CONFIG_BUSES] == original_config[CONFIG_BUSES]
150+
151+
@pytest.mark.asyncio
152+
async def test_when_bus_stop_is_invalid_then_no_errors_returned():
153+
# Arrange
154+
original_config = {
155+
CONFIG_NAME: "test",
156+
CONFIG_STOP: "123",
157+
CONFIG_BUSES: ""
158+
}
159+
160+
async def async_mocked_get_bus_times(*args, **kwargs):
161+
return None
162+
163+
# Act
164+
with mock.patch.multiple(FirstBusApiClient, async_get_bus_times=async_mocked_get_bus_times):
165+
(errors, config) = await async_validate_main_config(original_config)
166+
167+
# Assert
168+
assert CONFIG_NAME not in errors
169+
assert CONFIG_BUSES not in errors
170+
assert CONFIG_STOP in errors
171+
assert errors[CONFIG_STOP] == "invalid_stop"
172+
173+
assert CONFIG_NAME in config
174+
assert config[CONFIG_NAME] == original_config[CONFIG_NAME]
175+
assert CONFIG_STOP in config
176+
assert config[CONFIG_STOP] == original_config[CONFIG_STOP]
177+
assert CONFIG_BUSES in config
178+
assert config[CONFIG_BUSES] == []

0 commit comments

Comments
 (0)