Change `Market.fetch_exchange_rate/1` and supporting code to be testable

Replace default test configuration for `ExchangeRates` to use a no-op
Source and bypass :ets. This allows higher level feature tests to
run without having to manage GenServer processes when they don’t need
Exchange rate information but allows the lower tests to exercise the
specific behavior as before. Add `Token.null`.


Co-authored-by: jimmay5469 <jimmay5469@gmail.com>
pull/173/head
Tim Mecklem 7 years ago
parent a773062b3a
commit f7d67b7a86
  1. 22
      apps/explorer/lib/explorer/exchange_rates/exchange_rates.ex
  2. 2
      apps/explorer/lib/explorer/exchange_rates/token.ex
  3. 5
      apps/explorer/lib/explorer/market/market.ex
  4. 10
      apps/explorer/test/explorer/exchange_rates/exchange_rates_test.exs
  5. 17
      apps/explorer/test/explorer/market/market_test.exs
  6. 10
      apps/explorer/test/support/fakes/no_op_source.ex
  7. 7
      apps/explorer_web/lib/explorer_web/controllers/chain_controller.ex
  8. 8
      apps/explorer_web/lib/explorer_web/views/chain_view.ex
  9. 6
      apps/explorer_web/test/explorer_web/controllers/chain_controller_test.exs
  10. 4
      config/test.exs
  11. 2
      coveralls.json

