currency-rate 0.4.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -4
  3. data/README.md +41 -53
  4. data/VERSION +1 -1
  5. data/currency-rate.gemspec +92 -78
  6. data/lib/adapter.rb +37 -83
  7. data/lib/adapters/crypto/bitfinex_adapter.rb +17 -0
  8. data/lib/adapters/crypto/bitpay_adapter.rb +14 -0
  9. data/lib/adapters/crypto/bitstamp_adapter.rb +19 -0
  10. data/lib/adapters/crypto/btc_china_adapter.rb +11 -0
  11. data/lib/adapters/crypto/btc_e_adapter.rb +18 -0
  12. data/lib/adapters/crypto/coinbase_adapter.rb +14 -0
  13. data/lib/adapters/crypto/huobi_adapter.rb +17 -0
  14. data/lib/adapters/crypto/kraken_adapter.rb +29 -0
  15. data/lib/adapters/crypto/localbitcoins_adapter.rb +14 -0
  16. data/lib/adapters/crypto/okcoin_adapter.rb +19 -0
  17. data/lib/adapters/fiat/fixer_adapter.rb +16 -0
  18. data/lib/adapters/fiat/forge_adapter.rb +23 -0
  19. data/lib/adapters/fiat/yahoo_adapter.rb +35 -0
  20. data/lib/configuration.rb +21 -0
  21. data/lib/currency_rate.rb +52 -48
  22. data/lib/fetcher.rb +71 -0
  23. data/lib/storage/file_storage.rb +29 -0
  24. data/lib/storage/serializers/yaml_serializer.rb +15 -0
  25. data/lib/synchronizer.rb +31 -0
  26. data/spec/fixtures/adapters/bitfinex_adapter.yml +18 -0
  27. data/spec/fixtures/adapters/bitpay_adapter.yml +486 -0
  28. data/spec/fixtures/adapters/bitstamp_adapter.yml +30 -0
  29. data/spec/fixtures/adapters/btc_china_adapter.yml +11 -0
  30. data/spec/fixtures/adapters/btce_adapter.yml +60 -0
  31. data/spec/fixtures/adapters/coinbase_adapter.yml +680 -0
  32. data/spec/fixtures/adapters/fixer_adapter.yml +34 -0
  33. data/spec/fixtures/adapters/forge_adapter.yml +75 -0
  34. data/spec/fixtures/adapters/huobi_adapter.yml +22 -0
  35. data/spec/fixtures/adapters/kraken_adapter.yml +124 -0
  36. data/spec/fixtures/adapters/localbitcoins_adapter.yml +381 -0
  37. data/spec/fixtures/adapters/normalized/bitfinex_adapter.yml +2 -0
  38. data/spec/fixtures/adapters/normalized/bitpay_adapter.yml +161 -0
  39. data/spec/fixtures/adapters/normalized/bitstamp_adapter.yml +3 -0
  40. data/spec/fixtures/adapters/normalized/btc_china_adapter.yml +1 -0
  41. data/spec/fixtures/adapters/normalized/btce_adapter.yml +5 -0
  42. data/spec/fixtures/adapters/normalized/coinbase_adapter.yml +680 -0
  43. data/spec/fixtures/adapters/normalized/fixer_adapter.yml +32 -0
  44. data/spec/fixtures/adapters/normalized/forge_adapter.yml +16 -0
  45. data/spec/fixtures/adapters/normalized/huobi_adapter.yml +2 -0
  46. data/spec/fixtures/adapters/normalized/kraken_adapter.yml +4 -0
  47. data/spec/fixtures/adapters/normalized/localbitcoins_adapter.yml +57 -0
  48. data/spec/fixtures/adapters/normalized/okcoin_adapter.yml +4 -0
  49. data/spec/fixtures/adapters/normalized/yahoo_adapter.yml +160 -0
  50. data/spec/fixtures/adapters/okcoin_adapter.yml +40 -0
  51. data/spec/fixtures/adapters/yahoo_adapter.yml +1119 -0
  52. data/spec/lib/adapter_spec.rb +54 -0
  53. data/spec/lib/adapters/crypto/bitfinex_adapter_spec.rb +13 -0
  54. data/spec/lib/adapters/crypto/bitpay_adapter_spec.rb +13 -0
  55. data/spec/lib/adapters/crypto/bitstamp_adapter_spec.rb +13 -0
  56. data/spec/lib/adapters/crypto/btc_china_adapter_spec.rb +13 -0
  57. data/spec/lib/adapters/crypto/btc_e_adapter_spec.rb +13 -0
  58. data/spec/lib/adapters/crypto/coinbase_adapter_spec.rb +13 -0
  59. data/spec/lib/adapters/crypto/huobi_adapter_spec.rb +13 -0
  60. data/spec/lib/adapters/crypto/kraken_adapter_spec.rb +13 -0
  61. data/spec/lib/adapters/crypto/localbitcoins_adapter_spec.rb +13 -0
  62. data/spec/lib/adapters/crypto/okcoin_adapter_spec.rb +13 -0
  63. data/spec/lib/adapters/fiat/fixer_adapter_spec.rb +13 -0
  64. data/spec/lib/adapters/fiat/forge_adapter_spec.rb +23 -0
  65. data/spec/lib/adapters/fiat/yahoo_adapter_spec.rb +13 -0
  66. data/spec/lib/currency_rate_spec.rb +9 -0
  67. data/spec/lib/fetcher_spec.rb +156 -0
  68. data/spec/lib/storage/file_storage_spec.rb +38 -0
  69. data/spec/lib/synchronizer_spec.rb +65 -0
  70. data/spec/spec_helper.rb +23 -8
  71. data/spec/support/matchers/eq_any_of.rb +3 -0
  72. metadata +67 -64
  73. data/Gemfile.lock +0 -93
  74. data/lib/core_ext/deep_get.rb +0 -11
  75. data/lib/crypto_adapter.rb +0 -19
  76. data/lib/crypto_adapters/average_rate_adapter.rb +0 -54
  77. data/lib/crypto_adapters/bitfinex_adapter.rb +0 -40
  78. data/lib/crypto_adapters/bitpay_adapter.rb +0 -33
  79. data/lib/crypto_adapters/bitstamp_adapter.rb +0 -44
  80. data/lib/crypto_adapters/btcchina_adapter.rb +0 -19
  81. data/lib/crypto_adapters/btce_adapter.rb +0 -44
  82. data/lib/crypto_adapters/coinbase_adapter.rb +0 -23
  83. data/lib/crypto_adapters/huobi_adapter.rb +0 -40
  84. data/lib/crypto_adapters/kraken_adapter.rb +0 -42
  85. data/lib/crypto_adapters/localbitcoins_adapter.rb +0 -30
  86. data/lib/crypto_adapters/okcoin_adapter.rb +0 -43
  87. data/lib/fiat_adapter.rb +0 -17
  88. data/lib/fiat_adapters/fixer_adapter.rb +0 -38
  89. data/lib/fiat_adapters/yahoo_adapter.rb +0 -51
  90. data/lib/storage.rb +0 -20
  91. data/spec/currency_rate_spec.rb +0 -52
  92. data/spec/fixtures/vcr/exchange_rate_adapters/btc_adapters/average_rate_adapter.yml +0 -567
  93. data/spec/fixtures/vcr/exchange_rate_adapters/btc_adapters/bitfinex_adapter.yml +0 -121
  94. data/spec/fixtures/vcr/exchange_rate_adapters/btc_adapters/bitpay_adapter.yml +0 -272
  95. data/spec/fixtures/vcr/exchange_rate_adapters/btc_adapters/btcchina_adapter.yml +0 -55
  96. data/spec/fixtures/vcr/exchange_rate_adapters/btc_adapters/btce_adapter.yml +0 -223
  97. data/spec/fixtures/vcr/exchange_rate_adapters/btc_adapters/coinbase_adapter.yml +0 -96
  98. data/spec/fixtures/vcr/exchange_rate_adapters/btc_adapters/huobi_adapter.yml +0 -83
  99. data/spec/fixtures/vcr/exchange_rate_adapters/btc_adapters/kraken_adapter.yml +0 -179
  100. data/spec/fixtures/vcr/exchange_rate_adapters/btc_adapters/localbitcoins_adapter.yml +0 -133
  101. data/spec/fixtures/vcr/exchange_rate_adapters/btc_adapters/okcoin_adapter.yml +0 -223
  102. data/spec/fixtures/vcr/exchange_rate_adapters/fiat_adapters/fixer_adapter.yml +0 -85
  103. data/spec/fixtures/vcr/exchange_rate_adapters/fiat_adapters/yahoo_adapter.yml +0 -48
  104. data/spec/lib/crypto_adapter_spec.rb +0 -56
  105. data/spec/lib/crypto_adapters/average_rate_adapter_spec.rb +0 -46
  106. data/spec/lib/crypto_adapters/bitfinex_adapter_spec.rb +0 -38
  107. data/spec/lib/crypto_adapters/bitpay_adapter_spec.rb +0 -40
  108. data/spec/lib/crypto_adapters/bitstamp_adapter_spec.rb +0 -44
  109. data/spec/lib/crypto_adapters/btcchina_adapter_spec.rb +0 -36
  110. data/spec/lib/crypto_adapters/btce_adapter_spec.rb +0 -42
  111. data/spec/lib/crypto_adapters/coinbase_adapter_spec.rb +0 -39
  112. data/spec/lib/crypto_adapters/huobi_adapter_spec.rb +0 -38
  113. data/spec/lib/crypto_adapters/kraken_adapter_spec.rb +0 -38
  114. data/spec/lib/crypto_adapters/localbitcoins_adapter_spec.rb +0 -36
  115. data/spec/lib/crypto_adapters/okcoin_adapter_spec.rb +0 -43
  116. data/spec/lib/fiat_adapters/fixer_adapter_spec.rb +0 -25
  117. data/spec/lib/fiat_adapters/yahoo_adapter_spec.rb +0 -25
  118. data/spec/lib/storage_spec.rb +0 -32
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ EXCHANGE_STUB_DOMAIN = "http://exchange.stub.com"
4
+
5
+ RESPONSE = {
6
+ "btc_usd" => 5000,
7
+ "ltc_usd" => 50,
8
+ }
9
+
10
+ class HashUrlAdapter < CurrencyRate::Adapter
11
+ FETCH_URL = {
12
+ "btc_usd" => "#{EXCHANGE_STUB_DOMAIN}/btc_usd",
13
+ "ltc_usd" => "#{EXCHANGE_STUB_DOMAIN}/ltc_usd",
14
+ }
15
+ end
16
+
17
+ class StringUrlAdapter < CurrencyRate::Adapter
18
+ FETCH_URL = EXCHANGE_STUB_DOMAIN
19
+ end
20
+
21
+ RSpec.describe CurrencyRate::Adapter do
22
+ describe "#exchange_data" do
23
+ context "when FETCH_URL is not defined" do
24
+ before { @adapter = CurrencyRate::Adapter.instance }
25
+
26
+ it "raises an error" do
27
+ expect { @adapter.exchange_data }.to raise_error("FETCH_URL is not defined!")
28
+ end
29
+ end
30
+
31
+ context "when FETCH_URL is Hash" do
32
+ before do
33
+ @adapter = HashUrlAdapter.instance
34
+ @response_map = Hash[RESPONSE.map { |k, v| [k, { "price" => v } ] }]
35
+ RESPONSE.each { |k, v| stub_request(:get, "#{EXCHANGE_STUB_DOMAIN}/#{k}").to_return(body: { "price" => v }.to_json) }
36
+ end
37
+
38
+ it "fetches data for each defined pair" do
39
+ expect(@adapter.exchange_data).to eq(@response_map)
40
+ end
41
+ end
42
+
43
+ context "when FETCH_URL is String" do
44
+ before do
45
+ @adapter = StringUrlAdapter.instance
46
+ stub_request(:get, EXCHANGE_STUB_DOMAIN).to_return(body: RESPONSE.to_json)
47
+ end
48
+
49
+ it "fetches all data from exchange and retruns parsed Hash" do
50
+ expect(@adapter.exchange_data).to eq(RESPONSE)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,13 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe CurrencyRate::BitfinexAdapter do
4
+ before(:all) { @data, @normalized = data_for :bitfinex }
5
+
6
+ before { @adapter = CurrencyRate::BitfinexAdapter.instance }
7
+
8
+ describe "#normalize" do
9
+ it "brings data to canonical form" do
10
+ expect(@adapter.normalize(@data)).to eq(@normalized)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe CurrencyRate::BitpayAdapter do
4
+ before(:all) { @data, @normalized = data_for :bitpay }
5
+
6
+ before { @adapter = CurrencyRate::BitpayAdapter.instance }
7
+
8
+ describe "#normalize" do
9
+ it "brings data to cannonical form" do
10
+ expect(@adapter.normalize(@data)).to eq(@normalized)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe CurrencyRate::BitstampAdapter do
4
+ before(:all) { @data, @normalized = data_for :bitstamp }
5
+
6
+ before { @adapter = CurrencyRate::BitstampAdapter.instance }
7
+
8
+ describe "#normalize" do
9
+ it "brings data to cannonical form" do
10
+ expect(@adapter.normalize(@data)).to eq(@normalized)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe CurrencyRate::BtcChinaAdapter do
4
+ before(:all) { @data, @normalized = data_for :btc_china }
5
+
6
+ before { @adapter = CurrencyRate::BtcChinaAdapter.instance }
7
+
8
+ describe "#normalize" do
9
+ it "brings data to canonical form" do
10
+ expect(@adapter.normalize(@data)).to eq(@normalized)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe CurrencyRate::BtcEAdapter do
4
+ before(:all) { @data, @normalized = data_for :btce }
5
+
6
+ before { @adapter = CurrencyRate::BtcEAdapter.instance }
7
+
8
+ describe "#normalize" do
9
+ it "brings data to canonical form" do
10
+ expect(@adapter.normalize(@data)).to eq(@normalized)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe CurrencyRate::CoinbaseAdapter do
4
+ before(:all) { @data, @normalized = data_for :coinbase }
5
+
6
+ before { @adapter = CurrencyRate::CoinbaseAdapter.instance }
7
+
8
+ describe "#normalize" do
9
+ it "brings data to canonical form" do
10
+ expect(@adapter.normalize(@data)).to eq(@normalized)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe CurrencyRate::HuobiAdapter do
4
+ before(:all) { @data, @normalized = data_for :huobi }
5
+
6
+ before { @adapter = CurrencyRate::HuobiAdapter.instance }
7
+
8
+ describe "#normalize" do
9
+ it "brings data to canonical form" do
10
+ expect(@adapter.normalize(@data)).to eq(@normalized)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe CurrencyRate::KrakenAdapter do
4
+ before(:all) { @data, @normalized = data_for :kraken }
5
+
6
+ before { @adapter = CurrencyRate::KrakenAdapter.instance }
7
+
8
+ describe "#normalize" do
9
+ it "brings data to canonical form" do
10
+ expect(@adapter.normalize(@data)).to eq(@normalized)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe CurrencyRate::LocalbitcoinsAdapter do
4
+ before(:all) { @data, @normalized = data_for :localbitcoins }
5
+
6
+ before { @adapter = CurrencyRate::LocalbitcoinsAdapter.instance }
7
+
8
+ describe "#normalize" do
9
+ it "brings data to canonical form" do
10
+ expect(@adapter.normalize(@data)).to eq(@normalized)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe CurrencyRate::OkcoinAdapter do
4
+ before(:all) { @data, @normalized = data_for :okcoin }
5
+
6
+ before { @adapter = CurrencyRate::OkcoinAdapter.instance }
7
+
8
+ describe "#normalize" do
9
+ it "brings data to canonical form" do
10
+ expect(@adapter.normalize(@data)).to eq(@normalized)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe CurrencyRate::FixerAdapter do
4
+ before(:all) { @data, @normalized = data_for :fixer }
5
+
6
+ before { @adapter = CurrencyRate::FixerAdapter.instance }
7
+
8
+ describe "#normalize" do
9
+ it "brings data to canonical form" do
10
+ expect(@adapter.normalize(@data)).to eq(@normalized)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe CurrencyRate::ForgeAdapter do
4
+ before(:all) { @data, @normalized = data_for :forge }
5
+
6
+ before { stub_request(:get, /1forge/).to_return(body: @data) }
7
+
8
+ before { @adapter = CurrencyRate::ForgeAdapter.instance }
9
+
10
+ describe "#normalize" do
11
+ it "brings data to canonical form" do
12
+ expect(@adapter.normalize(@data)).to eq(@normalized)
13
+ end
14
+ end
15
+
16
+ describe "#exchange_data" do
17
+ context "when api_key not defined" do
18
+ it "returns nil" do
19
+ expect(@adapter.exchange_data).to be_nil
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe CurrencyRate::YahooAdapter do
4
+ before(:all) { @data, @normalized = data_for :yahoo }
5
+
6
+ before { @adapter = CurrencyRate::YahooAdapter.instance }
7
+
8
+ describe "#normalize" do
9
+ it "brings data to canonical form" do
10
+ expect(@adapter.normalize(@data)).to eq(@normalized)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe CurrencyRate do
4
+ it { is_expected.to respond_to(:configure) }
5
+ it { is_expected.to respond_to(:sync!) }
6
+ it { is_expected.to respond_to(:fetch_crypto) }
7
+ it { is_expected.to respond_to(:fetch_fiat) }
8
+ it { is_expected.to respond_to(:logger) }
9
+ end
@@ -0,0 +1,156 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe CurrencyRate::Fetcher do
4
+ before do
5
+ @usd_try = BigDecimal.new("22")
6
+ @usd_eur = BigDecimal.new("0.8457")
7
+ @eur_try = @usd_eur / @usd_try
8
+ @storage_double = double("storage")
9
+ @fetcher = CurrencyRate::Fetcher.new(storage: @storage_double)
10
+ end
11
+
12
+ it "uses FileStorage by default" do
13
+ fetcher = CurrencyRate::Fetcher.new
14
+ expect(fetcher.storage).to be_a(CurrencyRate::FileStorage)
15
+ end
16
+
17
+ describe "#fetch_crypto" do
18
+ before do
19
+ @from = "BTC"
20
+ @to = "TRY"
21
+ @btc_try = BigDecimal.new("3832.5432")
22
+ @exchange = "Bitstamp"
23
+ end
24
+
25
+ subject { @fetcher.fetch_crypto(@exchange, @from, @to) }
26
+
27
+ context "when rates for selected exchange are not available" do
28
+ before { allow(@storage_double).to receive(:read).with(@exchange).and_return(nil) }
29
+
30
+ it { is_expected.to be_nil }
31
+ end
32
+
33
+ context "when pair supported by selected exchange" do
34
+ before do
35
+ allow(@storage_double).to receive(:read).with(@exchange).and_return({ "BTC_TRY" => @btc_try })
36
+ end
37
+
38
+ it { is_expected.to eq(@btc_try) }
39
+
40
+ it { is_expected.to be_a(BigDecimal) }
41
+ end
42
+
43
+ context "when requested cryptocurrency not supported by selected exchange" do
44
+ before do
45
+ allow(@storage_double).to receive(:read).with(@exchange).and_return({ "LTC_TRY" => @btc_try })
46
+ end
47
+
48
+ it { is_expected.to be_nil }
49
+ end
50
+
51
+ context "when requested fiat currency not supported by selected exchange" do
52
+ context "when selected exchange supports USD" do
53
+ before do
54
+ @btc_usd = @btc_try
55
+ allow(@storage_double).to receive(:read).with(@exchange).and_return({ "BTC_USD" => @btc_usd })
56
+ end
57
+
58
+ context "when any of fiat exchanges supports rate from USD to requested fiat currency" do
59
+ before do
60
+ allow(@fetcher).to receive(:fetch_fiat).with("USD", "TRY").and_return(@usd_try)
61
+ end
62
+
63
+ it { is_expected.to eq(@btc_usd * @usd_try) }
64
+ end
65
+ end
66
+
67
+ context "when when selected exchange doesn't support USD" do
68
+ before do
69
+ @btc_eur = @btc_try
70
+ allow(@storage_double).to receive(:read).with(@exchange).and_return({ "BTC_EUR" => @btc_eur })
71
+ end
72
+
73
+ context "when any of fiat exchanges supports rate for one of supported by selected exchange currency" do
74
+ before do
75
+ allow(@fetcher).to receive(:fetch_fiat).with("EUR", "TRY").and_return(@eur_try)
76
+ end
77
+
78
+ it { is_expected.to eq(@btc_eur * @eur_try) }
79
+ end
80
+
81
+ context "when none of fiat exchanges support rate for one of supported by selected exchange currency" do
82
+ before do
83
+ allow(@fetcher).to receive(:fetch_fiat).with("EUR", "TRY").and_return(nil)
84
+ end
85
+
86
+ it { is_expected.to be_nil }
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ describe "#fetch_fiat" do
93
+ before do
94
+ @from = "EUR"
95
+ @to = "TRY"
96
+ @fiat_exchanges = ["Yahoo", "Fixer", "Forge"]
97
+ end
98
+
99
+ subject { @fetcher.fetch_fiat(@from, @to) }
100
+
101
+ it "uses Yahoo -> Fixer -> Forge priority order" do
102
+ expect(@storage_double).to receive(:read).with("Yahoo").and_return(nil).ordered
103
+ expect(@storage_double).to receive(:read).with("Fixer").and_return(nil).ordered
104
+ expect(@storage_double).to receive(:read).with("Forge").and_return(nil).ordered
105
+ @fetcher.fetch_fiat("EUR", "TRY")
106
+ end
107
+
108
+ it "uses next exchange if rates for current do not exist" do
109
+ allow(@storage_double).to receive(:read).with(@fiat_exchanges.first).and_return(nil)
110
+ expect(@storage_double).to receive(:read).with(@fiat_exchanges[1]).and_return(nil)
111
+ expect(@storage_double).to receive(:read).with(@fiat_exchanges[2]).and_return(nil)
112
+ @fetcher.fetch_fiat("EUR", "TRY")
113
+ end
114
+
115
+ context 'when first exchange has "from" currency as an anchor' do
116
+ before do
117
+ allow(@storage_double).to receive(:read).with(@fiat_exchanges.first).and_return({ "anchor" => "EUR", "TRY" => @eur_try })
118
+ end
119
+
120
+ it { is_expected.to eq(@eur_try) }
121
+ end
122
+
123
+ context 'when first exchange has "to" currency as an anchor' do
124
+ before do
125
+ allow(@storage_double).to receive(:read).with(@fiat_exchanges.first).and_return({ "anchor" => "TRY", "EUR" => (BigDecimal.new(1) / @eur_try).round(16) })
126
+ end
127
+
128
+ it "returns reversed to anchor rate" do
129
+ expect(subject.round(16)).to eq(@eur_try.round(16))
130
+ end
131
+ end
132
+
133
+ context "when first exchange has different from requested anchor currency" do
134
+ context "when first exchange has rates for both requested currencies" do
135
+ before do
136
+ allow(@storage_double).to receive(:read).with(@fiat_exchanges.first).and_return({
137
+ "anchor" => "USD",
138
+ "EUR" => @usd_eur,
139
+ "TRY" => @usd_try,
140
+ })
141
+ end
142
+
143
+ it { is_expected.to eq(@eur_try) }
144
+ end
145
+
146
+ context "when first exchange doen't have rates for requested currencies" do
147
+ before do
148
+ allow(@storage_double).to receive(:read).with(@fiat_exchanges.first).and_return(nil)
149
+ expect(@storage_double).to receive(:read).with(@fiat_exchanges[1]).and_return({ "anchor" => "EUR", "TRY" => @eur_try })
150
+ end
151
+
152
+ it { is_expected.to eq(@eur_try) }
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,38 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe CurrencyRate::FileStorage do
4
+ before do
5
+ @data = { "key" => "value" }
6
+ @serializer = CurrencyRate::Storage::YAMLSerializer.new
7
+ @serialized_data = @serializer.serialize(@data)
8
+ @storage = CurrencyRate::FileStorage.new(serializer: @serializer)
9
+ end
10
+
11
+ describe "#read" do
12
+ before do
13
+ allow(File).to receive(:read).and_return(@serialized_data)
14
+ allow(File).to receive(:exist?).and_return(true)
15
+ end
16
+
17
+ it "returns deserialized data from file" do
18
+ expect(@storage.read("exchange")).to eq(@data)
19
+ end
20
+
21
+ context "when exchange file doesn't exist" do
22
+ before do
23
+ allow(File).to receive(:read).and_raise(StandardError)
24
+ end
25
+
26
+ it "returns nil" do
27
+ expect(@storage.read("exchange")).to be_nil
28
+ end
29
+ end
30
+ end
31
+
32
+ describe "#write" do
33
+ it "writes serialized data to file" do
34
+ expect(File).to receive(:write).with(kind_of(String), @serialized_data)
35
+ @storage.write("exchange", @data)
36
+ end
37
+ end
38
+ end