# frozen_string_literal: true require_relative "helper" require "consensus" describe Consensus do def build_rates(quote: "USD", providers:) providers.map do |provider, rate| { date: Date.today, base: "EUR", quote:, rate:, provider: } end end it "filters a rate that deviates from consensus" do rates = build_rates(providers: { "A" => 1.10, "B" => 1.11, "C" => 1.10, "D" => 9.99 }) consensus = Consensus.new(rates) result = consensus.find _(result.none? { |r| r[:provider] == "D" }).must_equal(true) _(consensus.outliers.any? { |r| r[:provider] == "D" && r[:quote] == "USD" }).must_equal(true) end it "keeps all rates within consensus" do rates = build_rates(providers: { "A" => 1.10, "B" => 1.11, "C" => 1.12, "D" => 1.105 }) consensus = Consensus.new(rates) _(consensus.find.size).must_equal(4) _(consensus.outliers).must_be_empty end it "skips quotes with fewer than 4 providers" do rates = build_rates(providers: { "A" => 1.10, "B" => 9.99, "C" => 1.11 }) consensus = Consensus.new(rates) _(consensus.find.size).must_equal(3) _(consensus.outliers).must_be_empty end it "tolerates small fluctuations in low-volatility pairs" do rates = build_rates(providers: { "A" => 612.55, "B" => 612.50, "C" => 612.60, "D" => 610.40 }) consensus = Consensus.new(rates) consensus.find _(consensus.outliers).must_be_empty end it "filters per quote, not per provider" do rates = [ { date: Date.today, base: "EUR", quote: "USD", rate: 1.17, provider: "A" }, { date: Date.today, base: "EUR", quote: "AED", rate: 4.30, provider: "A" }, { date: Date.today, base: "EUR", quote: "USD", rate: 1.17, provider: "B" }, { date: Date.today, base: "EUR", quote: "AED", rate: 4.30, provider: "B" }, { date: Date.today, base: "EUR", quote: "USD", rate: 1.17, provider: "C" }, { date: Date.today, base: "EUR", quote: "AED", rate: 25.8, provider: "C" }, { date: Date.today, base: "EUR", quote: "USD", rate: 1.17, provider: "D" }, { date: Date.today, base: "EUR", quote: "AED", rate: 4.30, provider: "D" }, ] consensus = Consensus.new(rates) result = consensus.find _(consensus.outliers.any? { |r| r[:provider] == "C" && r[:quote] == "AED" }).must_equal(true) _(consensus.outliers.none? { |r| r[:provider] == "C" && r[:quote] == "USD" }).must_equal(true) _(result.any? { |r| r[:provider] == "C" && r[:quote] == "USD" }).must_equal(true) _(result.none? { |r| r[:provider] == "C" && r[:quote] == "AED" }).must_equal(true) end end