diff --git a/lib/ae_mdw/stats.ex b/lib/ae_mdw/stats.ex index 52b5d2723..9277f4e37 100644 --- a/lib/ae_mdw/stats.ex +++ b/lib/ae_mdw/stats.ex @@ -155,7 +155,8 @@ defmodule AeMdw.Stats do with {:ok, Model.stat(payload: {tps, tps_block_hash})} <- State.get(state, Model.Stat, @tps_stat_key), {:ok, Model.stat(payload: miners_count)} <- - State.get(state, Model.Stat, @miners_count_stat_key) do + State.get(state, Model.Stat, @miners_count_stat_key), + {:ok, minutes_per_block} <- minutes_per_block(state) do {{last_24hs_txs_count, trend}, {last_24hs_tx_fees_average, fees_trend}} = last_24hs_txs_count_and_fee_with_trend(state) @@ -167,7 +168,8 @@ defmodule AeMdw.Stats do last_24hs_transactions: last_24hs_txs_count, transactions_trend: trend, last_24hs_average_transaction_fees: last_24hs_tx_fees_average, - fees_trend: fees_trend + fees_trend: fees_trend, + minutes_per_block: minutes_per_block }} else :not_found -> @@ -674,4 +676,17 @@ defmodule AeMdw.Stats do 0 end end + + defp minutes_per_block(state) do + with {:ok, first_block} <- :aec_chain.get_key_block_by_height(1), + last_gen <- DbUtil.last_gen(state), + {:ok, last_block} <- :aec_chain.get_key_block_by_height(last_gen) do + first_block_time = :aec_blocks.time_in_msecs(first_block) + + last_block_time = + :aec_blocks.time_in_msecs(last_block) + + {:ok, div(last_block_time - first_block_time, last_gen)} + end + end end diff --git a/test/ae_mdw/stats_test.exs b/test/ae_mdw/stats_test.exs index cbdda1214..e50565619 100644 --- a/test/ae_mdw/stats_test.exs +++ b/test/ae_mdw/stats_test.exs @@ -1,12 +1,12 @@ defmodule AeMdw.StatsTest do use ExUnit.Case + import Mock alias AeMdw.Db.State alias AeMdw.Db.MemStore alias AeMdw.Db.Model alias AeMdw.Db.NullStore alias AeMdw.Error.Input - alias AeMdw.Db.Store alias AeMdw.Stats alias AeMdw.TestSamples, as: TS @@ -16,20 +16,38 @@ defmodule AeMdw.StatsTest do test "it displays the max tps and miners_count" do key_block_hash = TS.key_block_hash(0) enc_hash = :aeser_api_encoder.encode(:key_block_hash, key_block_hash) + now = :aeu_time.now_in_msecs() + three_minutes = 3 * 60 * 1000 state = NullStore.new() |> MemStore.new() - |> Store.put(Model.Stat, Model.stat(index: :max_tps, payload: {16.05, key_block_hash})) - |> Store.put(Model.Stat, Model.stat(index: :miners_count, payload: 20)) |> State.new() + |> State.put(Model.Stat, Model.stat(index: :max_tps, payload: {16.05, key_block_hash})) + |> State.put(Model.Stat, Model.stat(index: :miners_count, payload: 20)) + |> State.put(Model.Block, Model.block(index: {1, -1}, hash: <<1::256>>)) + |> State.put(Model.Block, Model.block(index: {10, -1}, hash: key_block_hash)) - assert {:ok, - %{ - max_transactions_per_second: 16.05, - max_transactions_per_second_block_hash: ^enc_hash, - miners_count: 20 - }} = Stats.fetch_stats(state) + with_mocks([ + {:aec_chain, [], + get_key_block_by_height: fn + 1 -> {:ok, :first_block} + _n -> {:ok, :other_block} + end}, + {:aec_blocks, [], + time_in_msecs: fn + :first_block -> now - 10 * three_minutes + :other_block -> now + end} + ]) do + assert {:ok, + %{ + max_transactions_per_second: 16.05, + max_transactions_per_second_block_hash: ^enc_hash, + miners_count: 20, + minutes_per_block: ^three_minutes + }} = Stats.fetch_stats(state) + end end test "when not stat found, it returns an error" do @@ -37,10 +55,25 @@ defmodule AeMdw.StatsTest do NullStore.new() |> MemStore.new() |> State.new() + |> State.put(Model.Block, Model.block(index: {1, -1}, hash: <<1::256>>)) + |> State.put(Model.Block, Model.block(index: {10, -1}, hash: <<10::256>>)) error_msg = "not found: no stats" - assert {:error, %Input{message: ^error_msg}} = Stats.fetch_stats(state) + with_mocks([ + {:aec_chain, [], + get_key_block_by_height: fn + 1 -> {:ok, :first_block} + _n -> {:ok, :other_block} + end}, + {:aec_blocks, [], + time_in_msecs: fn + :first_block -> 1 + :other_block -> 10 + end} + ]) do + assert {:error, %Input{message: ^error_msg}} = Stats.fetch_stats(state) + end end end end diff --git a/test/ae_mdw_web/controllers/stats_controller_test.exs b/test/ae_mdw_web/controllers/stats_controller_test.exs index 711ebcbd5..81344cf25 100644 --- a/test/ae_mdw_web/controllers/stats_controller_test.exs +++ b/test/ae_mdw_web/controllers/stats_controller_test.exs @@ -856,6 +856,7 @@ defmodule AeMdwWeb.StatsControllerTest do %{conn: conn, store: store} do now = :aeu_time.now_in_msecs() msecs_per_day = 3_600 * 24 * 1_000 + three_minutes = 3 * 60 * 1_000 delay = 500 last_24hs_start_txi = 8 @@ -871,21 +872,36 @@ defmodule AeMdwWeb.StatsControllerTest do |> Store.put(Model.Time, Model.time(index: {now - msecs_per_day * 2 + delay, 1})) |> Store.put(Model.Stat, Model.stat(index: :miners_count, payload: 2)) |> Store.put(Model.Stat, Model.stat(index: :max_tps, payload: {2, <<0::256>>})) + |> Store.put(Model.Block, Model.block(index: {1, -1}, hash: <<1::256>>)) + |> Store.put(Model.Block, Model.block(index: {10, -1}, hash: <<2::256>>)) txis = last_24hs_start_txi..last_txi fee_avg = Enum.sum(txis) / Enum.count(txis) - with_mocks([{AeMdw.Node.Db, [], get_tx_fee: fn <> -> i end}]) do + with_mocks([ + {AeMdw.Node.Db, [], get_tx_fee: fn <> -> i end}, + {:aec_chain, [], + get_key_block_by_height: fn + 1 -> {:ok, :first_block} + _n -> {:ok, :other_block} + end}, + {:aec_blocks, [], + time_in_msecs: fn + :first_block -> now - 10 * three_minutes + :other_block -> now + end} + ]) do assert %{ "last_24hs_transactions" => 13, "transactions_trend" => 0.46, "fees_trend" => 0.69, - "last_24hs_average_transaction_fees" => ^fee_avg + "last_24hs_average_transaction_fees" => ^fee_avg, + "minutes_per_block" => ^three_minutes } = conn |> with_store(store) - |> get("/v2/stats") + |> get("/v3/stats") |> json_response(200) end end