| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- import asyncio
- import json
- from unittest import mock
- import pytest
- from ocpp.exceptions import FormatViolationError, GenericError
- from ocpp.messages import CallError
- from ocpp.routing import after, create_route_map, on
- from ocpp.v16 import ChargePoint, call, call_result
- from ocpp.v16.enums import Action
- @pytest.mark.asyncio
- async def test_route_message_with_existing_route(
- base_central_system, boot_notification_call
- ):
- """Test if the correct handler is called when routing a message.
- Also test if payload of request is injected correctly in handler.
- """
- @on(Action.BootNotification)
- def on_boot_notification(charge_point_model, charge_point_vendor, **kwargs): # noqa
- assert charge_point_vendor == "Alfen BV"
- assert charge_point_model == "ICU Eve Mini"
- assert kwargs["firmware_version"] == "#1:3.4.0-2990#N:217H;1.0-223"
- return call_result.BootNotificationPayload(
- current_time="2018-05-29T17:37:05.495259",
- interval=350,
- status="Accepted",
- )
- @after(Action.BootNotification)
- async def after_boot_notification(
- charge_point_model, charge_point_vendor, **kwargs
- ): # noqa
- assert charge_point_vendor == "Alfen BV"
- assert charge_point_model == "ICU Eve Mini"
- setattr(base_central_system, "on_boot_notification", on_boot_notification)
- setattr(base_central_system, "after_boot_notification", after_boot_notification)
- base_central_system.route_map = create_route_map(base_central_system)
- await base_central_system.route_message(boot_notification_call)
- base_central_system._connection.send.assert_called_once_with(
- json.dumps(
- [
- 3,
- "1",
- {
- "currentTime": "2018-05-29T17:37:05.495259",
- "interval": 350,
- "status": "Accepted",
- },
- ],
- separators=(",", ":"),
- )
- )
- @pytest.mark.asyncio
- async def test_route_message_without_validation(base_central_system):
- @on(Action.BootNotification, skip_schema_validation=True)
- def on_boot_notification(**kwargs): # noqa
- assert kwargs["firmware_version"] == "#1:3.4.0-2990#N:217H;1.0-223"
- return call_result.BootNotificationPayload(
- current_time="2018-05-29T17:37:05.495259",
- interval=350,
- # 'Yolo' is not a valid value for for field status.
- status="Yolo",
- )
- setattr(base_central_system, "on_boot_notification", on_boot_notification)
- base_central_system.route_map = create_route_map(base_central_system)
- await base_central_system.route_message(
- json.dumps(
- [
- 2,
- 1,
- "BootNotification",
- {
- # The payload is missing the required fields 'chargepointVendor'
- # and 'chargePointModel'.
- "firmwareVersion": "#1:3.4.0-2990#N:217H;1.0-223"
- },
- ],
- separators=(",", ":"),
- )
- )
- base_central_system._connection.send.call_args == mock.call(
- json.dumps(
- [
- 3,
- "1",
- {
- "currentTime": "2018-05-29T17:37:05.495259",
- "interval": 350,
- "status": "Yolo",
- },
- ],
- separators=(",", ":"),
- )
- )
- @pytest.mark.asyncio
- async def test_route_message_not_supported(base_central_system, not_supported_call):
- """
- Test that a CALLERROR is sent back, reporting that it is
- not supported by OCPP version.
- """
- @on(Action.BootNotification)
- def on_boot_notification(**kwargs): # noqa
- assert kwargs["firmware_version"] == "#1:3.4.0-2990#N:217H;1.0-223"
- return call_result.BootNotificationPayload(
- current_time="2018-05-29T17:37:05.495259",
- interval=350,
- # 'Yolo' is not a valid value for for field status.
- status="Yolo",
- )
- setattr(base_central_system, "on_boot_notification", on_boot_notification)
- base_central_system.route_map = create_route_map(base_central_system)
- await base_central_system.route_message(not_supported_call)
- base_central_system._connection.send.assert_called_once_with(
- json.dumps(
- [
- 4,
- 1,
- "NotSupported",
- "Requested Action is not known by receiver",
- {"cause": "InvalidAction not supported by OCPP1.6."},
- ],
- separators=(",", ":"),
- )
- )
- @pytest.mark.asyncio
- async def test_route_message_with_no_route(base_central_system, heartbeat_call):
- """
- Test that a CALLERROR is sent back, reporting that no handler is
- registred for it.
- """
- # Empty the route map
- base_central_system.route_map = {}
- await base_central_system.route_message(heartbeat_call)
- base_central_system._connection.send.assert_called_once_with(
- json.dumps(
- [
- 4,
- 1,
- "NotImplemented",
- "Request Action is recognized but not supported by the receiver",
- {"cause": "No handler for Heartbeat registered."},
- ],
- separators=(",", ":"),
- )
- )
- @pytest.mark.asyncio
- async def test_send_call_with_timeout(connection):
- cs = ChargePoint(id=1234, connection=connection, response_timeout=0.1)
- payload = call.ResetPayload(type="Hard")
- with pytest.raises(asyncio.TimeoutError):
- await cs.call(payload)
- # Verify that lock is released if call() crahses. Not releasing the lock
- # in case of an exception could lead to a deadlock. See
- # https://github.com/mobilityhouse/ocpp/issues/46
- assert cs._call_lock.locked() is False
- @pytest.mark.asyncio
- async def test_send_invalid_call(base_central_system):
- payload = call.ResetPayload(type="Medium")
- with pytest.raises(FormatViolationError):
- await base_central_system.call(payload)
- @pytest.mark.asyncio
- async def test_raise_call_error(base_central_system):
- """
- Test that getting a CallError will raise an Exception
- if suppress argument is not True.
- """
- call_error = CallError(
- unique_id="1337",
- error_code="GenericError",
- error_description="test_raise_call_error",
- )
- await base_central_system.route_message(call_error.to_json())
- payload = call.ClearCachePayload()
- with pytest.raises(GenericError):
- await base_central_system.call(payload, suppress=False)
- @pytest.mark.asyncio
- async def test_suppress_call_error(base_central_system):
- """
- Test that getting a CallError will suppress Exception
- by default
- """
- call_error = CallError(
- unique_id="1337",
- error_code="GenericError",
- error_description="test_raise_call_error",
- )
- await base_central_system.route_message(call_error.to_json())
- payload = call.ClearCachePayload()
- await base_central_system.call(payload)
- @pytest.mark.asyncio
- async def test_call_with_unique_id_should_return_same_id(
- mock_boot_request, mock_base_central_system
- ):
- expected_unique_id = "12345"
- # Call the method being tested with a unique_id as a parameter
- await mock_base_central_system.call(mock_boot_request, unique_id=expected_unique_id)
- (
- actual_unique_id,
- _,
- ) = mock_base_central_system._get_specific_response.call_args_list[0][0]
- # Check the actual unique id is equals to the one passed to the call method
- assert actual_unique_id == expected_unique_id
- @pytest.mark.asyncio
- async def test_call_without_unique_id_should_return_a_random_value(
- mock_boot_request, mock_base_central_system
- ):
- expected_unique_id = str(mock_base_central_system._unique_id_generator())
- # Call the method being tested without passing a unique_id as a parameter
- await mock_base_central_system.call(mock_boot_request)
- (
- actual_unique_id,
- _,
- ) = mock_base_central_system._get_specific_response.call_args_list[0][0]
- # Check the actual unique id is equals to the one internally generated
- assert actual_unique_id == expected_unique_id
|