jonnifyechomq

The conformance suite · dive 01

The scenarios, in run order

A name, a one-line contract, and an externally visible verdict — the protocol written as a list.

scenarios/0 returns a keyword list — name: "one-line contract" — in run order. Each entry names one externally visible promise of the bus, and the contract is one sentence stating what an outside observer must see. The names are not test labels; they are the protocol's clauses. A scenario drives the public surface, or, where the contract is the wire, raw commands — and asserts the value a port in any language must reproduce. Pick a clause to read its verdict.

A clause · the externally visible verdict

a clause: name + contract order drive against a live server ZRANGE … REV BYLEX assert the externally visible verdict newest-first by name alone
Pick a clause below to read its verdict.

Clauses, not labels

Each entry of scenarios/0 is a clause of the protocol, written as a name and one sentence. The name picks out a single promise; the sentence states what an outside observer must see. They run in order, and the head of the list is the lifecycle's own order — the fence, then admission, then the claim, then the settle:

Pick a clause in the figure above to read the verdict, the verb or raw command it drives, and why that verdict is the externally visible fact a port must reproduce. The figure's six are the head of the list; the body runs on through the lanes (rotate, pause, limit), the schedule (schedule, repeat, backoff), the read plane (counts, state, metrics, rate, lane_depth), the operator and watch planes, the flow family, and the Stream Tier (stream_verbs … stream_time_travel).

Pick a clause · read its contract

The names are the protocol's clauses, not test labels: each one is a promise an outside caller can check, and the list is the order they are checked in.

The list

The keyword list, annotated — the head plus a few later clauses, to show the run order and the contract style. The full list is longer (and grows); the page teaches the shape, not a count.

# echo_mq — EchoMQ.Conformance
# The scenario names and their one-line contracts, in run order. A port of the
# client conforms when it drives the same server to the same verdicts — the
# scenarios are wire-level on purpose, so the harness ports by translation.
def scenarios do
  [
    fence:     "the version fence is claimed before any work and reads the current wire version",
    mint:      "enqueue admits a JOB name and writes the three-field row: state pending, attempts 0, payload",
    duplicate: "a second enqueue of the same name answers duplicate and changes nothing",
    kind:      "an ORD name in the job position is refused by the kind law before any write",
    order:     "the pending set walked REV BYLEX answers newest-first by name alone",
    claim:     "claim mints token 1, returns the payload, and moves the row to active",
    stale:     "a stale token's completion is refused EMQSTALE; the live token still settles",
    complete:  "complete retires the row everywhere -- nothing remains to browse",
    # … the lanes, the schedule, the read / operator / watch planes, the flow
    # family, and the Stream Tier follow, each one clause, each in run order …
    stream_group: "a consumer group delivers at-least-once with crash re-delivery"
  ]
end

The order is meaningful: the fence runs first (no work before the wire version is claimed), then admission, the claim, and the settle — the lifecycle's own order — before the planes and the families built on top. Reading the list top to bottom is reading the bus from its floor up.

Driving the surface, or the wire

Most clauses call the public surface and pattern-match the externally visible answer. The mint clause enqueues a branded JOB id and reads the row straight back with a raw HGETALL, asserting the exact three-field shape — the verdict is what an outside caller can see, nothing internal.

# echo_mq — EchoMQ.Conformance (the :mint scenario)
# Drive the PUBLIC surface (Jobs.enqueue), then read the row with a RAW command
# (HGETALL) and assert the externally visible shape — state pending, attempts 0,
# payload as enqueued. The verdict is what an outside caller can see, nothing more.
defp apply_scenario(:mint, conn, q) do
  id = BrandedId.generate!("JOB")

  with {:ok, :enqueued} <- Jobs.enqueue(conn, q, id, "cargo"),
       {:ok, row} <- Connector.command(conn, ["HGETALL", Keyspace.job_key(q, id)]) do
    if pairs(row) == %{"state" => "pending", "attempts" => "0", "payload" => "cargo"},
      do: :ok,
      else: {:fail, row}
  end
end

Where the contract is the wire — the byte order of the pending set, the fence key's value — the clause issues the raw command directly. Step the worked mint clause below to watch it drive the surface, read the row, and assert the shape.

The mint clause, worked · step it

mint JOB id generate!("JOB") Jobs.enqueue {:ok, :enqueued} HGETALL row raw command assert the exact three-field shape %{state: "pending", attempts: "0", payload: "cargo"} → :ok

Pattern & implementation

the pattern (Redis Patterns Applied)

A coordination primitive is correct only if its externally visible behavior holds under contention — atomic claim, the fencing token, idempotent admission. Coordination teaches the atomicity these clauses assert.

the implementation (echo_mq)

Each clause of scenarios/0 is one of those externally visible promises, asserted against a live server — the contract written as a name plus a sentence, driven by the public surface or raw commands, then run.

The pattern names the property; the clause asserts it on a running server. The list is the protocol, read top to bottom from the floor up.

Recap — the protocol as a list

scenarios/0 is the bus contract written as a keyword list of name and one-line contract, in run order. Each clause drives the public surface — or raw commands where the contract is the wire — against a live server and asserts the externally visible verdict a port must reproduce. The list has no fixed length on the page, because the set grows. Next: how run/2 walks the list, isolates each clause on its own sub-queue, and renders the verdict.

Next in the module: Run & the verdict — the loop, the per-scenario sub-queues, the purge, and the {:ok, n} tally.

References

Where the scenario list and its wire-level verdicts are grounded.

Sources

Related in this course