|
1 | | -# The fake broker keeps immediate delivery; no in-memory `TimerStore` |
2 | | - |
3 | | -**Decision:** `TestTimersBroker` keeps its current model — the fake producer encodes and dispatches |
4 | | -every published Timer immediately to the handler via `process_message`, regardless of its Activation |
5 | | -time. We do **not** add an `InMemoryTimerStore` adapter or a `TimerStore` `Protocol` for testing. |
6 | | - |
7 | | -## Context |
8 | | - |
9 | | -An architecture review proposed that "the fake fakes the wrong layer": put a real |
10 | | -`InMemoryTimerStore` behind the `TimerStore` seam so the fake broker exercises real Claim / Lease / |
11 | | -Commit and truthful inspection, instead of stubbing the Redis client and dispatching immediately. |
12 | | -Three defects were claimed: (1) the fake duplicates the envelope encode; (2) `has_pending`, |
13 | | -`get_pending_timers` and `cancel_all` are stubbed to canned empties ("lies"); (3) at-least-once and |
14 | | -Lease semantics cannot be tested through the fake. The options weighed were: A — drive the *real* |
15 | | -subscriber poll loop against an in-memory store; B — a real in-memory store for state with |
16 | | -deterministic delivery; or drop it. |
17 | | - |
18 | | -## Decision & rationale |
19 | | - |
20 | | -Researching FastStream's own Redis broker (`faststream/redis/testing.py`) dismantled the premises: |
21 | | - |
22 | | -- **The encode is not duplicated.** FastStream's `FakeProducer.publish` also encodes via |
23 | | - `build_message` / `message_format.encode` — encoding is inherent to the fake-producer pattern, not |
24 | | - a smell. `FakeTimersProducer` mirrors it. |
25 | | -- **The inspection stubs are not lies.** FastStream fakes *every* subscriber — including the polling |
26 | | - list subscriber, our closest analog — by bypassing the poll loop and calling |
27 | | - `handler.process_message` directly from the fake producer; it never runs the real loop in tests. |
28 | | - Under an immediate-delivery contract a published Timer has already fired and been removed, so |
29 | | - `has_pending → False`, `get_pending_timers → []` and `cancel_all → 0` are *truthful*, not stubbed |
30 | | - lies. |
31 | | -- **Option A is non-idiomatic and fragile.** Running the real `_consume` loop under FastStream's |
32 | | - `TestBroker` means skipping `_fake_start` — forfeiting the handler-mock wiring every FastStream |
33 | | - test broker relies on — and driving an infinite 50 ms poll loop inside a unit test; a spike doing |
34 | | - so hung at teardown. |
35 | | - |
36 | | -That leaves only one way to make inspection report *pending* future Timers: stop delivering future |
37 | | -Timers immediately. That is a **breaking change** to a public testing API — existing user tests |
38 | | -publish a future Timer and expect their handler to fire without waiting — so it is rejected. With |
39 | | -immediate delivery retained the inspection methods are already correct, and the proposal has no |
40 | | -remaining defect to fix. `scheduled_timers` already lets users assert what was Scheduled. |
41 | | - |
42 | | -**Revisit trigger:** a concrete need to test Schedule / Pending / cancel semantics — a future Timer |
43 | | -observed as Pending before it fires — through the fake. If that arises, prefer an **opt-in** |
44 | | -`TestTimersBroker(..., respect_activation=True)` (default off, so non-breaking) that withholds |
45 | | -not-yet-Due Timers; not a change to the default immediate-delivery contract, and not a real |
46 | | -subscriber loop in tests. |
| 1 | +# The fake broker delivers every Timer immediately |
| 2 | + |
| 3 | +`FakeTimersProducer` encodes each published Timer and hands it straight to the matching subscriber's |
| 4 | +`process_message`, ignoring the Activation time, and `_patch_broker` stubs the Redis client so |
| 5 | +`has_pending` is False, `get_pending_timers` empty and `cancel_all` zero. Putting a real in-memory |
| 6 | +store behind the `TimerStore` seam, so the fake exercised Claim, Lease and Commit, was rejected: |
| 7 | +FastStream fakes every subscriber, its polling list subscriber included, by bypassing the poll loop, |
| 8 | +and under immediate delivery a published Timer has already fired, so those inspection results are |
| 9 | +truthful rather than canned. Driving the real `_consume` loop under `TestBroker` means skipping |
| 10 | +`_fake_start` and running a 50 ms poll loop inside a unit test; a spike doing so hung at teardown. |
| 11 | +Reporting a future Timer as Pending requires withholding it, which breaks every user test that |
| 12 | +publishes one and expects its handler to fire, so it could only arrive as an opt-in flag. |
0 commit comments