Skip to content

Testing and interop

sparkplugb-interop packages six independent Sparkplug B implementations as edge nodes and host applications in Docker images with one container contract. go-sparkplugb pulls those images, pinned by digest, and runs every one of its roles against them on every push. The suite owns the references and the fixtures; the library owns its own scenarios and assertions.

Reference Version Language Edge node Host
Eclipse Tahu 1.0.21 Java yes yes
Eclipse Tahu sparkplug-client 3.2.4 JavaScript yes no
pysparkplug 0.6.1 Python yes yes
SparkplugNet 1.4.1 C# yes yes
srad 0.5.0 Rust yes yes
sparkplughost v0.4.0 Go no yes

They run on four MQTT brokers, also pinned by digest: Eclipse Mosquitto 2.1.2, HiveMQ CE 2026.5, EMQX 6.3.2 and NanoMQ 0.25.7. Each image says what it supports (print-capabilities): only Tahu (Java) waits for a primary host, Tahu and tahu-js rebirth on Node Control/Rebirth, pysparkplug and SparkplugNet edges publish data by name rather than alias, and SparkplugNet’s host neither resolves aliases nor checks seq. A test skips a step that needs a capability a reference does not declare.

Suite go-sparkplugb role Against Steps
TestHost Primary host application Every reference edge node, both fixtures Births, aliases, every update, NCMD and DCMD writes, rebirth on request and by call, device death and birth, NDEATH on a clean stop, the will after a crash, the edge’s view of the primary host going offline
TestApplication Application Every reference edge node Births, updates, device death and birth, the node’s death
TestEdge Edge node Every reference host Births with aliases, alias-only data, the host’s view of the node, commands, rebirth on request, device death and birth, NDEATH, the will, the primary host’s STATE going offline

The two fixtures are baseline, a node with two devices and writable metrics, and scalars, all fourteen scalar datatypes at their limits. Run it with make interop in the go-sparkplugb repository (Docker needed).

Reference go-sparkplugb host and application, reference edge go-sparkplugb edge, reference host
Eclipse Tahu (Java) pass pass
pysparkplug pass ¹ pass ² ⁵
SparkplugNet pass ³ pass ⁴
srad pass pass ⁵
Eclipse Tahu sparkplug-client pass edge only
sparkplughost host only pass ⁵

“pass” means every step passes on all four brokers, except the steps a reference cannot do or that hit one of its upstream bugs: ¹ pysparkplug’s NDEATH carries the next bdSeq, so the will after a crash is checked instead; ² pysparkplug’s host cannot decode a negative Int8 or Int16 sign-extended to 32 bits, so the scalars fixture is skipped; ³ without device writes, which SparkplugNet’s edge drops; ⁴ without the steps after a death, because SparkplugNet’s host keeps dead nodes online; ⁵ not on NanoMQ and HiveMQ CE, which reorder a node’s messages across topics in a way these hosts do not resequence.

Running six stacks against each other on four brokers found real differences. sparkplugb-interop’s COMPATIBILITY.md describes each one; these shaped go-sparkplugb’s wire behaviour:

Finding What go-sparkplugb does
Int8 and Int16 are encoded two ways: sign-extended (Tahu, SparkplugNet) or width-sized (pysparkplug, srad) Sends sign-extended, reads both
Tahu (Java and JavaScript) encodes UInt32 in long_value, not int_value Reads integers from whichever field carries them
srad and Tahu send command metrics without a datatype, as the specification recommends; several stacks cannot decode that Takes the datatype from the birth
NanoMQ and HiveMQ CE can deliver a node’s DBIRTH before its NBIRTH, and data out of order across topics Holds out-of-order messages and applies them in seq order
A broker may deliver an old session’s will late Ignores an NDEATH whose bdSeq is not the current birth’s

Several fixes in go-sparkplugb’s first releases came from these runs: integers in long_value read as 0, data from a node without a birth dropped instead of answered with a rebirth request, a retained STATE missed by an edge node, and a device lost when a restarted node’s DBIRTH arrived before its NBIRTH.

  • The fixtures cover scalars, aliases, devices and writes. Templates, DataSets, arrays and properties are covered by go-sparkplugb’s own codec tests only, because no reference fixture has them yet.
  • Every adapter speaks MQTT 3.1.1, except srad, which speaks MQTT 5 only. TLS and broker authentication are not part of the interop runs.
  • The suite has not been run against commercial Sparkplug implementations. Interoperability reports are welcome in the go-sparkplugb repository.

sparkplugb-interop also runs every reference host against every reference edge node on each broker, so a broken reference is caught before a library is blamed for it. On 27 October 2026 it passed on all four brokers with no failures, with every known upstream bug listed in tests/known-bugs.tsv and checked for staleness, and a weekly workflow that tries the latest release of every reference and broker against its pin.