skip to content

Long-Lived Channels

Driving a connection that outlives one request: opening a push stream before the action that feeds it, and receiving callbacks the system sends out. Interviewers probe isolation under parallel runs.

on this pageshow

questions

8

How does an automated case observe an outbound callback that the system sends to a configured destination?

level: juniorimportance: must knowfreq 55%

answer

  1. The evidence leaves the system outward
  2. Somebody has to be listening
  3. The run owns the destination address
  4. Reachable from where the system runs
  5. Store the whole request, not a count

basics

~20 s

Point the system's configured destination at a receiver the run stands up, then read the recorded request back from it. The receiver must be reachable from where the system runs, not only from the machine running the suite.

solid answer

~50 s

The response to the triggering call proves only that the request was accepted; the callback leaves the system afterwards, through a door the case is not standing at. So the case has to own the far end. Stand up a small receiver that accepts requests at a known path, stores the **whole** request - path, headers and raw body - and exposes them for read-back; then arrange the system's destination to point at it, ideally by registering that address through the product's own configuration surface as part of the case's setup. Reachability is what usually breaks: a receiver bound to the machine running the suite is invisible to a system deployed elsewhere, so it has to sit somewhere the system's network can reach. Scope what it stores to the run, and answer with a success status so the system's own delivery behaviour does not muddy the evidence.

code

pseudocode · 9 lines
pseudocode
receiver = start_receiver(path = "/callbacks/" + case_id)
configure_destination(address = receiver.public_address)

response = trigger_action(reference = case_id)
assert response.status == ACCEPTED        # acceptance only, not delivery

arrived = receiver.requests_at("/callbacks/" + case_id)
assert arrived.count == 1
assert arrived[0].raw_body.contains(case_id)

go deeper

for a junior

Be ready to say what an outbound callback is and why the response to the action that caused it cannot prove it happened. Know that the run has to stand up something that receives the request and stores it.

for a middle

Explain the wiring: how the destination gets pointed at the receiver, what the receiver keeps about each request, and why a receiver bound to the machine running the suite records nothing against a deployed system.

for a senior

Show judgement about the receiver as shared test infrastructure - who owns it, how long it retains requests, what status it answers, what happens to cases when it is down - and explain how you prove absence rather than only presence.

for a principal

Own the argument that a product whose callback destination cannot be set per subscription is expensive to test, and treat that as a design conversation with the team building it rather than a workaround buried in the suite.

An **outbound callback** is a request the system under test makes on its own initiative, to an address it was configured with, after the interaction that caused it has already returned. A payment settles and the system calls the merchant's address; a long-running job finishes and the system calls the address the submitter registered; an approval completes and the system notifies a partner. It is the mirror image of everything else an automated case does. Normally the case asks and the system answers, so the evidence arrives in the response the case is already holding. Here the evidence leaves the system through a door the case is not standing at, and the response to the trigger says nothing about whether it ever went. ## The trigger's response is not the evidence The action that causes a callback usually returns as soon as the work is accepted. An accepted status proves the request was taken, not that the resulting notification was built, signed, addressed and sent. Everything interesting happens after that response: the record reaches its final state, a component decides a notification is due, a payload is rendered from that record, and a delivery attempt is made against a destination read from configuration. Any of those steps can be missing, mis-wired or silently disabled, and every one of them is invisible from the caller's side. A case that stops at the trigger's response is asserting on the first link of a chain and calling it the chain. ## Three things the run has to own 1. **A receiver** - a process that accepts requests at a known path and durably stores what it received for the length of the run. 2. **A settable destination** - a way for the case to make the system call *that* receiver: a per-subscription address registered through the product's own configuration surface, a per-tenant setting, or, least good, a deployment-wide value the whole suite shares. 3. **A read-back interface** - something the case queries to fetch the recorded requests, on its own terms, without removing them. Miss any one and the case degrades: no receiver and there is nothing to assert on; no settable destination and every case's callbacks land in the same pile; no read-back and the receiver is a black box that only its own logs can explain. ## Reachability decides where the receiver lives This is the failure that costs a day, because it works perfectly on a laptop against a locally started system and records nothing the moment the target is a deployed environment. | Where the receiver runs | Works when | Fails when | | --- | --- | --- | | On the machine running the suite | The system under test runs on that same machine | The system is deployed anywhere else and has no route back | | Deployed alongside the system | The suite can reach it to read back what arrived | Nobody owns its lifecycle and it drifts or disappears | | Behind a forwarding address that reaches the runner | The system is remote but the runner cannot be addressed | The forwarding address is per-developer and not reproducible in a pipeline | The rule underneath the table: the destination has to be resolvable **and routable from the system's network**, and the read-back has to be reachable from the suite's. Those are two different directions and both must hold. ## Record the request, not a verdict The receiver should be dumb and complete. It stores; the case decides. Keep, per request: - the **path** it arrived at, which is often where a per-case key lives; - every **header**, including the declared content type and any signature or correlation values; - the **raw body bytes**, captured before anything parses them; - the **order and arrival time** relative to the run, so a case can reason about sequence; - the status the receiver answered with. A receiver that only counts requests, or only keeps the two fields today's case asserts on, throws away the evidence that diagnoses tomorrow's failure. Storage is cheap; a callback you cannot reconstruct is not. ## What the receiver answers matters Answer with a success status unless the case is specifically about how the sender behaves when its callback is refused. The receiver is evidence, not a participant: an error answer changes what the system does next, and a slow answer can make the sender's own send path look degraded. Keep it fast and boring. ## Failure modes worth naming - **Runner-local receiver, remote system.** Records nothing, reports "no callback sent", and sends someone hunting a product bug that is not there. - **Asserting absence from an empty store.** Sending is asynchronous, so an empty receiver an instant after the trigger means nothing. Prove absence against a checked landmark: perform an action that *does* produce a callback, then assert the receiver holds that one and nothing matching the first action. - **A receiver shared across runs with no retention scope**, so a case can match a request from an hour ago. - **A receiver nobody owns**, left running from a demo, quietly collecting production-shaped traffic.

  • What should the receiver answer to the system when it accepts a callback?
    A success status, unless the case is specifically about how the sender behaves when its callback is refused. The receiver is evidence, not a participant: an error answer changes what the system does next and makes the observation part of the experiment. Answer quickly too - a slow receiver makes the sender's own send path look degraded and can turn one case's receiver into another case's problem.
  • How do you assert that the system did not send a callback for an action that should not produce one?
    Not from an empty store. Sending is asynchronous, so emptiness immediately after the trigger is meaningless. Give the system a landmark instead: perform a second action that definitely does produce a callback, then assert the receiver holds that one and holds nothing carrying the first action's reference. Absence proved against something that did arrive is far stronger than absence proved against nothing.
  • The receiver was up but recorded nothing, and the product team says it sent the callback. Where do you look first?
    At the destination the system actually used, then at routing. Read back the configured address for that subscription and compare it byte for byte with the receiver's address, including path and scheme. Then check the direction that fails silently: whether the system's network can reach that address at all. Only after both hold does the sender's own send log become the interesting artefact.

