Files
claude[bot] e94f065451 fix: update default TCP port from 12345 to 5000 in code and tests
Updates the default TCP port throughout the codebase to match the
documentation changes made in the previous commit.

Changes:
- config.py: Update TCP port validator default (line 136)
- config.py: Update environment variable fallback default (line 255)
- test_config.py: Update test assertions (lines 117, 145)
- test_bridge.py: Update test assertion and fixture (lines 23, 63)
- test_command_forwarding.py: Update test fixture (line 20)
- test_rate_limiting.py: Update test fixtures (lines 22, 37)

Co-authored-by: JingleManSweep <jinglemansweep@users.noreply.github.com>
2025-11-16 22:32:55 +00:00

321 lines
11 KiB
Python

"""Tests for configuration system."""
import json
import tempfile
from pathlib import Path
import pytest
import yaml
from pydantic import ValidationError
from meshcore_mqtt.config import Config, ConnectionType, MeshCoreConfig, MQTTConfig
class TestMQTTConfig:
"""Test MQTT configuration."""
def test_valid_config(self) -> None:
"""Test valid MQTT configuration."""
config = MQTTConfig(broker="localhost")
assert config.broker == "localhost"
assert config.port == 1883
assert config.username is None
assert config.password is None
assert config.topic_prefix == "meshcore"
assert config.qos == 0
assert config.retain is False
def test_invalid_port(self) -> None:
"""Test invalid port numbers."""
with pytest.raises(ValidationError):
MQTTConfig(broker="localhost", port=0)
with pytest.raises(ValidationError):
MQTTConfig(broker="localhost", port=65536)
def test_invalid_qos(self) -> None:
"""Test invalid QoS values."""
with pytest.raises(ValidationError):
MQTTConfig(broker="localhost", qos=-1)
with pytest.raises(ValidationError):
MQTTConfig(broker="localhost", qos=3)
def test_tls_config_default(self) -> None:
"""Test default TLS configuration."""
config = MQTTConfig(broker="localhost")
assert config.tls_enabled is False
assert config.tls_ca_cert is None
assert config.tls_client_cert is None
assert config.tls_client_key is None
assert config.tls_insecure is False
def test_tls_enabled(self) -> None:
"""Test TLS enabled configuration."""
config = MQTTConfig(broker="localhost", tls_enabled=True)
assert config.tls_enabled is True
def test_tls_with_certificates(self) -> None:
"""Test TLS configuration with certificates."""
# Create temporary certificate files for testing
with tempfile.NamedTemporaryFile(
mode="w", suffix=".pem", delete=False
) as ca_cert:
ca_cert.write("dummy ca cert")
ca_cert_path = ca_cert.name
with tempfile.NamedTemporaryFile(
mode="w", suffix=".pem", delete=False
) as client_cert:
client_cert.write("dummy client cert")
client_cert_path = client_cert.name
with tempfile.NamedTemporaryFile(
mode="w", suffix=".key", delete=False
) as client_key:
client_key.write("dummy client key")
client_key_path = client_key.name
try:
config = MQTTConfig(
broker="localhost",
tls_enabled=True,
tls_ca_cert=ca_cert_path,
tls_client_cert=client_cert_path,
tls_client_key=client_key_path,
)
assert config.tls_enabled is True
assert config.tls_ca_cert == ca_cert_path
assert config.tls_client_cert == client_cert_path
assert config.tls_client_key == client_key_path
finally:
# Clean up temporary files
Path(ca_cert_path).unlink()
Path(client_cert_path).unlink()
Path(client_key_path).unlink()
def test_tls_invalid_certificate_path(self) -> None:
"""Test TLS configuration with invalid certificate paths."""
with pytest.raises(ValidationError):
MQTTConfig(
broker="localhost",
tls_enabled=True,
tls_ca_cert="/nonexistent/path/ca.pem",
)
class TestMeshCoreConfig:
"""Test MeshCore configuration."""
def test_valid_tcp_config(self) -> None:
"""Test valid TCP configuration."""
config = MeshCoreConfig(
connection_type=ConnectionType.TCP, address="192.168.1.100", port=5000
)
assert config.connection_type == ConnectionType.TCP
assert config.address == "192.168.1.100"
assert config.port == 5000
assert config.timeout == 5
def test_valid_serial_config(self) -> None:
"""Test valid serial configuration."""
config = MeshCoreConfig(
connection_type=ConnectionType.SERIAL, address="/dev/ttyUSB0"
)
assert config.connection_type == ConnectionType.SERIAL
assert config.address == "/dev/ttyUSB0"
assert config.port is None
assert config.baudrate == 115200
def test_valid_ble_config(self) -> None:
"""Test valid BLE configuration."""
config = MeshCoreConfig(
connection_type=ConnectionType.BLE, address="AA:BB:CC:DD:EE:FF"
)
assert config.connection_type == ConnectionType.BLE
assert config.address == "AA:BB:CC:DD:EE:FF"
assert config.port is None
assert config.baudrate == 115200
def test_tcp_sets_default_port(self) -> None:
"""Test that TCP connections get a default port."""
config = MeshCoreConfig(
connection_type=ConnectionType.TCP, address="192.168.1.100", port=None
)
assert config.port == 5000
def test_custom_baudrate(self) -> None:
"""Test custom baudrate for serial connections."""
config = MeshCoreConfig(
connection_type=ConnectionType.SERIAL,
address="/dev/ttyUSB0",
baudrate=9600,
)
assert config.baudrate == 9600
def test_invalid_port(self) -> None:
"""Test invalid port numbers."""
with pytest.raises(ValidationError):
MeshCoreConfig(
connection_type=ConnectionType.TCP, address="192.168.1.100", port=0
)
with pytest.raises(ValidationError):
MeshCoreConfig(
connection_type=ConnectionType.TCP, address="192.168.1.100", port=65536
)
def test_invalid_timeout(self) -> None:
"""Test invalid timeout values."""
with pytest.raises(ValidationError):
MeshCoreConfig(
connection_type=ConnectionType.SERIAL, address="/dev/ttyUSB0", timeout=0
)
class TestConfig:
"""Test main configuration."""
def test_valid_config(self) -> None:
"""Test valid complete configuration."""
config = Config(
mqtt=MQTTConfig(broker="localhost"),
meshcore=MeshCoreConfig(
connection_type=ConnectionType.TCP, address="192.168.1.100", port=12345
),
)
assert config.mqtt.broker == "localhost"
assert config.meshcore.connection_type == ConnectionType.TCP
assert config.log_level == "INFO"
def test_invalid_log_level(self) -> None:
"""Test invalid log level."""
with pytest.raises(ValidationError, match="log_level must be one of"):
Config(
mqtt=MQTTConfig(broker="localhost"),
meshcore=MeshCoreConfig(
connection_type=ConnectionType.TCP,
address="192.168.1.100",
port=12345,
),
log_level="INVALID",
)
def test_log_level_case_normalization(self) -> None:
"""Test that log levels are normalized to uppercase."""
config = Config(
mqtt=MQTTConfig(broker="localhost"),
meshcore=MeshCoreConfig(
connection_type=ConnectionType.TCP, address="192.168.1.100", port=12345
),
log_level="debug",
)
assert config.log_level == "DEBUG"
def test_from_json_file(self) -> None:
"""Test loading configuration from JSON file."""
config_data = {
"mqtt": {
"broker": "test-broker",
"port": 1883,
"topic_prefix": "test",
"qos": 1,
"retain": True,
},
"meshcore": {
"connection_type": "serial",
"address": "/dev/ttyUSB0",
"timeout": 10,
},
"log_level": "DEBUG",
}
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
json.dump(config_data, f)
temp_path = Path(f.name)
try:
config = Config.from_file(temp_path)
assert config.mqtt.broker == "test-broker"
assert config.mqtt.port == 1883
assert config.mqtt.topic_prefix == "test"
assert config.mqtt.qos == 1
assert config.mqtt.retain is True
assert config.meshcore.connection_type == ConnectionType.SERIAL
assert config.meshcore.address == "/dev/ttyUSB0"
assert config.meshcore.timeout == 10
assert config.log_level == "DEBUG"
finally:
temp_path.unlink()
def test_from_yaml_file(self) -> None:
"""Test loading configuration from YAML file."""
config_data = {
"mqtt": {
"broker": "yaml-broker",
"port": 8883,
},
"meshcore": {
"connection_type": "ble",
"address": "AA:BB:CC:DD:EE:FF",
},
}
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
yaml.dump(config_data, f)
temp_path = Path(f.name)
try:
config = Config.from_file(temp_path)
assert config.mqtt.broker == "yaml-broker"
assert config.mqtt.port == 8883
assert config.meshcore.connection_type == ConnectionType.BLE
assert config.meshcore.address == "AA:BB:CC:DD:EE:FF"
finally:
temp_path.unlink()
def test_from_nonexistent_file(self) -> None:
"""Test loading from non-existent file."""
with pytest.raises(FileNotFoundError):
Config.from_file("/nonexistent/config.json")
def test_from_invalid_json(self) -> None:
"""Test loading from invalid JSON file."""
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
f.write("invalid json {")
temp_path = Path(f.name)
try:
with pytest.raises(ValueError, match="Invalid JSON configuration"):
Config.from_file(temp_path)
finally:
temp_path.unlink()
def test_from_invalid_yaml(self) -> None:
"""Test loading from invalid YAML file."""
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
f.write("invalid: yaml: content: [")
temp_path = Path(f.name)
try:
with pytest.raises(ValueError, match="Invalid YAML configuration"):
Config.from_file(temp_path)
finally:
temp_path.unlink()
class TestConnectionType:
"""Test connection type enum."""
def test_valid_values(self) -> None:
"""Test valid connection type values."""
assert ConnectionType.SERIAL.value == "serial"
assert ConnectionType.BLE.value == "ble"
assert ConnectionType.TCP.value == "tcp"
def test_from_string(self) -> None:
"""Test creating connection type from string."""
assert ConnectionType("serial") == ConnectionType.SERIAL
assert ConnectionType("ble") == ConnectionType.BLE
assert ConnectionType("tcp") == ConnectionType.TCP