Improve paging query #200
pull/256/head
Tim Mecklem 6 years ago committed by GitHub
commit fa455e331d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 111
      apps/explorer/lib/explorer/chain.ex
  2. 7
      apps/explorer/lib/explorer/chain/transaction.ex
  3. 13
      apps/explorer/lib/explorer/paging_options.ex
  4. 11
      apps/explorer/test/explorer/chain_test.exs
  5. 7
      apps/explorer_web/lib/explorer_web/controller.ex
  6. 36
      apps/explorer_web/lib/explorer_web/controllers/transaction_controller.ex
  7. 10
      apps/explorer_web/lib/explorer_web/templates/address/_link.html.eex
  8. 4
      apps/explorer_web/lib/explorer_web/templates/address_internal_transaction/index.html.eex
  9. 4
      apps/explorer_web/lib/explorer_web/templates/address_transaction/index.html.eex
  10. 4
      apps/explorer_web/lib/explorer_web/templates/block_transaction/index.html.eex
  11. 2
      apps/explorer_web/lib/explorer_web/templates/chain/_blocks.html.eex
  12. 4
      apps/explorer_web/lib/explorer_web/templates/chain/_transactions.html.eex
  13. 4
      apps/explorer_web/lib/explorer_web/templates/pending_transaction/index.html.eex
  14. 28
      apps/explorer_web/lib/explorer_web/templates/transaction/index.html.eex
  15. 4
      apps/explorer_web/lib/explorer_web/templates/transaction_internal_transaction/index.html.eex
  16. 3
      apps/explorer_web/lib/explorer_web/views/address_view.ex
  17. 4
      apps/explorer_web/lib/explorer_web/views/error_view.ex
  18. 9
      apps/explorer_web/priv/gettext/default.pot
  19. 9
      apps/explorer_web/priv/gettext/en/LC_MESSAGES/default.po
  20. 57
      apps/explorer_web/test/explorer_web/controllers/transaction_controller_test.exs
  21. 4
      apps/explorer_web/test/explorer_web/views/error_view_test.exs

