Files
tcp-ingestion/test/fixtures/teltonika/README.md
T
julian 381287bacc Implement Phase 1 tasks 1.5-1.7 + 1.9 (Codec 8/8E/16 parsers + fixture suite)
- Codec 8 parser (1-byte IO IDs, no NX/Generation Type)
- Codec 8 Extended parser (2-byte IO IDs + variable-length NX section)
- Codec 16 parser (mixed widths + Generation Type, supports IO IDs > 255)
- Shared GPS element / timestamp helpers in gps-element.ts
- Fixture loader with bigint/Buffer sentinel encoding and auto-discovery
- 12 fixture pairs across codec8/8E/16 (canonical doc + synthetic edge cases)
- Cross-checked Codec 8 against Traccar's TeltonikaProtocolDecoder (no discrepancies)

26 new tests. Total 62 passing across 10 test files.
typecheck/lint/test/build all clean.
2026-04-30 16:24:17 +02:00

2.6 KiB

Teltonika Fixture Suite

Binary protocol fixtures for the Codec 8, 8E, and 16 parsers.

File format

Each fixture is a pair:

File Purpose
<name>.hex Hex-encoded AVL body (CodecID + N1 + records + N2)
<name>.expected.json Expected Position[] output and ACK record count

.hex format

The body slice only — not the full TCP packet (no preamble, no DataFieldLength, no CRC). This is exactly what frame.payload contains and what handler.handle(body, ctx) receives.

Whitespace and newlines are stripped before parsing, so you can write multi-line hex for readability.

.expected.json format

{
  "positions": [
    {
      "device_id": "FIXTURE",
      "timestamp": "2019-06-10T10:04:46.000Z",
      "latitude": 0,
      "longitude": 0,
      "altitude": 0,
      "angle": 0,
      "speed": 0,
      "satellites": 0,
      "priority": 1,
      "attributes": {
        "21": 3,
        "78": "__bigint:0",
        "100": "__buffer_b64:AAEC"
      }
    }
  ],
  "ack_record_count": 1
}

device_id is always "FIXTURE" — the IMEI comes from the session context, not the body.

Attribute value sentinels

Sentinel Decoded type Example
number literal number 24079
"__bigint:<decimal>" bigint "__bigint:893700218"
"__buffer_b64:<base64>" Buffer "__buffer_b64:qw=="

For a zero-length Buffer, use "__buffer_b64:" (empty base64 string).

How to add a new fixture

  1. Get the AVL body bytes (from a live capture, a device emulator, or hand-computed).
  2. Write the body as hex into <name>.hex.
  3. Manually trace the expected parse output and write <name>.expected.json.
  4. Run pnpm test — the new fixture is automatically discovered and tested.

No changes to any test file are needed.

Bootstrap vs. synthetic fixtures

  • Bootstrap fixtures (01-canonical, etc.) are sourced directly from the Teltonika documentation hex examples. Their expected outputs are read from the doc's parsed tables.
  • Synthetic fixtures are hand-constructed for edge cases not covered by the canonical examples. Their expected outputs are computed manually and cross-checked.

Cross-check methodology

Synthetic Codec 8 fixtures were cross-checked against the Traccar open-source decoder (TeltonikaProtocolDecoder.java) for field widths and IO section parsing. No discrepancies were found for the basic N1/N2/N4/N8 sections. The NX section in Codec 8E has no equivalent in older Traccar versions; it was verified by byte-level manual trace only.