feat: capture one fixture per packet type (fixes #177)

Add per-payload-type packet detail fixtures captured from production:
- packet-type-advert.json (payload_type=4, ADVERT)
- packet-type-grptxt-decrypted.json (payload_type=5, decrypted GRP_TXT)
- packet-type-grptxt-undecrypted.json (payload_type=5, decryption_failed GRP_TXT)
- packet-type-txtmsg.json (payload_type=1, TXT_MSG)
- packet-type-req.json (payload_type=0, REQ)

Update validate-protos.py to validate all 5 new fixtures against
PacketDetailResponse proto message.

Update CI deploy workflow to automatically capture per-type fixtures
on each deploy, including both decrypted and undecrypted GRP_TXT.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Kpa-clawbotandCopilot committed 2026-03-27 18:19:55 -07:00
1 parent 9b278d8e41
commit 9d7a3eb2d1
7 files changed
+4485

No files matched your search

+43
View File
@@ -458,6 +458,49 @@ jobs:
echo " ⚠ No packets available — skipping packet-detail"
fi
# ─── Per-type packet fixtures (one of each payload type) ──────
echo ""
echo "Fetching per-type packet fixtures..."
# payload_type: 0=REQ, 1=TXT_MSG, 4=ADVERT, 5=GRP_TXT
TYPES="0:req 1:txtmsg 4:advert 5:grptxt"
for entry in $TYPES; do
TYPE_NUM="${entry%%:*}"
TYPE_NAME="${entry##*:}"
TYPE_HASH=$(docker exec meshcore-prod wget -qO- "http://localhost:3000/api/packets?type=${TYPE_NUM}&limit=1" 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); pkts=d.get('packets',[]); print(pkts[0]['hash'] if pkts else '')" 2>/dev/null || echo "")
if [ -n "$TYPE_HASH" ]; then
if [ "$TYPE_NAME" = "grptxt" ]; then
# Save first as decrypted (most common), then find an undecrypted one
docker exec meshcore-prod wget -qO- "http://localhost:3000/api/packets/$TYPE_HASH" \
> "proto/testdata/node-fixtures/packet-type-grptxt-decrypted.json" 2>/dev/null && \
echo " ✓ packet-type-grptxt-decrypted.json (hash: $TYPE_HASH)"
# Find a decryption_failed packet
UNDEC_HASH=$(docker exec meshcore-prod wget -qO- "http://localhost:3000/api/packets?type=5&limit=20" 2>/dev/null | python3 -c "
import sys,json
d=json.load(sys.stdin)
for p in d.get('packets',[]):
dj = p.get('decoded_json','')
if dj:
dd = json.loads(dj)
if dd.get('decryptionStatus') in ('no_key','decryption_failed'):
print(p['hash']); break
" 2>/dev/null || echo "")
if [ -n "$UNDEC_HASH" ]; then
docker exec meshcore-prod wget -qO- "http://localhost:3000/api/packets/$UNDEC_HASH" \
> "proto/testdata/node-fixtures/packet-type-grptxt-undecrypted.json" 2>/dev/null && \
echo " ✓ packet-type-grptxt-undecrypted.json (hash: $UNDEC_HASH)"
else
echo " ⚠ No undecrypted GRP_TXT found"
fi
else
docker exec meshcore-prod wget -qO- "http://localhost:3000/api/packets/$TYPE_HASH" \
> "proto/testdata/node-fixtures/packet-type-${TYPE_NAME}.json" 2>/dev/null && \
echo " ✓ packet-type-${TYPE_NAME}.json (hash: $TYPE_HASH)"
fi
else
echo " ⚠ No type=$TYPE_NUM ($TYPE_NAME) packets available"
fi
done
# Packet timestamps
docker exec meshcore-prod wget -qO- "http://localhost:3000/api/packets/timestamps?since=2026-03-01T00:00:00Z" \
> "proto/testdata/node-fixtures/packet-timestamps.json" 2>/dev/null && \
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+73
View File
@@ -0,0 +1,73 @@
{
"packet": {
"id": 56862,
"raw_hex": "0200331f3d018f5e63b16998a6adb8da91ef9558ecaf",
"hash": "fa8ec84a7ca6ced2",
"first_seen": "2026-03-28T01:16:01.295Z",
"timestamp": "2026-03-28T01:16:01.295Z",
"route_type": 2,
"payload_type": 0,
"decoded_json": "{\"type\":\"REQ\",\"destHash\":\"33\",\"srcHash\":\"1f\",\"mac\":\"3d01\",\"encryptedData\":\"8f5e63b16998a6adb8da91ef9558ecaf\"}",
"observations": [
{
"id": 2564587,
"transmission_id": 56862,
"hash": "fa8ec84a7ca6ced2",
"observer_id": "1F445B75F5EB65EEC244A17A1E8660812A2529727A2329DDA37C44684D8841EA",
"observer_name": "lutin-observer-1",
"snr": null,
"rssi": null,
"path_json": "[]",
"timestamp": "2026-03-28T01:16:01.295Z"
}
],
"observation_count": 1,
"observer_id": "1F445B75F5EB65EEC244A17A1E8660812A2529727A2329DDA37C44684D8841EA",
"observer_name": "lutin-observer-1",
"snr": null,
"rssi": null,
"path_json": "[]",
"_parsedPath": []
},
"path": [],
"breakdown": {
"ranges": [
{
"start": 0,
"end": 0,
"color": "red",
"label": "Header"
},
{
"start": 1,
"end": 1,
"color": "orange",
"label": "Path Length"
},
{
"start": 2,
"end": 21,
"color": "yellow",
"label": "Payload"
}
]
},
"observation_count": 1,
"observations": [
{
"id": 2564587,
"transmission_id": 56862,
"hash": "fa8ec84a7ca6ced2",
"observer_id": "1F445B75F5EB65EEC244A17A1E8660812A2529727A2329DDA37C44684D8841EA",
"observer_name": "lutin-observer-1",
"snr": null,
"rssi": null,
"path_json": "[]",
"timestamp": "2026-03-28T01:16:01.295Z",
"raw_hex": "0200331f3d018f5e63b16998a6adb8da91ef9558ecaf",
"payload_type": 0,
"decoded_json": "{\"type\":\"REQ\",\"destHash\":\"33\",\"srcHash\":\"1f\",\"mac\":\"3d01\",\"encryptedData\":\"8f5e63b16998a6adb8da91ef9558ecaf\"}",
"route_type": 2
}
]
}
+73
View File
@@ -0,0 +1,73 @@
{
"packet": {
"id": 56863,
"raw_hex": "06001f330a3ebfca2495c738befb247875e4571da31088b8ac01b859b361fdc4bf5d59daf971ad28649a8d27404adfb69d915a9ee291e6a7b2c87a8bf14d72ccf69ab9213ccb",
"hash": "9b254fed5cad798a",
"first_seen": "2026-03-28T01:16:02.017Z",
"timestamp": "2026-03-28T01:16:02.017Z",
"route_type": 2,
"payload_type": 1,
"decoded_json": "{\"type\":\"RESPONSE\",\"destHash\":\"1f\",\"srcHash\":\"33\",\"mac\":\"0a3e\",\"encryptedData\":\"bfca2495c738befb247875e4571da31088b8ac01b859b361fdc4bf5d59daf971ad28649a8d27404adfb69d915a9ee291e6a7b2c87a8bf14d72ccf69ab9213ccb\"}",
"observations": [
{
"id": 2564597,
"transmission_id": 56863,
"hash": "9b254fed5cad798a",
"observer_id": "1F445B75F5EB65EEC244A17A1E8660812A2529727A2329DDA37C44684D8841EA",
"observer_name": "lutin-observer-1",
"snr": null,
"rssi": null,
"path_json": "[]",
"timestamp": "2026-03-28T01:16:02.017Z"
}
],
"observation_count": 1,
"observer_id": "1F445B75F5EB65EEC244A17A1E8660812A2529727A2329DDA37C44684D8841EA",
"observer_name": "lutin-observer-1",
"snr": null,
"rssi": null,
"path_json": "[]",
"_parsedPath": []
},
"path": [],
"breakdown": {
"ranges": [
{
"start": 0,
"end": 0,
"color": "red",
"label": "Header"
},
{
"start": 1,
"end": 1,
"color": "orange",
"label": "Path Length"
},
{
"start": 2,
"end": 69,
"color": "yellow",
"label": "Payload"
}
]
},
"observation_count": 1,
"observations": [
{
"id": 2564597,
"transmission_id": 56863,
"hash": "9b254fed5cad798a",
"observer_id": "1F445B75F5EB65EEC244A17A1E8660812A2529727A2329DDA37C44684D8841EA",
"observer_name": "lutin-observer-1",
"snr": null,
"rssi": null,
"path_json": "[]",
"timestamp": "2026-03-28T01:16:02.017Z",
"raw_hex": "06001f330a3ebfca2495c738befb247875e4571da31088b8ac01b859b361fdc4bf5d59daf971ad28649a8d27404adfb69d915a9ee291e6a7b2c87a8bf14d72ccf69ab9213ccb",
"payload_type": 1,
"decoded_json": "{\"type\":\"RESPONSE\",\"destHash\":\"1f\",\"srcHash\":\"33\",\"mac\":\"0a3e\",\"encryptedData\":\"bfca2495c738befb247875e4571da31088b8ac01b859b361fdc4bf5d59daf971ad28649a8d27404adfb69d915a9ee291e6a7b2c87a8bf14d72ccf69ab9213ccb\"}",
"route_type": 2
}
]
}
+5
View File
@@ -248,6 +248,11 @@ FIXTURE_TO_MESSAGE = {
'packets-grouped.json': ('GroupedPacketListResponse', 'object'),
'packets-since.json': ('GroupedPacketListResponse', 'object'),
'packet-detail.json': ('PacketDetailResponse', 'object'),
'packet-type-advert.json': ('PacketDetailResponse', 'object'),
'packet-type-grptxt-decrypted.json': ('PacketDetailResponse', 'object'),
'packet-type-grptxt-undecrypted.json': ('PacketDetailResponse', 'object'),
'packet-type-txtmsg.json': ('PacketDetailResponse', 'object'),
'packet-type-req.json': ('PacketDetailResponse', 'object'),
'packet-timestamps.json': ('PacketTimestampsResponse', 'bare-array'),
'channels.json': ('ChannelListResponse', 'object'),
'channel-messages.json': ('ChannelMessagesResponse', 'object'),