@ -3,7 +3,8 @@ defmodule Explorer.Chain do
The chain context.
"""
import Ecto.Query, only: [from: 2, join: 4, or_where: 3, order_by: 2, order_by: 3, preload: 2, where: 2, where: 3]
import Ecto.Query,
only: [from: 2, join: 4, limit: 2, or_where: 3, order_by: 2, order_by: 3, preload: 2, where: 2, where: 3]
alias Ecto.{Changeset, Multi}
@ -19,7 +20,7 @@ defmodule Explorer.Chain do
}
alias Explorer.Chain.Block.Reward
alias Explorer.Repo
alias Explorer.{PagingOptions, Repo}
@typedoc """
The name of an association on the `t:Ecto.Schema.t/0`
@ -45,11 +46,11 @@ defmodule Explorer.Chain do
"""
@type pagination :: map()
@typep after_hash_option :: {:after_hash, Hash.t()}
@typep direction_option :: {:direction, direction}
@typep inserted_after_option :: {:inserted_after, DateTime.t()}
@typep necessity_by_association_option :: {:necessity_by_association, necessity_by_association}
@typep pagination_option :: {:pagination, pagination}
@typep paging_options :: {:paging_options, PagingOptions.t()}
@typep params_option :: {:params, map()}
@typep timeout_option :: {:timeout, timeout}
@typep timestamps :: %{inserted_at: DateTime.t(), updated_at: DateTime.t()}
@ -1301,80 +1302,40 @@ defmodule Explorer.Chain do
end
@doc """
Returns the list of collated transactions that occurred recently (10).
Returns the paged list of collated transactions that occurred recently from newest to oldest using `block_number`
and `index`.
iex> 2 |> insert_list(:transaction) |> with_block()
iex> insert(:transaction) # unvalidated transaction
iex> 8 |> insert_list(:transaction) |> with_block()
iex> recent_collated_transactions = Explorer.Chain.recent_collated_transactions()
iex> newest_first_transactions = 50 |> insert_list(:transaction) |> with_block() |> Enum.reverse()
iex> oldest_seen = Enum.at(newest_first_transactions, 9)
iex> paging_options = %Explorer.PagingOptions{page_size: 10, key: {oldest_seen.block_number, oldest_seen.index}}
iex> recent_collated_transactions = Explorer.Chain.recent_collated_transactions(paging_options: paging_options)
iex> length(recent_collated_transactions)
10
iex> Enum.all?(recent_collated_transactions, fn %Explorer.Chain.Transaction{block_hash: block_hash} ->
...> !is_nil(block_hash)
...> end)
iex> hd(recent_collated_transactions).hash == Enum.at(newest_first_transactions, 10).hash
true
A `t:Explorer.Chain.Transaction.t/0` `hash` can be supplied to the `:after_hash` option, then only transactions in
after the transaction (with a greater index) in the same block or in a later block (with a greater number) will be
returned. This can be used to generate paging for collated transaction.
iex> first_block = insert(:block, number: 1)
iex> first_transaction_in_first_block = :transaction |> insert() |> with_block(first_block)
iex> second_transaction_in_first_block = :transaction |> insert() |> with_block(first_block)
iex> second_block = insert(:block, number: 2)
iex> first_transaction_in_second_block = :transaction |> insert() |> with_block(second_block)
iex> after_first_transaciton_in_first_block = Explorer.Chain.recent_collated_transactions(
...> after_hash: first_transaction_in_first_block.hash
...> )
iex> length(after_first_transaciton_in_first_block)
2
iex> after_second_transaciton_in_first_block = Explorer.Chain.recent_collated_transactions(
...> after_hash: second_transaction_in_first_block.hash
...> )
iex> length(after_second_transaciton_in_first_block)
1
iex> after_first_transaciton_in_second_block = Explorer.Chain.recent_collated_transactions(
...> after_hash: first_transaction_in_second_block.hash
...> )
iex> length(after_first_transaciton_in_second_block)
0
When there are no collated transactions, an empty list is returned.
iex> insert(:transaction)
iex> Explorer.Chain.recent_collated_transactions()
[]
Using an unvalidated transaction's hash for `:after_hash` will also yield an empty list.
iex> %Explorer.Chain.Transaction{hash: hash} = insert(:transaction)
iex> insert(:transaction)
iex> Explorer.Chain.recent_collated_transactions(after_hash: hash)
[]
## Options
* `:necessity_by_association` - use to load `t:association/0` as `:required` or `:optional`. If an association is
`:required`, and the `t:Explorer.Chain.InternalTransaction.t/0` has no associated record for that association,
then the `t:Explorer.Chain.InternalTransaction.t/0` will not be included in the list.
* `:paging_options` - a `t:Explorer.PagingOptions.t/0` used to specify the `:page_size` and
`:key` (a tuple of the lowest/oldest {block_number, index}) and. Results will be the transactions older than
the block number and index that are passed.
"""
@spec recent_collated_transactions([after_hash_option | necessity_by_association_option]) :: [
@spec recent_collated_transactions([paging_options | necessity_by_association_option]) :: [
Transaction.t()
]
def recent_collated_transactions(options \\ []) when is_list(options) do
necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
paging_options = Keyword.get(options, :paging_options, %PagingOptions{page_size: 50})
query =
from(
transaction in Transaction,
where: not is_nil(transaction.block_number) and not is_nil(transaction.index),
order_by: [desc: transaction.block_number, desc: transaction.index],
limit: 10
)
query
|> after_hash(options)
Transaction
|> where([transaction], not is_nil(transaction.block_number) and not is_nil(transaction.index))
|> page_transaction(paging_options)
|> limit(^paging_options.page_size)
|> order_by([transaction], desc: transaction.block_number, desc: transaction.index)
|> join_associations(necessity_by_association)
|> Repo.all()
end
@ -1674,25 +1635,6 @@ defmodule Explorer.Chain do
|> Repo.paginate(pagination)
end
defp after_hash(query, options) do
case Keyword.fetch(options, :after_hash) do
{:ok, hash} ->
from(
transaction in query,
inner_join: block in assoc(transaction, :block),
join: hash_transaction in Transaction,
on: hash_transaction.hash == ^hash,
inner_join: hash_block in assoc(hash_transaction, :block),
where:
block.number > hash_block.number or
(block.number == hash_block.number and transaction.index > hash_transaction.index)
)
:error ->
query
end
end
@spec changes_list(params :: map, [{:for, module}]) :: {:ok, changes :: map} | {:error, [Changeset.t()]}
defp changes_list(params, named_arguments) when is_list(named_arguments) do
ecto_schema_module = Keyword.fetch!(named_arguments, :for)
@ -1953,6 +1895,17 @@ defmodule Explorer.Chain do
end)
end
defp page_transaction(query, %PagingOptions{key: nil}), do: query
defp page_transaction(query, %PagingOptions{key: {block_number, index}}) do
query
|> where(
[transaction],
transaction.block_number < ^block_number or
(transaction.block_number == ^block_number and transaction.index < ^index)
)
end
defp reverse_chronologically(query) do
from(q in query, order_by: [desc: q.inserted_at, desc: q.hash])
end

@ -52,6 +52,11 @@ defmodule Explorer.Chain.Transaction do
"""
@type standard_v :: 0..3
@typedoc """
The index of the transaction in its block.
"""
@type transaction_index :: non_neg_integer()
@typedoc """
`t:standard_v/0` + `27`
@ -114,7 +119,7 @@ defmodule Explorer.Chain.Transaction do
gas_price: wei_per_gas,
gas_used: Gas.t() | nil,
hash: Hash.t(),
index: non_neg_integer() | nil,
index: transaction_index | nil,
input: Data.t(),
internal_transactions: %Ecto.Association.NotLoaded{} | [InternalTransaction.t()],
internal_transactions_indexed_at: DateTime.t(),

@ -0,0 +1,13 @@
defmodule Explorer.PagingOptions do
@moduledoc """
Defines paging options for paging by a stable key such as a timestamp or block
number and index.
"""
@type t :: %__MODULE__{key: key, page_size: page_size}
@typep key :: any()
@typep page_size :: non_neg_integer()
defstruct [:key, :page_size]
end

@ -861,4 +861,15 @@ defmodule Explorer.ChainTest do
assert block_reward.reward == Chain.block_reward(block)
end
end
describe "recent_collated_transactions/1" do
test "with no collated transactions it returns an empty list" do
assert [] == Explorer.Chain.recent_collated_transactions()
end
test "it excludes pending transactions" do
insert(:transaction)
assert [] == Explorer.Chain.recent_collated_transactions()
end
end
end

@ -15,4 +15,11 @@ defmodule ExplorerWeb.Controller do
|> put_view(ExplorerWeb.ErrorView)
|> render("404.html")
end
def unprocessable_entity(conn) do
conn
|> put_status(:unprocessable_entity)
|> put_view(ExplorerWeb.ErrorView)
|> render("422.html")
end
end

@ -1,17 +1,24 @@
defmodule ExplorerWeb.TransactionController do
use ExplorerWeb, :controller
alias Explorer.Chain
alias Explorer.{Chain, PagingOptions}
def index(conn, params) do
with %{"last_seen_collated_hash" => last_seen_collated_hash_string} <- params,
{:ok, last_seen_collated_hash} <- Chain.string_to_transaction_hash(last_seen_collated_hash_string) do
do_index(conn, after_hash: last_seen_collated_hash)
@default_paging_options %PagingOptions{page_size: 50}
def index(conn, %{"block_number" => block_number_string, "index" => index_string}) do
with {block_number, ""} <- Integer.parse(block_number_string),
{index, ""} <- Integer.parse(index_string) do
do_index(conn, paging_options: %{@default_paging_options | key: {block_number, index}})
else
_ -> do_index(conn)
_ ->
unprocessable_entity(conn)
end
end
def index(conn, _params) do
do_index(conn)
end
def show(conn, %{"id" => id, "locale" => locale}) do
redirect(conn, to: transaction_internal_transaction_path(conn, :index, locale, id))
end
@ -21,30 +28,29 @@ defmodule ExplorerWeb.TransactionController do
Keyword.merge(
[
necessity_by_association: %{
block: :required,
from_address: :optional,
to_address: :optional
}
block: :required
},
paging_options: @default_paging_options
],
options
)
transactions = Chain.recent_collated_transactions(full_options)
last_seen_collated_hash = last_seen_collated_hash(transactions)
transaction_count = Chain.transaction_count()
render(
conn,
"index.html",
last_seen_collated_hash: last_seen_collated_hash,
earliest: earliest(transactions),
transaction_count: transaction_count,
transactions: transactions
)
end
defp last_seen_collated_hash([]), do: nil
defp earliest([]), do: nil
defp last_seen_collated_hash(transactions) do
List.last(transactions).hash
defp earliest(transactions) do
last = List.last(transactions)
%{block_number: last.block_number, index: last.index}
end
end

@ -1,14 +1,14 @@
<div class="address-link">
<%= if @address do %>
<%= link to: address_path(@conn, :show, @conn.assigns.locale, @address),
<%= if @address_hash do %>
<%= link to: address_path(@conn, :show, @conn.assigns.locale, @address_hash),
"data-toggle": "tooltip",
"data-placement": "top",
class: "address-link__font",
title: @address do %>
title: @address_hash do %>
<i class="address-link__type fas fa-address-card"></i>
<%= @address |> hash |> String.slice(0..3) %><i class="fas fa-ellipsis-v address-link__seperator"></i><%= @address |> hash |> String.slice(-4..-1) %>
<%= @address_hash |> hash() |> String.slice(0..3) %><i class="fas fa-ellipsis-v address-link__seperator"></i><%= @address_hash |> hash() |> String.slice(-4..-1) %>
<% end %>
<button class="address-link__copy-button" data-clipboard-text="<%= @address %>" aria-label="copy address">
<button class="address-link__copy-button" data-clipboard-text="<%= @address_hash %>" aria-label="copy address">
<i class="fa fa-copy"></i>
</button>
<% end %>

@ -96,10 +96,10 @@
</td>
<td><%= ExplorerWeb.BlockView.age(internal_transaction.transaction.block) %></td>
<td>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: internal_transaction.from_address %>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address_hash: internal_transaction.from_address_hash %>
</td>
<td>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: internal_transaction.to_address %>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address_hash: internal_transaction.to_address_hash %>
</td>
<td><%= ExplorerWeb.TransactionView.value(internal_transaction, include_label: false) %></td>
</tr>

@ -103,11 +103,11 @@
</td>
<td><%= transaction.block.timestamp |> Timex.from_now %></td>
<td class="address-cell">
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: transaction.from_address %>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address_hash: transaction.from_address_hash %>
</td>
<td class="u-text-center"><i class="fas fa-arrow-circle-right"></i></td>
<td>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: transaction.to_address %>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address_hash: transaction.to_address_hash %>
</td>
<td><%= ExplorerWeb.TransactionView.value(transaction, include_label: false) %></td>
<td><%= ExplorerWeb.TransactionView.formatted_fee(transaction, denomination: :ether) %></td>

@ -164,11 +164,11 @@
<%= transaction.block.timestamp |> Timex.from_now %>
</td>
<td class="address-cell">
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: transaction.from_address %>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address_hash: transaction.from_address_hash %>
</td>
<td class="u-text-center"><i class="fas fa-arrow-circle-right"></i></td>
<td>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: transaction.to_address %>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address_hash: transaction.to_address_hash %>
</td>
<td>
<%= ExplorerWeb.TransactionView.value(transaction) %>

@ -21,7 +21,7 @@
<td><%= block.transactions |> Enum.count %></td>
<td><%= block.gas_used |> Cldr.Number.to_string! %></td>
<td>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: block.miner %>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address_hash: block.miner_hash %>
</td>
</tr>
<% end %>

@ -17,10 +17,10 @@
<%= render ExplorerWeb.TransactionView, "_link.html", conn: @conn, transaction: transaction %>
</td>
<td>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: transaction.from_address %>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address_hash: transaction.from_address_hash %>
</td>
<td>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: transaction.to_address %>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address_hash: transaction.to_address_hash %>
</td>
<td><%= ExplorerWeb.TransactionView.value(transaction, include_label: false) %> </td>
<td><%= transaction.block.timestamp |> Timex.from_now() %></td>

@ -50,10 +50,10 @@
</td>
<td><%= last_seen(transaction) %></td>
<td>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: transaction.from_address %>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address_hash: transaction.from_address_hash %>
</td>
<td>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: transaction.to_address %>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address_hash: transaction.to_address_hash %>
</td>
<td>
<%= ExplorerWeb.TransactionView.value(transaction, include_label: false) %>

@ -50,19 +50,19 @@
</td>
<td>
<%= link(
transaction.block,
transaction.block_number,
class: "transactions__link",
to: block_path(@conn, :show, @conn.assigns.locale, transaction.block)
) %>
</td>
<td>
<%= transaction.block.timestamp |> Timex.from_now %>
<%= transaction.block.timestamp %>
</td>
<td>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: transaction.from_address %>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address_hash: transaction.from_address_hash %>
</td>
<td>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: transaction.to_address %>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address_hash: transaction.to_address_hash %>
</td>
<td>
<%= ExplorerWeb.TransactionView.value(transaction, include_label: false) %>
@ -73,16 +73,16 @@
</table>
</div>
</div>
<%= if @last_seen_collated_hash do %>
<%= if @earliest do %>
<%= link(
gettext("Next Page"),
class: "button button--secondary button--sm u-float-right mt-3",
to: transaction_path(
@conn,
:index,
@conn.assigns.locale,
%{"last_seen_collated_hash" => to_string(@last_seen_collated_hash)}
)
) %>
gettext("Older"),
class: "button button--secondary button--sm u-float-right mt-3",
to: transaction_path(
@conn,
:index,
@conn.assigns.locale,
%{"block_number" => @earliest.block_number, "index" => @earliest.index}
)
) %>
<% end %>
</section>

@ -38,10 +38,10 @@
<tr>
<td><%= internal_transaction.call_type %></td>
<td>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: internal_transaction.from_address %>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address_hash: internal_transaction.from_address_hash %>
</td>
<td>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address: internal_transaction.to_address %>
<%= render ExplorerWeb.AddressView, "_link.html", conn: @conn, address_hash: internal_transaction.to_address_hash %>
</td>
<td><%= ExplorerWeb.TransactionView.value(internal_transaction, include_label: false) %></td>
<td><%= ExplorerWeb.TransactionView.gas(internal_transaction) %></td>

@ -2,6 +2,7 @@ defmodule ExplorerWeb.AddressView do
use ExplorerWeb, :view
alias Explorer.Chain.{Address, Wei}
alias Explorer.Chain.Hash
alias Explorer.ExchangeRates.Token
alias ExplorerWeb.ExchangeRates.USD
@ -41,7 +42,7 @@ defmodule ExplorerWeb.AddressView do
end
end
def hash(%Address{hash: hash}) do
def hash(%Hash{} = hash) do
to_string(hash)
end

@ -5,6 +5,10 @@ defmodule ExplorerWeb.ErrorView do
"Page not found"
end
def render("422.html", _assigns) do
"Unprocessable entity"
end
def render("500.html", _assigns) do
"Internal server error"
end

@ -155,7 +155,7 @@ msgstr ""
#: lib/explorer_web/templates/address/overview.html.eex:11
#: lib/explorer_web/templates/transaction_log/index.html.eex:29
#: lib/explorer_web/views/address_view.ex:17
#: lib/explorer_web/views/address_view.ex:18
msgid "Address"
msgstr ""
@ -302,7 +302,6 @@ msgid "TPM"
msgstr ""
#: lib/explorer_web/templates/pending_transaction/index.html.eex:69
#: lib/explorer_web/templates/transaction/index.html.eex:78
msgid "Next Page"
msgstr ""
@ -497,7 +496,7 @@ msgstr ""
msgid "Contract"
msgstr ""
#: lib/explorer_web/views/address_view.ex:15
#: lib/explorer_web/views/address_view.ex:16
msgid "Contract Address"
msgstr ""
@ -508,3 +507,7 @@ msgstr ""
#: lib/explorer_web/templates/block_transaction/index.html.eex:181
msgid "There are no Transactions"
msgstr ""
#: lib/explorer_web/templates/transaction/index.html.eex:78
msgid "Older"
msgstr ""

@ -167,7 +167,7 @@ msgstr "%{count} transactions in this block"
#: lib/explorer_web/templates/address/overview.html.eex:11
#: lib/explorer_web/templates/transaction_log/index.html.eex:29
#: lib/explorer_web/views/address_view.ex:17
#: lib/explorer_web/views/address_view.ex:18
msgid "Address"
msgstr "Address"
@ -314,7 +314,6 @@ msgid "TPM"
msgstr ""
#: lib/explorer_web/templates/pending_transaction/index.html.eex:69
#: lib/explorer_web/templates/transaction/index.html.eex:78
msgid "Next Page"
msgstr ""
@ -509,7 +508,7 @@ msgstr ""
msgid "Contract"
msgstr ""
#: lib/explorer_web/views/address_view.ex:15
#: lib/explorer_web/views/address_view.ex:16
msgid "Contract Address"
msgstr ""
@ -520,3 +519,7 @@ msgstr ""
#: lib/explorer_web/templates/block_transaction/index.html.eex:181
msgid "There are no Transactions"
msgstr ""
#: lib/explorer_web/templates/transaction/index.html.eex:78
msgid "Older"
msgstr ""

@ -1,10 +1,11 @@
defmodule ExplorerWeb.TransactionControllerTest do
use ExplorerWeb.ConnCase
alias Explorer.Chain.Transaction
import ExplorerWeb.Router.Helpers, only: [transaction_path: 4, transaction_internal_transaction_path: 4]
describe "GET index/2" do
test "returns a transaction with a receipt", %{conn: conn} do
test "returns a collated transactions", %{conn: conn} do
transaction =
:transaction
|> insert()
@ -22,43 +23,61 @@ defmodule ExplorerWeb.TransactionControllerTest do
conn = get(conn, "/en/transactions")
assert length(conn.assigns.transactions) == 1
assert conn.assigns.transaction_count == 1
end
test "returns no pending transactions", %{conn: conn} do
insert(:transaction)
conn = get(conn, "/en/transactions")
assert conn.assigns.transactions == []
end
test "excludes pending transactions", %{conn: conn} do
%Transaction{hash: hash} =
:transaction
|> insert()
|> with_block()
test "only returns transactions that have a receipt", %{conn: conn} do
insert(:transaction)
conn = get(conn, "/en/transactions")
assert length(conn.assigns.transactions) == 0
assert [%Transaction{hash: ^hash}] = conn.assigns.transactions
end
test "paginates transactions using the last seen transaction", %{conn: conn} do
transaction =
test "returns next page of results based on last seen transaction", %{conn: conn} do
second_page_hashes =
50
|> insert_list(:transaction)
|> with_block()
|> Enum.map(& &1.hash)
%Transaction{block_number: block_number, index: index} =
:transaction
|> insert()
|> with_block()
conn =
get(
conn,
"/en/transactions",
last_seen_collated_hash: to_string(transaction.hash)
)
get(conn, "/en/transactions", %{
"block_number" => Integer.to_string(block_number),
"index" => Integer.to_string(index)
})
assert conn.assigns.transactions == []
actual_hashes =
conn.assigns.transactions
|> Enum.map(& &1.hash)
|> Enum.reverse()
assert second_page_hashes == actual_hashes
end
test "guards against bad block_number input", %{conn: conn} do
conn = get(conn, "/en/transactions", %{"block_number" => "foo", "index" => "2"})
assert html_response(conn, 422)
end
test "guards against bad index input", %{conn: conn} do
conn = get(conn, "/en/transactions", %{"block_number" => "2", "index" => "bar"})
assert html_response(conn, 422)
end
test "sends back the number of transactions", %{conn: conn} do
insert(:transaction)
|> with_block()
conn = get(conn, "/en/transactions")

@ -8,6 +8,10 @@ defmodule ExplorerWeb.ErrorViewTest do
assert render_to_string(ExplorerWeb.ErrorView, "404.html", []) == "Page not found"
end
test "renders 422.html" do
assert render_to_string(ExplorerWeb.ErrorView, "422.html", []) == "Unprocessable entity"
end
test "render 500.html" do
assert render_to_string(ExplorerWeb.ErrorView, "500.html", []) == "Internal server error"
end

Loading…
Cancel
Save