Brak opisu

test_v16_charge_point.py 8.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. import asyncio
  2. import json
  3. from unittest import mock
  4. import pytest
  5. from ocpp.exceptions import FormatViolationError, GenericError
  6. from ocpp.messages import CallError
  7. from ocpp.routing import after, create_route_map, on
  8. from ocpp.v16 import ChargePoint, call, call_result
  9. from ocpp.v16.enums import Action
  10. @pytest.mark.asyncio
  11. async def test_route_message_with_existing_route(
  12. base_central_system, boot_notification_call
  13. ):
  14. """Test if the correct handler is called when routing a message.
  15. Also test if payload of request is injected correctly in handler.
  16. """
  17. @on(Action.BootNotification)
  18. def on_boot_notification(charge_point_model, charge_point_vendor, **kwargs): # noqa
  19. assert charge_point_vendor == "Alfen BV"
  20. assert charge_point_model == "ICU Eve Mini"
  21. assert kwargs["firmware_version"] == "#1:3.4.0-2990#N:217H;1.0-223"
  22. return call_result.BootNotificationPayload(
  23. current_time="2018-05-29T17:37:05.495259",
  24. interval=350,
  25. status="Accepted",
  26. )
  27. @after(Action.BootNotification)
  28. async def after_boot_notification(
  29. charge_point_model, charge_point_vendor, **kwargs
  30. ): # noqa
  31. assert charge_point_vendor == "Alfen BV"
  32. assert charge_point_model == "ICU Eve Mini"
  33. setattr(base_central_system, "on_boot_notification", on_boot_notification)
  34. setattr(base_central_system, "after_boot_notification", after_boot_notification)
  35. base_central_system.route_map = create_route_map(base_central_system)
  36. await base_central_system.route_message(boot_notification_call)
  37. base_central_system._connection.send.assert_called_once_with(
  38. json.dumps(
  39. [
  40. 3,
  41. "1",
  42. {
  43. "currentTime": "2018-05-29T17:37:05.495259",
  44. "interval": 350,
  45. "status": "Accepted",
  46. },
  47. ],
  48. separators=(",", ":"),
  49. )
  50. )
  51. @pytest.mark.asyncio
  52. async def test_route_message_without_validation(base_central_system):
  53. @on(Action.BootNotification, skip_schema_validation=True)
  54. def on_boot_notification(**kwargs): # noqa
  55. assert kwargs["firmware_version"] == "#1:3.4.0-2990#N:217H;1.0-223"
  56. return call_result.BootNotificationPayload(
  57. current_time="2018-05-29T17:37:05.495259",
  58. interval=350,
  59. # 'Yolo' is not a valid value for for field status.
  60. status="Yolo",
  61. )
  62. setattr(base_central_system, "on_boot_notification", on_boot_notification)
  63. base_central_system.route_map = create_route_map(base_central_system)
  64. await base_central_system.route_message(
  65. json.dumps(
  66. [
  67. 2,
  68. 1,
  69. "BootNotification",
  70. {
  71. # The payload is missing the required fields 'chargepointVendor'
  72. # and 'chargePointModel'.
  73. "firmwareVersion": "#1:3.4.0-2990#N:217H;1.0-223"
  74. },
  75. ],
  76. separators=(",", ":"),
  77. )
  78. )
  79. base_central_system._connection.send.call_args == mock.call(
  80. json.dumps(
  81. [
  82. 3,
  83. "1",
  84. {
  85. "currentTime": "2018-05-29T17:37:05.495259",
  86. "interval": 350,
  87. "status": "Yolo",
  88. },
  89. ],
  90. separators=(",", ":"),
  91. )
  92. )
  93. @pytest.mark.asyncio
  94. async def test_route_message_not_supported(base_central_system, not_supported_call):
  95. """
  96. Test that a CALLERROR is sent back, reporting that it is
  97. not supported by OCPP version.
  98. """
  99. @on(Action.BootNotification)
  100. def on_boot_notification(**kwargs): # noqa
  101. assert kwargs["firmware_version"] == "#1:3.4.0-2990#N:217H;1.0-223"
  102. return call_result.BootNotificationPayload(
  103. current_time="2018-05-29T17:37:05.495259",
  104. interval=350,
  105. # 'Yolo' is not a valid value for for field status.
  106. status="Yolo",
  107. )
  108. setattr(base_central_system, "on_boot_notification", on_boot_notification)
  109. base_central_system.route_map = create_route_map(base_central_system)
  110. await base_central_system.route_message(not_supported_call)
  111. base_central_system._connection.send.assert_called_once_with(
  112. json.dumps(
  113. [
  114. 4,
  115. 1,
  116. "NotSupported",
  117. "Requested Action is not known by receiver",
  118. {"cause": "InvalidAction not supported by OCPP1.6."},
  119. ],
  120. separators=(",", ":"),
  121. )
  122. )
  123. @pytest.mark.asyncio
  124. async def test_route_message_with_no_route(base_central_system, heartbeat_call):
  125. """
  126. Test that a CALLERROR is sent back, reporting that no handler is
  127. registred for it.
  128. """
  129. # Empty the route map
  130. base_central_system.route_map = {}
  131. await base_central_system.route_message(heartbeat_call)
  132. base_central_system._connection.send.assert_called_once_with(
  133. json.dumps(
  134. [
  135. 4,
  136. 1,
  137. "NotImplemented",
  138. "Request Action is recognized but not supported by the receiver",
  139. {"cause": "No handler for Heartbeat registered."},
  140. ],
  141. separators=(",", ":"),
  142. )
  143. )
  144. @pytest.mark.asyncio
  145. async def test_send_call_with_timeout(connection):
  146. cs = ChargePoint(id=1234, connection=connection, response_timeout=0.1)
  147. payload = call.ResetPayload(type="Hard")
  148. with pytest.raises(asyncio.TimeoutError):
  149. await cs.call(payload)
  150. # Verify that lock is released if call() crahses. Not releasing the lock
  151. # in case of an exception could lead to a deadlock. See
  152. # https://github.com/mobilityhouse/ocpp/issues/46
  153. assert cs._call_lock.locked() is False
  154. @pytest.mark.asyncio
  155. async def test_send_invalid_call(base_central_system):
  156. payload = call.ResetPayload(type="Medium")
  157. with pytest.raises(FormatViolationError):
  158. await base_central_system.call(payload)
  159. @pytest.mark.asyncio
  160. async def test_raise_call_error(base_central_system):
  161. """
  162. Test that getting a CallError will raise an Exception
  163. if suppress argument is not True.
  164. """
  165. call_error = CallError(
  166. unique_id="1337",
  167. error_code="GenericError",
  168. error_description="test_raise_call_error",
  169. )
  170. await base_central_system.route_message(call_error.to_json())
  171. payload = call.ClearCachePayload()
  172. with pytest.raises(GenericError):
  173. await base_central_system.call(payload, suppress=False)
  174. @pytest.mark.asyncio
  175. async def test_suppress_call_error(base_central_system):
  176. """
  177. Test that getting a CallError will suppress Exception
  178. by default
  179. """
  180. call_error = CallError(
  181. unique_id="1337",
  182. error_code="GenericError",
  183. error_description="test_raise_call_error",
  184. )
  185. await base_central_system.route_message(call_error.to_json())
  186. payload = call.ClearCachePayload()
  187. await base_central_system.call(payload)
  188. @pytest.mark.asyncio
  189. async def test_call_with_unique_id_should_return_same_id(
  190. mock_boot_request, mock_base_central_system
  191. ):
  192. expected_unique_id = "12345"
  193. # Call the method being tested with a unique_id as a parameter
  194. await mock_base_central_system.call(mock_boot_request, unique_id=expected_unique_id)
  195. (
  196. actual_unique_id,
  197. _,
  198. ) = mock_base_central_system._get_specific_response.call_args_list[0][0]
  199. # Check the actual unique id is equals to the one passed to the call method
  200. assert actual_unique_id == expected_unique_id
  201. @pytest.mark.asyncio
  202. async def test_call_without_unique_id_should_return_a_random_value(
  203. mock_boot_request, mock_base_central_system
  204. ):
  205. expected_unique_id = str(mock_base_central_system._unique_id_generator())
  206. # Call the method being tested without passing a unique_id as a parameter
  207. await mock_base_central_system.call(mock_boot_request)
  208. (
  209. actual_unique_id,
  210. _,
  211. ) = mock_base_central_system._get_specific_response.call_args_list[0][0]
  212. # Check the actual unique id is equals to the one internally generated
  213. assert actual_unique_id == expected_unique_id