@ -33,7 +33,7 @@ defmodule Explorer.ExchangeRates do
{symbol, token}
end
:ets.insert(table_name(), records)
if store() == :ets, do: :ets.insert(table_name(), records)
{:noreply, state}
end
@ -67,7 +67,7 @@ defmodule Explorer.ExchangeRates do
write_concurrency: true
]
:ets.new(table_name(), table_opts)
if store() == :ets, do: :ets.new(table_name(), table_opts)
{:ok, %{}}
end
@ -83,10 +83,7 @@ defmodule Explorer.ExchangeRates do
"""
@spec list :: [Token.t()]
def list do
table_name()
|> :ets.tab2list()
|> Enum.map(fn {_, rate} -> rate end)
|> Enum.sort_by(fn %Token{symbol: symbol} -> symbol end)
list_from_store(store())
end
## Undocumented public functions
@ -113,4 +110,17 @@ defmodule Explorer.ExchangeRates do
exchange_rates_source().fetch_exchange_rates()
end)
end
defp list_from_store(:ets) do
table_name()
|> :ets.tab2list()
|> Enum.map(fn {_, rate} -> rate end)
|> Enum.sort_by(fn %Token{symbol: symbol} -> symbol end)
end
defp list_from_store(_), do: []
defp store do
config(:store) || :ets
end
end

@ -29,4 +29,6 @@ defmodule Explorer.ExchangeRates.Token do
}
defstruct ~w(available_supply btc_value id last_updated market_cap_usd name symbol usd_value volume_24h_usd)a
def null, do: %__MODULE__{}
end

@ -5,10 +5,9 @@ defmodule Explorer.Market do
import Ecto.Query
alias Explorer.ExchangeRates
alias Explorer.{ExchangeRates, Repo}
alias Explorer.ExchangeRates.Token
alias Explorer.Market.MarketHistory
alias Explorer.Repo
@doc """
Get most recent exchange rate for the given symbol.
@ -16,7 +15,7 @@ defmodule Explorer.Market do
@spec fetch_exchange_rate(String.t()) :: Token.t()
def fetch_exchange_rate(symbol) do
ExchangeRates.list()
|> Enum.find(fn(token) -> token.symbol == symbol end)
|> Enum.find(fn token -> token.symbol == symbol end)
end
@doc """

@ -11,6 +11,16 @@ defmodule Explorer.ExchangeRatesTest do
setup :verify_on_exit!
setup do
# Use TestSource mock and ets table for this test set
configuration = Application.get_env(:explorer, Explorer.ExchangeRates)
Application.put_env(:explorer, Explorer.ExchangeRates, source: TestSource)
on_exit(fn ->
Application.put_env(:explorer, Explorer.ExchangeRates, configuration)
end)
end
test "start_link" do
stub(TestSource, :fetch_exchange_rates, fn -> {:ok, [%Token{}]} end)
set_mox_global()

@ -9,6 +9,8 @@ defmodule Explorer.MarketTest do
describe "fetch_exchange_rate/1" do
setup do
use_ets_store()
{:ok, _} = ExchangeRates.start_link([])
rate = %Token{id: "POA", symbol: "POA"}
:ets.insert(ExchangeRates.table_name(), {rate.id, rate})
@ -103,4 +105,19 @@ defmodule Explorer.MarketTest do
assert fetched_record.opening_price == new_record.opening_price
end
end
defp use_ets_store do
# Use ets tables as ExchangeRates store and put some test data in to
# exercise Context filtering
exchange_config = Application.get_env(:explorer, Explorer.ExchangeRates)
Application.put_env(
:explorer,
Explorer.ExchangeRates,
Keyword.put(exchange_config, :store, :ets)
)
on_exit(fn ->
Application.put_env(:explorer, Explorer.ExchangeRates, exchange_config)
end)
end
end

@ -0,0 +1,10 @@
defmodule Explorer.ExchangeRates.Source.NoOpSource do
@moduledoc false
alias Explorer.ExchangeRates.Source
@behaviour Source
@impl Source
def fetch_exchange_rates, do: {:ok, []}
end

@ -2,6 +2,7 @@ defmodule ExplorerWeb.ChainController do
use ExplorerWeb, :controller
alias Explorer.Chain.{Address, Block, Statistics, Transaction}
alias Explorer.ExchangeRates.Token
alias Explorer.Market
alias ExplorerWeb.Chain
@ -11,7 +12,7 @@ defmodule ExplorerWeb.ChainController do
"show.html",
chain: Statistics.fetch(),
market_history_data: Market.fetch_recent_history(30),
exchange_rate: Market.fetch_exchange_rate(Application.get_env(:explorer, :coin))
exchange_rate: Market.fetch_exchange_rate(coin()) || Token.null()
)
end
@ -28,6 +29,10 @@ defmodule ExplorerWeb.ChainController do
end
end
defp coin do
Application.get_env(:explorer, :coin)
end
defp redirect_search_results(conn, %Address{} = item) do
redirect(conn, to: address_path(conn, :show, Gettext.get_locale(), item.hash))
end

@ -3,11 +3,11 @@ defmodule ExplorerWeb.ChainView do
def encode_market_history_data(market_history_data) do
market_history_data
|> Enum.map(fn(day) -> Map.take(day, [:closing_price, :date]) end)
|> Enum.map(fn day -> Map.take(day, [:closing_price, :date]) end)
|> Jason.encode()
|> case do
{:ok, data} -> data
_ -> []
end
{:ok, data} -> data
_ -> []
end
end
end

@ -60,13 +60,13 @@ defmodule ExplorerWeb.ChainControllerTest do
end
test "returns market history data", %{conn: conn} do
today = Date.utc_today
for day <- -14..0, do: insert(:market_history, date: Date.add(today, day))
today = Date.utc_today()
for day <- -40..0, do: insert(:market_history, date: Date.add(today, day))
conn = get(conn, "/en")
assert Map.has_key?(conn.assigns, :market_history_data)
assert length(conn.assigns.market_history_data) == 8
assert length(conn.assigns.market_history_data) == 30
end
end

@ -3,4 +3,6 @@ use Mix.Config
# Print only warnings and errors during test
config :logger, level: :warn
config :explorer, Explorer.ExchangeRates, source: Explorer.ExchangeRates.Source.TestSource
config :explorer, Explorer.ExchangeRates,
source: Explorer.ExchangeRates.Source.NoOpSource,
store: :none

@ -1,7 +1,7 @@
{
"coverage_options": {
"treat_no_relevant_lines_as_covered": true,
"minimum_coverage": 85
"minimum_coverage": 86
},
"terminal_options": {
"file_column_width": 120

Loading…
Cancel
Save