ud3tn/test/integration/test_aap2_behavior.py
Felix Walter b627eb1f24 python: Join nested with statements with comma
Instead of

with a:
    with b:
        ...

we can use:

with a, b:
    ...

This makes the code more compact.

Signed-off-by: Felix Walter <felix.walter@d3tn.com>
2024-06-21 11:16:07 +02:00

255 lines
8.3 KiB
Python

# SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
import pytest
import select
import time
from ud3tn_utils.aap2 import (
AAP2UnixClient,
AAPMessage,
BundleADU,
Keepalive,
ResponseStatus,
AAP2OperationFailed,
)
from .helpers import (
AAP2_AGENT_ID,
AAP2_SECRET,
AAP2_SOCKET,
TEST_AAP2,
AAP2_BDM_SECRET,
)
WAIT_TIMEOUT = 3
TIMING_MARGIN = 0.5
AAP2_AGENT_TIMEOUT = 1
def _wait_for_data(sock, timeout=WAIT_TIMEOUT):
rl, _, _ = select.select([sock], [], [], timeout)
assert len(rl) > 0, "timed out"
def _wait_until_connection_closed(sock, timeout=WAIT_TIMEOUT):
_wait_for_data(sock, timeout)
try:
rv = sock.recv(1)
assert not rv
except ConnectionResetError:
pass
@pytest.mark.skipif(not TEST_AAP2, reason="TEST_AAP2 disabled via environment")
def test_aap2_re_register():
# Test that we can register again with the same agent but a different
# secret after closing the connection.
client = AAP2UnixClient(address=AAP2_SOCKET)
with client:
client.configure(
AAP2_AGENT_ID + "-b",
subscribe=False,
secret=AAP2_SECRET,
)
client.send(AAPMessage(keepalive=Keepalive()))
response = client.receive_response()
assert response.response_status == ResponseStatus.RESPONSE_STATUS_ACK
client = AAP2UnixClient(address=AAP2_SOCKET)
with client:
client.configure(
AAP2_AGENT_ID + "-b",
subscribe=False,
secret=AAP2_SECRET + "123",
)
client.send(AAPMessage(keepalive=Keepalive()))
response = client.receive_response()
assert response.response_status == ResponseStatus.RESPONSE_STATUS_ACK
@pytest.mark.skipif(not TEST_AAP2, reason="TEST_AAP2 disabled via environment")
def test_aap2_reconfigure():
client = AAP2UnixClient(address=AAP2_SOCKET)
with client:
client.configure(
AAP2_AGENT_ID + "-b3",
subscribe=False,
secret=AAP2_SECRET,
)
client.configure(
AAP2_AGENT_ID + "-b4",
subscribe=False,
secret=AAP2_SECRET,
)
assert client.agent_id == AAP2_AGENT_ID + "-b4"
@pytest.mark.skipif(not TEST_AAP2, reason="TEST_AAP2 disabled via environment")
def test_aap2_fail_on_wrong_secret():
rpc_client = AAP2UnixClient(address=AAP2_SOCKET)
sub_client = AAP2UnixClient(address=AAP2_SOCKET)
with rpc_client, sub_client:
s1 = rpc_client.configure(
AAP2_AGENT_ID + "-b5",
subscribe=False,
)
# Test reconfiguration with correct secret returning it correctly
s2 = sub_client.configure(
AAP2_AGENT_ID + "-b5",
subscribe=True,
secret=s1,
)
assert s1 == s2
# Test providing invalid secret
try:
rpc_client.configure(
AAP2_AGENT_ID + "-b5",
subscribe=False,
secret="test-invalid",
)
except AAP2OperationFailed:
pass
else:
assert False, "did not fail as expected"
@pytest.mark.skipif(AAP2_BDM_SECRET is None,
reason="AAP2_BDM_SECRET not specified")
def test_aap2_succeed_on_admin_secret():
rpc_client = AAP2UnixClient(address=AAP2_SOCKET)
sub_client = AAP2UnixClient(address=AAP2_SOCKET)
with rpc_client, sub_client:
s1 = rpc_client.configure(
AAP2_AGENT_ID + "-b5",
subscribe=False,
)
# Test reconfiguration with correct secret returning it correctly
s2 = sub_client.configure(
AAP2_AGENT_ID + "-b5",
subscribe=True,
secret=s1,
)
assert s1 == s2
# Test providing admin secret
s3 = rpc_client.configure(
AAP2_AGENT_ID + "-b5",
subscribe=False,
secret=AAP2_BDM_SECRET,
)
assert s3 == AAP2_BDM_SECRET
@pytest.mark.skipif(not TEST_AAP2, reason="TEST_AAP2 disabled via environment")
def test_aap2_keepalive():
sub_client = AAP2UnixClient(address=AAP2_SOCKET)
with sub_client:
sub_client.configure(
AAP2_AGENT_ID + "-b6",
subscribe=True,
secret=AAP2_SECRET,
keepalive_seconds=1,
)
# Test keepalive timing
t1 = time.time()
_wait_for_data(sub_client.socket)
ka_msg = sub_client.receive_msg()
t2 = time.time()
assert ka_msg.WhichOneof("msg") == "keepalive"
assert t1 + 1 + TIMING_MARGIN >= t2
sub_client.send_response_status(ResponseStatus.RESPONSE_STATUS_ACK)
t3 = time.time()
_wait_for_data(sub_client.socket)
ka_msg = sub_client.receive_msg()
t4 = time.time()
assert ka_msg.WhichOneof("msg") == "keepalive"
assert t3 + 1 + TIMING_MARGIN >= t4
# Test sending the wrong response
sub_client.send_response_status(ResponseStatus.RESPONSE_STATUS_ERROR)
_wait_until_connection_closed(sub_client.socket)
# Test sending no response
sub_client = AAP2UnixClient(address=AAP2_SOCKET)
with sub_client:
sub_client.configure(
AAP2_AGENT_ID + "-b7",
subscribe=True,
secret=AAP2_SECRET,
keepalive_seconds=1,
)
t5 = time.time()
_wait_for_data(sub_client.socket)
ka_msg = sub_client.receive_msg()
t6 = time.time()
assert ka_msg.WhichOneof("msg") == "keepalive"
assert t5 + 1 + TIMING_MARGIN >= t6
_wait_until_connection_closed(sub_client.socket)
t7 = time.time()
assert t6 + AAP2_AGENT_TIMEOUT + TIMING_MARGIN >= t7
# Test RPC keepalive behavior
rpc_client = AAP2UnixClient(address=AAP2_SOCKET)
with rpc_client:
rpc_client.configure(
AAP2_AGENT_ID + "-b72",
subscribe=False,
keepalive_seconds=1,
)
rpc_client.send(AAPMessage(keepalive=Keepalive()))
response = rpc_client.receive_response()
assert response.response_status == ResponseStatus.RESPONSE_STATUS_ACK
# Test sending nothing -> uD3TN should close the connection after
# *twice* the configured timeout.
t8 = time.time()
_wait_until_connection_closed(rpc_client.socket)
t9 = time.time()
assert t8 + AAP2_AGENT_TIMEOUT + TIMING_MARGIN < t9
# There can be some tolerance for uD3TN to notice a closed connection.
assert t8 + AAP2_AGENT_TIMEOUT * 2 + TIMING_MARGIN * 5 >= t9
@pytest.mark.skipif(not TEST_AAP2, reason="TEST_AAP2 disabled via environment")
def test_aap2_bundle_ack():
rpc_client = AAP2UnixClient(address=AAP2_SOCKET)
sub_client = AAP2UnixClient(address=AAP2_SOCKET)
with rpc_client, sub_client:
s = rpc_client.configure(
AAP2_AGENT_ID + "-b8",
subscribe=False,
)
sub_client.configure(
AAP2_AGENT_ID + "-b8",
subscribe=True,
secret=s,
)
payload = b"testbundle"
rpc_client.send_adu(
BundleADU(
dst_eid=rpc_client.eid,
payload_length=len(payload),
),
payload,
)
response = rpc_client.receive_response()
assert (response.response_status ==
ResponseStatus.RESPONSE_STATUS_SUCCESS)
msg = sub_client.receive_msg()
adu_msg, bundle_data = sub_client.receive_adu(msg.adu)
# Check that reporting processing failure is ok and valid.
sub_client.send_response_status(
ResponseStatus.RESPONSE_STATUS_ERROR
)
# Send another ADU.
rpc_client.send_adu(
BundleADU(
dst_eid=rpc_client.eid,
payload_length=len(payload),
),
payload,
)
response = rpc_client.receive_response()
assert (response.response_status ==
ResponseStatus.RESPONSE_STATUS_SUCCESS)
msg = sub_client.receive_msg()
adu_msg, bundle_data = sub_client.receive_adu(msg.adu)
# Do not acknowledge the ADU and check that the agent closes
# the connection properly.
t1 = time.time()
_wait_until_connection_closed(sub_client.socket)
t2 = time.time()
assert t1 + AAP2_AGENT_TIMEOUT + TIMING_MARGIN >= t2