mirror of
https://github.com/meshtastic/python.git
synced 2026-01-11 17:27:56 -05:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a73f432f42 | ||
|
|
c3c5ce64dd | ||
|
|
683dd23d63 | ||
|
|
57d43b84e4 | ||
|
|
4f6d183ed1 |
@@ -253,6 +253,7 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
"channel": "Channel",
|
"channel": "Channel",
|
||||||
"lastHeard": "LastHeard",
|
"lastHeard": "LastHeard",
|
||||||
"since": "Since",
|
"since": "Since",
|
||||||
|
"isFavorite": "Fav",
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,7 +301,7 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
showFields = ["N", "user.longName", "user.id", "user.shortName", "user.hwModel", "user.publicKey",
|
showFields = ["N", "user.longName", "user.id", "user.shortName", "user.hwModel", "user.publicKey",
|
||||||
"user.role", "position.latitude", "position.longitude", "position.altitude",
|
"user.role", "position.latitude", "position.longitude", "position.altitude",
|
||||||
"deviceMetrics.batteryLevel", "deviceMetrics.channelUtilization",
|
"deviceMetrics.batteryLevel", "deviceMetrics.channelUtilization",
|
||||||
"deviceMetrics.airUtilTx", "snr", "hopsAway", "channel", "lastHeard", "since"]
|
"deviceMetrics.airUtilTx", "snr", "hopsAway", "channel", "isFavorite", "lastHeard", "since"]
|
||||||
else:
|
else:
|
||||||
# Always at least include the row number.
|
# Always at least include the row number.
|
||||||
showFields.insert(0, "N")
|
showFields.insert(0, "N")
|
||||||
@@ -342,6 +343,8 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
formatted_value = "Powered"
|
formatted_value = "Powered"
|
||||||
else:
|
else:
|
||||||
formatted_value = formatFloat(raw_value, 0, "%")
|
formatted_value = formatFloat(raw_value, 0, "%")
|
||||||
|
elif field == "isFavorite":
|
||||||
|
formatted_value = "*" if raw_value else ""
|
||||||
elif field == "lastHeard":
|
elif field == "lastHeard":
|
||||||
formatted_value = getLH(raw_value)
|
formatted_value = getLH(raw_value)
|
||||||
elif field == "position.latitude":
|
elif field == "position.latitude":
|
||||||
|
|||||||
221
meshtastic/tests/test_showNodes_favorite.py
Normal file
221
meshtastic/tests/test_showNodes_favorite.py
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
"""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 == ""
|
||||||
Reference in New Issue
Block a user