删除所有测试文件

This commit is contained in:
2026-08-07 16:26:30 +08:00
parent 7bc2e53ce6
commit 01aee47024
20 changed files with 0 additions and 3960 deletions
-215
View File
@@ -1,215 +0,0 @@
package mqtpp
import "testing"
func TestBuildTextMessageServiceEnvelopeRoundTrip(t *testing.T) {
key, err := ExpandPSK("AQ==")
if err != nil {
t.Fatalf("ExpandPSK() error = %v", err)
}
raw, err := BuildTextMessageServiceEnvelope(TextMessageBuildOptions{
PacketBuildOptions: PacketBuildOptions{
FromNodeNum: 0x12345678,
ToNodeNum: NodeNumBroadcast,
PacketID: 0x87654321,
ChannelID: "LongFast",
GatewayID: "!12345678",
PSK: key,
Encrypt: true,
ViaMQTT: true,
},
Text: "hello from bot",
})
if err != nil {
t.Fatalf("BuildTextMessageServiceEnvelope() error = %v", err)
}
valid, _, record := MQTTPP("msh/2/e/LongFast/!12345678", raw, key, Options{})
if !valid {
t.Fatalf("MQTTPP() valid = false, record = %#v", record)
}
if record["type"] != "text_message" {
t.Fatalf("record type = %v", record["type"])
}
if record["text"] != "hello from bot" {
t.Fatalf("text = %v", record["text"])
}
if record["from_num"] != uint32(0x12345678) {
t.Fatalf("from_num = %v", record["from_num"])
}
if record["packet_to_num"] != uint32(NodeNumBroadcast) {
t.Fatalf("packet_to_num = %v", record["packet_to_num"])
}
if record["decrypt_success"] != true {
t.Fatalf("decrypt_success = %v", record["decrypt_success"])
}
}
func TestBuildTextMessageServiceEnvelopeDirectRoundTrip(t *testing.T) {
key, err := ExpandPSK("AQ==")
if err != nil {
t.Fatalf("ExpandPSK() error = %v", err)
}
raw, err := BuildTextMessageServiceEnvelope(TextMessageBuildOptions{
PacketBuildOptions: PacketBuildOptions{
FromNodeNum: 0x12345678,
ToNodeNum: 0x10203040,
PacketID: 0x11111111,
ChannelID: "LongFast",
GatewayID: "!12345678",
PSK: key,
Encrypt: true,
ViaMQTT: true,
},
Text: "direct hello",
})
if err != nil {
t.Fatalf("BuildTextMessageServiceEnvelope() error = %v", err)
}
valid, _, record := MQTTPP("msh/2/e/LongFast/!12345678", raw, key, Options{})
if !valid {
t.Fatalf("MQTTPP() valid = false, record = %#v", record)
}
if record["text"] != "direct hello" {
t.Fatalf("text = %v", record["text"])
}
if record["packet_to"] != "!10203040" {
t.Fatalf("packet_to = %v", record["packet_to"])
}
if record["packet_to_num"] != uint32(0x10203040) {
t.Fatalf("packet_to_num = %v", record["packet_to_num"])
}
}
func TestBuildNodeInfoServiceEnvelopeRoundTrip(t *testing.T) {
key, err := ExpandPSK("AQ==")
if err != nil {
t.Fatalf("ExpandPSK() error = %v", err)
}
raw, err := BuildNodeInfoServiceEnvelope(NodeInfoBuildOptions{
PacketBuildOptions: PacketBuildOptions{
FromNodeNum: 0x12345678,
ToNodeNum: NodeNumBroadcast,
PacketID: 0x22222222,
ChannelID: "LongFast",
GatewayID: "!12345678",
PSK: key,
Encrypt: true,
ViaMQTT: true,
},
NodeID: "!12345678",
LongName: "MQTT Bot",
ShortName: "BT",
HWModel: 255,
Role: 0,
IsLicensed: false,
PublicKey: []byte{1, 2, 3},
})
if err != nil {
t.Fatalf("BuildNodeInfoServiceEnvelope() error = %v", err)
}
valid, _, record := MQTTPP("msh/2/e/LongFast/!12345678", raw, key, Options{})
if !valid {
t.Fatalf("MQTTPP() valid = false, record = %#v", record)
}
if record["type"] != "nodeinfo" {
t.Fatalf("record type = %v", record["type"])
}
if record["long_name"] != "MQTT Bot" {
t.Fatalf("long_name = %v", record["long_name"])
}
if record["short_name"] != "BT" {
t.Fatalf("short_name = %v", record["short_name"])
}
if record["hw_model"] != "PRIVATE_HW" {
t.Fatalf("hw_model = %v", record["hw_model"])
}
if record["role"] != "CLIENT" {
t.Fatalf("role = %v", record["role"])
}
if record["is_licensed"] != false {
t.Fatalf("is_licensed = %v", record["is_licensed"])
}
if record["public_key"] != "010203" {
t.Fatalf("public_key = %v", record["public_key"])
}
}
func TestBuildNodeInfoTruncatesNanopbStrings(t *testing.T) {
key, err := ExpandPSK("AQ==")
if err != nil {
t.Fatalf("ExpandPSK() error = %v", err)
}
raw, err := BuildNodeInfoServiceEnvelope(NodeInfoBuildOptions{
PacketBuildOptions: PacketBuildOptions{FromNodeNum: 0x12345678, ToNodeNum: NodeNumBroadcast, PacketID: 0x33333333, ChannelID: "LongFast", GatewayID: "!12345678", PSK: key, Encrypt: true, ViaMQTT: true},
NodeID: "!12345678",
LongName: "这是一个非常非常非常非常长的机器人节点名称",
ShortName: "机器人",
})
if err != nil {
t.Fatalf("BuildNodeInfoServiceEnvelope() error = %v", err)
}
valid, _, record := MQTTPP("msh/2/e/LongFast/!12345678", raw, key, Options{})
if !valid {
t.Fatalf("MQTTPP() valid = false, record = %#v", record)
}
if len([]byte(record["long_name"].(string))) > 40 {
t.Fatalf("long_name byte length = %d", len([]byte(record["long_name"].(string))))
}
if len([]byte(record["short_name"].(string))) > 5 {
t.Fatalf("short_name byte length = %d", len([]byte(record["short_name"].(string))))
}
}
func TestBuildAckServiceEnvelopeRoundTrip(t *testing.T) {
key, err := ExpandPSK("AQ==")
if err != nil {
t.Fatalf("ExpandPSK: %v", err)
}
const requestID uint32 = 0xabcd1234
raw, err := BuildAckServiceEnvelope(AckBuildOptions{
PacketBuildOptions: PacketBuildOptions{
FromNodeNum: 0x10101010,
ToNodeNum: 0x20202020,
PacketID: 0x30303030,
ChannelID: "LongFast",
GatewayID: "!10101010",
PSK: key,
Encrypt: true,
ViaMQTT: true,
},
RequestID: requestID,
})
if err != nil {
t.Fatalf("BuildAckServiceEnvelope: %v", err)
}
valid, _, record := MQTTPP("msh/2/e/LongFast/!10101010", raw, key, Options{})
if !valid {
t.Fatalf("MQTTPP not valid: %#v", record)
}
if record["portnum"] != "ROUTING_APP" {
t.Fatalf("portnum = %v", record["portnum"])
}
if record["type"] != "routing" {
t.Fatalf("type = %v", record["type"])
}
}
func TestParseNodeID(t *testing.T) {
num, err := ParseNodeID("!1234abcd")
if err != nil {
t.Fatalf("ParseNodeID() error = %v", err)
}
if num != 0x1234abcd {
t.Fatalf("num = %#x", num)
}
if NodeNumToID(num) != "!1234abcd" {
t.Fatalf("NodeNumToID() = %s", NodeNumToID(num))
}
}
-48
View File
@@ -1,48 +0,0 @@
package mqtpp
import (
"testing"
"google.golang.org/protobuf/encoding/protowire"
)
func TestMQTTPPEncryptedPacketDefaultRejected(t *testing.T) {
raw := encryptedServiceEnvelopeTestPayload()
valid, payload, record := MQTTPP("msh/test", raw, nil, Options{})
if valid {
t.Fatalf("valid = true, want false")
}
if payload != nil {
t.Fatalf("payload = %v, want nil", payload)
}
if record["type"] != "encrypted_packet" {
t.Fatalf("type = %v, want encrypted_packet", record["type"])
}
if record["error"] != "cannot be decrypted" {
t.Fatalf("error = %v, want cannot be decrypted", record["error"])
}
}
func TestMQTTPPEncryptedPacketAllowed(t *testing.T) {
raw := encryptedServiceEnvelopeTestPayload()
valid, payload, record := MQTTPP("msh/test", raw, nil, Options{AllowEncryptedForwarding: true})
if !valid {
t.Fatalf("valid = false, want true: %+v", record)
}
if string(payload) != string(raw) {
t.Fatalf("payload = %v, want raw payload", payload)
}
if record["type"] != "encrypted_packet" {
t.Fatalf("type = %v, want encrypted_packet", record["type"])
}
if record["error"] != nil {
t.Fatalf("error = %v, want nil", record["error"])
}
}
func encryptedServiceEnvelopeTestPayload() []byte {
packet := protowire.AppendTag(nil, 5, protowire.BytesType)
packet = protowire.AppendBytes(packet, []byte{1, 2, 3, 4})
envelope := protowire.AppendTag(nil, 1, protowire.BytesType)
return protowire.AppendBytes(envelope, packet)
}
-273
View File
@@ -1,273 +0,0 @@
package mqtpp
import (
"bytes"
"crypto/ecdh"
"crypto/rand"
"encoding/binary"
"testing"
"google.golang.org/protobuf/encoding/protowire"
)
func TestBuildPKITextMessageRoundTrip(t *testing.T) {
curve := ecdh.X25519()
senderPriv, err := curve.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("generate sender key: %v", err)
}
recipientPriv, err := curve.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("generate recipient key: %v", err)
}
const text = "hello over PKI 你好"
const fromNum uint32 = 0x12345678
const toNum uint32 = 0xa1b2c3d4
const packetID uint32 = 0xdeadbeef
raw, err := BuildPKITextMessageServiceEnvelope(PKITextMessageBuildOptions{
FromNodeNum: fromNum,
ToNodeNum: toNum,
PacketID: packetID,
GatewayID: NodeNumToID(fromNum),
ViaMQTT: true,
SenderPrivate: senderPriv.Bytes(),
RecipientPub: recipientPriv.PublicKey().Bytes(),
SenderPublic: senderPriv.PublicKey().Bytes(),
Text: text,
})
if err != nil {
t.Fatalf("BuildPKITextMessageServiceEnvelope: %v", err)
}
env, err := parseServiceEnvelope(raw)
if err != nil {
t.Fatalf("parseServiceEnvelope: %v", err)
}
if env.ChannelID != PKIChannelID {
t.Fatalf("channel_id = %q want %q", env.ChannelID, PKIChannelID)
}
if env.GatewayID != NodeNumToID(fromNum) {
t.Fatalf("gateway_id = %q", env.GatewayID)
}
pkt := env.Packet
if pkt.From != fromNum || pkt.To != toNum || pkt.ID != packetID {
t.Fatalf("packet header mismatch: %+v", pkt)
}
if !pkt.PKIEncrypted {
t.Fatalf("pki_encrypted = false")
}
if !pkt.ViaMQTT {
t.Fatalf("via_mqtt = false")
}
if pkt.Channel != 0 {
t.Fatalf("channel = %d want 0", pkt.Channel)
}
if pkt.PayloadVariant != "encrypted" || len(pkt.Encrypted) <= pkcOverhead {
t.Fatalf("encrypted payload missing: %+v", pkt)
}
// 收件人用对端私钥 + 发件人公钥推导共享密钥并解密
sharedKey, err := pkiSharedKey(recipientPriv.Bytes(), senderPriv.PublicKey().Bytes())
if err != nil {
t.Fatalf("pkiSharedKey: %v", err)
}
encryptedLen := len(pkt.Encrypted) - pkcOverhead
ciphertext := pkt.Encrypted[:encryptedLen]
auth := pkt.Encrypted[encryptedLen : encryptedLen+8]
extraNonce := binary.LittleEndian.Uint32(pkt.Encrypted[encryptedLen+8:])
plaintext, err := aesCCMDecrypt(sharedKey, pkiNonce(packetID, fromNum, extraNonce), ciphertext, auth)
if err != nil {
t.Fatalf("aesCCMDecrypt: %v", err)
}
data, err := parseDataPacket(plaintext)
if err != nil {
t.Fatalf("parseDataPacket: %v", err)
}
if data.Portnum != textMessageApp {
t.Fatalf("portnum = %d", data.Portnum)
}
if string(data.Payload) != text {
t.Fatalf("text = %q want %q", string(data.Payload), text)
}
// 同样用 MQTTPP 解析路径:PKI 包对外应被识别为 encrypted_packet(无法解密),
// 但用错的 PSK 不应误报“channel hash mismatch” 之外的奇怪错误。
dummyPSK, _ := ExpandPSK("AQ==")
_, _, record := MQTTPP("msh/2/e/PKI/!12345678", raw, dummyPSK, Options{AllowEncryptedForwarding: true})
if record["channel_id"] != PKIChannelID {
t.Fatalf("MQTTPP record channel_id = %v", record["channel_id"])
}
if record["pki_encrypted"] != true {
t.Fatalf("pki_encrypted record = %v", record["pki_encrypted"])
}
}
func TestPKINonceLayoutMatchesFirmware(t *testing.T) {
// 复刻 firmware initNonce(fromNode, packetId, extraNonce) 期望的字节布局:
// nonce[0..8) = packetId(uint64 LE)
// nonce[4..8) 被 extraNonce(uint32 LE) 覆盖(当 extraNonce != 0
// nonce[8..12) = fromNode(uint32 LE)
// nonce[12] = 0
got := pkiNonce(0xaabbccdd, 0x11223344, 0x55667788)
want := []byte{
0xdd, 0xcc, 0xbb, 0xaa, // packetId low 4 bytes,未被 extraNonce 覆盖前
0x88, 0x77, 0x66, 0x55, // extraNonce 覆盖 nonce[4..8)
0x44, 0x33, 0x22, 0x11, // fromNode
0x00,
}
if !bytes.Equal(got, want) {
t.Fatalf("pkiNonce = % x\nwant % x", got, want)
}
}
func TestBuildPKITextMessageRejectsBroadcast(t *testing.T) {
curve := ecdh.X25519()
priv, _ := curve.GenerateKey(rand.Reader)
pub, _ := curve.GenerateKey(rand.Reader)
if _, err := BuildPKITextMessageServiceEnvelope(PKITextMessageBuildOptions{
FromNodeNum: 0x1,
ToNodeNum: NodeNumBroadcast,
PacketID: 0x2,
SenderPrivate: priv.Bytes(),
RecipientPub: pub.PublicKey().Bytes(),
Text: "hi",
}); err == nil {
t.Fatalf("expected error for broadcast destination")
}
}
// 确认 MeshPacket 中确实带上 pki_encrypted (tag 17) 与 public_key (tag 16)
func TestBuildPKIMeshPacketTags(t *testing.T) {
encrypted := []byte{0x01, 0x02, 0x03}
pub := make([]byte, 32)
for i := range pub {
pub[i] = byte(i)
}
raw := buildPKIMeshPacket(0x11, 0x22, 0x33, true, encrypted, pub)
tags := map[protowire.Number]bool{}
if err := walkFields(raw, func(num protowire.Number, _ protowire.Type, _ any) error {
tags[num] = true
return nil
}); err != nil {
t.Fatalf("walkFields: %v", err)
}
for _, want := range []protowire.Number{1, 2, 5, 6, 14, 16, 17} {
if !tags[want] {
t.Fatalf("missing tag %d", want)
}
}
}
// 端到端:发送方构造 PKI 包,接收方通过 PKIKeyResolver 解密并还原文本消息记录。
func TestMQTTPPDecryptsPKIWithResolver(t *testing.T) {
curve := ecdh.X25519()
senderPriv, _ := curve.GenerateKey(rand.Reader)
recipientPriv, _ := curve.GenerateKey(rand.Reader)
const text = "hello PKI inbound"
const fromNum uint32 = 0xaaaa1111
const toNum uint32 = 0xbbbb2222
const packetID uint32 = 0x77777777
raw, err := BuildPKITextMessageServiceEnvelope(PKITextMessageBuildOptions{
FromNodeNum: fromNum,
ToNodeNum: toNum,
PacketID: packetID,
GatewayID: NodeNumToID(fromNum),
ViaMQTT: true,
SenderPrivate: senderPriv.Bytes(),
RecipientPub: recipientPriv.PublicKey().Bytes(),
SenderPublic: senderPriv.PublicKey().Bytes(),
Text: text,
})
if err != nil {
t.Fatalf("build: %v", err)
}
resolver := func(to, from uint32) ([]byte, []byte, bool) {
if to != toNum || from != fromNum {
return nil, nil, false
}
return recipientPriv.Bytes(), senderPriv.PublicKey().Bytes(), true
}
dummyPSK, _ := ExpandPSK("AQ==")
valid, _, record := MQTTPP("msh/2/e/PKI/!aaaa1111", raw, dummyPSK, Options{PKIKeyResolver: resolver})
if !valid {
t.Fatalf("MQTTPP not valid: %#v", record)
}
if record["type"] != "text_message" {
t.Fatalf("type = %v, want text_message", record["type"])
}
if record["text"] != text {
t.Fatalf("text = %v", record["text"])
}
if record["pki_encrypted"] != true {
t.Fatalf("pki_encrypted = %v", record["pki_encrypted"])
}
}
func TestBuildPKIAckRoundTrip(t *testing.T) {
curve := ecdh.X25519()
botPriv, _ := curve.GenerateKey(rand.Reader)
devicePriv, _ := curve.GenerateKey(rand.Reader)
const fromNum uint32 = 0x0000beef // bot
const toNum uint32 = 0xfeed0000 // 原 device
const ackPacketID uint32 = 0xaaaa5555
const requestID uint32 = 0xdeadbeef
raw, err := BuildPKIAckServiceEnvelope(PKIAckBuildOptions{
FromNodeNum: fromNum,
ToNodeNum: toNum,
PacketID: ackPacketID,
RequestID: requestID,
GatewayID: NodeNumToID(fromNum),
ViaMQTT: true,
SenderPrivate: botPriv.Bytes(),
RecipientPub: devicePriv.PublicKey().Bytes(),
SenderPublic: botPriv.PublicKey().Bytes(),
})
if err != nil {
t.Fatalf("BuildPKIAckServiceEnvelope: %v", err)
}
// 设备侧解密
env, err := parseServiceEnvelope(raw)
if err != nil {
t.Fatalf("parse: %v", err)
}
if env.ChannelID != PKIChannelID {
t.Fatalf("channel_id = %q", env.ChannelID)
}
pkt := env.Packet
if !pkt.PKIEncrypted || pkt.From != fromNum || pkt.To != toNum || pkt.ID != ackPacketID {
t.Fatalf("ack header mismatch: %+v", pkt)
}
encryptedLen := len(pkt.Encrypted) - pkcOverhead
cipher := pkt.Encrypted[:encryptedLen]
auth := pkt.Encrypted[encryptedLen : encryptedLen+8]
extraNonce := binary.LittleEndian.Uint32(pkt.Encrypted[encryptedLen+8:])
sharedKey, err := pkiSharedKey(devicePriv.Bytes(), botPriv.PublicKey().Bytes())
if err != nil {
t.Fatalf("shared: %v", err)
}
plain, err := aesCCMDecrypt(sharedKey, pkiNonce(ackPacketID, fromNum, extraNonce), cipher, auth)
if err != nil {
t.Fatalf("decrypt: %v", err)
}
data, err := parseDataPacket(plain)
if err != nil {
t.Fatalf("data: %v", err)
}
if data.Portnum != routingApp {
t.Fatalf("portnum = %d, want ROUTING_APP(%d)", data.Portnum, routingApp)
}
// Routing payload 解析: 期望 oneof error_reason=NONE(0),即 wire 字节 0x18 0x00
wantRouting := []byte{0x18, 0x00}
if !bytes.Equal(data.Payload, wantRouting) {
t.Fatalf("routing payload = % x, want % x", data.Payload, wantRouting)
}
}