E2E testing of LogosDelivery Reliable Channel API — channel-layer focus
Curated E2E / interop scenarios for the Reliable Channel API (logos_delivery/channels), driven against liblogosdelivery. This round is scoped to what the channel layer owns on its own: its call contract and the ChannelReceived delivery path. Store validation / the --reliability flag is out of scope (see rationale below).
Scope decision — reliability/store is a messaging concern, not a channel one:
- The channel layer's own reliability (SDS: acks, retransmits, causal ordering, dedup) is always on — there is no toggle.
- The channel layer is store-agnostic: it never queries a store; it only reacts to messaging-layer events.
- Today the channel's send-terminal events (
ChannelSent / ChannelError) are gated entirely on the messaging layer's store validation (they need a MessageSent, which only fires from a store-v3 check when --reliability + a store are configured). That coupling is a messaging-layer property, so ChannelSent / ChannelError are excluded from this channel-focused round and tracked separately.
- By contrast, the receiver's
ChannelReceived needs only relay propagation + SDS unwrap — no store, no reliability flag — so it is the real channel-layer surface we exercise here.
FFI surface:
logosdelivery_channel_create(ctx, cb, ud, channelId, contentTopic, senderId),
logosdelivery_channel_send(ctx, cb, ud, channelId, messageJson) where messageJson = {"payload": <base64>, "ephemeral": <bool>},
logosdelivery_channel_close(ctx, cb, ud, channelId).
Channel events via logosdelivery_set_event_callback: onChannelMessageReceived / onChannelMessageSent / onChannelMessageError.
Event-name legend (shorthand → FFI callback event → Nim event type):
| Shorthand |
FFI callback event |
Nim event |
ChannelReceived |
onChannelMessageReceived |
ChannelMessageReceivedEvent |
ChannelSent (out of scope) |
onChannelMessageSent |
ChannelMessageSentEvent |
ChannelError (out of scope) |
onChannelMessageError |
ChannelMessageErrorEvent |
Behaviors worth asserting:
channel_send returns Ok(channelReqId) immediately; delivery is async. In these tests the sender's channelReqId is a handle only — we assert on the receiver's ChannelReceived, not on a sender terminal event.
- Ingress drops messages without the
RELIABLE-CHANNEL-API/1 marker, on the wrong content topic, or for a foreign SDS channelId.
- SDS delivers to the app in causal order and de-duplicates — observable purely through
ChannelReceived.
- Topology (peers) is set at
create_node time via config (staticnode); there is no peer-connect FFI call. No storenode / --reliability needed for any scenario here.
Recommended Minimal Matrix (prioritize): RC01, RC02, RC03, RC04, RC05, RC06, RC08, RC09.
Scenario Catalog (channel-layer E2E / [IT])
A. Lifecycle & API contract (single node; no events, no topology)
B. Two-party delivery & ingress filtering (assert on receiver's ChannelReceived)
C. SDS delivery semantics (two-party; assert on receiver's ChannelReceived)
Out of scope for this round
Send-terminal events (ChannelSent / ChannelError) — currently gated on the messaging layer's store validation (--reliability + a reachable store), not on channel-layer behavior. Excluded here; track under messaging-layer E2E, or revisit if channel success is redefined to be SDS-ack-based rather than store-validated. (Covers: store-validated send → ChannelSent; propagated-but-not-validated → no terminal; isolated sender → ChannelError; ephemeral → never ChannelSent.)
Already covered in-process by tests/channels/test_reliable_channel_send_receive.nim (not re-implemented at E2E): history survives close/re-create; foreign channelId dropped; out-of-order parking; duplicate delivered once; unacked acked by later remote message; reply advances the lamport clock; three-deep dependency chain; sync envelope consumed silently; concurrent sibling sends finalise independently.
Not yet written (Nim-side, not E2E): encryption provider fails → ChannelError; decryption failure on ingress → MessageError; non-SDS/undecodable payload → MessageError; SDS state survives node restart.
Deferred (feature not implemented — segmentation / encryption skeletons): multi-segment split/reassemble; multi-segment aggregation; Reed-Solomon parity recovery; real encryption round-trip; selective-field encryption; wrong-key decryption.
logos-messaging/logos-delivery#3959
E2E testing of LogosDelivery Reliable Channel API — channel-layer focus
Curated E2E / interop scenarios for the Reliable Channel API (
logos_delivery/channels), driven againstliblogosdelivery. This round is scoped to what the channel layer owns on its own: its call contract and theChannelReceiveddelivery path. Store validation / the--reliabilityflag is out of scope (see rationale below).Scope decision — reliability/store is a messaging concern, not a channel one:
ChannelSent/ChannelError) are gated entirely on the messaging layer's store validation (they need aMessageSent, which only fires from a store-v3 check when--reliability+ a store are configured). That coupling is a messaging-layer property, soChannelSent/ChannelErrorare excluded from this channel-focused round and tracked separately.ChannelReceivedneeds only relay propagation + SDS unwrap — no store, no reliability flag — so it is the real channel-layer surface we exercise here.FFI surface:
logosdelivery_channel_create(ctx, cb, ud, channelId, contentTopic, senderId),logosdelivery_channel_send(ctx, cb, ud, channelId, messageJson)wheremessageJson = {"payload": <base64>, "ephemeral": <bool>},logosdelivery_channel_close(ctx, cb, ud, channelId).Channel events via
logosdelivery_set_event_callback:onChannelMessageReceived/onChannelMessageSent/onChannelMessageError.Event-name legend (shorthand → FFI callback event → Nim event type):
ChannelReceivedonChannelMessageReceivedChannelMessageReceivedEventChannelSent(out of scope)onChannelMessageSentChannelMessageSentEventChannelError(out of scope)onChannelMessageErrorChannelMessageErrorEventBehaviors worth asserting:
channel_sendreturnsOk(channelReqId)immediately; delivery is async. In these tests the sender'schannelReqIdis a handle only — we assert on the receiver'sChannelReceived, not on a sender terminal event.RELIABLE-CHANNEL-API/1marker, on the wrong content topic, or for a foreign SDSchannelId.ChannelReceived.create_nodetime via config (staticnode); there is no peer-connect FFI call. Nostorenode/--reliabilityneeded for any scenario here.Recommended Minimal Matrix (prioritize): RC01, RC02, RC03, RC04, RC05, RC06, RC08, RC09.
Scenario Catalog (channel-layer E2E /
[IT])A. Lifecycle & API contract (single node; no events, no topology)
RC01. Create channel; duplicate create rejected [IT]
mode = Core); reliable channel manager mounted automatically.channel_create(id, contentTopic, senderId); thenchannel_createagain with the sameid."channel already exists: <id>".RC02. Send on unknown channel [IT]
channel_send(unknownId, {...})for an id never created."unknown channel: <id>". Expected events: none.RC03. Close channel; close unknown rejected [IT]
channel_close(id)on an existing channel; thenchannel_closeagain / on an unknown id."unknown channel: <id>".RC04. Send empty payload; send returns a handle immediately [IT]
channel_send(id, {"payload": "", "ephemeral": false}); then a normalchannel_sendwith a non-empty payload."empty payload"; non-empty →Ok(channelReqId)returned synchronously (delivery is async).send.B. Two-party delivery & ingress filtering (assert on receiver's ChannelReceived)
RC05. A → B on the same channel + topic → ChannelReceived [IT]
liblogosdeliverynodes, bothchannel_createwith the samechannelId+contentTopic, connected on the shared shard; bothsubscribe(contentTopic). No store, no--reliability.channel_send(payload).ChannelReceivedwith the exactpayload(byte-equal after SDS unwrap/reassemble) and A'ssenderId. (A'schannelReqIdis a handle only; no sender terminal event is asserted.)RC06. Missing spec marker → dropped [IT]
logosdelivery_send, no channel marker) on the node-under-test'scontentTopic.ChannelReceivedmust not fire).RC07. Wrong content topic → dropped [IT]
contentTopic.C. SDS delivery semantics (two-party; assert on receiver's ChannelReceived)
RC08. Bidirectional exchange preserves causal order [IT]
ChannelReceivedin causal order.RC09. A sends while B is offline; B joins later and recovers [IT]
ChannelReceivedvia SDS sync/repair.RC10. Missing dependency triggers an SDS-R repair rebroadcast [IT, two-party]
ChannelReceived.RC11. Retransmission of an unacked send [IT, two-party]
DefaultAcknowledgementTimeoutMs), up toDefaultMaxRetransmissions, until the receiver delivers it (ChannelReceived) / it is acked.RC12. Three participants on one channel [IT]
ChannelReceived; causal metadata stays consistent.Out of scope for this round
Send-terminal events (
ChannelSent/ChannelError) — currently gated on the messaging layer's store validation (--reliability+ a reachable store), not on channel-layer behavior. Excluded here; track under messaging-layer E2E, or revisit if channel success is redefined to be SDS-ack-based rather than store-validated. (Covers: store-validated send →ChannelSent; propagated-but-not-validated → no terminal; isolated sender →ChannelError; ephemeral → neverChannelSent.)Already covered in-process by
tests/channels/test_reliable_channel_send_receive.nim(not re-implemented at E2E): history survives close/re-create; foreign channelId dropped; out-of-order parking; duplicate delivered once; unacked acked by later remote message; reply advances the lamport clock; three-deep dependency chain; sync envelope consumed silently; concurrent sibling sends finalise independently.Not yet written (Nim-side, not E2E): encryption provider fails →
ChannelError; decryption failure on ingress →MessageError; non-SDS/undecodable payload →MessageError; SDS state survives node restart.Deferred (feature not implemented — segmentation / encryption skeletons): multi-segment split/reassemble; multi-segment aggregation; Reed-Solomon parity recovery; real encryption round-trip; selective-field encryption; wrong-key decryption.
logos-messaging/logos-delivery#3959