A password-reset email can be requested repeatedly; how do you pin down how many of its links stay usable?
answer
- Not always one link at a time
- Count what stays usable
- Attempt captured links oldest first
- Probing must not complete the flow
- Assert the number, do not record it
basics
~20 sRequest the reset several times, keep every link, then attempt them oldest first without completing the flow and count the acceptances. That count is the product's outstanding-link policy, and the case should assert it as an expected number.
solid answer
~40 sProducts differ in how many single-use links they leave live at once: one, where each request supersedes the last; a small fixed window of the most recent few; or unbounded until expiry. To measure it, request the reset several times, capture every link in issue order, then attempt them oldest first and record which are accepted. The boundary is where acceptance flips. Two traps decide whether the number is real. Consuming a link completes the flow and refuses all the others for a different reason, so probe without submitting, or give each probe its own account. And the count has to be asserted against the configured policy rather than merely recorded, because a case that asserts whatever it saw can never fail. Keep this separate from how often the request itself is allowed.
code
pseudocode · 14 lineslinks = []
for i in 1..5:
message = request_password_reset(account)
links.append(link_from(message))
usable = 0
for link in links: # oldest first
outcome = open_without_submitting(link)
if outcome == NEXT_STEP_OFFERED:
usable = usable + 1
else:
assert outcome == REFUSED_SUPERSEDED
assert usable == configured_outstanding_links()go deeper
Know that asking again usually produces another single-use link, and that a flow may leave more than one of them working at the same time. If asked, say you would try the older links rather than assume they are already dead.
Explain the measurement: capture the links in issue order, attempt them oldest first, and find where acceptance flips. Be able to say why consuming a link during the measurement refuses all the rest for an unrelated reason.
Show that you turn the observation into a falsifiable assertion tied to the configured policy, keep every probe inside the link lifetime so expiry cannot be mistaken for eviction, and keep the count distinct from how often the request is allowed.
Decide the policy rather than only measure it. A wider window of live links is gentler on users who open an older message and lengthens the exposure of any link that leaks. Name the product conditions that would make you pick the narrower window.
"Outstanding" here means issued, delivered, and not yet consumed or expired. A flow that lets a user ask again as often as they like accumulates these, and the number the product keeps usable at once is a policy. It is rarely written down, and it is easy to change by accident when the storage behind it changes. ## The policies you will actually meet - **Exactly one.** Each new request supersedes the previous link. The strictest, and the easiest to assert. - **The most recent few.** A fixed window of the newest two, three or five, with older links evicted as new ones are issued. Chosen to tolerate the user who opens the message they saw first. - **Unbounded until expiry.** Every link issued stays usable for its whole lifetime. Common when a link's validity is derived from its own contents rather than from a stored record. | Policy | What the case counts | The failure it prevents | | --- | --- | --- | | Exactly one | one acceptance, the rest refused | an old message still opening the flow | | Most recent few | acceptances equal to the window | the window silently widening | | Unbounded until expiry | every captured link accepted | an eviction rule appearing unannounced | ## Measuring without destroying the measurement 1. Request the flow **n** times, with **n** comfortably larger than the policy you expect. 2. Capture each link in issue order and keep them all. 3. Attempt them **oldest first**, recording accepted or refused for each. 4. Find the flip point. Suppose the window is three and you made five requests: the two oldest are refused and the three newest are accepted. 5. Assert the count against the configured policy, not against what this particular run happened to see. Step 3 carries the trap that ruins the result. **Consuming a link completes the flow**, and once the flow is complete every other link is refused because there is nothing left to do. If the probe submits the form behind the link, the first acceptance destroys the evidence for all the rest and the measured count is always one. Probe without consuming: open the link and assert that the flow offers its next step rather than an error, or give each probe its own account so completion is isolated. ## A recorded number is not an assertion An assertion has to be able to fail. `usable == 5`, where the 5 came from a previous run, is a recording: when the policy widens to unbounded, the run reports twenty usable links and someone updates the case to match. Bind the expectation to the policy the deployment is configured with, so behaviour and configuration disagreeing is exactly what breaks the build. If the policy genuinely is unbounded until expiry, assert that every captured link is accepted, which is still a real assertion with a real failure mode. ## What confounds the count - **Expiry masquerading as eviction.** A link refused because its lifetime ran out looks the same as one evicted by a newer link. Keep every probe well inside the lifetime and separate the two by the refusal the product gives. - **The request limit.** How often a user may ask is a different control from how many links stay usable. Trip the request limit while capturing and you measure the wrong thing entirely, so capture inside the allowed rate. - **A shared recipient.** If the messages land somewhere another run is also using, the link you believe is the third may be someone else's first. - **Ordering.** Delivery order and issue order are not guaranteed to match. Order the links by something the case knows, which is the order it triggered the requests, not the order the messages appeared. - **Partial consumption.** Some flows mark a link used the moment it is opened, others only when the form behind it is submitted. Establish which before treating "opened" as a safe probe. ## Why the number is worth pinning A wider window of usable links is a genuine kindness. The common support ticket in strict flows is a user who patiently opened the first message after asking twice, and was told it is invalid. It is also genuine exposure: every additional live link is another that can be forwarded, logged by a scanning gateway, or read from an account more than one person opens. The point of measuring is not to prefer one number. It is that the number is a decision, and an untested decision migrates.
- Why is the case recording five usable links not yet an assertion?Recording is observation, and an assertion has to be able to fail. Bind the expected count to the policy the deployment is configured with and compare against it, so a change from three outstanding links to unbounded breaks the run instead of quietly updating the number someone wrote down.
- How does link expiry confuse this measurement?A link can be unusable because a newer one evicted it or because its lifetime ran out, and both surface as a refusal. Keep every probe well inside the lifetime, and separate the two by the refusal the product actually gives rather than treating any failure as eviction.
saying these in an interview costs you the question
- Assumes one outstanding link is universal across products
- Consumes each link while measuring how many are usable
- Records the observed count instead of asserting an expected one
- Confuses expiry with eviction by a newer link
- Measures how often you may ask and calls it the link count