592 lines
17 KiB
Go
592 lines
17 KiB
Go
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
|
|
}
|