diff --git a/apps/block_scout_web/test/block_scout_web/controllers/tokens/inventory_controller_test.ex b/apps/block_scout_web/test/block_scout_web/controllers/tokens/inventory_controller_test.ex new file mode 100644 index 0000000000..7ce68af4d1 --- /dev/null +++ b/apps/block_scout_web/test/block_scout_web/controllers/tokens/inventory_controller_test.ex @@ -0,0 +1,121 @@ +defmodule BlockScoutWeb.Tokens.InventoryControllerTest do + use BlockScoutWeb.ConnCase + + describe "GET index/3" do + test "with invalid address hash", %{conn: conn} do + conn = get(conn, token_inventory_path(conn, :index, "invalid_address")) + + assert html_response(conn, 404) + end + + test "with a token that doesn't exist", %{conn: conn} do + address = build(:address) + conn = get(conn, token_inventory_path(conn, :index, address.hash)) + + assert html_response(conn, 404) + end + + test "successfully renders the page", %{conn: conn} do + token_contract_address = insert(:contract_address) + token = insert(:token, type: "ERC-721", contract_address: token_contract_address) + + transaction = + :transaction + |> insert() + |> with_block() + + insert( + :token_transfer, + transaction: transaction, + token_contract_address: token_contract_address, + token: token + ) + + conn = + get( + conn, + token_inventory_path(conn, :index, token_contract_address.hash) + ) + + assert html_response(conn, 200) + end + + test "returns next page of results based on last seen token balance", %{conn: conn} do + token = insert(:token, type: "ERC-721") + + transaction = + :transaction + |> insert() + |> with_block() + + second_page_token_balances = + Enum.map( + 1..50, + &insert( + :token_transfer, + transaction: transaction, + token_contract_address: token.contract_address, + token: token, + token_id: &1 + 1000 + ) + ) + + conn = + get(conn, token_inventory_path(conn, :index, token.contract_address_hash), %{ + "token_id" => "999" + }) + + assert Enum.map(conn.assigns.unique_tokens, & &1.token_id) == Enum.map(second_page_token_balances, & &1.token_id) + end + + test "next_page_params exists if not on last page", %{conn: conn} do + token = insert(:token, type: "ERC-721") + + transaction = + :transaction + |> insert() + |> with_block() + + Enum.each( + 1..51, + &insert( + :token_transfer, + transaction: transaction, + token_contract_address: token.contract_address, + token: token, + token_id: &1 + 1000 + ) + ) + + expected_next_page_params = %{ + "token_id" => to_string(token.contract_address_hash), + "unique_token" => 1050 + } + + conn = get(conn, token_inventory_path(conn, :index, token.contract_address_hash)) + + assert conn.assigns.next_page_params == expected_next_page_params + end + + test "next_page_params are empty if on last page", %{conn: conn} do + token = insert(:token, type: "ERC-721") + + transaction = + :transaction + |> insert() + |> with_block() + + insert( + :token_transfer, + transaction: transaction, + token_contract_address: token.contract_address, + token: token, + token_id: 1000 + ) + + conn = get(conn, token_inventory_path(conn, :index, token.contract_address_hash)) + + refute conn.assigns.next_page_params + end + end +end diff --git a/apps/explorer/lib/explorer/chain/token_transfer.ex b/apps/explorer/lib/explorer/chain/token_transfer.ex index c50bc23c64..80c46fa64d 100644 --- a/apps/explorer/lib/explorer/chain/token_transfer.ex +++ b/apps/explorer/lib/explorer/chain/token_transfer.ex @@ -135,6 +135,10 @@ defmodule Explorer.Chain.TokenTransfer do def page_token_transfer(query, %PagingOptions{key: nil}), do: query + def page_token_transfer(query, %PagingOptions{key: {token_id}}) do + where(query, [token_transfer], token_transfer.token_id > ^token_id) + end + def page_token_transfer(query, %PagingOptions{key: {block_number, log_index}}) do where( query, diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index 7c4dbdf8e0..fb6994fe9e 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -3131,7 +3131,7 @@ defmodule Explorer.ChainTest do token_id: 29 ) - paging_options = %PagingOptions{key: {first_page.block_number, first_page.log_index}, page_size: 1} + paging_options = %PagingOptions{key: {first_page.token_id}, page_size: 1} unique_tokens_ids_paginated = token_contract_address.hash