meshtastic: support
This commit is contained in:
591
protocol/meshtastic/packet_test.go
Normal file
591
protocol/meshtastic/packet_test.go
Normal file
@@ -0,0 +1,591 @@
|
||||
package meshtastic
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
meshtasticpb "git.maze.io/go/ham/protocol/meshtastic/pb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestPacketDecodeTextPayload(t *testing.T) {
|
||||
dataMsg := &meshtasticpb.Data{
|
||||
Portnum: meshtasticpb.PortNum_TEXT_MESSAGE_APP,
|
||||
Payload: []byte("hello mesh"),
|
||||
}
|
||||
|
||||
encodedData, err := proto.Marshal(dataMsg)
|
||||
if err != nil {
|
||||
t.Fatalf("marshaling data protobuf: %v", err)
|
||||
}
|
||||
|
||||
rawPacket := make([]byte, 16+len(encodedData))
|
||||
binary.LittleEndian.PutUint32(rawPacket[0:], 0x01020304)
|
||||
binary.LittleEndian.PutUint32(rawPacket[4:], 0x05060708)
|
||||
binary.LittleEndian.PutUint32(rawPacket[8:], 0x090a0b0c)
|
||||
rawPacket[12] = 0x01
|
||||
rawPacket[13] = 0x02
|
||||
rawPacket[14] = 0x03
|
||||
rawPacket[15] = 0x04
|
||||
copy(rawPacket[16:], encodedData)
|
||||
|
||||
var packet Packet
|
||||
if err := packet.Decode(rawPacket); err != nil {
|
||||
t.Fatalf("decoding packet: %v", err)
|
||||
}
|
||||
|
||||
if packet.Data == nil {
|
||||
t.Fatal("expected Data protobuf to be decoded")
|
||||
}
|
||||
if packet.Data.GetPortnum() != meshtasticpb.PortNum_TEXT_MESSAGE_APP {
|
||||
t.Fatalf("unexpected portnum: got %v", packet.Data.GetPortnum())
|
||||
}
|
||||
if packet.TextPayload != "hello mesh" {
|
||||
t.Fatalf("unexpected text payload: got %q", packet.TextPayload)
|
||||
}
|
||||
if packet.DecodedPayload != nil {
|
||||
t.Fatalf("expected no decoded protobuf payload for text app, got %T", packet.DecodedPayload)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPacketDecodeRejectsOversizedPayload(t *testing.T) {
|
||||
rawPacket := make([]byte, 16+maxPayloadSize+1)
|
||||
|
||||
var packet Packet
|
||||
err := packet.Decode(rawPacket)
|
||||
if err != ErrInvalidPacket {
|
||||
t.Fatalf("expected ErrInvalidPacket, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPacketJSONSerialization(t *testing.T) {
|
||||
dataMsg := &meshtasticpb.Data{
|
||||
Portnum: meshtasticpb.PortNum_TEXT_MESSAGE_APP,
|
||||
Payload: []byte("test message"),
|
||||
}
|
||||
|
||||
encodedData, err := proto.Marshal(dataMsg)
|
||||
if err != nil {
|
||||
t.Fatalf("marshaling data protobuf: %v", err)
|
||||
}
|
||||
|
||||
rawPacket := make([]byte, 16+len(encodedData))
|
||||
binary.LittleEndian.PutUint32(rawPacket[0:], 0x12345678) // source
|
||||
binary.LittleEndian.PutUint32(rawPacket[4:], 0xabcdef01) // destination
|
||||
binary.LittleEndian.PutUint32(rawPacket[8:], 0x99887766) // id
|
||||
rawPacket[12] = 0x11 // flags
|
||||
rawPacket[13] = 0x22 // channelHash
|
||||
rawPacket[14] = 0x33 // nextHop
|
||||
rawPacket[15] = 0x44 // relayNode
|
||||
copy(rawPacket[16:], encodedData)
|
||||
|
||||
var packet Packet
|
||||
if err := packet.Decode(rawPacket); err != nil {
|
||||
t.Fatalf("decoding packet: %v", err)
|
||||
}
|
||||
|
||||
jsonBytes, err := json.Marshal(&packet)
|
||||
if err != nil {
|
||||
t.Fatalf("marshaling to JSON: %v", err)
|
||||
}
|
||||
|
||||
var decoded map[string]interface{}
|
||||
if err := json.Unmarshal(jsonBytes, &decoded); err != nil {
|
||||
t.Fatalf("unmarshaling JSON: %v", err)
|
||||
}
|
||||
|
||||
// Check camelCase field names
|
||||
if _, ok := decoded["destination"]; !ok {
|
||||
t.Error("missing 'destination' field")
|
||||
}
|
||||
if _, ok := decoded["source"]; !ok {
|
||||
t.Error("missing 'source' field")
|
||||
}
|
||||
if _, ok := decoded["channelHash"]; !ok {
|
||||
t.Error("missing 'channelHash' field")
|
||||
}
|
||||
if _, ok := decoded["nextHop"]; !ok {
|
||||
t.Error("missing 'nextHop' field")
|
||||
}
|
||||
if _, ok := decoded["relayNode"]; !ok {
|
||||
t.Error("missing 'relayNode' field")
|
||||
}
|
||||
|
||||
// Check raw payload field
|
||||
if _, ok := decoded["raw"]; !ok {
|
||||
t.Error("missing 'raw' field")
|
||||
}
|
||||
|
||||
// Check text payload
|
||||
if textPayload, ok := decoded["textPayload"].(string); !ok || textPayload != "test message" {
|
||||
t.Errorf("expected textPayload='test message', got %v", decoded["textPayload"])
|
||||
}
|
||||
|
||||
// Verify PayloadLength and Payload are not in JSON
|
||||
if _, ok := decoded["PayloadLength"]; ok {
|
||||
t.Error("PayloadLength should not be in JSON output")
|
||||
}
|
||||
if _, ok := decoded["Payload"]; ok {
|
||||
t.Error("Payload should not be in JSON output")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProtobufMessagesCamelCaseJSON(t *testing.T) {
|
||||
// Test that protobuf messages serialize with camelCase JSON tags
|
||||
pos := &meshtasticpb.Position{
|
||||
LatitudeI: proto.Int32(379208045),
|
||||
LongitudeI: proto.Int32(-1223928905),
|
||||
Altitude: proto.Int32(120),
|
||||
Time: 1234567890,
|
||||
}
|
||||
|
||||
jsonBytes, err := json.Marshal(pos)
|
||||
if err != nil {
|
||||
t.Fatalf("marshaling Position to JSON: %v", err)
|
||||
}
|
||||
|
||||
var decoded map[string]interface{}
|
||||
if err := json.Unmarshal(jsonBytes, &decoded); err != nil {
|
||||
t.Fatalf("unmarshaling JSON: %v", err)
|
||||
}
|
||||
|
||||
// Check camelCase field names (not snake_case)
|
||||
if _, ok := decoded["latitudeI"]; !ok {
|
||||
t.Error("missing camelCase 'latitudeI' field")
|
||||
}
|
||||
if _, ok := decoded["longitudeI"]; !ok {
|
||||
t.Error("missing camelCase 'longitudeI' field")
|
||||
}
|
||||
if _, ok := decoded["altitude"]; !ok {
|
||||
t.Error("missing 'altitude' field")
|
||||
}
|
||||
|
||||
// Check that snake_case fields are NOT present
|
||||
if _, ok := decoded["latitude_i"]; ok {
|
||||
t.Error("found snake_case 'latitude_i' field, expected camelCase 'latitudeI'")
|
||||
}
|
||||
if _, ok := decoded["longitude_i"]; ok {
|
||||
t.Error("found snake_case 'longitude_i' field, expected camelCase 'longitudeI'")
|
||||
}
|
||||
|
||||
// Test User message with underscored fields
|
||||
user := &meshtasticpb.User{
|
||||
LongName: "Test User",
|
||||
ShortName: "TU",
|
||||
HwModel: meshtasticpb.HardwareModel_TBEAM,
|
||||
}
|
||||
|
||||
jsonBytes, err = json.Marshal(user)
|
||||
if err != nil {
|
||||
t.Fatalf("marshaling User to JSON: %v", err)
|
||||
}
|
||||
|
||||
decoded = make(map[string]interface{})
|
||||
if err := json.Unmarshal(jsonBytes, &decoded); err != nil {
|
||||
t.Fatalf("unmarshaling JSON: %v", err)
|
||||
}
|
||||
|
||||
// Check camelCase field names
|
||||
if _, ok := decoded["longName"]; !ok {
|
||||
t.Error("missing camelCase 'longName' field")
|
||||
}
|
||||
if _, ok := decoded["shortName"]; !ok {
|
||||
t.Error("missing camelCase 'shortName' field")
|
||||
}
|
||||
if _, ok := decoded["hwModel"]; !ok {
|
||||
t.Error("missing camelCase 'hwModel' field")
|
||||
}
|
||||
|
||||
// Check that snake_case fields are NOT present
|
||||
if _, ok := decoded["long_name"]; ok {
|
||||
t.Error("found snake_case 'long_name' field, expected camelCase 'longName'")
|
||||
}
|
||||
if _, ok := decoded["short_name"]; ok {
|
||||
t.Error("found snake_case 'short_name' field, expected camelCase 'shortName'")
|
||||
}
|
||||
if _, ok := decoded["hw_model"]; ok {
|
||||
t.Error("found snake_case 'hw_model' field, expected camelCase 'hwModel'")
|
||||
}
|
||||
}
|
||||
|
||||
// Test POSITION_APP portnum decoding
|
||||
func TestPacketDecodePositionApp(t *testing.T) {
|
||||
pos := &meshtasticpb.Position{
|
||||
LatitudeI: proto.Int32(379208045),
|
||||
LongitudeI: proto.Int32(-1223928905),
|
||||
Altitude: proto.Int32(120),
|
||||
Time: 1234567890,
|
||||
}
|
||||
|
||||
dataMsg := &meshtasticpb.Data{
|
||||
Portnum: meshtasticpb.PortNum_POSITION_APP,
|
||||
Payload: encodeProto(t, pos),
|
||||
}
|
||||
|
||||
packet := decodeTestPacket(t, dataMsg)
|
||||
|
||||
if packet.Data == nil {
|
||||
t.Fatal("expected Data protobuf to be decoded")
|
||||
}
|
||||
if packet.Data.GetPortnum() != meshtasticpb.PortNum_POSITION_APP {
|
||||
t.Fatalf("unexpected portnum: got %v", packet.Data.GetPortnum())
|
||||
}
|
||||
|
||||
decodedPos, ok := packet.DecodedPayload.(*meshtasticpb.Position)
|
||||
if !ok {
|
||||
t.Fatalf("expected *Position, got %T", packet.DecodedPayload)
|
||||
}
|
||||
if decodedPos.GetLatitudeI() != 379208045 {
|
||||
t.Errorf("unexpected latitude: got %d", decodedPos.GetLatitudeI())
|
||||
}
|
||||
if decodedPos.GetLongitudeI() != -1223928905 {
|
||||
t.Errorf("unexpected longitude: got %d", decodedPos.GetLongitudeI())
|
||||
}
|
||||
if decodedPos.GetAltitude() != 120 {
|
||||
t.Errorf("unexpected altitude: got %d", decodedPos.GetAltitude())
|
||||
}
|
||||
}
|
||||
|
||||
// Test NODEINFO_APP portnum decoding
|
||||
func TestPacketDecodeNodeInfoApp(t *testing.T) {
|
||||
user := &meshtasticpb.User{
|
||||
LongName: "Mesh Node",
|
||||
ShortName: "MN",
|
||||
HwModel: meshtasticpb.HardwareModel_TBEAM,
|
||||
IsLicensed: true,
|
||||
Macaddr: []byte{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
|
||||
}
|
||||
|
||||
dataMsg := &meshtasticpb.Data{
|
||||
Portnum: meshtasticpb.PortNum_NODEINFO_APP,
|
||||
Payload: encodeProto(t, user),
|
||||
}
|
||||
|
||||
packet := decodeTestPacket(t, dataMsg)
|
||||
|
||||
if packet.Data == nil {
|
||||
t.Fatal("expected Data protobuf to be decoded")
|
||||
}
|
||||
if packet.Data.GetPortnum() != meshtasticpb.PortNum_NODEINFO_APP {
|
||||
t.Fatalf("unexpected portnum: got %v", packet.Data.GetPortnum())
|
||||
}
|
||||
|
||||
decodedUser, ok := packet.DecodedPayload.(*meshtasticpb.User)
|
||||
if !ok {
|
||||
t.Fatalf("expected *User, got %T", packet.DecodedPayload)
|
||||
}
|
||||
if decodedUser.GetLongName() != "Mesh Node" {
|
||||
t.Errorf("unexpected long name: got %s", decodedUser.GetLongName())
|
||||
}
|
||||
if decodedUser.GetShortName() != "MN" {
|
||||
t.Errorf("unexpected short name: got %s", decodedUser.GetShortName())
|
||||
}
|
||||
if decodedUser.GetHwModel() != meshtasticpb.HardwareModel_TBEAM {
|
||||
t.Errorf("unexpected hardware model: got %v", decodedUser.GetHwModel())
|
||||
}
|
||||
}
|
||||
|
||||
// Test TELEMETRY_APP portnum decoding
|
||||
func TestPacketDecodeTelemetryApp(t *testing.T) {
|
||||
batteryLevel := uint32(85)
|
||||
voltage := float32(12.5)
|
||||
channelUtil := float32(45.3)
|
||||
airUtilTx := float32(15.2)
|
||||
|
||||
telemetry := &meshtasticpb.Telemetry{
|
||||
Time: 1234567890,
|
||||
Variant: &meshtasticpb.Telemetry_DeviceMetrics_{
|
||||
DeviceMetrics: &meshtasticpb.DeviceMetrics{
|
||||
BatteryLevel: &batteryLevel,
|
||||
Voltage: &voltage,
|
||||
ChannelUtilization: &channelUtil,
|
||||
AirUtilTx: &airUtilTx,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
dataMsg := &meshtasticpb.Data{
|
||||
Portnum: meshtasticpb.PortNum_TELEMETRY_APP,
|
||||
Payload: encodeProto(t, telemetry),
|
||||
}
|
||||
|
||||
packet := decodeTestPacket(t, dataMsg)
|
||||
|
||||
if packet.Data == nil {
|
||||
t.Fatal("expected Data protobuf to be decoded")
|
||||
}
|
||||
if packet.Data.GetPortnum() != meshtasticpb.PortNum_TELEMETRY_APP {
|
||||
t.Fatalf("unexpected portnum: got %v", packet.Data.GetPortnum())
|
||||
}
|
||||
|
||||
decodedTelem, ok := packet.DecodedPayload.(*meshtasticpb.Telemetry)
|
||||
if !ok {
|
||||
t.Fatalf("expected *Telemetry, got %T", packet.DecodedPayload)
|
||||
}
|
||||
if decodedTelem.GetTime() != 1234567890 {
|
||||
t.Errorf("unexpected time: got %d", decodedTelem.GetTime())
|
||||
}
|
||||
deviceMetrics := decodedTelem.GetDeviceMetrics()
|
||||
if deviceMetrics == nil {
|
||||
t.Fatal("expected DeviceMetrics to be set")
|
||||
}
|
||||
if deviceMetrics.GetBatteryLevel() != 85 {
|
||||
t.Errorf("unexpected battery level: got %d", deviceMetrics.GetBatteryLevel())
|
||||
}
|
||||
}
|
||||
|
||||
// Test ROUTING_APP portnum decoding
|
||||
func TestPacketDecodeRoutingApp(t *testing.T) {
|
||||
routing := &meshtasticpb.Routing{
|
||||
Variant: &meshtasticpb.Routing_RouteReply_{
|
||||
RouteReply: &meshtasticpb.Routing_RouteReply{},
|
||||
},
|
||||
}
|
||||
|
||||
dataMsg := &meshtasticpb.Data{
|
||||
Portnum: meshtasticpb.PortNum_ROUTING_APP,
|
||||
Payload: encodeProto(t, routing),
|
||||
}
|
||||
|
||||
packet := decodeTestPacket(t, dataMsg)
|
||||
|
||||
if packet.Data == nil {
|
||||
t.Fatal("expected Data protobuf to be decoded")
|
||||
}
|
||||
if packet.Data.GetPortnum() != meshtasticpb.PortNum_ROUTING_APP {
|
||||
t.Fatalf("unexpected portnum: got %v", packet.Data.GetPortnum())
|
||||
}
|
||||
|
||||
decodedRouting, ok := packet.DecodedPayload.(*meshtasticpb.Routing)
|
||||
if !ok {
|
||||
t.Fatalf("expected *Routing, got %T", packet.DecodedPayload)
|
||||
}
|
||||
if decodedRouting.GetRouteReply() == nil {
|
||||
t.Fatal("expected RouteReply to be set")
|
||||
}
|
||||
}
|
||||
|
||||
// Test ADMIN_APP portnum decoding
|
||||
func TestPacketDecodeAdminApp(t *testing.T) {
|
||||
// AdminMessage has a sessionPasskey field
|
||||
admin := &meshtasticpb.AdminMessage{
|
||||
SessionPasskey: []byte{0x01, 0x02, 0x03, 0x04},
|
||||
}
|
||||
|
||||
dataMsg := &meshtasticpb.Data{
|
||||
Portnum: meshtasticpb.PortNum_ADMIN_APP,
|
||||
Payload: encodeProto(t, admin),
|
||||
}
|
||||
|
||||
packet := decodeTestPacket(t, dataMsg)
|
||||
|
||||
if packet.Data == nil {
|
||||
t.Fatal("expected Data protobuf to be decoded")
|
||||
}
|
||||
if packet.Data.GetPortnum() != meshtasticpb.PortNum_ADMIN_APP {
|
||||
t.Fatalf("unexpected portnum: got %v", packet.Data.GetPortnum())
|
||||
}
|
||||
|
||||
decodedAdmin, ok := packet.DecodedPayload.(*meshtasticpb.AdminMessage)
|
||||
if !ok {
|
||||
t.Fatalf("expected *AdminMessage, got %T", packet.DecodedPayload)
|
||||
}
|
||||
if len(decodedAdmin.GetSessionPasskey()) != 4 {
|
||||
t.Errorf("unexpected session passkey length: got %d", len(decodedAdmin.GetSessionPasskey()))
|
||||
}
|
||||
}
|
||||
|
||||
// Test WAYPOINT_APP portnum decoding
|
||||
func TestPacketDecodeWaypointApp(t *testing.T) {
|
||||
latI := int32(379208045)
|
||||
lonI := int32(-1223928905)
|
||||
|
||||
waypoint := &meshtasticpb.Waypoint{
|
||||
Id: 1001,
|
||||
Name: "Home Sweet Home",
|
||||
Description: "My house",
|
||||
LatitudeI: &latI,
|
||||
LongitudeI: &lonI,
|
||||
Expire: 1234567890,
|
||||
}
|
||||
|
||||
dataMsg := &meshtasticpb.Data{
|
||||
Portnum: meshtasticpb.PortNum_WAYPOINT_APP,
|
||||
Payload: encodeProto(t, waypoint),
|
||||
}
|
||||
|
||||
packet := decodeTestPacket(t, dataMsg)
|
||||
|
||||
if packet.Data == nil {
|
||||
t.Fatal("expected Data protobuf to be decoded")
|
||||
}
|
||||
if packet.Data.GetPortnum() != meshtasticpb.PortNum_WAYPOINT_APP {
|
||||
t.Fatalf("unexpected portnum: got %v", packet.Data.GetPortnum())
|
||||
}
|
||||
|
||||
decodedWaypoint, ok := packet.DecodedPayload.(*meshtasticpb.Waypoint)
|
||||
if !ok {
|
||||
t.Fatalf("expected *Waypoint, got %T", packet.DecodedPayload)
|
||||
}
|
||||
if decodedWaypoint.GetName() != "Home Sweet Home" {
|
||||
t.Errorf("unexpected waypoint name: got %s", decodedWaypoint.GetName())
|
||||
}
|
||||
if decodedWaypoint.GetId() != 1001 {
|
||||
t.Errorf("unexpected waypoint id: got %d", decodedWaypoint.GetId())
|
||||
}
|
||||
}
|
||||
|
||||
// Test NEIGHBORINFO_APP portnum decoding
|
||||
func TestPacketDecodeNeighborInfoApp(t *testing.T) {
|
||||
neighborInfo := &meshtasticpb.NeighborInfo{
|
||||
NodeId: 12345,
|
||||
NodeBroadcastIntervalSecs: 60,
|
||||
}
|
||||
|
||||
dataMsg := &meshtasticpb.Data{
|
||||
Portnum: meshtasticpb.PortNum_NEIGHBORINFO_APP,
|
||||
Payload: encodeProto(t, neighborInfo),
|
||||
}
|
||||
|
||||
packet := decodeTestPacket(t, dataMsg)
|
||||
|
||||
if packet.Data == nil {
|
||||
t.Fatal("expected Data protobuf to be decoded")
|
||||
}
|
||||
if packet.Data.GetPortnum() != meshtasticpb.PortNum_NEIGHBORINFO_APP {
|
||||
t.Fatalf("unexpected portnum: got %v", packet.Data.GetPortnum())
|
||||
}
|
||||
|
||||
decodedNeighbor, ok := packet.DecodedPayload.(*meshtasticpb.NeighborInfo)
|
||||
if !ok {
|
||||
t.Fatalf("expected *NeighborInfo, got %T", packet.DecodedPayload)
|
||||
}
|
||||
if decodedNeighbor.GetNodeId() != 12345 {
|
||||
t.Errorf("unexpected node id: got %d", decodedNeighbor.GetNodeId())
|
||||
}
|
||||
}
|
||||
|
||||
// Test NODE_STATUS_APP portnum decoding
|
||||
func TestPacketDecodeNodeStatusApp(t *testing.T) {
|
||||
statusMsg := &meshtasticpb.StatusMessage{
|
||||
Status: "Device online",
|
||||
}
|
||||
|
||||
dataMsg := &meshtasticpb.Data{
|
||||
Portnum: meshtasticpb.PortNum_NODE_STATUS_APP,
|
||||
Payload: encodeProto(t, statusMsg),
|
||||
}
|
||||
|
||||
packet := decodeTestPacket(t, dataMsg)
|
||||
|
||||
if packet.Data == nil {
|
||||
t.Fatal("expected Data protobuf to be decoded")
|
||||
}
|
||||
if packet.Data.GetPortnum() != meshtasticpb.PortNum_NODE_STATUS_APP {
|
||||
t.Fatalf("unexpected portnum: got %v", packet.Data.GetPortnum())
|
||||
}
|
||||
|
||||
decodedStatus, ok := packet.DecodedPayload.(*meshtasticpb.StatusMessage)
|
||||
if !ok {
|
||||
t.Fatalf("expected *StatusMessage, got %T", packet.DecodedPayload)
|
||||
}
|
||||
if decodedStatus.GetStatus() != "Device online" {
|
||||
t.Errorf("unexpected status: got %s", decodedStatus.GetStatus())
|
||||
}
|
||||
}
|
||||
|
||||
// Test REMOTE_HARDWARE_APP portnum decoding
|
||||
func TestPacketDecodeRemoteHardwareApp(t *testing.T) {
|
||||
hwMsg := &meshtasticpb.HardwareMessage{
|
||||
Type: meshtasticpb.HardwareMessage_READ_GPIOS,
|
||||
GpioMask: 0xFF,
|
||||
}
|
||||
|
||||
dataMsg := &meshtasticpb.Data{
|
||||
Portnum: meshtasticpb.PortNum_REMOTE_HARDWARE_APP,
|
||||
Payload: encodeProto(t, hwMsg),
|
||||
}
|
||||
|
||||
packet := decodeTestPacket(t, dataMsg)
|
||||
|
||||
if packet.Data == nil {
|
||||
t.Fatal("expected Data protobuf to be decoded")
|
||||
}
|
||||
if packet.Data.GetPortnum() != meshtasticpb.PortNum_REMOTE_HARDWARE_APP {
|
||||
t.Fatalf("unexpected portnum: got %v", packet.Data.GetPortnum())
|
||||
}
|
||||
|
||||
decodedHw, ok := packet.DecodedPayload.(*meshtasticpb.HardwareMessage)
|
||||
if !ok {
|
||||
t.Fatalf("expected *HardwareMessage, got %T", packet.DecodedPayload)
|
||||
}
|
||||
if decodedHw.GetType() != meshtasticpb.HardwareMessage_READ_GPIOS {
|
||||
t.Errorf("unexpected hardware message type: got %v", decodedHw.GetType())
|
||||
}
|
||||
}
|
||||
|
||||
// Test text payload portnums
|
||||
func TestPacketDecodeTextPayloads(t *testing.T) {
|
||||
textPortNums := map[meshtasticpb.PortNum]string{
|
||||
meshtasticpb.PortNum_TEXT_MESSAGE_APP: "text message app",
|
||||
meshtasticpb.PortNum_ALERT_APP: "alert message",
|
||||
meshtasticpb.PortNum_DETECTION_SENSOR_APP: "detection sensor",
|
||||
meshtasticpb.PortNum_REPLY_APP: "reply message",
|
||||
meshtasticpb.PortNum_RANGE_TEST_APP: "range test message",
|
||||
}
|
||||
|
||||
for portNum, expectedText := range textPortNums {
|
||||
t.Run(portNum.String(), func(t *testing.T) {
|
||||
dataMsg := &meshtasticpb.Data{
|
||||
Portnum: portNum,
|
||||
Payload: []byte(expectedText),
|
||||
}
|
||||
|
||||
packet := decodeTestPacket(t, dataMsg)
|
||||
|
||||
if packet.Data == nil {
|
||||
t.Fatal("expected Data protobuf to be decoded")
|
||||
}
|
||||
if packet.Data.GetPortnum() != portNum {
|
||||
t.Fatalf("unexpected portnum: got %v", packet.Data.GetPortnum())
|
||||
}
|
||||
if packet.TextPayload != expectedText {
|
||||
t.Errorf("unexpected text payload: got %q, want %q", packet.TextPayload, expectedText)
|
||||
}
|
||||
if packet.DecodedPayload != nil {
|
||||
t.Errorf("expected no decoded payload for text portnum, got %T", packet.DecodedPayload)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to encode a proto.Message
|
||||
func encodeProto(t *testing.T, msg proto.Message) []byte {
|
||||
encoded, err := proto.Marshal(msg)
|
||||
if err != nil {
|
||||
t.Fatalf("marshaling protobuf: %v", err)
|
||||
}
|
||||
return encoded
|
||||
}
|
||||
|
||||
// Helper function to create and decode a test packet
|
||||
func decodeTestPacket(t *testing.T, dataMsg *meshtasticpb.Data) *Packet {
|
||||
encodedData := encodeProto(t, dataMsg)
|
||||
|
||||
rawPacket := make([]byte, 16+len(encodedData))
|
||||
binary.LittleEndian.PutUint32(rawPacket[0:], 0x12345678) // source
|
||||
binary.LittleEndian.PutUint32(rawPacket[4:], 0xabcdef01) // destination
|
||||
binary.LittleEndian.PutUint32(rawPacket[8:], 0x99887766) // id
|
||||
rawPacket[12] = 0x11 // flags
|
||||
rawPacket[13] = 0x22 // channelHash
|
||||
rawPacket[14] = 0x33 // nextHop
|
||||
rawPacket[15] = 0x44 // relayNode
|
||||
copy(rawPacket[16:], encodedData)
|
||||
|
||||
var packet Packet
|
||||
if err := packet.Decode(rawPacket); err != nil {
|
||||
t.Fatalf("decoding packet: %v", err)
|
||||
}
|
||||
return &packet
|
||||
}
|
||||
Reference in New Issue
Block a user