An in-process Webull, for a consumer's tier-1 tests and for the conformance suite.
It is not a mock. Nothing is stubbed, no expectation is recorded, no call is
verified. It is a real implementation of DpExchange.Core.Venue answering from memory,
and it runs the same conformance suite as the real adapter.
Two rules
Less capable is allowed. Differently capable is not. Where this cannot answer it returns an error, never an empty success.
It never rewrites a value the caller supplied, and never stamps the current clock —
@at is fixed, because a fake that stamps utc_now/0 cannot be used to test anything
about freshness and is itself the substitution this family refuses.
It models the three things that make this venue different
- Credentials are required for market data.
get_price/2without them is{:refused, :missing_credentials}, because the real venue signs every call and has no anonymous endpoint. A fake that answered anyway would let a consumer's test pass while the real call returns 401. - No volume.
volumeisnilon every quote and every bar, matching a venue that reports none. Returning0would look like a real measurement of no trading. - UAT has no stream.
subscribe/2underenvironment: :uatrefuses, exactly as the real feed does, rather than delivering fake production data.
Failure injection and anonymous mode
Every function below that has a real success path (not an unconditional
Venue.not_supported()) checks DpExchange.Core.FakeInjection.next_outcome/1 or /2
first — a queued or always-set outcome from FakeInjection.queue_failures/2,3 or
fail_always/2,3 short-circuits the fake's normal logic and is returned as-is.
authenticated/1 also checks FakeInjection.credentials_bypassed?/1 before its normal
{:refused, :missing_credentials} path. Neither changes anything for a test that never
calls FakeInjection — see that module for the full contract.
subscribe/2, unsubscribe/2 and update_symbols/2 are NOT wired: each takes a list
of symbols in one call, and "this one symbol in the batch fails, the rest succeed" is a
case whole-call injection cannot express — see FakeInjection's own moduledoc.
place_orders/3 is the same shape for the same reason: one call, many orders.