mirror of
https://github.com/meshtastic/python.git
synced 2026-01-10 16:57:58 -05:00
Compare commits
1 Commits
master
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b91e145270 |
@@ -253,7 +253,6 @@ class MeshInterface: # pylint: disable=R0902
|
||||
"channel": "Channel",
|
||||
"lastHeard": "LastHeard",
|
||||
"since": "Since",
|
||||
"isFavorite": "Fav",
|
||||
|
||||
}
|
||||
|
||||
@@ -301,7 +300,7 @@ class MeshInterface: # pylint: disable=R0902
|
||||
showFields = ["N", "user.longName", "user.id", "user.shortName", "user.hwModel", "user.publicKey",
|
||||
"user.role", "position.latitude", "position.longitude", "position.altitude",
|
||||
"deviceMetrics.batteryLevel", "deviceMetrics.channelUtilization",
|
||||
"deviceMetrics.airUtilTx", "snr", "hopsAway", "channel", "isFavorite", "lastHeard", "since"]
|
||||
"deviceMetrics.airUtilTx", "snr", "hopsAway", "channel", "lastHeard", "since"]
|
||||
else:
|
||||
# Always at least include the row number.
|
||||
showFields.insert(0, "N")
|
||||
@@ -343,8 +342,6 @@ class MeshInterface: # pylint: disable=R0902
|
||||
formatted_value = "Powered"
|
||||
else:
|
||||
formatted_value = formatFloat(raw_value, 0, "%")
|
||||
elif field == "isFavorite":
|
||||
formatted_value = "*" if raw_value else ""
|
||||
elif field == "lastHeard":
|
||||
formatted_value = getLH(raw_value)
|
||||
elif field == "position.latitude":
|
||||
|
||||
@@ -1,221 +0,0 @@
|
||||
"""Meshtastic unit tests for showNodes favorite column feature"""
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from ..mesh_interface import MeshInterface
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def _iface_with_favorite_nodes():
|
||||
"""Fixture to setup nodes with favorite flags."""
|
||||
nodesById = {
|
||||
"!9388f81c": {
|
||||
"num": 2475227164,
|
||||
"user": {
|
||||
"id": "!9388f81c",
|
||||
"longName": "Favorite Node",
|
||||
"shortName": "FAV1",
|
||||
"macaddr": "RBeTiPgc",
|
||||
"hwModel": "TBEAM",
|
||||
},
|
||||
"position": {},
|
||||
"lastHeard": 1640204888,
|
||||
"isFavorite": True,
|
||||
},
|
||||
"!12345678": {
|
||||
"num": 305419896,
|
||||
"user": {
|
||||
"id": "!12345678",
|
||||
"longName": "Regular Node",
|
||||
"shortName": "REG1",
|
||||
"macaddr": "ABCDEFGH",
|
||||
"hwModel": "TLORA_V2",
|
||||
},
|
||||
"position": {},
|
||||
"lastHeard": 1640204999,
|
||||
"isFavorite": False,
|
||||
},
|
||||
"!abcdef00": {
|
||||
"num": 2882400000,
|
||||
"user": {
|
||||
"id": "!abcdef00",
|
||||
"longName": "Legacy Node",
|
||||
"shortName": "LEG1",
|
||||
"macaddr": "XYZABC00",
|
||||
"hwModel": "HELTEC_V3",
|
||||
},
|
||||
"position": {},
|
||||
"lastHeard": 1640205000,
|
||||
# Note: No isFavorite field - testing backward compatibility
|
||||
},
|
||||
}
|
||||
|
||||
nodesByNum = {
|
||||
2475227164: {
|
||||
"num": 2475227164,
|
||||
"user": {
|
||||
"id": "!9388f81c",
|
||||
"longName": "Favorite Node",
|
||||
"shortName": "FAV1",
|
||||
"macaddr": "RBeTiPgc",
|
||||
"hwModel": "TBEAM",
|
||||
},
|
||||
"position": {"time": 1640206266},
|
||||
"lastHeard": 1640206266,
|
||||
"isFavorite": True,
|
||||
},
|
||||
305419896: {
|
||||
"num": 305419896,
|
||||
"user": {
|
||||
"id": "!12345678",
|
||||
"longName": "Regular Node",
|
||||
"shortName": "REG1",
|
||||
"macaddr": "ABCDEFGH",
|
||||
"hwModel": "TLORA_V2",
|
||||
},
|
||||
"position": {"time": 1640206200},
|
||||
"lastHeard": 1640206200,
|
||||
"isFavorite": False,
|
||||
},
|
||||
2882400000: {
|
||||
"num": 2882400000,
|
||||
"user": {
|
||||
"id": "!abcdef00",
|
||||
"longName": "Legacy Node",
|
||||
"shortName": "LEG1",
|
||||
"macaddr": "XYZABC00",
|
||||
"hwModel": "HELTEC_V3",
|
||||
},
|
||||
"position": {"time": 1640206100},
|
||||
"lastHeard": 1640206100,
|
||||
# Note: No isFavorite field - testing backward compatibility
|
||||
},
|
||||
}
|
||||
|
||||
iface = MeshInterface(noProto=True)
|
||||
iface.nodes = nodesById
|
||||
iface.nodesByNum = nodesByNum
|
||||
myInfo = MagicMock()
|
||||
iface.myInfo = myInfo
|
||||
iface.myInfo.my_node_num = 2475227164
|
||||
return iface
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_showNodes_favorite_column_header(capsys, _iface_with_favorite_nodes):
|
||||
"""Test that 'Fav' column header appears in showNodes output"""
|
||||
iface = _iface_with_favorite_nodes
|
||||
iface.showNodes()
|
||||
out, err = capsys.readouterr()
|
||||
assert "Fav" in out
|
||||
assert err == ""
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_showNodes_favorite_asterisk_display(capsys, _iface_with_favorite_nodes):
|
||||
"""Test that favorite nodes show asterisk and non-favorites show empty"""
|
||||
iface = _iface_with_favorite_nodes
|
||||
iface.showNodes()
|
||||
out, err = capsys.readouterr()
|
||||
|
||||
# Check that the output contains the "Fav" column
|
||||
assert "Fav" in out
|
||||
|
||||
# Find lines containing our nodes
|
||||
lines = out.split('\n')
|
||||
favorite_line = None
|
||||
regular_line = None
|
||||
legacy_line = None
|
||||
for line in lines:
|
||||
if "Favorite Node" in line or "FAV1" in line:
|
||||
favorite_line = line
|
||||
if "Regular Node" in line or "REG1" in line:
|
||||
regular_line = line
|
||||
if "Legacy Node" in line or "LEG1" in line:
|
||||
legacy_line = line
|
||||
|
||||
# Verify all nodes are present in the output
|
||||
assert favorite_line is not None, "Favorite node should be in output"
|
||||
assert regular_line is not None, "Regular node should be in output"
|
||||
assert legacy_line is not None, "Legacy node should be in output"
|
||||
|
||||
# Verify the favorite node has an asterisk in its row
|
||||
assert "*" in favorite_line, "Favorite node should have an asterisk"
|
||||
|
||||
# Verify the regular (non-favorite) node does NOT have an asterisk
|
||||
assert regular_line.count("*") == 0, "Non-favorite node should not have an asterisk"
|
||||
|
||||
# Verify the legacy node (without isFavorite field) does NOT have an asterisk
|
||||
assert legacy_line.count("*") == 0, "Legacy node without isFavorite field should not have an asterisk"
|
||||
|
||||
assert err == ""
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_showNodes_favorite_field_formatting():
|
||||
"""Test the formatting logic for isFavorite field"""
|
||||
# Test favorite node
|
||||
raw_value = True
|
||||
formatted_value = "*" if raw_value else ""
|
||||
assert formatted_value == "*"
|
||||
|
||||
# Test non-favorite node
|
||||
raw_value = False
|
||||
formatted_value = "*" if raw_value else ""
|
||||
assert formatted_value == ""
|
||||
|
||||
# Test None/missing value
|
||||
raw_value = None
|
||||
formatted_value = "*" if raw_value else ""
|
||||
assert formatted_value == ""
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_showNodes_with_custom_fields_including_favorite(capsys, _iface_with_favorite_nodes):
|
||||
"""Test that isFavorite can be specified in custom showFields"""
|
||||
iface = _iface_with_favorite_nodes
|
||||
custom_fields = ["user.longName", "isFavorite"]
|
||||
iface.showNodes(showFields=custom_fields)
|
||||
out, err = capsys.readouterr()
|
||||
|
||||
# Should still show the Fav column when explicitly requested
|
||||
assert "Fav" in out
|
||||
assert err == ""
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_showNodes_default_fields_includes_favorite(_iface_with_favorite_nodes):
|
||||
"""Test that isFavorite is included in default fields"""
|
||||
iface = _iface_with_favorite_nodes
|
||||
|
||||
# Call showNodes which uses default fields
|
||||
result = iface.showNodes()
|
||||
|
||||
# The result should contain the formatted table as a string
|
||||
assert "Fav" in result
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_showNodes_backward_compatibility_missing_field(capsys, _iface_with_favorite_nodes):
|
||||
"""Test that nodes without isFavorite field are handled gracefully"""
|
||||
iface = _iface_with_favorite_nodes
|
||||
iface.showNodes()
|
||||
out, err = capsys.readouterr()
|
||||
|
||||
# Find the legacy node line
|
||||
lines = out.split('\n')
|
||||
legacy_line = None
|
||||
for line in lines:
|
||||
if "Legacy Node" in line or "LEG1" in line:
|
||||
legacy_line = line
|
||||
break
|
||||
|
||||
# Verify the legacy node appears in output
|
||||
assert legacy_line is not None, "Legacy node without isFavorite field should appear in output"
|
||||
|
||||
# Verify it doesn't have an asterisk (should be treated as non-favorite)
|
||||
assert legacy_line.count("*") == 0, "Legacy node should not have asterisk (treated as non-favorite)"
|
||||
|
||||
assert err == ""
|
||||
32
poetry.lock
generated
32
poetry.lock
generated
@@ -1,4 +1,4 @@
|
||||
# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "altgraph"
|
||||
@@ -3438,7 +3438,7 @@ description = "Type annotations for pandas"
|
||||
optional = true
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
markers = "python_version < \"3.11\" and extra == \"analysis\""
|
||||
markers = "extra == \"analysis\""
|
||||
files = [
|
||||
{file = "pandas_stubs-2.2.2.240807-py3-none-any.whl", hash = "sha256:893919ad82be4275f0d07bb47a95d08bae580d3fdea308a7acfcb3f02e76186e"},
|
||||
{file = "pandas_stubs-2.2.2.240807.tar.gz", hash = "sha256:64a559725a57a449f46225fbafc422520b7410bff9252b661a225b5559192a93"},
|
||||
@@ -3448,23 +3448,6 @@ files = [
|
||||
numpy = ">=1.23.5"
|
||||
types-pytz = ">=2022.1.1"
|
||||
|
||||
[[package]]
|
||||
name = "pandas-stubs"
|
||||
version = "2.3.2.250926"
|
||||
description = "Type annotations for pandas"
|
||||
optional = true
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
markers = "extra == \"analysis\" and python_version >= \"3.11\""
|
||||
files = [
|
||||
{file = "pandas_stubs-2.3.2.250926-py3-none-any.whl", hash = "sha256:81121818453dcfe00f45c852f4dceee043640b813830f6e7bd084a4ef7ff7270"},
|
||||
{file = "pandas_stubs-2.3.2.250926.tar.gz", hash = "sha256:c64b9932760ceefb96a3222b953e6a251321a9832a28548be6506df473a66406"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
numpy = ">=1.23.5"
|
||||
types-pytz = ">=2022.1.1"
|
||||
|
||||
[[package]]
|
||||
name = "pandocfilters"
|
||||
version = "1.5.1"
|
||||
@@ -4309,6 +4292,7 @@ python-versions = ">=3.3, <4"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "Pypubsub-4.0.3-py3-none-any.whl", hash = "sha256:7f716bae9388afe01ff82b264ba8a96a8ae78b42bb1f114f2716ca8f9e404e2a"},
|
||||
{file = "pypubsub-4.0.3.tar.gz", hash = "sha256:32d662de3ade0fb0880da92df209c62a4803684de5ccb8d19421c92747a258c7"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5512,21 +5496,21 @@ dev = ["flake8", "flake8-annotations", "flake8-bandit", "flake8-bugbear", "flake
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.5.0"
|
||||
version = "2.6.3"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main", "analysis", "dev"]
|
||||
files = [
|
||||
{file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"},
|
||||
{file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"},
|
||||
{file = "urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4"},
|
||||
{file = "urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""]
|
||||
brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""]
|
||||
h2 = ["h2 (>=4,<5)"]
|
||||
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||
zstd = ["zstandard (>=0.18.0)"]
|
||||
zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""]
|
||||
|
||||
[[package]]
|
||||
name = "wcwidth"
|
||||
|
||||
Reference in New Issue
Block a user