Files
meshcore-analyzer/cmd/server/issue1279_p2_test.go
T
Kpa-clawbot 749fdc114f feat(decoder+ui): close remaining P2 items from #1279 — payloadTypeNames, legend, TransportCodes, Feat1/2, RAW_CUSTOM, sensor docs (#1291)
RED commit: `dc4c0800` — CI:
https://github.com/Kpa-clawbot/CoreScope/actions?query=branch%3Afix%2Fissue-1279-p2

Closes the remaining six 🟢 P2 items in umbrella #1279 (PR #1280 shipped
P0+P1, PR #1276 shipped ACK/RESPONSE/PATH legend rows).

### Item-by-item

| # | Item | Where | Test |
|---|---|---|---|
| 1 | `payloadTypeNames` parity | `cmd/server/store.go` |
`cmd/server/issue1279_p2_test.go::TestPayloadTypeNamesAll13` |
| 2 | Legend rows: Anon Req / Grp Data / Multipart / Control / Raw
Custom | `public/live.js` | `test-issue-1279-legend-p2-e2e.js`
(Playwright) |
| 3 | TransportCodes detail-row + `code1=` / `code2=` filter grammar |
`public/packets.js`, `public/packet-filter.js` |
`test-issue-1279-p2-code-filter.js` (6 cases) |
| 4 | Multibyte capability badge on node detail/list rows |
`public/nodes.js::renderNodeBadges` | `n.hash_size >= 2` (observable
Feat1/Feat2 proxy; firmware `AdvertDataHelpers.h:14-16`) |
| 5 | RAW_CUSTOM (0x0F) `{rawLength, firstByteTag}` decode + detail-row
| `cmd/server/decoder.go`, `cmd/ingestor/decoder.go`,
`public/packets.js` | `TestDecodeRawCustomExposesLengthAndTag` × 2 +
updated `TestDecodePayloadRAWCustom` |
| 6 | Sensor advert telemetry firmware-derivation comments |
`cmd/ingestor/decoder.go:363-380` | pure comments — exempt per AGENTS |

### Firmware refs cited inline
- `firmware/src/Packet.h:19-32` — PAYLOAD_TYPE_* constants
- `firmware/src/Packet.h:46` — TransportCodes wire layout
- `firmware/src/Mesh.cpp:577` — `createRawData`
- `firmware/src/helpers/SensorMesh.{h,cpp}` — sensor advert telemetry
derivation
- `firmware/src/helpers/AdvertDataHelpers.h:14-16` — Feat1/Feat2

### TDD
Red `dc4c0800` proves the assertions gate behavior:
- `payloadTypeNames` had only 12 entries (no 0x0F).
- RAW_CUSTOM decoded as `UNKNOWN` with no envelope fields.

Green `<HEAD>` makes both green; per-item tests included.

### Cross-stack note
Cross-stack: justified — items 1/5 add decoder output fields; items
2/3/4/5 surface those fields in the UI in the same PR per #1279
acceptance.

### Out of scope
Item 4 surfaces the observable multibyte capability via the persisted
`hash_size` (Feat1/Feat2 wire bits are only on transient adverts and not
stored per-node today); persisting raw Feat1/Feat2 per-node is left for
a follow-up.

Fixes #1279

---------

Co-authored-by: bot <bot@corescope>
2026-05-19 08:08:28 -07:00

63 lines
2.1 KiB
Go

package main
// Tests for issue #1279 P2 items:
// - Item 1: payloadTypeNames map must include ALL 13 firmware payload types.
// - Item 5: RAW_CUSTOM (0x0F) decoder must expose rawLength + firstByteTag.
//
// Firmware refs:
// - firmware/src/Packet.h:19-32 (PAYLOAD_TYPE_*) — 0..0xB plus 0xF (RAW_CUSTOM)
// - firmware/src/Mesh.cpp:577 (createRawData) — application-defined payload
import (
"strings"
"testing"
)
func TestPayloadTypeNamesAll13(t *testing.T) {
// Firmware-defined: 0..9, 0x0A, 0x0B, 0x0F — 13 total.
want := map[int]string{
0x00: "REQ", 0x01: "RESPONSE", 0x02: "TXT_MSG", 0x03: "ACK",
0x04: "ADVERT", 0x05: "GRP_TXT", 0x06: "GRP_DATA", 0x07: "ANON_REQ",
0x08: "PATH", 0x09: "TRACE", 0x0A: "MULTIPART", 0x0B: "CONTROL",
0x0F: "RAW_CUSTOM",
}
if len(payloadTypeNames) != len(want) {
t.Errorf("payloadTypeNames has %d entries, want %d", len(payloadTypeNames), len(want))
}
for code, name := range want {
got, ok := payloadTypeNames[code]
if !ok {
t.Errorf("payloadTypeNames missing 0x%02X (%s)", code, name)
continue
}
if got != name {
t.Errorf("payloadTypeNames[0x%02X] = %q, want %q", code, got, name)
}
}
}
func TestDecodeRawCustomExposesLengthAndTag(t *testing.T) {
// Build a RAW_CUSTOM packet: header byte = (route<<6 | type<<2 | ver),
// type=0x0F. Route FLOOD (1), version 1: (1<<6)|(0x0F<<2)|1 = 0x7D.
// Path byte: 0 hops, hash_size=1 → upper bits 0, lower 0 → 0x00.
// Payload: first byte tag 0xA5, then arbitrary data.
hexStr := "7D00A5DEADBEEF"
pkt, err := DecodePacket(hexStr, false)
if err != nil {
t.Fatalf("decode: %v", err)
}
if pkt.Payload.Type != "RAW_CUSTOM" {
t.Fatalf("payload type = %q, want RAW_CUSTOM", pkt.Payload.Type)
}
if pkt.Payload.RawLength == nil {
t.Fatal("RawLength should be set for RAW_CUSTOM")
}
// payload = 4 bytes (A5 DE AD BE EF) — wait, A5 DE AD BE EF = 5 bytes.
if *pkt.Payload.RawLength != 5 {
t.Errorf("RawLength=%d, want 5", *pkt.Payload.RawLength)
}
if !strings.EqualFold(pkt.Payload.FirstByteTag, "A5") {
t.Errorf("FirstByteTag=%q, want A5", pkt.Payload.FirstByteTag)
}
}