Skip to content

Commit

Permalink
fix(client): prevent KeyError when inputs/outputs/sectors are not in …
Browse files Browse the repository at this point in the history
…sync with the cloud
  • Loading branch information
palazzem committed Dec 6, 2023
1 parent a254504 commit 74ea4ef
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 1 deletion.
5 changes: 4 additions & 1 deletion src/elmo/api/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -653,13 +653,16 @@ def query(self, query):
# Address potential data inconsistency between cloud data and main unit.
# In some installations, they may be out of sync, resulting in the cloud
# providing a sector/input/output that doesn't actually exist in the main unit.
# This case happens also when all inputs or sectors or outputs are used in the
# main unit, but their strings are not synchronized with the cloud.
# To handle this, we default the name to "Unknown" if its description
# isn't found in the cloud data to prevent KeyError.
description = descriptions.get(query, {})
item = {
"id": entry.get("Id"),
"index": entry.get("Index"),
"element": entry.get("Element"),
"name": descriptions[query].get(entry["Index"], "Unknown"),
"name": description.get(entry["Index"], "Unknown"),
}

if query == q.SECTORS:
Expand Down
131 changes: 131 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1928,6 +1928,137 @@ def test_client_get_outputs_status(server, mocker):
}


def test_client_missing_sectors_strings(server, mocker):
"""The query should return an empty list if outputs strings are not synchronized.
Regression test for: https://github.com/palazzem/ha-econnect-alarm/issues/115
"""
html = """[
{
"Active": true,
"ActivePartial": false,
"Max": false,
"Activable": true,
"ActivablePartial": false,
"InUse": true,
"Id": 1,
"Index": 0,
"Element": 1,
"CommandId": 0,
"InProgress": false
}
]"""
server.add(responses.POST, "https://example.com/api/areas", body=html, status=200)
client = ElmoClient(base_url="https://example.com", domain="domain")
client._session_id = "test"
mocker.patch.object(client, "_get_descriptions")
client._get_descriptions.return_value = {}
# Test
sectors = client.query(query.SECTORS)
# Expected output
assert client._get_descriptions.called is True
assert len(server.calls) == 1
assert sectors == {
"last_id": 1,
"sectors": {
0: {
"element": 1,
"id": 1,
"index": 0,
"status": True,
"activable": True,
"name": "Unknown",
},
},
}


def test_client_missing_inputs_strings(server, mocker):
"""The query should return an empty list if outputs strings are not synchronized.
Regression test for: https://github.com/palazzem/ha-econnect-alarm/issues/115
"""
html = """[
{
"Alarm": true,
"MemoryAlarm": false,
"Excluded": false,
"InUse": true,
"IsVideo": false,
"Id": 1,
"Index": 0,
"Element": 1,
"CommandId": 0,
"InProgress": false
}
]"""
server.add(responses.POST, "https://example.com/api/inputs", body=html, status=200)
client = ElmoClient(base_url="https://example.com", domain="domain")
client._session_id = "test"
mocker.patch.object(client, "_get_descriptions")
client._get_descriptions.return_value = {}
# Test
inputs = client.query(query.INPUTS)
# Expected output
assert client._get_descriptions.called is True
assert len(server.calls) == 1
assert inputs == {
"last_id": 1,
"inputs": {
0: {
"element": 1,
"id": 1,
"index": 0,
"status": True,
"excluded": False,
"name": "Unknown",
},
},
}


def test_client_missing_outputs_strings(server, mocker):
"""The query should return an empty list if outputs strings are not synchronized.
Regression test for: https://github.com/palazzem/ha-econnect-alarm/issues/115
"""
html = """[
{
"Active": true,
"InUse": true,
"DoNotRequireAuthentication": true,
"ControlDeniedToUsers": false,
"Id": 400258,
"Index": 0,
"Element": 1,
"CommandId": 0,
"InProgress": false
}
]"""
server.add(responses.POST, "https://example.com/api/outputs", body=html, status=200)
client = ElmoClient(base_url="https://example.com", domain="domain")
client._session_id = "test"
mocker.patch.object(client, "_get_descriptions")
client._get_descriptions.return_value = {}
# Test
outputs = client.query(query.OUTPUTS)
# Expected output
assert client._get_descriptions.called is True
assert len(server.calls) == 1

assert outputs == {
"last_id": 400258,
"outputs": {
0: {
"control_denied_to_users": False,
"do_not_require_authentication": True,
"element": 1,
"id": 400258,
"index": 0,
"name": "Unknown",
"status": True,
},
},
}


def test_client_get_sectors_missing_area(server, mocker):
"""Should set an Unknown `sector` name if the description is missing.
Regression test for: https://github.com/palazzem/econnect-python/issues/91"""
Expand Down

0 comments on commit 74ea4ef

Please sign in to comment.