Blockchain explorer for Ethereum based network and a tool for inspecting and analyzing EVM based blockchains.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
blockscout/apps/explorer/test/explorer/graphql_test.exs

347 lines
10 KiB

defmodule Explorer.GraphQLTest do
use Explorer.DataCase
import Explorer.Factory
alias Explorer.{GraphQL, Repo}
alias Explorer.Chain.Address
describe "address_to_transactions_query/1" do
test "with address hash with zero transactions" do
result =
:address
|> insert()
|> Map.get(:hash)
|> GraphQL.address_to_transactions_query(:desc)
|> Repo.replica().all()
assert result == []
end
test "with matching 'to_address_hash'" do
%Address{hash: address_hash} = address = insert(:address)
transaction = insert(:transaction, to_address: address)
insert(:transaction)
[found_transaction] =
address_hash
|> GraphQL.address_to_transactions_query(:desc)
|> Repo.replica().all()
assert found_transaction.hash == transaction.hash
end
test "with matching 'from_address_hash'" do
%Address{hash: address_hash} = address = insert(:address)
transaction = insert(:transaction, from_address: address)
insert(:transaction)
[found_transaction] =
address_hash
|> GraphQL.address_to_transactions_query(:desc)
|> Repo.replica().all()
assert found_transaction.hash == transaction.hash
end
test "with matching 'created_contract_address_hash'" do
%Address{hash: address_hash} = address = insert(:address)
transaction = insert(:transaction, created_contract_address: address)
insert(:transaction)
[found_transaction] =
address_hash
|> GraphQL.address_to_transactions_query(:desc)
|> Repo.replica().all()
assert found_transaction.hash == transaction.hash
end
test "orders by descending block and index" do
first_block = insert(:block)
second_block = insert(:block)
third_block = insert(:block)
%Address{hash: address_hash} = address = insert(:address)
3
|> insert_list(:transaction, from_address: address)
|> with_block(second_block)
3
|> insert_list(:transaction, from_address: address)
|> with_block(third_block)
3
|> insert_list(:transaction, from_address: address)
|> with_block(first_block)
found_transactions =
address_hash
|> GraphQL.address_to_transactions_query(:desc)
|> Repo.replica().all()
block_number_and_index_order =
Enum.map(found_transactions, fn transaction ->
{transaction.block_number, transaction.index}
end)
assert block_number_and_index_order == Enum.sort(block_number_and_index_order, &(&1 >= &2))
end
end
describe "get_internal_transaction/1" do
test "returns existing internal transaction" do
transaction = insert(:transaction) |> with_block()
internal_transaction =
insert(:internal_transaction,
transaction: transaction,
index: 0,
block_hash: transaction.block_hash,
block_index: 0
)
clauses = %{transaction_hash: transaction.hash, index: internal_transaction.index}
{:ok, found_internal_transaction} = GraphQL.get_internal_transaction(clauses)
assert found_internal_transaction.transaction_hash == transaction.hash
assert found_internal_transaction.index == internal_transaction.index
end
test "returns error tuple for non-existent internal transaction" do
transaction = build(:transaction)
internal_transaction = build(:internal_transaction, transaction: transaction, index: 0)
clauses = %{transaction_hash: transaction.hash, index: internal_transaction.index}
assert GraphQL.get_internal_transaction(clauses) == {:error, "Internal transaction not found."}
end
end
describe "transaction_to_internal_transactions_query/1" do
test "with transaction with one internal transaction" do
transaction1 = insert(:transaction) |> with_block()
transaction2 = insert(:transaction) |> with_block()
internal_transaction =
insert(:internal_transaction_create,
transaction: transaction1,
index: 0,
block_hash: transaction1.block_hash,
block_index: 0
)
insert(:internal_transaction_create,
transaction: transaction2,
index: 0,
block_hash: transaction2.block_hash,
block_index: 0
)
[found_internal_transaction] =
transaction1
|> GraphQL.transaction_to_internal_transactions_query()
|> Repo.replica().all()
assert found_internal_transaction.transaction_hash == transaction1.hash
assert found_internal_transaction.index == internal_transaction.index
end
test "with transaction with multiple internal transactions" do
transaction1 = insert(:transaction) |> with_block()
transaction2 = insert(:transaction) |> with_block()
for index <- 0..2 do
insert(:internal_transaction_create,
transaction: transaction1,
index: index,
block_hash: transaction1.block_hash,
block_index: index
)
end
insert(:internal_transaction_create,
transaction: transaction2,
index: 0,
block_hash: transaction2.block_hash,
block_index: 0
)
found_internal_transactions =
transaction1
|> GraphQL.transaction_to_internal_transactions_query()
|> Repo.replica().all()
assert length(found_internal_transactions) == 3
for found_internal_transaction <- found_internal_transactions do
assert found_internal_transaction.transaction_hash == transaction1.hash
end
end
test "orders internal transactions by ascending index" do
transaction = insert(:transaction) |> with_block()
insert(:internal_transaction_create,
transaction: transaction,
index: 2,
block_hash: transaction.block_hash,
block_index: 2
)
insert(:internal_transaction_create,
transaction: transaction,
index: 0,
block_hash: transaction.block_hash,
block_index: 0
)
insert(:internal_transaction_create,
transaction: transaction,
index: 1,
block_hash: transaction.block_hash,
block_index: 1
)
found_internal_transactions =
transaction
|> GraphQL.transaction_to_internal_transactions_query()
|> Repo.replica().all()
index_order = Enum.map(found_internal_transactions, & &1.index)
assert index_order == Enum.sort(index_order)
end
# Note that `transaction_to_internal_transactions_query/1` relies on
# `Explorer.Chain.where_transaction_has_multiple_transactions/1` to ensure the
# following behavior:
#
# * exclude internal transactions of type call with no siblings in the
# transaction
#
# * include internal transactions of type create, reward, or suicide
# even when they are alone in the parent transaction
#
# These two requirements are tested in `Explorer.ChainTest`.
end
describe "get_token_transfer/1" do
test "returns existing token transfer" do
transaction = insert(:transaction)
token_transfer = insert(:token_transfer, transaction: transaction)
clauses = %{transaction_hash: token_transfer.transaction_hash, log_index: token_transfer.log_index}
{:ok, found_token_transfer} = GraphQL.get_token_transfer(clauses)
assert found_token_transfer.transaction_hash == token_transfer.transaction_hash
assert found_token_transfer.log_index == token_transfer.log_index
end
test " returns error tuple for non-existing token transfer" do
transaction = insert(:transaction)
token_transfer = build(:token_transfer, transaction: transaction)
clauses = %{transaction_hash: transaction.hash, log_index: token_transfer.log_index}
assert GraphQL.get_token_transfer(clauses) == {:error, "Token transfer not found."}
end
end
describe "list_token_transfers_query/1" do
test "with token contract address hash with zero token transfers" do
result =
:address
|> insert()
|> Map.get(:hash)
|> GraphQL.list_token_transfers_query()
|> Repo.replica().all()
assert result == []
end
test "returns all expected token transfer fields" do
transaction = insert(:transaction)
token_transfer = insert(:token_transfer, transaction: transaction)
[found_token_transfer] =
token_transfer.token_contract_address_hash
|> GraphQL.list_token_transfers_query()
|> Repo.replica().all()
expected_fields = ~w(
amount
block_number
log_index
token_id
from_address_hash
to_address_hash
token_contract_address_hash
transaction_hash
)a
for expected_field <- expected_fields do
assert Map.get(found_token_transfer, expected_field) == Map.get(token_transfer, expected_field)
end
end
test "orders token transfers by descending block number" do
first_block = insert(:block)
second_block = insert(:block)
third_block = insert(:block)
transactions_block2 =
2
|> insert_list(:transaction)
|> with_block(second_block)
transactions_block3 =
2
|> insert_list(:transaction)
|> with_block(third_block)
transactions_block1 =
2
|> insert_list(:transaction)
|> with_block(first_block)
all_transactions = Enum.concat([transactions_block2, transactions_block3, transactions_block1])
token_address = insert(:contract_address)
insert(:token, contract_address: token_address)
for transaction <- all_transactions do
token_transfer_attrs1 = %{
block_number: transaction.block_number,
log_index: 0,
transaction: transaction,
token_contract_address: token_address
}
token_transfer_attrs2 = %{
block_number: transaction.block_number,
log_index: 1,
transaction: transaction,
token_contract_address: token_address
}
insert(:token_transfer, token_transfer_attrs1)
insert(:token_transfer, token_transfer_attrs2)
end
found_token_transfers =
token_address.hash
|> GraphQL.list_token_transfers_query()
|> Repo.replica().all()
|> Repo.replica().preload(:transaction)
block_number_order = Enum.map(found_token_transfers, & &1.block_number)
assert block_number_order == Enum.sort(block_number_order, &(&1 >= &2))
end
end
end