7.3 KiB
Capability Spec Delta: currency-provider-cryptocompare (currency-provider-cryptocompare)
Package: @suaveplan/currency-provider-cryptocompare
Status: ARCHIVED
Every requirement below carries a
**Verified by:**line at authoring time.
ADDED Requirements
Requirement: CryptoCompareProvider — public pricemulti rate fetch
The package SHALL export CryptoCompareProvider implementing
CurrencyProvider from @suaveplan/currency with name === "cryptocompare",
supportsCrypto === true, supportsFiat === false. fetchRates(options?)
SHALL call
GET <baseUrl>/pricemulti?fsyms=<targets>&tsyms=<base> via the shared
httpRequest helper from @suaveplan/currency-providers, defaulting
base to "USD" and targets to the 15-symbol built-in list when
options.targets is omitted or empty. Returned rates SHALL be the
inverted spot price (1 / price) for every symbol whose quote in the
requested base is a finite positive number; non-finite and non-positive
quotes SHALL be skipped.
Scenario: defaults base to USD and inverts pricemulti payload
- WHEN
fetchRates()is called against{ BTC: { USD: 67000 }, ETH: { USD: 3500 }, FAKE: { USD: 0 } } - THEN
result.base === "USD",result.rates.BTC≈1 / 67000,result.rates.ETH≈1 / 3500, andresult.rates.FAKEis absent.
Scenario: honours custom baseUrl and explicit targets
- WHEN
fetchRates({ base: "EUR", targets: ["BTC", "ETH"] })is called withbaseUrl: "https://staging.example.com/data" - THEN the request URL is exactly
https://staging.example.com/data/pricemulti?fsyms=BTC,ETH&tsyms=EUR.
Scenario: empty targets array falls back to built-in defaults
- WHEN
fetchRates({ base: "USD", targets: [] })is called - THEN the request URL
fsymsparameter starts withBTC,ETH,SOL,USDT.
Verified by: src/cryptocompare/cryptocompare.test.ts::"reports stable metadata identifying the provider" AND src/cryptocompare/cryptocompare.test.ts::"inverts pricemulti payload, skips zero prices, and defaults base to USD" AND src/cryptocompare/cryptocompare.test.ts::"honours custom baseUrl and explicit targets list" AND src/cryptocompare/cryptocompare.test.ts::"falls back to defaults when targets array is empty"
Requirement: Optional API key attached via Apikey authorization header
The package SHALL accept an optional apiKey field on the constructor
config. When apiKey is provided and non-whitespace, every outbound
request SHALL carry an authorization: Apikey <key> header per
CryptoCompare's documented Apikey auth scheme. When apiKey is omitted,
undefined, or whitespace-only, the request SHALL be issued with no
authorization header (keyless free-tier mode).
Scenario: keyless construction issues no authorization header
- WHEN
new CryptoCompareProvider({ fetch }).fetchRates(...)runs - THEN the injected fetch sees
init?.headers === undefined.
Scenario: keyed construction issues authorization: Apikey
- WHEN
new CryptoCompareProvider({ fetch, apiKey: "secret-key" }).fetchRates(...)runs - THEN the injected fetch sees
init.headers.authorization === "Apikey secret-key".
Scenario: whitespace-only apiKey is treated as absent
- WHEN
new CryptoCompareProvider({ fetch, apiKey: " " }).fetchRates(...)runs - THEN the injected fetch sees
init?.headers === undefined.
Verified by: src/cryptocompare/cryptocompare.test.ts::"sends authorization header only when an apiKey is provided" AND src/cryptocompare/cryptocompare.test.ts::"treats whitespace-only apiKey as absent and omits the header"
Requirement: Typed-error mapping for HTTP, parse, upstream, and unknown-currency failures
The package SHALL surface every failure as CurrencyProviderError from
@suaveplan/currency-providers with providerName === "cryptocompare".
Non-2xx responses SHALL raise HTTP_ERROR (via the shared httpRequest
helper, populating status). Invalid JSON bodies and non-object parsed
payloads SHALL raise PARSE_ERROR. Payloads carrying Response: "Error"
SHALL raise UPSTREAM_ERROR with upstreamMessage set to the upstream
Message field, or to "unknown upstream error" when Message is
absent. fetchRates() SHALL raise UNKNOWN_CURRENCY when no requested
symbol resolves to a finite positive quote in the configured base.
Scenario: non-2xx response raises HTTP_ERROR
- WHEN the injected fetch returns HTTP 502
- THEN the call rejects with
code === "CURRENCY_PROVIDER.HTTP_ERROR"andstatus === 502.
Scenario: invalid JSON body raises PARSE_ERROR
- WHEN the body is
"<<not json>>"withcontent-type: application/json - THEN the call rejects with
code === "CURRENCY_PROVIDER.PARSE_ERROR".
Scenario: non-object payload (e.g. JSON null) raises PARSE_ERROR
- WHEN the body is
"null"parsed as JSON - THEN the call rejects with
code === "CURRENCY_PROVIDER.PARSE_ERROR".
Scenario: upstream error payload raises UPSTREAM_ERROR
- WHEN the payload is
{ Response: "Error", Message: "tsym XYZ is not available" } - THEN the call rejects with
code === "CURRENCY_PROVIDER.UPSTREAM_ERROR"andupstreamMessagecontains"tsym XYZ".
Scenario: upstream error payload without Message uses default text
- WHEN the payload is
{ Response: "Error" } - THEN the call rejects with
code === "CURRENCY_PROVIDER.UPSTREAM_ERROR"andupstreamMessage === "unknown upstream error".
Scenario: empty result map raises UNKNOWN_CURRENCY
- WHEN
fetchRates({ base: "USD", targets: ["BTC"] })is called and the payload is{} - THEN the call rejects with
code === "CURRENCY_PROVIDER.UNKNOWN_CURRENCY".
Scenario: every quote non-finite or non-positive raises UNKNOWN_CURRENCY
- WHEN the payload contains only
0,NaN, and negative quotes - THEN the call rejects with
code === "CURRENCY_PROVIDER.UNKNOWN_CURRENCY".
Verified by: src/cryptocompare/cryptocompare.test.ts::"raises HTTP_ERROR on non-2xx response" AND src/cryptocompare/cryptocompare.test.ts::"raises PARSE_ERROR when the response body is not valid JSON" AND src/cryptocompare/cryptocompare.test.ts::"raises PARSE_ERROR when the parsed payload is not an object" AND src/cryptocompare/cryptocompare.test.ts::"raises UPSTREAM_ERROR when payload carries Response='Error' with Message" AND src/cryptocompare/cryptocompare.test.ts::"raises UPSTREAM_ERROR with default message when Message field is absent" AND src/cryptocompare/cryptocompare.test.ts::"raises UNKNOWN_CURRENCY when no symbols resolve to a quote" AND src/cryptocompare/cryptocompare.test.ts::"raises UNKNOWN_CURRENCY when every quote is non-finite or non-positive"
Requirement: Caller AbortSignal forwarded into shared httpRequest
The package SHALL forward RateFetchOptions.signal (when provided)
through the shared httpRequest helper so caller-initiated aborts
propagate to the underlying FetchLike call.
Scenario: caller signal reaches the injected fetch
- WHEN
fetchRates({ signal: controller.signal, ... })is called with a recordingFetchLike - THEN the recorded
init.signalis exactlycontroller.signal.
Verified by: src/cryptocompare/cryptocompare.test.ts::"forwards a caller AbortSignal through httpRequest"