An in-process Coinbase, for a consumer's tier-1 tests and for the conformance suite's active-endpoint assertion.
It is not a mock. Nothing is stubbed, no expectation is recorded, and no call is
verified. It is a real implementation of DpExchange.Core.Venue that answers from
memory instead of from the network, and it runs the same conformance suite as the
real adapter.
Two rules, from thirteen real bug reports about a comparable fake
Eleven of those thirteen were the fake diverging from the real client. Six were loud — the fake rejecting what the real thing accepts — which costs time and nothing else. Three were silent, and those are the ones this is designed against.
Less capable is allowed. Differently capable is not. Where this cannot answer, it
returns an error. It never returns an empty success for something unsupported: the
original answered {:ok, []} to an unsupported query and dropped clauses it could not
parse, so callers got plausible wrong answers rather than failures.
It never rewrites a value the caller supplied. The original discarded the caller's timestamp and substituted the current clock, landing points written 900 seconds apart microseconds apart.
It models Coinbase's refusals, not just its successes
A fake where everything works proves only half the contract. This one refuses a symbol
it does not carry with {:refused, :not_listed} — permanent, distinct from a transient
error — and refuses a timeframe Coinbase does not serve, including 12h, which the
shared vocabulary models and this venue does not.
It also enforces the 350-candle boundary as a refusal rather than a truncation, because that is what the venue does: 351 candles requested returns zero and an error, not the first 350.