228 lines
7.2 KiB
Ruby
228 lines
7.2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require_relative "helper"
|
|
require "currency"
|
|
|
|
describe Currency do
|
|
before do
|
|
Rate.dataset.delete
|
|
Rate.multi_insert([
|
|
{ provider: "ECB", date: Date.today, base: "EUR", quote: "USD", rate: 1.1 },
|
|
{ provider: "ECB", date: Date.today, base: "EUR", quote: "GBP", rate: 0.85 },
|
|
{ provider: "BOC", date: Date.today, base: "CAD", quote: "USD", rate: 0.74 },
|
|
{ provider: "ECB", date: Date.today - 365, base: "EUR", quote: "SEK", rate: 11.0 },
|
|
])
|
|
|
|
db = Sequel::Model.db
|
|
db[:currencies].delete
|
|
db.run(<<~SQL)
|
|
INSERT OR REPLACE INTO currencies (iso_code, start_date, end_date)
|
|
SELECT iso_code, MIN(start_date), MAX(end_date)
|
|
FROM (
|
|
SELECT quote AS iso_code, MIN(date) AS start_date, MAX(date) AS end_date
|
|
FROM rates GROUP BY quote
|
|
UNION ALL
|
|
SELECT base AS iso_code, MIN(date) AS start_date, MAX(date) AS end_date
|
|
FROM rates GROUP BY base
|
|
)
|
|
GROUP BY iso_code
|
|
ORDER BY iso_code
|
|
SQL
|
|
|
|
db[:currency_coverages].delete
|
|
db.run(<<~SQL)
|
|
INSERT OR REPLACE INTO currency_coverages (provider_key, iso_code, start_date, end_date)
|
|
SELECT provider, iso_code, MIN(date), MAX(date)
|
|
FROM (
|
|
SELECT provider, quote AS iso_code, date FROM rates
|
|
UNION ALL
|
|
SELECT provider, base AS iso_code, date FROM rates
|
|
)
|
|
GROUP BY provider, iso_code
|
|
ORDER BY provider, iso_code
|
|
SQL
|
|
end
|
|
|
|
it "lists all currencies" do
|
|
codes = Currency.all.map(&:iso_code).sort
|
|
|
|
_(codes).must_include("USD")
|
|
_(codes).must_include("EUR")
|
|
_(codes).must_include("CAD")
|
|
end
|
|
|
|
it "merges date ranges across quote and base" do
|
|
usd = Currency.find("USD")
|
|
|
|
_(usd).wont_be_nil
|
|
_(usd.start_date.to_s).must_equal(Date.today.to_s)
|
|
_(usd.end_date.to_s).must_equal(Date.today.to_s)
|
|
end
|
|
|
|
it "includes base currencies" do
|
|
eur = Currency.find("EUR")
|
|
|
|
_(eur).wont_be_nil
|
|
end
|
|
|
|
it "filters active currencies" do
|
|
active_codes = Currency.active.map(&:iso_code)
|
|
|
|
_(active_codes).must_include("USD")
|
|
_(active_codes).wont_include("SEK")
|
|
end
|
|
|
|
it "returns nil for unknown currency" do
|
|
_(Currency.find("XYZ")).must_be_nil
|
|
end
|
|
|
|
it "formats to hash" do
|
|
usd = Currency.find("USD")
|
|
|
|
_(usd.to_h[:name]).must_equal("United States Dollar")
|
|
_(usd.to_h[:symbol]).must_equal("$")
|
|
_(usd.to_h[:iso_numeric]).must_equal("840")
|
|
end
|
|
|
|
it "includes providers" do
|
|
usd = Currency.find("USD")
|
|
|
|
_(usd.providers).must_include("ECB")
|
|
_(usd.providers).must_include("BOC")
|
|
end
|
|
|
|
it "is case insensitive" do
|
|
_(Currency.find("usd")).wont_be_nil
|
|
end
|
|
|
|
it "filters by providers" do
|
|
codes = Currency.with_providers(["ECB"]).map(&:iso_code)
|
|
|
|
_(codes).must_include("USD")
|
|
_(codes).must_include("EUR")
|
|
_(codes).wont_include("CAD")
|
|
end
|
|
|
|
it "excludes pegged currencies when filtering by providers" do
|
|
codes = Currency.with_providers(["ECB"]).map(&:iso_code)
|
|
|
|
_(codes).wont_include("BMD")
|
|
end
|
|
|
|
it "includes pegged currencies in the list" do
|
|
codes = Currency.all.map(&:iso_code)
|
|
|
|
_(codes).must_include("BMD")
|
|
_(codes).must_include("FKP")
|
|
end
|
|
|
|
it "derives pegged currency date range from anchor" do
|
|
bmd = Currency.find("BMD")
|
|
|
|
_(bmd).wont_be_nil
|
|
usd = Currency.find("USD")
|
|
|
|
_(bmd.end_date.to_s).must_equal(usd.end_date.to_s)
|
|
end
|
|
|
|
it "uses later of peg since and anchor start_date" do
|
|
bmd = Currency.find("BMD")
|
|
usd = Currency.find("USD")
|
|
|
|
_(bmd.start_date.to_s).must_be(:>=, usd.start_date.to_s)
|
|
end
|
|
|
|
it "extends start_date back to peg start when provider data is newer" do
|
|
# AED is pegged to USD since 1997-11-02. Insert provider data starting
|
|
# much later, but anchor (USD) data going back further.
|
|
db = Sequel::Model.db
|
|
Rate.multi_insert([
|
|
{ provider: "ECB", date: "1990-01-02", base: "EUR", quote: "USD", rate: 1.0 },
|
|
{ provider: "TCMB", date: Date.today, base: "USD", quote: "AED", rate: 3.6725 },
|
|
])
|
|
db[:currencies].insert_conflict(:replace).insert(iso_code: "USD", start_date: "1990-01-02", end_date: Date.today.to_s)
|
|
db[:currencies].insert_conflict(:replace).insert(iso_code: "AED", start_date: Date.today.to_s, end_date: Date.today.to_s)
|
|
db[:currency_coverages].insert_conflict(:replace).insert(provider_key: "TCMB", iso_code: "AED", start_date: Date.today.to_s, end_date: Date.today.to_s)
|
|
|
|
aed = Currency.find("AED")
|
|
|
|
_(aed.start_date.to_s).must_equal("1997-11-02")
|
|
_(aed.peg).wont_be_nil
|
|
_(aed.providers).must_include("TCMB")
|
|
end
|
|
|
|
it "extends start_date in currency list for pegged currencies with provider data" do
|
|
db = Sequel::Model.db
|
|
Rate.multi_insert([
|
|
{ provider: "ECB", date: "1990-01-02", base: "EUR", quote: "USD", rate: 1.0 },
|
|
{ provider: "TCMB", date: Date.today, base: "USD", quote: "AED", rate: 3.6725 },
|
|
])
|
|
db[:currencies].insert_conflict(:replace).insert(iso_code: "USD", start_date: "1990-01-02", end_date: Date.today.to_s)
|
|
db[:currencies].insert_conflict(:replace).insert(iso_code: "AED", start_date: Date.today.to_s, end_date: Date.today.to_s)
|
|
db[:currency_coverages].insert_conflict(:replace).insert(provider_key: "TCMB", iso_code: "AED", start_date: Date.today.to_s, end_date: Date.today.to_s)
|
|
|
|
aed = Currency.all.find { |c| c.iso_code == "AED" }
|
|
|
|
_(aed.start_date.to_s).must_equal("1997-11-02")
|
|
end
|
|
|
|
it "extends end_date for pegged currency with stale provider data" do
|
|
db = Sequel::Model.db
|
|
# ANG is pegged to USD. Insert a stale ANG row (end_date in the past)
|
|
# but USD is current. The peg should extend ANG's end_date to match USD.
|
|
db[:currencies].insert_conflict(:replace).insert(iso_code: "ANG", start_date: "1999-01-04", end_date: "2025-03-28")
|
|
db[:currency_coverages].insert_conflict(:replace).insert(provider_key: "BDI", iso_code: "ANG", start_date: "1999-01-04", end_date: "2025-03-28")
|
|
|
|
usd = Currency.find("USD")
|
|
|
|
# find
|
|
ang = Currency.find("ANG")
|
|
|
|
_(ang.end_date.to_s).must_equal(usd.end_date.to_s)
|
|
|
|
# all (scope=all)
|
|
ang_all = Currency.all.find { |c| c.iso_code == "ANG" }
|
|
|
|
_(ang_all.end_date.to_s).must_equal(usd.end_date.to_s)
|
|
|
|
# active
|
|
ang_active = Currency.active.find { |c| c.iso_code == "ANG" }
|
|
|
|
_(ang_active.end_date.to_s).must_equal(usd.end_date.to_s)
|
|
end
|
|
|
|
it "formats pegged currency to hash" do
|
|
bmd = Currency.find("BMD")
|
|
|
|
_(bmd.to_h[:iso_code]).must_equal("BMD")
|
|
_(bmd.to_h[:name]).must_equal("Bermudian Dollar")
|
|
_(bmd.to_h[:start_date]).wont_be_nil
|
|
_(bmd.to_h[:end_date]).wont_be_nil
|
|
end
|
|
|
|
it "returns peg metadata for pegged currencies" do
|
|
h = Currency.find("BMD").to_h_with_providers
|
|
|
|
_(h[:peg][:base]).must_equal("USD")
|
|
_(h[:peg][:rate]).must_equal(1.0)
|
|
_(h[:peg][:authority]).must_equal("Bermuda Monetary Authority")
|
|
end
|
|
|
|
it "returns providers for pegged currencies" do
|
|
h = Currency.find("BMD").to_h_with_providers
|
|
|
|
_(h[:providers]).must_be_kind_of(Array)
|
|
end
|
|
|
|
it "returns providers for non-pegged currencies" do
|
|
h = Currency.find("USD").to_h_with_providers
|
|
|
|
_(h[:providers]).must_be_kind_of(Array)
|
|
end
|
|
|
|
it "does not return peg metadata for non-pegged currencies" do
|
|
h = Currency.find("USD").to_h_with_providers
|
|
|
|
_(h).wont_include(:peg)
|
|
end
|
|
end
|