frankfurter/spec/provider/adapters/imf_spec.rb

172 lines
5.3 KiB
Ruby
Raw Permalink Normal View History

2026-05-21 01:38:14 -06:00
# frozen_string_literal: true
require_relative "../../helper"
require "provider/adapters/imf"
class Provider < Sequel::Model(:providers)
module Adapters
describe IMF do
before do
VCR.insert_cassette("imf", match_requests_on: [:method, :uri])
end
after do
VCR.eject_cassette
end
let(:adapter) { IMF.new }
it "fetches rates across both Currency blocks in the TSV response" do
dataset = adapter.fetch(after: Date.new(2026, 3, 1), upto: Date.new(2026, 3, 31))
dates = dataset.map { |r| r[:date] }.uniq
_(dates).must_include(Date.new(2026, 3, 17))
_(dates.size).must_be(:>, 11)
end
it "fetches multiple currencies per date without duplicating records" do
dataset = adapter.fetch(after: Date.new(2026, 3, 1), upto: Date.new(2026, 3, 31))
dates = dataset.map { |r| r[:date] }.uniq
sample = dataset.select { |r| r[:date] == dates.first }
_(sample.size).must_be(:>, 1)
keys = dataset.map { |r| [r[:date], r[:base], r[:quote]] }
_(keys.size).must_equal(keys.uniq.size)
end
it "parses indirect quotes with (1) suffix" do
tsv = <<~TSV
Representative Exchange Rates for Selected Currencies for January 2026
Currency\tJanuary 02, 2026\tJanuary 05, 2026
Euro(1)\t1.1698\t1.1606
Japanese yen\t156.40\t157.41
U.S. dollar\t1.0000\t1.0000
TSV
records = adapter.parse(tsv)
eur = records.select { |r| r[:base] == "EUR" || r[:quote] == "EUR" }
_(eur.first[:base]).must_equal("EUR")
_(eur.first[:quote]).must_equal("USD")
jpy = records.select { |r| r[:base] == "USD" && r[:quote] == "JPY" }
_(jpy.first[:rate]).must_equal(156.4)
end
it "parses KWD as direct quote (units per USD)" do
tsv = <<~TSV
Representative Exchange Rates for Selected Currencies for March 2026
Currency\tMarch 02, 2026
Kuwaiti dinar\t0.306600
TSV
records = adapter.parse(tsv)
kwd = records.find { |r| r[:quote] == "KWD" || r[:base] == "KWD" }
_(kwd[:base]).must_equal("USD")
_(kwd[:quote]).must_equal("KWD")
_(kwd[:rate]).must_equal(0.3066)
end
it "skips USD rows" do
tsv = <<~TSV
Representative Exchange Rates for Selected Currencies for January 2026
Currency\tJanuary 02, 2026
U.S. dollar\t1.0000
Euro(1)\t1.1698
TSV
records = adapter.parse(tsv)
_(records.none? { |r| r[:base] == "USD" && r[:quote] == "USD" }).must_equal(true)
end
it "parses Continued blocks with their own date header" do
tsv = <<~TSV
Representative Exchange Rates for Selected Currencies for March 2026
Currency\tMarch 02, 2026\tMarch 03, 2026
Chinese yuan\t6.882900\t6.897100
U.S. dollar\t1.000000\t1.000000
Representative Exchange Rates for Selected Currencies for March 2026 Continued
Currency\tMarch 17, 2026\tMarch 18, 2026
Chinese yuan\t6.888300\t6.875600
U.S. dollar\t1.000000\t1.000000
TSV
records = adapter.parse(tsv)
cny = records.select { |r| r[:quote] == "CNY" }.sort_by { |r| r[:date] }
_(cny.map { |r| r[:date] }).must_equal([
Date.new(2026, 3, 2),
Date.new(2026, 3, 3),
Date.new(2026, 3, 17),
Date.new(2026, 3, 18),
])
_(cny.find { |r| r[:date] == Date.new(2026, 3, 17) }[:rate]).must_equal(6.8883)
end
it "does not emit duplicate (date, base, quote) records across blocks" do
tsv = <<~TSV
Currency\tMarch 02, 2026\tMarch 03, 2026
Chinese yuan\t6.882900\t6.897100
Currency\tMarch 17, 2026\tMarch 18, 2026
Chinese yuan\t6.888300\t6.875600
TSV
records = adapter.parse(tsv)
keys = records.map { |r| [r[:date], r[:base], r[:quote]] }
_(keys.size).must_equal(keys.uniq.size)
end
it "emits SDR cross rates only as USD/XDR" do
dataset = adapter.fetch(after: Date.new(2026, 3, 1), upto: Date.new(2026, 3, 31))
sdr = dataset.select { |r| r[:quote] == "XDR" }
_(sdr).wont_be_empty
_(sdr.map { |r| r[:base] }.uniq).must_equal(["USD"])
end
it "parses USD/XDR from SDR cross rates and ignores other currencies" do
tsv = <<~TSV
SDRs per Currency unit for April 2026
Currency\tApril 01, 2026\tApril 02, 2026
Euro\t0.8507900000\t0.8483160000
Japanese yen\t0.0046154900\t0.0046392700
U.S. dollar\t0.7331240000\t0.7360660000
TSV
records = adapter.parse_sdrcv(tsv)
_(records.map { |r| r[:base] }.uniq).must_equal(["USD"])
_(records.size).must_equal(2)
usd = records.find { |r| r[:date] == Date.new(2026, 4, 1) }
_(usd[:quote]).must_equal("XDR")
_(usd[:rate]).must_equal(0.7331240)
end
it "skips SDR rows with NA values" do
tsv = <<~TSV
SDRs per Currency unit for April 2026
Currency\tApril 01, 2026\tApril 02, 2026
U.S. dollar\tNA\t0.7360660000
TSV
records = adapter.parse_sdrcv(tsv)
_(records.size).must_equal(1)
_(records.first[:date]).must_equal(Date.new(2026, 4, 2))
end
end
end
end