skip to content

For a feature that streams a generated answer into the page, which presentation states need exact expected results?

level: juniorimportance: should knowfreq 48%

answer

  1. Text varies; the states around it do not
  2. Assemble and terminate, never timing
  3. Cut short needs a visible marker
  4. Empty and refused each have one rendering

basics

~20 s

Four states have fixed expected results: the streamed pieces must assemble and terminate cleanly, a length ceiling must show the defined cut-short marker, nothing to answer from must show the defined empty state, and a caller without permission must be refused outright.

solid answer

~50 s

Around a variable answer sits a small set of states that behave like any other interface and deserve exact assertions. **Streaming**: the pieces must assemble into the completed answer, the stream must terminate, and the interface must leave its in-progress state — assert the assembly and the ending, never the timing or the contents of an individual piece. **Cut short**: when a length ceiling stops the answer, the defined marker and whatever continuation control the design specifies must appear, and the stored record must say the answer was incomplete. **Empty**: when nothing matched and there is nothing to answer from, the defined empty message renders and no citation block or partial panel appears. **Not permitted**: a caller without access gets the refusal, and no fragment of the answer reaches them through the stream, the logs or the stored conversation. Each of these has one right rendering, so each is a plain equality case.

code

pseudocode · 17 lines
pseudocode
pieces = collect(open_answer_stream(request))

assert stream.terminated            == true
assert join(pieces)                 == result.answerText
assert view.state_after             == "complete"

when result.stoppedAtCeiling:
    assert view.shows("incomplete_marker")
    assert stored.answer.complete   == false

when matched_documents == 0:
    assert view.body                == "empty_state"
    assert view.has("citation_block") == false

when caller.permitted == false:
    assert response.status          == "refused"
    assert length(join(pieces))     == 0

go deeper

for a junior

Be ready to list the states beyond the happy path and say what each should show: pieces assembling, an answer cut short by a ceiling, nothing to answer from, and a caller without access. Each has one agreed rendering.

for a middle

Explain why the assertions are structural rather than textual: assembly and termination instead of timing, the incomplete marker plus the stored incomplete flag, the empty message with no source list beside it.

for a senior

Demonstrate the leakage judgement — no fragment reaching an unpermitted caller through delivery, error payloads, caches or logs — and the drift between what the page rendered and what was stored as the answer.

for a principal

Own the definitions. Decide what incomplete means to downstream consumers of stored answers, whether a refusal may disclose existence, and which of these states are product promises rather than implementation details.

## States are not opinions The text a generative step produces varies; the states the surrounding feature can be in do not. There are a small number of them, each has exactly one correct rendering agreed in the design, and each is testable with the same equality assertions used anywhere else in the product. These states are also where user-visible defects cluster, because they are the paths a demo never exercises. | State | Exact expectation | What the case must not assert | | --- | --- | --- | | Streaming in progress | pieces assemble, stream terminates, in-progress indicator clears | timing, piece boundaries, wording | | Cut short at a length ceiling | defined marker plus the specified continuation control; stored as incomplete | how much text arrived first | | Nothing to answer from | defined empty message, no citation block, no empty panel | any generated apology text | | Caller not permitted | refusal returned, no fragment delivered or stored | what the answer would have said | ## Streaming: assert assembly, not arrival When an answer is delivered in pieces, the assertions that hold are structural. Concatenating the pieces must equal the completed answer the feature reports; the stream must end rather than hang; the in-progress indicator must clear; and if the user navigates away or cancels, the delivery must stop and nothing half-finished may be stored as though it were complete. What must never be asserted is how many pieces arrive, how fast they arrive, or where the boundaries fall — those vary, and a case built on them is intermittent by construction. A subtle failure worth a dedicated case: the final assembled text and the copy the user can select or share must be the same text. Features that render pieces into the page and separately store a full answer often drift between the two. ## Cut short by a length ceiling Every such feature has a ceiling on how much it will produce. When the ceiling is hit, the product owes the user an honest signal: a marker that the answer is incomplete, whatever continuation control the design specifies, and a stored record flagged incomplete so that later reads do not treat a fragment as a full answer. All three are exact. The wrong behaviour, and a common one, is an answer that simply stops mid-sentence with no indication, leaving the user to assume the feature had nothing more to say. ## Nothing to answer from When the material the feature draws on turns up nothing relevant, the correct result is the defined empty state, not an answer written anyway. The case asserts the empty message renders, that no citation block or source list appears beside it, and that whatever the user can do next — refine the request, browse instead — is present and enabled. This is a product decision made once and then asserted forever. ## Not permitted Permission behaviour is entirely ordinary and entirely unforgiving. A caller without access must receive the refusal, and no part of the answer may reach them by any route: not through the stream, not in an error payload, not in a client-side cache, not in a shared log line. Two cases are worth having. One checks that the refusal is returned and that the delivered content is empty. The other checks the refusal is not distinguishable in a way that discloses whether the underlying material even exists, where that distinction matters to the product. ## Ordering the work 1. Write the permission case first; it is the one with consequences beyond an unhappy user. 2. Write the empty state next; it is the most frequently skipped and the most frequently ugly. 3. Write the cut-short case; assert the marker and the stored incompleteness, not the length. 4. Write the streaming assembly case last, and keep it structural. None of these cases reads the wording of the answer, which is why all four keep working when the generated text changes. They are also the cases most likely to be missing entirely from a suite that jumped straight to judging the quality of the prose.

  • Why assert that the assembled pieces equal the completed answer rather than asserting each piece's contents?
    Piece boundaries are an artefact of delivery and shift between requests, so asserting them produces intermittent failures with no defect behind them. The assembly, by contrast, is a real contract: what the user reads must equal what the feature reports and stores, and that equality catches lost, duplicated or reordered pieces.
  • A caller without permission currently receives a generic error rather than the defined refusal. Why does that matter?
    A generic error hides an authorisation defect behind what looks like a transient problem, so nobody investigates and clients often retry. The defined refusal is a stable, assertable contract: the case can require it exactly, require that no answer fragment was delivered, and require that nothing was stored against the caller's history.

saying these in an interview costs you the question

  • Asserts how many pieces arrive or how fast
  • Lets a cut-short answer end silently mid-sentence
  • Expects the feature to write an answer when nothing matched
  • Treats a refusal as acceptable if the panel merely looks empty
  • Skips these states because the wording varies anyway