Testing an outbound callback is like checking that a shop posted your receipt: standing at the counter watching them take your money proves nothing, so you have to give them an address you can open yourself.

saying these in an interview costs you the question

  • Treating the trigger's success response as proof the callback was sent
  • Binding the receiver to the suite's own machine when the system runs elsewhere
  • Recording only a request count and discarding headers and body
  • Answering callbacks with an error status and calling the case green
  • Reading an empty receiver at once and reporting that nothing was sent
open as a page

Why must an automated case open a push connection before triggering the action that feeds it?

level: middleimportance: must knowfreq 62%

basics

~20 s

Open first, then act. A push connection delivers only what is emitted after it is established, so a case that triggers first can miss the update it exists to check and then passes or fails on timing alone.

open as a page

What goes wrong when an automated case leaves the push connection it opened still open?

level: juniorimportance: should knowfreq 45%

basics

~20 s

Leaked connections accumulate until the run exhausts its limits, and their receivers keep collecting, so a later case can match an update an earlier one caused. Close in a cleanup step the harness runs even when the case fails.

open as a page

A shared receiver has recorded many callbacks; how does a case prove which one its own trigger caused?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Mint a value the case controls before triggering - a generated reference on the record it creates - and require that value to come back in the callback body or in the destination path. Select by that value, never by recency.

open as a page

How do you give parallel cases their own callback destinations when the system's callback address is a single deployed setting?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Prefer routing over sharing: register a destination per case, usually one receiver with a per-case path segment, through the product's own configuration surface. Where the address truly is global, make every read filtered and non-destructive so no case drains another's evidence.

open as a page

How should an automated case assert on updates that a push connection delivers at unpredictable moments?

level: seniorimportance: should knowfreq 50%

basics

~20 s

Collect, then assert over the collection. Attach a receiver at subscription time that appends every delivery to a buffer the case owns, then assert that the buffer eventually holds one matching a correlation identifier the case injected.

open as a page

Why should a test's callback receiver verify a signature over the exact bytes it received rather than a re-serialized body?

level: middleimportance: nice to knowfreq 25%

basics

~20 s

A signature covers the byte sequence that was sent. Parsing the body and encoding it again changes whitespace, key order or number formatting, so the recomputed value no longer matches and the check fails on a perfectly genuine callback.

open as a page

After a push connection drops and reopens mid-case, which assertions are still valid?

level: seniorimportance: nice to knowfreq 22%

basics

~20 s

Only assertions that survive missing evidence. A gap leaves the case holding an incomplete record, so existence checks on items received after the reopen can stand, while counts, ordering and any claim that nothing was sent are void.

open as a page