frankfurter/spec/rate_spec.rb

189 lines
5.9 KiB
Ruby
Raw Permalink Normal View History

2026-05-21 01:38:14 -06:00
# frozen_string_literal: true
require_relative "helper"
require "rate"
require "carry_forward"
describe Rate do
describe CarryForward, ".apply" do
it "returns latest available rates on given date" do
date = Fixtures.latest_date
rows = Rate.where(date: (date - 14)..date).naked.all
data = CarryForward.apply(rows, date:)
_(data.map { |r| r[:date] }.max).must_equal(date)
end
it "snaps to nearest prior date when requested date has no rates" do
sunday = Fixtures.recent_sunday
friday = Fixtures.preceding_friday(sunday)
rows = Rate.where(provider: "ECB", date: (sunday - 14)..sunday).naked.all
data = CarryForward.apply(rows, date: sunday)
_(data.map { |r| r[:date] }.uniq).must_equal([friday])
end
it "includes each provider's most recent date" do
date = Fixtures.latest_date
rows = Rate.where(date: (date - 14)..date).naked.all
data = CarryForward.apply(rows, date:)
providers = data.map { |r| r[:provider] }.uniq.sort
_(providers).must_include("ECB")
_(providers).must_include("BOC")
end
it "excludes providers more than 14 days behind the target" do
date = Fixtures.latest_date
Rate.dataset.insert(date: date - 20, base: "EUR", quote: "XTS", rate: 1.08, provider: "STALE")
rows = Rate.where(date: (date - 14)..date).naked.all
data = CarryForward.apply(rows, date:)
providers = data.map { |r| r[:provider] }.uniq
_(providers).must_include("ECB")
_(providers).wont_include("STALE")
end
it "includes providers within 14 days of the target" do
date = Fixtures.latest_date
Rate.dataset.insert(date: date - 10, base: "USD", quote: "XTS", rate: 0.92, provider: "FRED")
rows = Rate.where(date: (date - 14)..date).naked.all
data = CarryForward.apply(rows, date:)
providers = data.map { |r| r[:provider] }.uniq
_(providers).must_include("ECB")
_(providers).must_include("FRED")
end
it "includes rates from different dates within the same provider" do
date = Fixtures.latest_date
older_date = date - 3
Rate.dataset.insert(date: older_date, base: "XTS", quote: "PLN", rate: 0.05, provider: "ECB")
rows = Rate.where(date: (date - 14)..date).naked.all
data = CarryForward.apply(rows, date:)
quotes = data.select { |r| r[:provider] == "ECB" }.map { |r| r[:quote] }
_(quotes).must_include("PLN")
end
it "returns nothing if date predates dataset" do
date = Date.parse("1901-01-01")
rows = Rate.where(date: (date - 14)..date).naked.all
_(CarryForward.apply(rows, date:)).must_be_empty
end
it "returns latest rates when client date is ahead of server" do
future_date = Date.today + 1
rows = Rate.where(date: (future_date - 14)..future_date).naked.all
data = CarryForward.apply(rows, date: future_date)
_(data).wont_be_empty
today_rows = Rate.where(date: (Date.today - 14)..Date.today).naked.all
today_data = CarryForward.apply(today_rows, date: Date.today)
_(data.map { |r| r[:date] }.uniq.sort).must_equal(today_data.map { |r| r[:date] }.uniq.sort)
end
end
describe ".between" do
it "returns rates between given working dates" do
start_date = Fixtures.latest_date - 30
end_date = Fixtures.latest_date
dates = Rate.between(start_date..end_date).map(:date).sort.uniq
_(dates.first).must_be(:<=, start_date)
_(dates.last).must_equal(end_date)
end
it "starts on preceding business day if start date is a weekend" do
sunday = Fixtures.recent_sunday
monday = sunday + 1
friday = Fixtures.preceding_friday(sunday)
dates = Rate.between(sunday..monday).map(:date).uniq
_(dates).must_include(friday)
end
it "returns nothing if end date predates dataset" do
interval = (Date.parse("1901-01-01")..Date.parse("1901-01-31"))
_(Rate.between(interval)).must_be_empty
end
it "allows start date to predate dataset" do
start_date = Date.parse("1901-01-01")
end_date = Fixtures.latest_date
dates = Rate.between(start_date..end_date).map(:date)
_(dates).wont_be_empty
end
it "returns nothing when start date is in the future" do
start_date = Date.today + 1
end_date = start_date + 1
dates = Rate.between(start_date..end_date).map(:date)
_(dates).must_be_empty
end
end
describe ".only" do
it "returns only rate pairs involving the given currencies" do
currencies = ["CAD", "USD"]
date = Fixtures.latest_date
data = Rate.where(date: (date - 14)..date).only(*currencies).all
data.each do |r|
_([r.base, r.quote].any? { |c| currencies.include?(c) }).must_equal(true)
end
end
it "includes rates from providers where currency appears as base" do
date = Fixtures.latest_date
data = Rate.where(date: (date - 14)..date).only("USD", "EUR").all
providers = data.map(&:provider).uniq
_(providers).must_include("BOC")
end
it "includes providers whose rates span both sides of the pair" do
date = Fixtures.latest_date
data = Rate.where(date: (date - 14)..date).only("JPY", "EUR").all
providers = data.map(&:provider).uniq
_(providers).must_include("BOJ")
end
it "returns nothing if no matches" do
_(Rate.only("FOO").all).must_be_empty
end
end
describe ".downsample" do
let(:interval) { (Fixtures.latest_date - 366)..Fixtures.latest_date }
it "groups by week" do
dates = Rate.between(interval).downsample("week")
_(dates.map(:date).uniq.count).must_be(:<=, 55)
end
it "groups by month" do
dates = Rate.between(interval).downsample("month")
_(dates.map(:date).uniq.count).must_be(:<=, 14)
end
it "sorts by date" do
dates = Rate.between(interval).downsample("week").map(:date)
_(dates).must_equal(dates.sort)
end
end
end