mirror of
https://github.com/eclipse-mosquitto/mosquitto.git
synced 2026-08-17 17:41:58 +08:00
Test refactor: Add support for checking broker logs
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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)"))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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" \
|
||||
|
||||
@@ -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)."))
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}"
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user