Expand financial providers (#1407)

* Initial implementation

* Tiingo fixes

* Adds 2 providers, remove 2

* Add  extra checks

* FIX a big hotwire race condition

// Fix hotwire_combobox race condition: when typing quickly, a slow response for
// an early query (e.g. "A") can overwrite the correct results for the final query
// (e.g. "AAPL"). We abort the previous in-flight request whenever a new one fires,
// so stale Turbo Stream responses never reach the DOM.

* pipelock

* Update price_test.rb

* Reviews

* i8n

* fixes

* fixes

* Update tiingo.rb

* fixes

* Improvements

* Big revamp

* optimisations

* Update 20260408151837_add_offline_reason_to_securities.rb

* Add missing tests, fixes

* small rank tests

* FIX tests

* Update show.html.erb

* Update resolver.rb

* Update usd_converter.rb

* Update holdings_controller.rb

* Update holdings_controller.rb

* Update holdings_controller.rb

* Update holdings_controller.rb

* Update holdings_controller.rb

* Update _yahoo_finance_settings.html.erb
This commit is contained in:
soky srm
2026-04-09 18:33:59 +02:00
committed by GitHub
parent ab13093634
commit 7908f7d8a4
50 changed files with 2553 additions and 206 deletions

View File

@@ -1,11 +1,6 @@
require "test_helper"
class Security::ResolverTest < ActiveSupport::TestCase
setup do
@provider = mock
Security.stubs(:provider).returns(@provider)
end
test "resolves DB security" do
# Given an existing security in the DB that exactly matches the lookup params
db_security = Security.create!(ticker: "TSLA", exchange_operating_mic: "XNAS", country_code: "US")
@@ -75,4 +70,73 @@ class Security::ResolverTest < ActiveSupport::TestCase
assert_raises(ArgumentError) { Security::Resolver.new(nil).resolve }
assert_raises(ArgumentError) { Security::Resolver.new("").resolve }
end
test "persists explicit price_provider on DB match" do
db_security = Security.create!(ticker: "CSPX", exchange_operating_mic: "XLON", country_code: "GB")
Security.expects(:search_provider).never
Setting.stubs(:enabled_securities_providers).returns([ "tiingo" ])
resolved = Security::Resolver.new(
"CSPX",
exchange_operating_mic: "XLON",
country_code: "GB",
price_provider: "tiingo"
).resolve
assert_equal db_security, resolved
assert_equal "tiingo", resolved.reload.price_provider
end
test "persists price_provider on provider match" do
match = Security.new(ticker: "VWCE", exchange_operating_mic: "XETR", country_code: "DE", price_provider: "eodhd")
Security.expects(:search_provider)
.with("VWCE", exchange_operating_mic: "XETR")
.returns([ match ])
Setting.stubs(:enabled_securities_providers).returns([ "eodhd" ])
resolved = Security::Resolver.new(
"VWCE",
exchange_operating_mic: "XETR",
price_provider: "eodhd"
).resolve
assert resolved.persisted?
assert_equal "eodhd", resolved.price_provider
end
test "rejects unknown price_provider" do
db_security = Security.create!(ticker: "AAPL2", exchange_operating_mic: "XNAS", country_code: "US")
Security.expects(:search_provider).never
resolved = Security::Resolver.new(
"AAPL2",
exchange_operating_mic: "XNAS",
country_code: "US",
price_provider: "fake_provider"
).resolve
assert_equal db_security, resolved
assert_nil resolved.reload.price_provider, "Unknown providers should be rejected"
end
test "rejects disabled price_provider" do
db_security = Security.create!(ticker: "GOOG2", exchange_operating_mic: "XNAS", country_code: "US")
Security.expects(:search_provider).never
Setting.stubs(:enabled_securities_providers).returns([ "twelve_data" ])
resolved = Security::Resolver.new(
"GOOG2",
exchange_operating_mic: "XNAS",
country_code: "US",
price_provider: "tiingo"
).resolve
assert_equal db_security, resolved
assert_nil resolved.reload.price_provider, "Disabled providers should be rejected"
end
end