mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-06-30 16:41:41 +00:00
e96f0f9f9f
## Summary Ports the firmware-1.16.0 extended ACK decoding from the ingestor (PR #1618, issue #1610) into the server-side re-decoder. Previously `cmd/server/decoder.go` silently dropped `ackLen`, `ackAttempt`, and `ackRand` (and the multipart inner equivalents) — the server emitted plain 4-byte ACKs even when the wire carried the 5/6-byte extended form. Now both decoders agree byte-for-byte. Closes #1694. ## What changed - `cmd/server/decoder.go::decodeAck`: sets `AckLen` (capped at 6), `AckAttempt` (`buf[4]` when `len>=5`), `AckRand` (`buf[5]` when `len>=6`). Mirrors `cmd/ingestor/decoder.go:279-305`. - `cmd/server/decoder.go::decodeMultipart` ACK branch: sets `InnerAckLen = len(buf)-1` (capped at 6), `InnerAckAttempt`, `InnerAckRand`. Mirrors `cmd/ingestor/decoder.go:696-714`. - `Payload` struct gains six `*int` fields tagged `omitempty`: `AckLen`, `AckAttempt`, `AckRand`, `InnerAckLen`, `InnerAckAttempt`, `InnerAckRand`. Backward-compatible JSON — legacy 4-byte ACKs leave attempt/rand nil and the fields are omitted from the output. No other decoder consumer is touched. Routes / store auto-surface the new fields via JSON marshaling. ## Test layout `cmd/server/decoder_ack_extended_test.go` drives `decodeAck` table-driven across the three wire shapes: | Buffer | AckLen | AckAttempt | AckRand | |---|---|---|---| | `EF BE AD DE` (CRC only) | 4 | nil | nil | | `EF BE AD DE 07` | 5 | 7 | nil | | `EF BE AD DE 07 42` | 6 | 7 | 0x42 | Plus `TestDecodeMultipartAckExtendedInner` for a 7-byte multipart buffer (`0x33` header + 6-byte inner ACK), asserting `InnerAckLen=6`, `InnerAckAttempt=7`, `InnerAckRand=0x42`. ## TDD trail - **Red commit** (test + struct stubs only, `decodeAck`/`decodeMultipart` unchanged) → assertions fail on `AckLen=nil`. - **Green commit** (port implementation) → all assertions pass. Full `cd cmd/server && go test ./...` passes locally. ## Firmware refs - `firmware/src/helpers/BaseChatMesh.cpp:218-234` (extended ACK layout) - firmware commit `f6e6fdaa` (attempt counter) - firmware commit `a130a95a` (RNG byte) --------- Co-authored-by: Kpa-clawbot <bot@kpa-clawbot>
97 lines
2.7 KiB
Go
97 lines
2.7 KiB
Go
package main
|
|
|
|
// Tests for issue #1694 — server-side decoder parity with the ingestor's
|
|
// firmware-1.16.0 extended ACK support (issue #1610). Wire vectors mirror
|
|
// the ingestor's tests so both decoders agree byte-for-byte.
|
|
//
|
|
// - decodeAck: firmware/src/helpers/BaseChatMesh.cpp:218-234
|
|
// - decodeMultipart: firmware/src/Mesh.cpp:287-310
|
|
|
|
import "testing"
|
|
|
|
func TestDecodeAckExtended(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
buf []byte
|
|
wantLen int
|
|
wantAttPtr bool
|
|
wantAtt int
|
|
wantRndPtr bool
|
|
wantRnd int
|
|
}{
|
|
{
|
|
name: "legacy 4-byte ACK (CRC only)",
|
|
buf: []byte{0xEF, 0xBE, 0xAD, 0xDE},
|
|
wantLen: 4,
|
|
},
|
|
{
|
|
name: "5-byte ACK (CRC + attempt)",
|
|
buf: []byte{0xEF, 0xBE, 0xAD, 0xDE, 0x07},
|
|
wantLen: 5,
|
|
wantAttPtr: true,
|
|
wantAtt: 7,
|
|
},
|
|
{
|
|
name: "6-byte ACK (CRC + attempt + rand)",
|
|
buf: []byte{0xEF, 0xBE, 0xAD, 0xDE, 0x07, 0x42},
|
|
wantLen: 6,
|
|
wantAttPtr: true,
|
|
wantAtt: 7,
|
|
wantRndPtr: true,
|
|
wantRnd: 0x42,
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
p := decodeAck(tc.buf)
|
|
if p.Type != "ACK" {
|
|
t.Fatalf("type=%q want ACK", p.Type)
|
|
}
|
|
if p.AckLen == nil {
|
|
t.Fatalf("AckLen=nil want %d", tc.wantLen)
|
|
}
|
|
if *p.AckLen != tc.wantLen {
|
|
t.Errorf("AckLen=%d want %d", *p.AckLen, tc.wantLen)
|
|
}
|
|
if tc.wantAttPtr {
|
|
if p.AckAttempt == nil {
|
|
t.Errorf("AckAttempt=nil want %d", tc.wantAtt)
|
|
} else if *p.AckAttempt != tc.wantAtt {
|
|
t.Errorf("AckAttempt=%d want %d", *p.AckAttempt, tc.wantAtt)
|
|
}
|
|
} else if p.AckAttempt != nil {
|
|
t.Errorf("AckAttempt=%d want nil", *p.AckAttempt)
|
|
}
|
|
if tc.wantRndPtr {
|
|
if p.AckRand == nil {
|
|
t.Errorf("AckRand=nil want %d", tc.wantRnd)
|
|
} else if *p.AckRand != tc.wantRnd {
|
|
t.Errorf("AckRand=%d want %d", *p.AckRand, tc.wantRnd)
|
|
}
|
|
} else if p.AckRand != nil {
|
|
t.Errorf("AckRand=%d want nil", *p.AckRand)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDecodeMultipartAckExtendedInner(t *testing.T) {
|
|
// byte0 = (remaining<<4)|inner_type = (3<<4)|0x03 = 0x33
|
|
// inner ACK = CRC(deadbeef LE) + attempt(0x07) + rand(0x42) = 6 bytes
|
|
// total buf = 1 + 6 = 7 bytes.
|
|
buf := []byte{0x33, 0xEF, 0xBE, 0xAD, 0xDE, 0x07, 0x42}
|
|
p := decodeMultipart(buf)
|
|
if p.InnerAckCrc != "deadbeef" {
|
|
t.Fatalf("InnerAckCrc=%q want deadbeef", p.InnerAckCrc)
|
|
}
|
|
if p.InnerAckLen == nil || *p.InnerAckLen != 6 {
|
|
t.Errorf("InnerAckLen=%v want 6", p.InnerAckLen)
|
|
}
|
|
if p.InnerAckAttempt == nil || *p.InnerAckAttempt != 7 {
|
|
t.Errorf("InnerAckAttempt=%v want 7", p.InnerAckAttempt)
|
|
}
|
|
if p.InnerAckRand == nil || *p.InnerAckRand != 0x42 {
|
|
t.Errorf("InnerAckRand=%v want 0x42", p.InnerAckRand)
|
|
}
|
|
}
|