Move back to brandfetch (#1427)

* Move back to brandfetch

* Update security.rb

* Update security.rb
This commit is contained in:
soky srm
2026-04-10 17:42:16 +02:00
committed by GitHub
parent 0aca297e9c
commit dcebda05de
6 changed files with 129 additions and 189 deletions

View File

@@ -4,9 +4,6 @@ class Provider::BinancePublicTest < ActiveSupport::TestCase
setup do
@provider = Provider::BinancePublic.new
@provider.stubs(:throttle_request)
# Logo cache is keyed per base asset and persists across tests; clear it
# so verified_logo_url tests don't see each other's results.
Rails.cache.delete_matched("binance_public:logo:*")
end
# ================================
@@ -399,41 +396,16 @@ class Provider::BinancePublicTest < ActiveSupport::TestCase
# Info
# ================================
test "fetch_security_info returns crypto kind" do
stub_logo_head_success
test "fetch_security_info returns crypto kind and nil logo_url" do
response = @provider.fetch_security_info(symbol: "BTCUSD", exchange_operating_mic: "BNCX")
assert response.success?
assert_equal "BTC", response.data.name
assert_equal "crypto", response.data.kind
assert_match(/binance\.com/, response.data.links)
end
test "fetch_security_info returns the CDN logo URL when the HEAD succeeds" do
stub_logo_head_success
response = @provider.fetch_security_info(symbol: "ETHEUR", exchange_operating_mic: "BNCX")
assert_equal "https://cdn.jsdelivr.net/gh/lindomar-oliveira/binance-data-plus/assets/img/ETH.png", response.data.logo_url
end
test "fetch_security_info returns nil logo_url on a HEAD 403" do
stub_logo_head_raising(Faraday::ForbiddenError.new("403"))
response = @provider.fetch_security_info(symbol: "NOPECOINUSD", exchange_operating_mic: "BNCX")
# Nil (rather than a baked-in fallback URL) lets Security#display_logo_url
# substitute a Brandfetch binance.com URL at render time — a path that
# depends on runtime config and can't live on this provider.
assert_nil response.data.logo_url
end
test "fetch_security_info returns nil logo_url when the CDN HEAD times out" do
stub_logo_head_raising(Faraday::TimeoutError.new("timeout"))
response = @provider.fetch_security_info(symbol: "BTCUSD", exchange_operating_mic: "BNCX")
# logo_url is always nil — crypto logos are resolved at render time via
# Security#display_logo_url using the Brandfetch probe verdict, so the
# provider has nothing sensible to persist here.
assert_nil response.data.logo_url
end
@@ -492,40 +464,24 @@ class Provider::BinancePublicTest < ActiveSupport::TestCase
# Logo URL plumbing
# ================================
test "search_securities sets the optimistic CDN logo URL on every result" do
test "search_securities populates each result with the Brandfetch crypto URL" do
@provider.stubs(:exchange_info_symbols).returns(sample_exchange_info)
Setting.stubs(:brand_fetch_client_id).returns("test-client-id")
Setting.stubs(:brand_fetch_logo_size).returns(120)
response = @provider.search_securities("BTC")
assert response.data.all? { |s| s.logo_url == "https://cdn.jsdelivr.net/gh/lindomar-oliveira/binance-data-plus/assets/img/BTC.png" }
expected = "https://cdn.brandfetch.io/crypto/BTC/icon/fallback/lettermark/w/120/h/120?c=test-client-id"
assert response.data.all? { |s| s.logo_url == expected }
end
test "verified_logo_url caches the happy-path result per base asset" do
with_memory_cache do
mock_logo_client = mock
mock_logo_client.expects(:head).once.returns(mock)
@provider.stubs(:logo_client).returns(mock_logo_client)
test "search_securities leaves logo_url nil when Brandfetch is not configured" do
@provider.stubs(:exchange_info_symbols).returns(sample_exchange_info)
Setting.stubs(:brand_fetch_client_id).returns(nil)
url1 = @provider.send(:verified_logo_url, "BTC")
url2 = @provider.send(:verified_logo_url, "BTC")
response = @provider.search_securities("BTC")
assert_equal "https://cdn.jsdelivr.net/gh/lindomar-oliveira/binance-data-plus/assets/img/BTC.png", url1
assert_equal url1, url2
end
end
test "verified_logo_url caches the nil fallback per base asset" do
with_memory_cache do
mock_logo_client = mock
mock_logo_client.expects(:head).once.raises(Faraday::ForbiddenError.new("403"))
@provider.stubs(:logo_client).returns(mock_logo_client)
url1 = @provider.send(:verified_logo_url, "NEVERCOIN")
url2 = @provider.send(:verified_logo_url, "NEVERCOIN")
assert_nil url1
assert_nil url2
end
assert response.data.all? { |s| s.logo_url.nil? }
end
# ================================
@@ -583,18 +539,6 @@ class Provider::BinancePublicTest < ActiveSupport::TestCase
@provider.stubs(:client).returns(mock_client)
end
def stub_logo_head_success
mock_logo_client = mock
mock_logo_client.stubs(:head).returns(mock)
@provider.stubs(:logo_client).returns(mock_logo_client)
end
def stub_logo_head_raising(error)
mock_logo_client = mock
mock_logo_client.stubs(:head).raises(error)
@provider.stubs(:logo_client).returns(mock_logo_client)
end
# Rails.cache in the test env is a NullStore by default, so Rails.cache.fetch
# re-runs the block every time. Swap in a real MemoryStore so cache-hit
# assertions are meaningful, then restore the original.

View File

@@ -161,17 +161,16 @@ class Security::ResolverTest < ActiveSupport::TestCase
# Documents that find_or_create_provider_match! intentionally copies only
# ticker, MIC, country_code, and price_provider from the match — not name
# or logo_url. This means Security#import_provider_details always has
# blank metadata on first resolution and does NOT short-circuit at
# `return if self.name.present? && ...`, so fetch_security_info runs as
# expected on the first sync. Regression guard: if someone adds name/logo
# copying to the resolver, the Binance logo-fallback path would become
# dead code on first sync.
# blank metadata on first resolution and runs fetch_security_info +
# probe_brandfetch_crypto_coverage! as expected. Regression guard: if
# someone adds name/logo copying to the resolver, the Brandfetch
# coverage probe would be bypassed on first sync.
match = Security.new(
ticker: "BTCUSD",
exchange_operating_mic: "BNCX",
country_code: nil,
name: "BTC",
logo_url: "https://cdn.jsdelivr.net/gh/lindomar-oliveira/binance-data-plus/assets/img/BTC.png",
logo_url: "https://cdn.brandfetch.io/crypto/BTC/icon/fallback/lettermark/w/120/h/120?c=test",
price_provider: "binance_public"
)

View File

@@ -39,50 +39,6 @@ class SecurityTest < ActiveSupport::TestCase
assert_equal [ "has already been taken" ], duplicate.errors[:ticker]
end
test "first_provider_price_on resets when price_provider changes" do
sec = Security.create!(
ticker: "TEST",
exchange_operating_mic: "XNAS",
price_provider: "twelve_data",
first_provider_price_on: Date.parse("2020-01-03")
)
sec.update!(price_provider: "yahoo_finance")
assert_nil sec.reload.first_provider_price_on
end
test "first_provider_price_on is preserved when unrelated fields change" do
sec = Security.create!(
ticker: "TEST",
exchange_operating_mic: "XNAS",
price_provider: "twelve_data",
first_provider_price_on: Date.parse("2020-01-03"),
offline: false
)
sec.update!(offline: true, failed_fetch_count: 3)
assert_equal Date.parse("2020-01-03"), sec.reload.first_provider_price_on
end
test "first_provider_price_on respects explicit assignment alongside provider change" do
sec = Security.create!(
ticker: "TEST",
exchange_operating_mic: "XNAS",
price_provider: "twelve_data",
first_provider_price_on: Date.parse("2020-01-03")
)
# Caller changes both in the same save — honor the explicit value.
sec.update!(
price_provider: "yahoo_finance",
first_provider_price_on: Date.parse("2024-03-21")
)
assert_equal Date.parse("2024-03-21"), sec.reload.first_provider_price_on
end
test "cash_for lazily creates a per-account synthetic cash security" do
account = accounts(:investment)
@@ -123,25 +79,58 @@ class SecurityTest < ActiveSupport::TestCase
assert_not offline.crypto?
end
test "display_logo_url for crypto prefers logo_url and falls back to brandfetch with binance.com" do
test "crypto_base_asset strips the display-currency suffix" do
%w[USD EUR JPY BRL TRY].each do |quote|
sec = Security.new(ticker: "BTC#{quote}", exchange_operating_mic: Provider::BinancePublic::BINANCE_MIC)
assert_equal "BTC", sec.crypto_base_asset, "expected BTC#{quote} -> BTC"
end
end
test "crypto_base_asset returns nil for non-crypto securities" do
sec = Security.new(ticker: "AAPL", exchange_operating_mic: "XNAS")
assert_nil sec.crypto_base_asset
end
test "brandfetch_crypto_url uses the /crypto/ route and current size setting" do
Setting.stubs(:brand_fetch_client_id).returns("test-client-id")
Setting.stubs(:brand_fetch_logo_size).returns(120)
with_logo = Security.new(
assert_equal(
"https://cdn.brandfetch.io/crypto/BTC/icon/fallback/lettermark/w/120/h/120?c=test-client-id",
Security.brandfetch_crypto_url("BTC")
)
end
test "brandfetch_crypto_url returns nil when Brandfetch is not configured" do
Setting.stubs(:brand_fetch_client_id).returns(nil)
assert_nil Security.brandfetch_crypto_url("BTC")
end
test "display_logo_url for crypto returns the /crypto/{base} Brandfetch URL" do
Setting.stubs(:brand_fetch_client_id).returns("test-client-id")
Setting.stubs(:brand_fetch_logo_size).returns(120)
sec = Security.new(
ticker: "BTCUSD",
exchange_operating_mic: Provider::BinancePublic::BINANCE_MIC
)
assert_equal(
"https://cdn.brandfetch.io/crypto/BTC/icon/fallback/lettermark/w/120/h/120?c=test-client-id",
sec.display_logo_url
)
end
test "display_logo_url for crypto falls back to stored logo_url when Brandfetch is disabled" do
Setting.stubs(:brand_fetch_client_id).returns(nil)
sec = Security.new(
ticker: "BTCUSD",
exchange_operating_mic: Provider::BinancePublic::BINANCE_MIC,
logo_url: "https://cdn.jsdelivr.net/gh/lindomar-oliveira/binance-data-plus/assets/img/BTC.png"
logo_url: "https://example.com/btc.png"
)
assert_equal "https://cdn.jsdelivr.net/gh/lindomar-oliveira/binance-data-plus/assets/img/BTC.png",
with_logo.display_logo_url
without_logo = Security.new(
ticker: "NOPECOIN",
exchange_operating_mic: Provider::BinancePublic::BINANCE_MIC,
logo_url: nil
)
assert_equal "https://cdn.brandfetch.io/binance.com/icon/fallback/lettermark/w/120/h/120?c=test-client-id",
without_logo.display_logo_url
assert_equal "https://example.com/btc.png", sec.display_logo_url
end
test "display_logo_url for non-crypto prefers brandfetch over stored logo_url" do
@@ -170,4 +159,19 @@ class SecurityTest < ActiveSupport::TestCase
assert_equal "https://example.com/aapl.png", sec.display_logo_url
end
test "before_save writes the /crypto/{base} URL to logo_url for new crypto securities" do
Setting.stubs(:brand_fetch_client_id).returns("test-client-id")
Setting.stubs(:brand_fetch_logo_size).returns(120)
sec = Security.create!(
ticker: "BTCUSD",
exchange_operating_mic: Provider::BinancePublic::BINANCE_MIC
)
assert_equal(
"https://cdn.brandfetch.io/crypto/BTC/icon/fallback/lettermark/w/120/h/120?c=test-client-id",
sec.logo_url
)
end
end