ETSI CIM · Testing Task Force

Interoperability tests with real brokers

The suite already has Test Cases with several real brokers: the interoperability Test Cases in IOP_TP, written for the 2026 test event. They cover Create Entity, Retrieve Entity and Query Entities over four broker topologies. This page builds on them: what they do, what a run shows, what they need next, and a long list of what else several real brokers could test, each item as a new IOP Test Case.

1. What the suite has today

Distributed operations against a mock

Interoperability: IOP_TP

The four topologies

TopologyBrokersRegistrationsTest Cases
cnf_013b1 → b2 inclusive, b1 → b3 exclusive001_01_01-02, 002_01_01-02, 003_01_01-02
cnf_024b1 → b2 inclusive, b1 → b3 redirect, b1 → b4 redirect001_02_01-02, 002_02_01-02, 003_02_01-02
cnf_034b1 → b2 auxiliary, b1 → b3 inclusive, b1 → b4 inclusive001_03_01-02, 002_03_01-02, 003_03_01-02
cnf_045b1 → b2 auxiliary, b1 → b3 inclusive; b2 → b4 redirect, b2 → b5 redirect; b3 → b5 exclusive. Two hops.001_04_01-02, 002_04_01-02, 003_04_01-02

Within a topology the registration files differ from one Test Case to the next (all attributes, a few attributes, one entity id), so the 24 files are 24 different setups.

2. What a run shows

Run on 6 November 2026 against five copies of one implementation, develop as of that day: 42 of 48 Test Cases pass, 21 of the 24 files. The three failing files:

IOP_003_02_02: the Test Case checks that b2's own OffStreetParking:1 equals b3's, and that b3's own OffStreetParking:2 equals b4's, but its setup creates different versions there (the full entities in b2 and b3, location and name only, with other locations, in b3 and b4). Each broker's own copy is to be compared with the data created on it.

A Test Case issue, twice. b1's answer is right: the location with observedAt wins, as TS 104-175 clause 8 says.

IOP_002_04_01: b1 is expected to return OffStreetParking:1 with the location held in b5. The only routes to b5 are b2's redirect for location, which b1 does not reach because its auxiliary registration towards b2 is limited to availableSpotsNumber and totalSpotsNumber, and b3's exclusive registration, which is exclusive-1: OffStreetParking:2, totalSpotsNumber.

Looks like a Test Case issue: 002_04_02 uses exclusive-3 (OffStreetParking:1, location) in the same place and passes.

IOP_003_03_02: a query by type=OffStreetParking,Vehicle on b1 also returned Vehicle:2, held in b4, whose registration covers OffStreetParking only.

That part of the Test Case is right and the fix belongs in the implementation: a forward never asks a source for more than its registration covers. With it fixed, a second check fails: b1's Vehicle:1, from a query with attrs=location, is compared with the full entity instead of the location-only one the Test Case builds two lines before. A Test Case issue.

One documentation slip as well: IOP_001_03_01's description says the entity "shall not be present in b3"; the Test Case checks, rightly, that it is not in b2.

3. What IOP_TP needs next

Much of what an earlier version of this page asked for is already there: several broker URLs, a keyword that creates registrations between brokers, entities created on a chosen broker, a teardown over every broker, brokers started outside the suite, a tag. What is missing:

4. The first new configuration: the RV

The RV is the case this page started with. In IOP_TP terms it is two new configurations, cnf_05 and cnf_06, with the client talking to b1 as in every IOP Test Case.

Split entity, two types

A recreational vehicle is both a vehicle and a building. Its vehicle part lives in one broker and its building part in another. A third broker, the one the client talks to, has a registration for each of them, both for the RV with both of its types (an EntityInfo type can be an array, TS 104-175 § 5.2.6.6.2).

Broker b2:  { "id": "urn:ngsi-ld:RV:1", "type": "Vehicle",  "speed":  { "type": "Property", "value": 80 } }
Broker b3:  { "id": "urn:ngsi-ld:RV:1", "type": "Building", "floors": { "type": "Property", "value": 1 } }

Broker b1:  inclusive registration urn:ngsi-ld:RV:1 / ["Vehicle", "Building"] -> b2
            inclusive registration urn:ngsi-ld:RV:1 / ["Vehicle", "Building"] -> b3

NGSI-LD has no way to ask for only part of an entity. Whatever the request, when the RV is in the answer it is the entire RV: "type": ["Vehicle", "Building"], with speed and floors.

Filters over both parts

Three brokers, no stubs. The Test Case checks only what b1 answers, so b1, b2 and b3 can each be any implementation. Entities and registrations are new data files next to the OffStreetParking ones; the keywords are the existing ones.

The same RV, registrations of different width

The case the Entity Map exists for. b1's registration towards b2 is narrow, Vehicle only. The one towards b3 is wide, both types:

Broker b1:  inclusive registration urn:ngsi-ld:RV:1 / "Vehicle"               -> b2
            inclusive registration urn:ngsi-ld:RV:1 / ["Vehicle", "Building"] -> b3

GET /entities?type=Vehicle&entityMap=true&splitEntities=true on b1:

  1. b2 has the RV as a Vehicle, so urn:ngsi-ld:RV:1 goes into the Entity Map.
  2. b3's registration also matches type=Vehicle, so it is in the map entry for urn:ngsi-ld:RV:1 too, although b3 holds the RV only as a Building.
  3. Each part is fetched by the entity's id, without the type: b2 gives the vehicle part, b3 the building part. Asked for type=Vehicle, b3 would have nothing, since it holds the RV only as a Building.
  4. The answer is the entire RV: "type": ["Vehicle", "Building"], speed and floors.

The same holds on every following page, and for the map read back through GET /entityMaps/{id}: the entry for urn:ngsi-ld:RV:1 lists both registrations.

The rule behind it: the type chooses the registrations and the entities; the parts of a chosen entity are then fetched by its id. It follows from TS 104-175 § 9.6: the query is "a unitary operation, masking the fact" that the data is distributed, so b1 answers as one broker holding the whole RV would; and with split entities the map holds "candidate Entities", checked on the assembled entity, not on each part.

Open for discussion

In the same setup, GET /entities?type=Building: b2's registration (Vehicle only) does not match, so b2 is not asked and the answer is only the building part of the RV, "type": "Building" and floors.

Either way, a client cannot ask for only the part of type X: NGSI-LD has no such query. The Test Case for type=Building is written once the group has chosen.

5. What we could test

A long list, grouped. The number in brackets is how many brokers a Test Case needs; b1 is always the one the client talks to. IOP_TP marks what the existing Test Cases already cover, with the file. Every item is run with both contexts, as today.

Split entities: one entity, parts in several brokers

Queries across brokers

Several hops and federations

Loops, for real

Writes across brokers

Subscriptions across brokers

Temporal

Discovery and registration details

Mixed implementations

This is what IOP_TP was written for, and it needs no new Test Cases: the same Test Cases with each of b1 to b5 a different implementation. With the start script of § 3 it becomes a matrix instead of one result: for each Test Case, which implementation sat where. The ones worth running first:

6. How it could go in

7. What Seamware asks of the group