The conformance suite every venue package runs in its own CI.
This is the mechanism. Prose in six CLAUDE.md files drifts; a suite that runs in
five CI pipelines cannot.
Why this lives in lib/ and not test/support/
It was in test/support/ first, following the reference repo, and that does not
work for a suite consumers run. elixirc_paths(:test) governs this package's own
build; a dependency is not compiled in the :test environment, so the file shipped
inside the tarball and was never compiled into the consumer. A venue package's
use DpExchange.Core.AdapterContract failed with module not loaded and could not be
found — the suite present on disk and absent from the code path.
Shipping a file is not the same as shipping a module. A public testing API belongs in
lib/, which is why Ecto.Adapters.SQL.Sandbox and Phoenix.ConnTest live there.
use ExUnit.Case appears only inside the generated block, so nothing here needs
ExUnit at compile time and this compiles cleanly as a dependency.
defmodule DpExchange.Coinbase.ContractTest do
use DpExchange.Core.AdapterContract,
venue: DpExchange.Coinbase,
symbol_format: DpExchange.Coinbase.SymbolFormat,
sample_pairs: ~w(BTC-USDC ETH-USDC),
credentials: %{api_key: "test", api_secret: "test"}
endWhat it asserts, and what it deliberately does not
Thirteen groups, listed in assertions/0. The load-bearing one is capabilities and
behaviour agreeing in both directions: over-declaring fails in a caller's hands at
runtime, and under-declaring hides working functionality. Holding both is what makes
capabilities/0 trustworthy enough for a consumer to branch on instead of branching on
venue identity.
Maturity is asserted for presence, not for truth. Every active endpoint must declare
:proven or :experimental, because an absent value is the failure this prevents.
Whether a :proven claim is true is not machine-checkable, and a suite that pretended
otherwise would be worse than one that admits the limit.
No assertion may name a socket, a channel string, a transport module or a polling interval. If one does, mechanism has leaked through the facade and the assertion is the bug, not the venue.
The fake satisfies the same suite as the real adapter
That is the ratchet. The suite's job is not to anticipate every way a fake can diverge — it is to make each discovered divergence permanent. The reference implementation this pattern comes from went from nothing to over a thousand lines by absorbing thirteen gaps its first consumer found, and none reached an external user across thousands of downloads.
Every gap found becomes a new assertion here. A gap fixed only in one venue's fake is a gap the next venue will reintroduce.
Summary
Functions
The assertion groups, for documentation and for a venue package to report against.
Functions
@spec assertions() :: [{pos_integer(), String.t()}]
The assertion groups, for documentation and for a venue package to report against.