- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
156 lines
5.3 KiB
Python
156 lines
5.3 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# Copyright Pionix GmbH and Contributors to EVerest
|
|
|
|
import json
|
|
|
|
import pytest
|
|
from everest.testing.core_utils.controller.test_controller_interface import (
|
|
TestController,
|
|
)
|
|
|
|
from ocpp.v201.datatypes import SetVariableDataType, VariableType, ComponentType
|
|
from ocpp.v201 import call as call201
|
|
from ocpp.messages import Call, _DecimalEncoder
|
|
from ocpp.charge_point import asdict, remove_nones, snake_to_camel_case
|
|
|
|
# fmt: off
|
|
from everest.testing.ocpp_utils.charge_point_utils import TestUtility
|
|
from everest_test_utils import get_everest_config_path_str, send_message_without_validation
|
|
from everest.testing.ocpp_utils.central_system import CentralSystem
|
|
from everest.testing.ocpp_utils.charge_point_v201 import ChargePoint201
|
|
from validations import wait_for_callerror_and_validate
|
|
# fmt: on
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.ocpp_version("ocpp2.0.1")
|
|
@pytest.mark.everest_core_config(
|
|
get_everest_config_path_str("everest-config-ocpp201.yaml")
|
|
)
|
|
async def test_invalid_payloads(
|
|
central_system_v201: CentralSystem,
|
|
charge_point_v201: ChargePoint201,
|
|
test_controller: TestController,
|
|
test_utility: TestUtility,
|
|
):
|
|
|
|
# a payload field that is too long should trigger a FormationViolation CALLERROR
|
|
too_long_component_type_name_payload = call201.SetVariables(
|
|
set_variable_data=[SetVariableDataType(attribute_value="abc", component=ComponentType(
|
|
name="ThisIsMuchLongerThan50charactersThisIsMuchLongerThan50charactersThisIsMuchLongerThan50charactersThisIsMuchLongerThan50charactersThisIsMuchLongerThan50characters"), variable=VariableType(name="VariableName"))]
|
|
)
|
|
camel_case_payload = snake_to_camel_case(
|
|
asdict(too_long_component_type_name_payload))
|
|
|
|
call_msg = Call(
|
|
unique_id=str(charge_point_v201._unique_id_generator()),
|
|
action=too_long_component_type_name_payload.__class__.__name__,
|
|
payload=remove_nones(camel_case_payload),
|
|
)
|
|
|
|
await send_message_without_validation(charge_point_v201, call_msg)
|
|
|
|
assert await wait_for_callerror_and_validate(
|
|
test_utility, charge_point_v201, "FormationViolation"
|
|
)
|
|
|
|
# a unknown action should trigger a FormationViolation CALLERROR
|
|
call_msg = Call(
|
|
unique_id=str(charge_point_v201._unique_id_generator()),
|
|
action="ThisIsAnUnknownAction",
|
|
payload="Invalid",
|
|
)
|
|
|
|
await send_message_without_validation(charge_point_v201, call_msg)
|
|
|
|
assert await wait_for_callerror_and_validate(
|
|
test_utility, charge_point_v201, "FormationViolation"
|
|
)
|
|
|
|
# a malformed CALL should trigger a RpcFrameworkError CALLERROR
|
|
call_msg = "{Malformed"
|
|
|
|
async with charge_point_v201._call_lock:
|
|
await charge_point_v201._send(call_msg)
|
|
|
|
assert await wait_for_callerror_and_validate(
|
|
test_utility, charge_point_v201, "RpcFrameworkError"
|
|
)
|
|
|
|
# a malformed CALL should trigger a RpcFrameworkError CALLERROR
|
|
call_msg = b"\xd8\x00\x00\x00"
|
|
|
|
async with charge_point_v201._call_lock:
|
|
await charge_point_v201._send(call_msg)
|
|
|
|
assert await wait_for_callerror_and_validate(
|
|
test_utility, charge_point_v201, "RpcFrameworkError"
|
|
)
|
|
|
|
# a invalid payload should trigger a FormationViolation CALLERROR
|
|
call_msg = Call(
|
|
unique_id=str(charge_point_v201._unique_id_generator()),
|
|
action="SetVariables",
|
|
payload=None,
|
|
)
|
|
|
|
await send_message_without_validation(charge_point_v201, call_msg)
|
|
|
|
assert await wait_for_callerror_and_validate(
|
|
test_utility, charge_point_v201, "FormationViolation"
|
|
)
|
|
|
|
# a CALL message without UUID, action and payload should trigger a RpcFrameworkError CALLERROR
|
|
json_data = json.dumps(
|
|
[
|
|
2, # CALL
|
|
],
|
|
# By default json.dumps() adds a white space after every separator.
|
|
# By setting the separator manually that can be avoided.
|
|
separators=(",", ":"),
|
|
cls=_DecimalEncoder,
|
|
)
|
|
|
|
async with charge_point_v201._call_lock:
|
|
await charge_point_v201._send(json_data)
|
|
|
|
assert await wait_for_callerror_and_validate(
|
|
test_utility, charge_point_v201, "RpcFrameworkError"
|
|
)
|
|
|
|
# a completely empty message should trigger a RpcFrameworkError CALLERROR
|
|
json_data = json.dumps(
|
|
[
|
|
],
|
|
# By default json.dumps() adds a white space after every separator.
|
|
# By setting the separator manually that can be avoided.
|
|
separators=(",", ":"),
|
|
cls=_DecimalEncoder,
|
|
)
|
|
|
|
async with charge_point_v201._call_lock:
|
|
await charge_point_v201._send(json_data)
|
|
|
|
assert await wait_for_callerror_and_validate(
|
|
test_utility, charge_point_v201, "RpcFrameworkError"
|
|
)
|
|
|
|
# a unknown RPC message should trigger a MessageTypeNotSupported CALLERROR
|
|
json_data = json.dumps(
|
|
[
|
|
99, # should be unknown
|
|
"MessageId",
|
|
],
|
|
# By default json.dumps() adds a white space after every separator.
|
|
# By setting the separator manually that can be avoided.
|
|
separators=(",", ":"),
|
|
cls=_DecimalEncoder,
|
|
)
|
|
|
|
async with charge_point_v201._call_lock:
|
|
await charge_point_v201._send(json_data)
|
|
|
|
assert await wait_for_callerror_and_validate(
|
|
test_utility, charge_point_v201, "MessageTypeNotSupported"
|
|
)
|