An in-process Robinhood, for a consumer's tier-1 tests and for the conformance suite.
It is not a mock. A real implementation of DpExchange.Core.Venue answering from
memory, running the same conformance suite as the real adapter.
What it models that is specific to this venue
- Credentials are required for every signed call, because the real venue signs every
request — including market data — and has no anonymous endpoint at all. Without them:
{:error, {:missing_credentials, :robinhood}}, the exact shapeDpExchange.Robinhood.Auth.headers/5returns for a request that never reaches the venue. Not{:refused, ...}— a refusal is the venue's own permanent word about a request it received (seeDpExchange.Core.Venue's moduledoc on the two); a missing local credential is never sent at all, and is:errorfor the same reasonAuth.headers/5is. Checked by every function below that reaches a real endpoint on this venue, market data and trading alike — there is no venue-internal function this venue leaves ungated, because there is no venue-internal function the real venue leaves unsigned. - Coverage is
:internal_poll, not:stream— the one place a consumer can see that this venue has no socket, and it shows up as what is arriving, never as how. - No candles, no order book, no volume. The venue serves none, so neither does this.
subscribe/2takes no:to. The real venue'sDpExchange.Core.Venue.subscribe/2has no notion of a per-call recipient — data reaches whoever the feed was supervised with, fixed at boot — so this fake ignores it too and always delivers to the calling process, the same way a caller of the real facade receives from whichever process it supervised the feed under.subscribe_notices/2is the one call on this venue that legitimately takes:to—DpExchange.Robinhood.Feed's own per-call notice registry — and this fake honours it there, correctly.
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
{:error, {:missing_credentials, :robinhood}} 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.
subscribe_notices/1 IS wired, unlike those three: it takes no symbol list, so a queued
or always-set outcome (for example {:error, :feed_not_started}, which the real facade
answers when its feed is not running) applies to the whole call the same way it does for
get_symbols/1 or market_status/1.
Summary
Functions
coverage/1, split by kind. Same single-key shape as the real venue, and for the
same reason: get_top_of_book/2 above is this fake's only source of subscription
data and it produces exclusively Types.TopOfBook, so :top_of_book is the only
kind there is to report — see DpExchange.Robinhood.coverage_by_kind/1 for why the
family requires this callback even where a venue has nothing to split.
Registers opts[:to] for this venue's own notices. Unlike subscribe/2, :to is
genuine here — see this module's moduledoc.
Functions
@spec coverage_by_kind(keyword()) :: %{ required(DpExchange.Core.Capabilities.data_kind()) => %{ required(DpExchange.Core.Venue.symbol()) => DpExchange.Core.Venue.route() } }
coverage/1, split by kind. Same single-key shape as the real venue, and for the
same reason: get_top_of_book/2 above is this fake's only source of subscription
data and it produces exclusively Types.TopOfBook, so :top_of_book is the only
kind there is to report — see DpExchange.Robinhood.coverage_by_kind/1 for why the
family requires this callback even where a venue has nothing to split.
Registers opts[:to] for this venue's own notices. Unlike subscribe/2, :to is
genuine here — see this module's moduledoc.
Routed through with_injection/2, unlike subscribe/2, unsubscribe/2 and
update_symbols/2: this call carries no symbol list, so there is no "one symbol in the
batch" case for whole-call injection to fail at. That is what makes
{:error, :feed_not_started} — the real facade's answer when its feed is not running —
reachable here at all: FakeInjection.fail_always(:robinhood, {:error, :feed_not_started})
or queue_failures/2,3 produces it, exactly as they produce any other queued outcome.
With nothing queued, this fake has no feed of its own to be down, so it answers :ok.