frankfurter/spec/reciprocal_consistency_spec.rb
BadMark d0a43e357a
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / publish (push) Has been cancelled
CI/CD Pipeline / release (push) Has been cancelled
up
2026-05-21 01:38:14 -06:00

56 lines
1.8 KiB
Ruby

# frozen_string_literal: true
require_relative "helper"
require "rack/test"
require "versions/v2"
# Contract tests for the V2 API:
# - reciprocal: rate(A,B) * rate(B,A) == 1 (rounded)
# - cross-rate transitivity: rate(A,B) * rate(B,C) * rate(C,A) == 1 (rounded)
# - pegged-base anchor: pegged base requests resolve to the peg, not provider noise
#
# The reciprocal and transitivity tests fail on `main` for non-USD bases — they're the
# defining checks of the fix. The pegged-base test passes today; it's a regression guard.
describe "V2 reciprocal consistency" do
include Rack::Test::Methods
let(:app) { Versions::V2.freeze }
def rate_for(base:, quote:)
get("/rates?base=#{base}&quotes=#{quote}")
raise "request failed: #{last_response.status}" unless last_response.ok?
row = Oj.load(last_response.body).find { |r| r["quote"] == quote }
raise "no rate returned for #{base}->#{quote}" unless row
row["rate"]
end
it "rate(A,B) * rate(B,A) rounds to 1.0 for USD/GBP" do
forward = rate_for(base: "USD", quote: "GBP")
backward = rate_for(base: "GBP", quote: "USD")
_((forward * backward).round(4)).must_equal(1.0)
end
it "rate(A,B) * rate(B,A) rounds to 1.0 for USD/CAD" do
forward = rate_for(base: "USD", quote: "CAD")
backward = rate_for(base: "CAD", quote: "USD")
_((forward * backward).round(4)).must_equal(1.0)
end
it "transitivity holds across USD, EUR, GBP" do
a_b = rate_for(base: "USD", quote: "EUR")
b_c = rate_for(base: "EUR", quote: "GBP")
c_a = rate_for(base: "GBP", quote: "USD")
_((a_b * b_c * c_a).round(4)).must_equal(1.0)
end
it "pegged base resolves to the peg, not provider noise" do
rate = rate_for(base: "AED", quote: "USD")
_(rate).must_equal((1.0 / 3.6725).round(5))
end
end