Test refactor: Add support for checking broker logs

This commit is contained in:
Roger A. Light
2026-05-13 19:38:00 +01:00
parent 568c527ebd
commit 698ecbb50b
16 changed files with 670 additions and 397 deletions
+6 -1
View File
@@ -17,7 +17,12 @@ connack_packet = mqtt_packets.gen_connack(rc=0, proto_ver=5)
def do_test(headers):
port = mosq_test.get_port()
broker_config = BrokerConfig(
listeners=[ ListenerConfig(port=port, enable_proxy_protocol=2) ],
listeners=[
ListenerConfig(
port=port,
enable_proxy_protocol=2
)
],
allow_anonymous=True,
)
broker = MosquittoBroker(config=broker_config)
+18 -33
View File
@@ -1,6 +1,10 @@
#!/usr/bin/env python3
from mosq_test_helper import *
from broker_config import BrokerConfig, ListenerConfig
from matchers import Contains
from mosquitto_broker import MosquittoBroker
from proxy_helper import *
import json
import shutil
@@ -8,45 +12,26 @@ import socket
mosq_test.require_features(["WITH_WEBSOCKETS", "WITH_WEBSOCKETS_BUILTIN"])
def write_config(filename, port):
with open(filename, 'w') as f:
f.write("log_type all\n")
f.write("listener %d\n" % (port))
f.write("allow_anonymous true\n")
f.write("enable_proxy_protocol 2\n")
port = mosq_test.get_port()
conf_file = os.path.basename(__file__).replace('.py', '.conf')
write_config(conf_file, port)
connect_packet = mqtt_packets.gen_connect("proxy-test", keepalive=42, clean_session=False, proto_ver=5)
connack_packet = mqtt_packets.gen_connack(rc=0, proto_ver=5)
broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
rc = 1
expect_log = "New client connected from 192.0.2.5:6275"
try:
port = mosq_test.get_port()
broker_config = BrokerConfig(
listeners = [
ListenerConfig(
port=port,
enable_proxy_protocol=2
)
],
allow_anonymous=True,
log_type="all",
)
broker = MosquittoBroker(config=broker_config)
with broker:
data = b"\xC0\x00\x02\x05" + b"\x00\x00\x00\x00" + b"\x18\x83" + b"\x00\x00"
sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, PROXY_FAM_IPV4 | PROXY_PROTO_TCP, data)
mosq_test.do_send_receive(sock, connect_packet, connack_packet, "connack")
mosq_test.do_ping(sock)
sock.close()
rc = 0
except mosq_test.TestError:
pass
finally:
os.remove(conf_file)
mosq_test.terminate_broker(broker)
if mosq_test.wait_for_subprocess(broker):
print("broker not terminated")
if rc == 0: rc=1
stde = mosq_test.broker_log(broker)
if rc != 0 or expect_log not in stde:
print(stde)
rc = 1
exit(rc)
broker.check_log(Contains("New client connected from 192.0.2.5:6275"))
+18 -33
View File
@@ -1,6 +1,10 @@
#!/usr/bin/env python3
from mosq_test_helper import *
from broker_config import BrokerConfig, ListenerConfig
from matchers import Contains
from mosquitto_broker import MosquittoBroker
from proxy_helper import *
import json
import shutil
@@ -8,27 +12,22 @@ import socket
mosq_test.require_features(["WITH_WEBSOCKETS", "WITH_WEBSOCKETS_BUILTIN"])
def write_config(filename, port):
with open(filename, 'w') as f:
f.write("log_type all\n")
f.write("listener %d\n" % (port))
f.write("allow_anonymous true\n")
f.write("enable_proxy_protocol 2\n")
port = mosq_test.get_port()
conf_file = os.path.basename(__file__).replace('.py', '.conf')
write_config(conf_file, port)
connect_packet = mqtt_packets.gen_connect("proxy-test", keepalive=42, clean_session=False, proto_ver=5)
connack_packet = mqtt_packets.gen_connack(rc=0, proto_ver=5)
broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
rc = 1
expected_log = "New client connected from 2001:db8:506:708:900::1:6275 as proxy-test (p5, c0, k42)"
try:
port = mosq_test.get_port()
broker_config = BrokerConfig(
listeners=[
ListenerConfig(
port=port,
enable_proxy_protocol=2
)
],
allow_anonymous=True,
log_type="all",
)
broker = MosquittoBroker(config=broker_config)
with broker:
data = b"\x20\x01\x0d\xb8\x05\x06\x07\x08\x09\x00\x00\x00\x00\x00\x00\x01" \
+ b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
+ b"\x18\x83" + b"\x00\x00"
@@ -36,19 +35,5 @@ try:
mosq_test.do_send_receive(sock, connect_packet, connack_packet, "connack")
mosq_test.do_ping(sock)
sock.close()
rc = 0
except mosq_test.TestError:
pass
finally:
os.remove(conf_file)
mosq_test.terminate_broker(broker)
if mosq_test.wait_for_subprocess(broker):
print("broker not terminated")
if rc == 0: rc=1
stde = mosq_test.broker_log(broker)
if rc != 0 or expected_log not in stde:
print(stde)
rc = 1
exit(rc)
broker.check_log(Contains("New client connected from 2001:db8:506:708:900::1:6275 as proxy-test (p5, c0, k42)"))
+21 -33
View File
@@ -1,6 +1,10 @@
#!/usr/bin/env python3
from mosq_test_helper import *
from broker_config import BrokerConfig, ListenerConfig
from matchers import Contains
from mosquitto_broker import MosquittoBroker
from proxy_helper import *
import json
import shutil
@@ -8,49 +12,33 @@ import socket
mosq_test.require_features(["WITH_WEBSOCKETS", "WITH_WEBSOCKETS_BUILTIN"])
def write_config(filename, port):
with open(filename, 'w') as f:
f.write("log_type all\n")
f.write("listener %d\n" % (port))
f.write("allow_anonymous true\n")
f.write("enable_proxy_protocol 2\n")
def do_test(fam):
port = mosq_test.get_port()
conf_file = os.path.basename(__file__).replace('.py', '.conf')
write_config(conf_file, port)
connect_packet = mqtt_packets.gen_connect("proxy-test", keepalive=42, clean_session=False, proto_ver=5)
connack_packet = mqtt_packets.gen_connack(rc=0, proto_ver=5)
broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
rc = 1
expect_log = "Bad socket read/write on client <unknown>: Invalid input"
try:
port = mosq_test.get_port()
broker_config = BrokerConfig(
listeners=[
ListenerConfig(
port=port,
enable_proxy_protocol=2
)
],
allow_anonymous=True,
log_type="all",
)
broker = MosquittoBroker(config=broker_config)
with broker:
data = b"a"*501
sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, fam | PROXY_PROTO_TCP, data)
try:
data = sock.recv(10)
if len(data) == 0:
rc = 0
if len(data) > 0:
raise ValueError(data)
except ConnectionResetError:
rc = 0
pass
sock.close()
except mosq_test.TestError:
pass
finally:
os.remove(conf_file)
mosq_test.terminate_broker(broker)
if mosq_test.wait_for_subprocess(broker):
print("broker not terminated")
if rc == 0: rc=1
stde = mosq_test.broker_log(broker)
if rc != 0 or expect_log not in stde:
print(stde)
rc = 1
broker.check_log(Contains("Client (null) [(null):0] disconnected: bad socket read/write: Invalid input"))
do_test(PROXY_FAM_IPV4)
do_test(PROXY_FAM_IPV6)
+20 -31
View File
@@ -1,6 +1,10 @@
#!/usr/bin/env python3
from mosq_test_helper import *
from broker_config import BrokerConfig, ListenerConfig
from matchers import Contains
from mosquitto_broker import MosquittoBroker
from proxy_helper import *
import json
import shutil
@@ -8,45 +12,30 @@ import socket
mosq_test.require_features(["WITH_TLS", "WITH_WEBSOCKETS", "WITH_WEBSOCKETS_BUILTIN"])
def write_config(filename, port):
with open(filename, 'w') as f:
f.write("log_type all\n")
f.write("listener %d\n" % (port))
f.write("allow_anonymous true\n")
f.write("enable_proxy_protocol 2\n")
def do_test(data):
expect_log = "Connection from 192.0.2.5:6275 negotiated TLSv1.3 cipher pqrstuv"
port = mosq_test.get_port()
conf_file = os.path.basename(__file__).replace('.py', '.conf')
write_config(conf_file, port)
broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
connect_packet = mqtt_packets.gen_connect("proxy-test", keepalive=42, clean_session=False, proto_ver=5, username="none", password="pw")
connack_packet = mqtt_packets.gen_connack(rc=0, proto_ver=5)
rc = 1
try:
port = mosq_test.get_port()
broker_config = BrokerConfig(
listeners=[
ListenerConfig(
port=port,
enable_proxy_protocol=2
)
],
allow_anonymous=True,
log_type="all",
)
broker = MosquittoBroker(config=broker_config)
with broker:
sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, PROXY_FAM_IPV4 | PROXY_PROTO_TCP, data)
mosq_test.do_send_receive(sock, connect_packet, connack_packet, "connack")
mosq_test.do_ping(sock)
sock.close()
rc = 0
except mosq_test.TestError:
pass
finally:
os.remove(conf_file)
mosq_test.terminate_broker(broker)
if mosq_test.wait_for_subprocess(broker):
print("broker not terminated")
if rc == 0: rc=1
stde = mosq_test.broker_log(broker)
if rc != 0 or expect_log not in stde:
print(stde)
rc = 1
raise ValueError(rc)
broker.check_log(Contains("Connection from 192.0.2.5:6275 negotiated TLSv1.3 cipher pqrstuv"))
data = b"\xC0\x00\x02\x05" + b"\x00\x00\x00\x00" + b"\x18\x83" + b"\x00\x00" \
+ b"\x20" \
@@ -1,6 +1,10 @@
#!/usr/bin/env python3
from mosq_test_helper import *
from broker_config import BrokerConfig, ListenerConfig
from matchers import Contains
from mosquitto_broker import MosquittoBroker
from proxy_helper import *
import json
import shutil
@@ -8,52 +12,36 @@ import socket
mosq_test.require_features(["WITH_TLS", "WITH_WEBSOCKETS", "WITH_WEBSOCKETS_BUILTIN"])
def write_config(filename, port):
with open(filename, 'w') as f:
f.write("log_type all\n")
f.write("listener %d\n" % (port))
f.write("allow_anonymous true\n")
f.write("enable_proxy_protocol 2\n")
f.write("require_certificate true\n")
f.write("use_identity_as_username true\n")
def do_test(data, expect_log):
port = mosq_test.get_port()
conf_file = os.path.basename(__file__).replace('.py', '.conf')
write_config(conf_file, port)
connect_packet = mqtt_packets.gen_connect("proxy-test", keepalive=42, clean_session=False, proto_ver=5)
connack_packet = mqtt_packets.gen_connack(rc=134, proto_ver=5)
broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
rc = 1
try:
port = mosq_test.get_port()
broker_config = BrokerConfig(
listeners=[
ListenerConfig(
port=port,
enable_proxy_protocol=2,
require_certificate=True,
use_identity_as_username=True,
)
],
allow_anonymous=True,
log_type="all",
)
broker = MosquittoBroker(config=broker_config)
with broker:
sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, PROXY_FAM_IPV4 | PROXY_PROTO_TCP, data)
sock.send(connect_packet)
try:
mosq_test.expect_packet(sock, "connack", connack_packet)
data = sock.recv(10)
if len(data) == 0:
rc = 0
if len(data) > 0:
raise ValueError(data)
except (BrokenPipeError, ConnectionResetError):
rc = 0
pass
sock.close()
except mosq_test.TestError:
pass
finally:
os.remove(conf_file)
mosq_test.terminate_broker(broker)
if mosq_test.wait_for_subprocess(broker):
print("broker not terminated")
if rc == 0: rc=1
stde = mosq_test.broker_log(broker)
if rc != 0 or expect_log not in stde:
print(stde)
print(expect_log)
rc = 1
raise ValueError(rc)
broker.check_log(Contains(expect_log))
# No SSL at all
data = b"\xC0\x00\x02\x05" + b"\x00\x00\x00\x00" + b"\x18\x83" + b"\x00\x00"
@@ -1,6 +1,10 @@
#!/usr/bin/env python3
from mosq_test_helper import *
from broker_config import BrokerConfig, ListenerConfig
from matchers import Contains
from mosquitto_broker import MosquittoBroker
from proxy_helper import *
import json
import shutil
@@ -8,47 +12,30 @@ import socket
mosq_test.require_features(["WITH_TLS", "WITH_WEBSOCKETS", "WITH_WEBSOCKETS_BUILTIN"])
def write_config(filename, port):
with open(filename, 'w') as f:
f.write("log_type all\n")
f.write("listener %d\n" % (port))
f.write("allow_anonymous true\n")
f.write("enable_proxy_protocol 2\n")
f.write("require_certificate true\n")
f.write("use_identity_as_username true\n")
def do_test(data):
expect_log = "New client connected from 192.0.2.5:6275 as proxy-test (p5, c0, k42, u'ppppppp')."
port = mosq_test.get_port()
conf_file = os.path.basename(__file__).replace('.py', '.conf')
write_config(conf_file, port)
broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
connect_packet = mqtt_packets.gen_connect("proxy-test", keepalive=42, clean_session=False, proto_ver=5, username="none", password="pw")
connack_packet = mqtt_packets.gen_connack(rc=0, proto_ver=5)
rc = 1
try:
port = mosq_test.get_port()
broker_config = BrokerConfig(
listeners=[
ListenerConfig(
port=port,
enable_proxy_protocol=2,
require_certificate=True,
use_identity_as_username=True,
)
],
allow_anonymous=True,
log_type="all",
)
broker = MosquittoBroker(config=broker_config)
with broker:
sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, PROXY_FAM_IPV4 | PROXY_PROTO_TCP, data)
mosq_test.do_send_receive(sock, connect_packet, connack_packet, "connack")
mosq_test.do_ping(sock)
sock.close()
rc = 0
except mosq_test.TestError:
pass
finally:
os.remove(conf_file)
mosq_test.terminate_broker(broker)
if mosq_test.wait_for_subprocess(broker):
print("broker not terminated")
if rc == 0: rc=1
stde = mosq_test.broker_log(broker)
if rc != 0 or expect_log not in stde:
print(stde)
rc = 1
raise ValueError(rc)
broker.check_log(Contains("New client connected from 192.0.2.5:6275 as proxy-test (p5, c0, k42, u'ppppppp')."))
data = b"\xC0\x00\x02\x05" + b"\x00\x00\x00\x00" + b"\x18\x83" + b"\x00\x00" \
+ b"\x20" \
@@ -1,6 +1,10 @@
#!/usr/bin/env python3
from mosq_test_helper import *
from broker_config import BrokerConfig, ListenerConfig
from matchers import Contains
from mosquitto_broker import MosquittoBroker
from proxy_helper import *
import json
import shutil
@@ -8,46 +12,30 @@ import socket
mosq_test.require_features(["WITH_TLS", "WITH_WEBSOCKETS", "WITH_WEBSOCKETS_BUILTIN"])
def write_config(filename, port):
with open(filename, 'w') as f:
f.write("log_type all\n")
f.write("listener %d\n" % (port))
f.write("allow_anonymous true\n")
f.write("enable_proxy_protocol 2\n")
f.write("require_certificate true\n")
def do_test(data):
port = mosq_test.get_port()
conf_file = os.path.basename(__file__).replace('.py', '.conf')
write_config(conf_file, port)
broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
rc = 1
expect_log = "Connection from 192.0.2.5:6275 rejected, client did not provide a certificate."
try:
broker_config = BrokerConfig(
listeners=[
ListenerConfig(
port=port,
enable_proxy_protocol=2,
require_certificate=True,
)
],
allow_anonymous=True,
log_type="all",
)
broker = MosquittoBroker(config=broker_config)
with broker:
sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, PROXY_FAM_IPV4 | PROXY_PROTO_TCP, data)
try:
data = sock.recv(10)
if len(data) == 0:
rc = 0
if len(data) > 0:
raise ValueError(data)
except ConnectionResetError:
rc = 0
pass
sock.close()
except mosq_test.TestError:
pass
finally:
os.remove(conf_file)
mosq_test.terminate_broker(broker)
if mosq_test.wait_for_subprocess(broker):
print("broker not terminated")
if rc == 0: rc=1
stde = mosq_test.broker_log(broker)
if rc != 0 or expect_log not in stde:
print(stde)
rc = 1
raise ValueError(rc)
broker.check_log(Contains("Connection from 192.0.2.5:6275 rejected, client did not provide a certificate."))
# No SSL at all
data = b"\xC0\x00\x02\x05" + b"\x00\x00\x00\x00" + b"\x18\x83" + b"\x00\x00"
@@ -1,6 +1,10 @@
#!/usr/bin/env python3
from mosq_test_helper import *
from broker_config import BrokerConfig, ListenerConfig
from matchers import Contains
from mosquitto_broker import MosquittoBroker
from proxy_helper import *
import json
import shutil
@@ -8,46 +12,29 @@ import socket
mosq_test.require_features(["WITH_TLS", "WITH_WEBSOCKETS", "WITH_WEBSOCKETS_BUILTIN"])
def write_config(filename, port):
with open(filename, 'w') as f:
f.write("log_type all\n")
f.write("listener %d\n" % (port))
f.write("allow_anonymous true\n")
f.write("enable_proxy_protocol 2\n")
f.write("require_certificate true\n")
def do_test(data):
port = mosq_test.get_port()
conf_file = os.path.basename(__file__).replace('.py', '.conf')
write_config(conf_file, port)
broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
connect_packet = mqtt_packets.gen_connect("proxy-test", keepalive=42, clean_session=False, proto_ver=5)
connack_packet = mqtt_packets.gen_connack(rc=0, proto_ver=5)
rc = 1
expect_log = "New client connected from 192.0.2.5:6275 as proxy-test (p5, c0, k42)."
try:
port = mosq_test.get_port()
broker_config = BrokerConfig(
listeners=[
ListenerConfig(
port=port,
enable_proxy_protocol=2,
require_certificate=True,
)
],
allow_anonymous=True,
log_type="all",
)
broker = MosquittoBroker(config=broker_config)
with broker:
sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, PROXY_FAM_IPV4 | PROXY_PROTO_TCP, data)
mosq_test.do_send_receive(sock, connect_packet, connack_packet, "connack")
mosq_test.do_ping(sock)
sock.close()
rc = 0
except mosq_test.TestError:
pass
finally:
os.remove(conf_file)
mosq_test.terminate_broker(broker)
if mosq_test.wait_for_subprocess(broker):
print("broker not terminated")
if rc == 0: rc=1
stde = mosq_test.broker_log(broker)
if rc != 0 or expect_log not in stde:
print(stde)
rc = 1
raise ValueError(rc)
broker.check_log(Contains("New client connected from 192.0.2.5:6275 as proxy-test (p5, c0, k42)."))
data = b"\xC0\x00\x02\x05" + b"\x00\x00\x00\x00" + b"\x18\x83" + b"\x00\x00" \
+ b"\x02" \
@@ -1,6 +1,10 @@
#!/usr/bin/env python3
from mosq_test_helper import *
from broker_config import BrokerConfig, ListenerConfig
from matchers import Contains
from mosquitto_broker import MosquittoBroker
from proxy_helper import *
import json
import shutil
@@ -8,46 +12,30 @@ import socket
mosq_test.require_features(["WITH_TLS", "WITH_WEBSOCKETS", "WITH_WEBSOCKETS_BUILTIN"])
def write_config(filename, port):
with open(filename, 'w') as f:
f.write("log_type all\n")
f.write("listener %d\n" % (port))
f.write("allow_anonymous true\n")
f.write("enable_proxy_protocol 2\n")
f.write("proxy_protocol_v2_require_tls true\n")
def do_test(data):
port = mosq_test.get_port()
conf_file = os.path.basename(__file__).replace('.py', '.conf')
write_config(conf_file, port)
broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
rc = 1
expect_log = "Connection from 192.0.2.5:6275 rejected, client did not connect using TLS."
try:
broker_config = BrokerConfig(
listeners=[
ListenerConfig(
port=port,
enable_proxy_protocol=2,
proxy_protocol_v2_require_tls=True,
)
],
allow_anonymous=True,
log_type="all",
)
broker = MosquittoBroker(config=broker_config)
with broker:
sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, PROXY_FAM_IPV4 | PROXY_PROTO_TCP, data)
try:
data = sock.recv(10)
if len(data) == 0:
rc = 0
if len(data) > 0:
raise ValueError(data)
except ConnectionResetError:
rc = 0
pass
sock.close()
except mosq_test.TestError:
pass
finally:
os.remove(conf_file)
mosq_test.terminate_broker(broker)
if mosq_test.wait_for_subprocess(broker):
print("broker not terminated")
if rc == 0: rc=1
stde = mosq_test.broker_log(broker)
if rc != 0 or expect_log not in stde:
print(stde)
rc = 1
raise ValueError(rc)
broker.check_log(Contains("Connection from 192.0.2.5:6275 rejected, client did not connect using TLS."))
# No SSL at all
data = b"\xC0\x00\x02\x05" + b"\x00\x00\x00\x00" + b"\x18\x83" + b"\x00\x00"
@@ -1,6 +1,10 @@
#!/usr/bin/env python3
from mosq_test_helper import *
from broker_config import BrokerConfig, ListenerConfig
from matchers import Contains
from mosquitto_broker import MosquittoBroker
from proxy_helper import *
import json
import shutil
@@ -8,46 +12,29 @@ import socket
mosq_test.require_features(["WITH_TLS", "WITH_WEBSOCKETS", "WITH_WEBSOCKETS_BUILTIN"])
def write_config(filename, port):
with open(filename, 'w') as f:
f.write("log_type all\n")
f.write("listener %d\n" % (port))
f.write("allow_anonymous true\n")
f.write("enable_proxy_protocol 2\n")
f.write("proxy_protocol_v2_require_tls true\n")
def do_test(data):
port = mosq_test.get_port()
conf_file = os.path.basename(__file__).replace('.py', '.conf')
write_config(conf_file, port)
broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
connect_packet = mqtt_packets.gen_connect("proxy-test", keepalive=42, clean_session=False, proto_ver=5)
connack_packet = mqtt_packets.gen_connack(rc=0, proto_ver=5)
rc = 1
expect_log = "New client connected from 192.0.2.5:6275 as proxy-test (p5, c0, k42)."
try:
port = mosq_test.get_port()
broker_config = BrokerConfig(
listeners=[
ListenerConfig(
port=port,
enable_proxy_protocol=2,
proxy_protocol_v2_require_tls=True,
)
],
allow_anonymous=True,
log_type="all",
)
broker = MosquittoBroker(config=broker_config)
with broker:
sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, PROXY_FAM_IPV4 | PROXY_PROTO_TCP, data)
mosq_test.do_send_receive(sock, connect_packet, connack_packet, "connack")
mosq_test.do_ping(sock)
sock.close()
rc = 0
except mosq_test.TestError:
pass
finally:
os.remove(conf_file)
mosq_test.terminate_broker(broker)
if mosq_test.wait_for_subprocess(broker):
print("broker not terminated")
if rc == 0: rc=1
stde = mosq_test.broker_log(broker)
if rc != 0 or expect_log not in stde:
print(stde)
rc = 1
raise ValueError(rc)
broker.check_log(Contains("New client connected from 192.0.2.5:6275 as proxy-test (p5, c0, k42)."))
data = b"\xC0\x00\x02\x05" + b"\x00\x00\x00\x00" + b"\x18\x83" + b"\x00\x00" \
+ b"\x02" \
+18 -34
View File
@@ -1,6 +1,10 @@
#!/usr/bin/env python3
from mosq_test_helper import *
from broker_config import BrokerConfig, ListenerConfig
from matchers import Contains
from mosquitto_broker import MosquittoBroker
from proxy_helper import *
import json
import shutil
@@ -8,45 +12,25 @@ import socket
mosq_test.require_features(["WITH_WEBSOCKETS", "WITH_WEBSOCKETS_BUILTIN"])
def write_config(filename, port):
with open(filename, 'w') as f:
f.write("log_type all\n")
f.write("listener %d\n" % (port))
f.write("allow_anonymous true\n")
f.write("enable_proxy_protocol 2\n")
port = mosq_test.get_port()
conf_file = os.path.basename(__file__).replace('.py', '.conf')
write_config(conf_file, port)
connect_packet = mqtt_packets.gen_connect("proxy-test", keepalive=42, clean_session=False, proto_ver=5)
connack_packet = mqtt_packets.gen_connack(rc=0, proto_ver=5)
broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
rc = 1
expected_log = "New client connected from /path:0 as proxy-test (p5, c0, k42)."
try:
port = mosq_test.get_port()
broker_config = BrokerConfig(
listeners=[
ListenerConfig(
port=port,
enable_proxy_protocol=2,
)
],
allow_anonymous=True,
log_type="all",
)
broker = MosquittoBroker(config=broker_config)
with broker:
data = b"/path"
sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, PROXY_FAM_UNIX | PROXY_PROTO_TCP, data)
mosq_test.do_send_receive(sock, connect_packet, connack_packet, "connack")
mosq_test.do_ping(sock)
sock.close()
rc = 0
except mosq_test.TestError:
pass
finally:
os.remove(conf_file)
mosq_test.terminate_broker(broker)
if mosq_test.wait_for_subprocess(broker):
print("broker not terminated")
if rc == 0: rc=1
stde = mosq_test.broker_log(broker)
if rc != 0 or expected_log not in stde:
print(stde)
rc = 1
exit(rc)
broker.check_log(Contains("New client connected from /path:0 as proxy-test (p5, c0, k42)."))
+19 -34
View File
@@ -1,6 +1,10 @@
#!/usr/bin/env python3
from mosq_test_helper import *
from broker_config import BrokerConfig, ListenerConfig
from matchers import Contains
from mosquitto_broker import MosquittoBroker
from proxy_helper import *
import json
import shutil
@@ -8,28 +12,23 @@ import socket
mosq_test.require_features(["WITH_WEBSOCKETS", "WITH_WEBSOCKETS_BUILTIN"])
def write_config(filename, port):
with open(filename, 'w') as f:
f.write("log_type all\n")
f.write("listener %d\n" % (port))
f.write("allow_anonymous true\n")
f.write("enable_proxy_protocol 2\n")
f.write("protocol websockets\n")
port = mosq_test.get_port()
conf_file = os.path.basename(__file__).replace('.py', '.conf')
write_config(conf_file, port)
connect_packet = mqtt_packets.gen_connect("proxy-test", keepalive=42, clean_session=False, proto_ver=5)
connack_packet = mqtt_packets.gen_connack(rc=0, proto_ver=5)
broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
rc = 1
expect_log = "New client connected from 192.0.2.5:6275"
try:
port = mosq_test.get_port()
broker_config = BrokerConfig(
listeners=[
ListenerConfig(
port=port,
enable_proxy_protocol=2,
protocol="websockets",
)
],
allow_anonymous=True,
log_type="all",
)
broker = MosquittoBroker(config=broker_config)
with broker:
data = b"\xC0\x00\x02\x05" + b"\x00\x00\x00\x00" + b"\x18\x83" + b"\x00\x00"
sock = do_proxy_v2_connect(port, PROXY_VER, PROXY_CMD_PROXY, PROXY_FAM_IPV4 | PROXY_PROTO_TCP, data)
websocket_req_good = b"GET /mqtt HTTP/1.1\r\n" \
@@ -69,19 +68,5 @@ try:
mosq_test.do_send_receive(sock, websocket_req_good, websocket_resp_good, "websocket handshake")
mosq_test.do_send_receive(sock, connect_frame, connack_frame, "connack")
sock.close()
rc = 0
except mosq_test.TestError:
pass
finally:
os.remove(conf_file)
mosq_test.terminate_broker(broker)
if mosq_test.wait_for_subprocess(broker):
print("broker not terminated")
if rc == 0: rc=1
stde = mosq_test.broker_log(broker)
if rc != 0 or expect_log not in stde:
print(stde)
rc = 1
exit(rc)
broker.check_log(Contains("New client connected from 192.0.2.5:6275"))
+1
View File
@@ -24,6 +24,7 @@ class ListenerConfig:
listener_auto_id_prefix: Optional[str] = None
mount_point: Optional[str] = None
protocol: Optional[str] = None
proxy_protocol_v2_require_tls: Optional[bool] = None
psk_hint: Optional[str] = None
require_certificate: Optional[bool] = None
use_identity_as_username: Optional[bool] = None
+420
View File
@@ -0,0 +1,420 @@
""" """
from collections.abc import Mapping, Sequence
import dataclasses
class Matcher:
def __init__(self):
self.last_message = ""
def __repr__(self):
return f"{self.__class__.__name__}"
def __call__(self, _):
raise NotImplementedError()
class UnaryMatcher(Matcher):
def __init__(self, expectation):
Matcher.__init__(self)
self._expectation = expectation
def __call__(self, _):
raise NotImplementedError()
def __len__(self):
return len(self._expectation)
class CountMatcher(Matcher):
def __init__(self, expectation, expected_count):
Matcher.__init__(self)
self._expectation = expectation
self._expected_count = expected_count
def __call__(self, _):
raise NotImplementedError()
def __len__(self):
return len(self._expectation * self._expected_count)
class And(Matcher):
def __init__(self, *matchers):
Matcher.__init__(self)
self._matchers = [
matcher if isinstance(matcher, Matcher) else RecursiveMatcher(matcher)
for matcher in matchers
]
def __call__(self, candidate):
self.last_message = ""
for matcher in self._matchers:
if not matcher(candidate):
self.last_message = matcher.last_message
return False
return True
def __repr__(self):
return " && ".join([str(m) for m in self._matchers])
class Or(Matcher):
def __init__(self, *matchers):
Matcher.__init__(self)
self._matchers = [
matcher if isinstance(matcher, Matcher) else RecursiveMatcher(matcher)
for matcher in matchers
]
def __call__(self, candidate):
self.last_message = ""
for matcher in self._matchers:
if matcher(candidate):
return True
else:
self.last_message += (
f"{' and ' if self.last_message else ''}{matcher.last_message}"
)
return False
def __len__(self):
first_len = len(self._matchers[0])
assert all(len(lst) == first_len for lst in self._matchers[1:])
return first_len
class Any(Matcher):
def __call__(self, candidate):
return True
class AnyOrder(UnaryMatcher):
def __call__(self, candidate):
candidate = list(candidate)
for expected_element in self._expectation:
removed, msg = self._remove_matching_element(candidate, expected_element)
if not removed:
self.last_message = f"Cannot find element {expected_element} in {candidate} due to {msg}"
return False
if len(candidate):
self.last_message = f"Found additional elements {candidate}"
return False
return True
def _remove_matching_element(self, candidate_list, expected_element):
matcher = (
expected_element
if isinstance(expected_element, Matcher)
else RecursiveMatcher(expected_element)
)
for candidate_element in candidate_list:
if matcher(candidate_element):
candidate_list.remove(candidate_element)
return True, ""
return False, matcher.last_message
def __repr__(self):
return f" = <any order> {self._expectation}"
class EqualTo(UnaryMatcher):
def __call__(self, candidate):
if candidate != self._expectation:
self.last_message = (
f"{candidate} (actual) != {self._expectation} (expected)"
)
return False
return True
def __repr__(self):
return f" == {self._expectation}"
class GreaterEqual(UnaryMatcher):
def __call__(self, candidate):
if candidate < self._expectation:
self.last_message = f"{candidate} < {self._expectation}"
return False
return True
def __repr__(self):
return f" >= {self._expectation}"
class GreaterThan(UnaryMatcher):
def __call__(self, candidate):
if candidate <= self._expectation:
self.last_message = f"{candidate} > {self._expectation}"
return False
return True
def __repr__(self):
return f" > {self._expectation}"
class LowerEqual(UnaryMatcher):
def __call__(self, candidate):
if candidate > self._expectation:
self.last_message = f"{candidate} > {self._expectation}"
return False
return True
def __repr__(self):
return f" <= {self._expectation}"
class LowerThan(UnaryMatcher):
def __call__(self, candidate):
if candidate >= self._expectation:
self.last_message = f"{candidate} > {self._expectation}"
return False
return True
def __repr__(self):
return f" < {self._expectation}"
class Field(Matcher):
def __init__(self, field_name, expectation):
Matcher.__init__(self)
self._field_name = field_name
self._field_matcher = (
expectation if isinstance(expectation, Matcher) else EqualTo(expectation)
)
def __call__(self, candidate):
if dataclasses.is_dataclass(candidate):
candidate = dataclasses.asdict(candidate)
if not isinstance(candidate, Mapping):
self.last_message = f"{candidate} is not a Mapping type"
return False
if not self._field_name in candidate:
self.last_message = f"{candidate} does not contain field {self._field_name}"
return False
result = self._field_matcher(candidate[self._field_name])
if not result:
self.last_message = (
f"{self._field_matcher.last_message} in field {self._field_name}"
)
return result
def __repr__(self):
return f"Field {self._field_name} {self._field_matcher}"
class FieldIni(Matcher):
def __init__(self, field_name, expectation):
Matcher.__init__(self)
self._field_name = field_name
self._field_matcher = (
expectation if isinstance(expectation, Matcher) else EqualTo(expectation)
)
def __call__(self, candidate):
if not isinstance(candidate, str):
self.last_message = f"{candidate} is not of string type"
return False
if not self._field_name in candidate:
self.last_message = f"{candidate} does not contain field {self._field_name}"
return False
value = [
line.split(" ")[-1]
for line in candidate.splitlines()
if line.startswith(self._field_name)
]
if len(value) > 1:
self.last_message = f"Found multiple values for {self._field_name}"
return False
result = self._field_matcher(value[-1])
if not result:
self.last_message = (
f"{self._field_matcher.last_message} in field {self._field_name}"
)
return result
def __repr__(self):
return f"Field {self._field_name} {self._field_matcher}"
class RecursiveMatcher(UnaryMatcher):
def __call__(self, candidate):
result, self.last_message, path = self._recursive_match(
candidate, self._expectation
)
self.last_message += f" at {path}"
return result
def _recursive_match(self, candidate, expectation):
if expectation is None:
return candidate is None, f"{candidate} is not None", ""
if isinstance(expectation, RecursiveMatcher):
return expectation._recursive_match(candidate, expectation._expectation)
if isinstance(expectation, Matcher):
result = expectation(candidate)
if not result:
return False, expectation.last_message, ""
return True, "", ""
if isinstance(expectation, Mapping):
if not isinstance(candidate, Mapping):
return False, f"{candidate} is not a Mapping type", ""
return self._match_mapping(candidate, expectation)
if isinstance(expectation, str):
if not isinstance(candidate, str):
return False, f"{candidate} is not a String", ""
return (
candidate == expectation,
f"{candidate} (actual) != {expectation} (expected)",
"",
)
if isinstance(expectation, Sequence):
if not isinstance(candidate, Sequence):
return False, f"{candidate} is not a Sequence", ""
return self._match_sequence(candidate, expectation)
return (
candidate == expectation,
f"{candidate} (actual) != {expectation} (expected)",
"",
)
def _match_mapping(self, candidate, expectation):
for key, value in expectation.items():
if not key in candidate:
return False, f"'{key}' not found in Mapping", ""
result, message, path = self._recursive_match(candidate.get(key), value)
if not result:
return False, f"{message}", f".{key}{path}"
return self._check_additional_mapping_entries(candidate, expectation)
def _check_additional_mapping_entries(self, candidate, expectation):
if len(candidate) != len(expectation):
left_overs = (
set(candidate.keys()) - set(expectation.keys())
if len(expectation) > 0
else candidate
)
return False, f"Additional elements found: {left_overs}", ""
return True, "", ""
def _match_sequence(self, candidate, expectation):
if len(candidate) != len(expectation):
return (
False,
f"sequence of different size. expected {len(expectation)} elements, but got {len(candidate)}",
"",
)
for index in range(len(expectation)):
result, message, path = self._recursive_match(
candidate[index], expectation[index]
)
if not result:
return (
False,
f"element does not match due to {message}",
f"[{index}]{path}",
)
if len(candidate) > len(expectation):
return (
False,
f"Additional elements found: {candidate[len(expectation):]}",
"[{len(expectation)-1}:{len(candidate)-1}]",
)
return True, "", ""
def __repr__(self):
if hasattr(self._expectation, "__repr__"):
return str(self._expectation)
return "RecursiveMatcher"
class IsSupersetOf(RecursiveMatcher):
def _check_additional_mapping_entries(self, candidate, expectation):
return True, "", ""
def _match_sequence(self, superset, subset):
message = ""
path = ""
superset_index = 0
for subset_index in range(len(subset)):
last_superset_match_index = superset_index
while superset_index < len(superset):
result, message, path = self._recursive_match(
superset[superset_index], subset[subset_index]
)
if result:
break
superset_index += 1
if superset_index == len(superset):
return (
False,
f"cannot find element due to {message}, checked elements {superset[last_superset_match_index:superset_index]}",
f"[{subset_index}]{path}",
)
superset_index += 1
return True, "", ""
class StartsWith(UnaryMatcher):
def __call__(self, candidate):
if not candidate.startswith(self._expectation):
self.last_message = f"{candidate} does not start with {self._expectation}"
return False
return True
def __repr__(self):
return f" startswith {self._expectation}"
class Contains(UnaryMatcher):
def __call__(self, candidate):
if self._expectation not in candidate:
self.last_message = f"{self._expectation} not contained in {candidate}"
return False
return True
def __repr__(self):
return f" contains {self._expectation}"
class SubstringCount(CountMatcher):
def __call__(self, candidate):
assert getattr(candidate, "count"), "'candidate' is not a string"
count = candidate.count(self._expectation)
if count != self._expected_count:
self.last_message = f"'{self._expectation}' contained {count} times, expected {self._expected_count} times"
return False
return True
def __repr__(self):
return f" contains {self._expectation} {self._expected_count} times"
class HasType(UnaryMatcher):
def __call__(self, candidate):
if not isinstance(candidate, self._expectation):
self.last_message = (
f"{candidate} has type {type(candidate)}, expected {self._expectation}"
)
return False
return True
def __repr__(self):
return f" has type {self._expectation}"
class OneOf(UnaryMatcher):
def __call__(self, candidate):
if self._expectation.count(candidate) == 1:
return True
self.last_message = f"{candidate} not one of {self._expectation}"
return False
def __repr__(self):
return f" one of {self._expectation}"
+6
View File
@@ -103,6 +103,12 @@ class MosquittoBroker:
def is_running(self):
return self._process and self._process.poll() is None
def check_log(self, matcher):
if not matcher(self.get_log()):
raise AssertionError(f"{matcher.last_message}")
else:
logging.info(f"{self} Check log {matcher}")
def get_log(self):
return mosq_test.broker_log(self._process)