mirror of
https://github.com/meshtastic/firmware.git
synced 2026-05-19 22:36:55 -04:00
* Start of MCP server and test suite * Add MCP server for interacting with meshtastic devices and testing framework / TUI * Update mcp-server/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix mcp-server review feedback from thread Agent-Logs-Url: https://github.com/meshtastic/firmware/sessions/91dc128a-ed50-4d07-8bb2-3dc6623a05f7 Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> * Enhance StreamAPI and PhoneAPI for improved log record handling and concurrency control * Semgrep fixes * Trunk and semgrep fixes * optimize pio streaming tee file writes Agent-Logs-Url: https://github.com/meshtastic/firmware/sessions/04e26c6b-6a2b-45be-bbeb-79ae4d0be633 Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> * chore: remove redundant log handle assignment Agent-Logs-Url: https://github.com/meshtastic/firmware/sessions/04e26c6b-6a2b-45be-bbeb-79ae4d0be633 Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> * Consolidate type imports and remove placeholder test files * Add tests for config persistence and more exchange messages * Refactor position test to validate on-demand request/reply behavior * Remove position request/reply test and update README for telemetry behavior * Fix transmit history file to get removed on factory reset --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
121 lines
4.7 KiB
Python
121 lines
4.7 KiB
Python
"""`userprefs.build_testing_profile` / `generate_psk` correctness.
|
|
|
|
The testing-profile generator is the critical primitive for automated test
|
|
labs: it must produce deterministic PSKs for a given seed (so every device
|
|
baked in a CI run joins the same mesh) and different PSKs for different seeds
|
|
(so concurrent labs don't collide).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
from meshtastic_mcp import userprefs
|
|
|
|
|
|
def test_generate_psk_is_32_bytes_formatted() -> None:
|
|
psk = userprefs.generate_psk(seed="deterministic")
|
|
# Format: "{ 0x.., 0x.., ... }" with 32 comma-separated hex bytes.
|
|
assert psk.startswith("{ ") and psk.endswith(" }")
|
|
bytes_part = psk.removeprefix("{ ").removesuffix(" }")
|
|
hex_bytes = [b.strip() for b in bytes_part.split(",")]
|
|
assert len(hex_bytes) == 32
|
|
for b in hex_bytes:
|
|
assert b.startswith("0x")
|
|
int(b, 16) # raises if not valid hex
|
|
|
|
|
|
def test_generate_psk_deterministic_under_same_seed() -> None:
|
|
a = userprefs.generate_psk(seed="pytest-session-123")
|
|
b = userprefs.generate_psk(seed="pytest-session-123")
|
|
assert a == b, "same seed must produce same PSK"
|
|
|
|
|
|
def test_generate_psk_varies_with_seed() -> None:
|
|
seeds = ["a", "b", "pytest-1", "pytest-2", "prod-fleet-alpha"]
|
|
psks = {userprefs.generate_psk(seed=s) for s in seeds}
|
|
assert len(psks) == len(seeds), "seed → PSK map must be injective"
|
|
|
|
|
|
def test_generate_psk_random_when_seedless() -> None:
|
|
a = userprefs.generate_psk(seed=None)
|
|
b = userprefs.generate_psk(seed=None)
|
|
# Not strictly guaranteed (birthday paradox), but 256-bit randomness makes
|
|
# a collision astronomically unlikely.
|
|
assert a != b
|
|
|
|
|
|
def test_testing_profile_contains_expected_keys() -> None:
|
|
profile = userprefs.build_testing_profile(psk_seed="ci-run-1")
|
|
|
|
required = {
|
|
"USERPREFS_CONFIG_LORA_REGION",
|
|
"USERPREFS_LORACONFIG_MODEM_PRESET",
|
|
"USERPREFS_LORACONFIG_CHANNEL_NUM",
|
|
"USERPREFS_CHANNELS_TO_WRITE",
|
|
"USERPREFS_CHANNEL_0_NAME",
|
|
"USERPREFS_CHANNEL_0_PSK",
|
|
"USERPREFS_CHANNEL_0_PRECISION",
|
|
"USERPREFS_CONFIG_LORA_IGNORE_MQTT",
|
|
"USERPREFS_MQTT_ENABLED",
|
|
"USERPREFS_CHANNEL_0_UPLINK_ENABLED",
|
|
"USERPREFS_CHANNEL_0_DOWNLINK_ENABLED",
|
|
}
|
|
assert required <= set(profile.keys())
|
|
|
|
# Defaults from the plan
|
|
assert profile["USERPREFS_CONFIG_LORA_REGION"].endswith("_US")
|
|
assert profile["USERPREFS_LORACONFIG_MODEM_PRESET"].endswith("_LONG_FAST")
|
|
assert profile["USERPREFS_LORACONFIG_CHANNEL_NUM"] == 88
|
|
assert profile["USERPREFS_CHANNEL_0_NAME"] == "McpTest"
|
|
|
|
|
|
def test_testing_profile_rejects_unknown_region() -> None:
|
|
with pytest.raises(ValueError, match="Unknown region"):
|
|
userprefs.build_testing_profile(region="ATLANTIS")
|
|
|
|
|
|
def test_testing_profile_rejects_unknown_modem_preset() -> None:
|
|
with pytest.raises(ValueError, match="Unknown modem_preset"):
|
|
userprefs.build_testing_profile(modem_preset="WARP_9")
|
|
|
|
|
|
def test_testing_profile_rejects_oversized_channel_name() -> None:
|
|
with pytest.raises(ValueError, match="11-char max"):
|
|
userprefs.build_testing_profile(channel_name="WayTooLongChannelName")
|
|
|
|
|
|
def test_testing_profile_rejects_oversized_short_name() -> None:
|
|
with pytest.raises(ValueError, match="≤4 chars"):
|
|
userprefs.build_testing_profile(short_name="TOOLONG")
|
|
|
|
|
|
def test_disable_mqtt_false_drops_mqtt_keys() -> None:
|
|
profile = userprefs.build_testing_profile(psk_seed="x", disable_mqtt=False)
|
|
# When disable_mqtt is False, the MQTT-gating keys should NOT be in the
|
|
# profile (device uses firmware defaults, whatever those are).
|
|
assert "USERPREFS_MQTT_ENABLED" not in profile
|
|
assert "USERPREFS_CHANNEL_0_UPLINK_ENABLED" not in profile
|
|
|
|
|
|
def test_disable_position_adds_gps_disabled() -> None:
|
|
profile = userprefs.build_testing_profile(psk_seed="x", disable_position=True)
|
|
assert profile["USERPREFS_CONFIG_GPS_MODE"].endswith("_DISABLED")
|
|
assert profile["USERPREFS_CONFIG_SMART_POSITION_ENABLED"] is False
|
|
|
|
|
|
def test_owner_names_included_when_provided() -> None:
|
|
profile = userprefs.build_testing_profile(
|
|
psk_seed="x", long_name="Lab Bench 1", short_name="LB1"
|
|
)
|
|
assert profile["USERPREFS_CONFIG_OWNER_LONG_NAME"] == "Lab Bench 1"
|
|
assert profile["USERPREFS_CONFIG_OWNER_SHORT_NAME"] == "LB1"
|
|
|
|
|
|
def test_psk_seed_isolation_across_ci_runs() -> None:
|
|
"""The core claim: two test labs running concurrently with different
|
|
session seeds produce different PSKs — their meshes cannot decode each
|
|
other's traffic."""
|
|
lab_a = userprefs.build_testing_profile(psk_seed="lab-A-nightly")
|
|
lab_b = userprefs.build_testing_profile(psk_seed="lab-B-nightly")
|
|
assert lab_a["USERPREFS_CHANNEL_0_PSK"] != lab_b["USERPREFS_CHANNEL_0_PSK"]
|