33import pytest
44
55from ocpp .charge_point import camel_to_snake_case , remove_nones , snake_to_camel_case
6- from ocpp .routing import create_route_map , on
6+ from ocpp .messages import Call
7+ from ocpp .routing import after , create_route_map , on
8+ from ocpp .v16 import ChargePoint as cp_16
79from ocpp .v16 .call import (
810 BootNotificationPayload ,
911 GetConfigurationPayload ,
1012 MeterValuesPayload ,
1113)
14+ from ocpp .v16 .call_result import (
15+ BootNotificationPayload as BootNotificationResultPayload ,
16+ )
1217from ocpp .v16 .datatypes import MeterValue , SampledValue
13- from ocpp .v16 .enums import Action
14- from ocpp .v20 import ChargePoint as cp
18+ from ocpp .v16 .enums import Action , RegistrationStatus
19+ from ocpp .v20 import ChargePoint as cp_20
1520from ocpp .v201 .call import SetNetworkProfilePayload
1621from ocpp .v201 .datatypes import NetworkConnectionProfileType
1722from ocpp .v201 .enums import OCPPInterfaceType , OCPPTransportType , OCPPVersionType
1823
1924
2025def test_getters_should_not_be_called_during_routemap_setup ():
21- class ChargePoint (cp ):
26+ class ChargePoint (cp_20 ):
2227 @property
2328 def foo (self ):
2429 raise RuntimeError ("this will be raised" )
@@ -31,12 +36,12 @@ def foo(self):
3136
3237
3338def test_multiple_classes_with_same_name_for_handler ():
34- class ChargerA (cp ):
39+ class ChargerA (cp_20 ):
3540 @on (Action .Heartbeat )
3641 def heartbeat (self , ** kwargs ):
3742 pass
3843
39- class ChargerB (cp ):
44+ class ChargerB (cp_20 ):
4045 @on (Action .Heartbeat )
4146 def heartbeat (self , ** kwargs ):
4247 pass
@@ -232,3 +237,101 @@ def test_remove_nones_with_list_of_strings():
232237 assert remove_nones (payload ) == {
233238 "key" : ["ClockAlignedDataInterval" , "ConnectionTimeOut" ]
234239 }
240+
241+
242+ @pytest .mark .asyncio
243+ async def test_call_unique_id_added_to_handler_args_correctly (connection ):
244+ """
245+ This test ensures that the `call_unique_id` is getting passed to the
246+ `on` and `after` handlers only if it is explicitly set in the handler signature.
247+
248+ To cover all possible cases, we define two chargers:
249+
250+ ChargerA:
251+ `call_unique_id` not required on `on` handler but required on `after` handler.
252+
253+ ChargerB:
254+ `call_unique_id` required on `on` handler but not required on `after` handler.
255+
256+ Each handler verifies a set of asserts to verify that the `call_unique_id`
257+ is passed correctly.
258+ To confirm that the handlers are actually being called and hence the asserts
259+ are being ran, we introduce a set of counters that increase each time a specific
260+ handler runs.
261+ """
262+ charger_a_test_call_unique_id = "charger_a_1234"
263+ charger_b_test_call_unique_id = "charger_b_5678"
264+ payload_a = {"chargePointVendor" : "foo_a" , "chargePointModel" : "bar_a" }
265+ payload_b = {"chargePointVendor" : "foo_b" , "chargePointModel" : "bar_b" }
266+
267+ class ChargerA (cp_16 ):
268+ on_boot_notification_call_count = 0
269+ after_boot_notification_call_count = 0
270+
271+ @on (Action .BootNotification )
272+ def on_boot_notification (self , * args , ** kwargs ):
273+ # call_unique_id should not be passed as arg nor kwarg
274+ assert kwargs == camel_to_snake_case (payload_a )
275+ assert args == ()
276+ ChargerA .on_boot_notification_call_count += 1
277+ return BootNotificationResultPayload (
278+ current_time = "foo" , interval = 1 , status = RegistrationStatus .accepted
279+ )
280+
281+ @after (Action .BootNotification )
282+ def after_boot_notification (self , call_unique_id , * args , ** kwargs ):
283+ assert call_unique_id == charger_a_test_call_unique_id
284+ assert kwargs == camel_to_snake_case (payload_a )
285+ # call_unique_id should not be passed as arg
286+ assert args == ()
287+ ChargerA .after_boot_notification_call_count += 1
288+ return BootNotificationResultPayload (
289+ current_time = "foo" , interval = 1 , status = RegistrationStatus .accepted
290+ )
291+
292+ class ChargerB (cp_16 ):
293+ on_boot_notification_call_count = 0
294+ after_boot_notification_call_count = 0
295+
296+ @on (Action .BootNotification )
297+ def on_boot_notification (self , call_unique_id , * args , ** kwargs ):
298+ assert call_unique_id == charger_b_test_call_unique_id
299+ assert kwargs == camel_to_snake_case (payload_b )
300+ # call_unique_id should not be passed as arg
301+ assert args == ()
302+ ChargerB .on_boot_notification_call_count += 1
303+ return BootNotificationResultPayload (
304+ current_time = "foo" , interval = 1 , status = RegistrationStatus .accepted
305+ )
306+
307+ @after (Action .BootNotification )
308+ def after_boot_notification (self , * args , ** kwargs ):
309+ # call_unique_id should not be passed as arg nor kwarg
310+ assert kwargs == camel_to_snake_case (payload_b )
311+ assert args == ()
312+ ChargerB .after_boot_notification_call_count += 1
313+ return BootNotificationResultPayload (
314+ current_time = "foo" , interval = 1 , status = RegistrationStatus .accepted
315+ )
316+
317+ charger_a = ChargerA ("charger_a_id" , connection )
318+ charger_b = ChargerB ("charger_b_id" , connection )
319+
320+ msg_a = Call (
321+ unique_id = charger_a_test_call_unique_id ,
322+ action = Action .BootNotification .value ,
323+ payload = payload_a ,
324+ )
325+ await charger_a ._handle_call (msg_a )
326+
327+ msg_b = Call (
328+ unique_id = charger_b_test_call_unique_id ,
329+ action = Action .BootNotification .value ,
330+ payload = payload_b ,
331+ )
332+ await charger_b ._handle_call (msg_b )
333+
334+ assert ChargerA .on_boot_notification_call_count == 1
335+ assert ChargerA .after_boot_notification_call_count == 1
336+ assert ChargerB .on_boot_notification_call_count == 1
337+ assert ChargerB .after_boot_notification_call_count == 1
0 commit comments