mirror of
https://github.com/trezor/blockbook.git
synced 2026-03-23 07:57:18 +01:00
293 lines
12 KiB
Go
293 lines
12 KiB
Go
//go:build integration
|
|
|
|
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/url"
|
|
"testing"
|
|
)
|
|
|
|
const (
|
|
evmHistoryPage = 1
|
|
evmHistoryPageSize = 3
|
|
)
|
|
|
|
func testGetAddressBasicEVM(t *testing.T, h *TestHandler) {
|
|
address := h.sampleEVMAddressOrSkip(t)
|
|
|
|
path := buildAddressDetailsPath(address, "basic", addressPage, addressPageSize)
|
|
var resp evmAddressTokenBalanceResponse
|
|
h.mustGetJSON(t, path, &resp)
|
|
|
|
assertEVMBasicAddressPayload(t, &resp, address, "GetAddressBasicEVM")
|
|
}
|
|
|
|
func testGetAddressTxidsPaginationEVM(t *testing.T, h *TestHandler) {
|
|
address := h.sampleEVMAddressOrSkip(t)
|
|
|
|
var page1 addressTxidsResponse
|
|
h.mustGetJSON(t, buildAddressDetailsPath(address, "txids", evmHistoryPage, evmHistoryPageSize), &page1)
|
|
|
|
assertAddressMatches(t, page1.Address, address, "GetAddressTxidsPaginationEVM.page1.address")
|
|
assertPageMeta(t, page1.Page, page1.ItemsOnPage, page1.TotalPages, page1.Txs, "GetAddressTxidsPaginationEVM.page1")
|
|
assertPageSizeUpperBound(t, len(page1.Txids), page1.ItemsOnPage, evmHistoryPageSize, "GetAddressTxidsPaginationEVM.page1.txids")
|
|
if len(page1.Txids) == 0 {
|
|
t.Fatalf("GetAddressTxidsPaginationEVM page 1 returned no txids")
|
|
}
|
|
for i := range page1.Txids {
|
|
assertNonEmptyString(t, page1.Txids[i], "GetAddressTxidsPaginationEVM.page1.txids")
|
|
}
|
|
|
|
if page1.TotalPages <= 1 || page1.Txs <= evmHistoryPageSize {
|
|
t.Skipf("Skipping pagination check, address %s has %d txs and %d page(s)", address, page1.Txs, page1.TotalPages)
|
|
}
|
|
|
|
var page2 addressTxidsResponse
|
|
h.mustGetJSON(t, buildAddressDetailsPath(address, "txids", evmHistoryPage+1, evmHistoryPageSize), &page2)
|
|
|
|
assertAddressMatches(t, page2.Address, address, "GetAddressTxidsPaginationEVM.page2.address")
|
|
assertPageMeta(t, page2.Page, page2.ItemsOnPage, page2.TotalPages, page2.Txs, "GetAddressTxidsPaginationEVM.page2")
|
|
assertPageSizeUpperBound(t, len(page2.Txids), page2.ItemsOnPage, evmHistoryPageSize, "GetAddressTxidsPaginationEVM.page2.txids")
|
|
if page2.Page != evmHistoryPage+1 {
|
|
t.Fatalf("GetAddressTxidsPaginationEVM page mismatch: got %d, want %d", page2.Page, evmHistoryPage+1)
|
|
}
|
|
if len(page2.Txids) == 0 {
|
|
t.Fatalf("GetAddressTxidsPaginationEVM page 2 returned no txids")
|
|
}
|
|
for i := range page2.Txids {
|
|
assertNonEmptyString(t, page2.Txids[i], "GetAddressTxidsPaginationEVM.page2.txids")
|
|
}
|
|
}
|
|
|
|
func testGetAddressTxsPaginationEVM(t *testing.T, h *TestHandler) {
|
|
address := h.sampleEVMAddressOrSkip(t)
|
|
|
|
var page1 addressTxsResponse
|
|
h.mustGetJSON(t, buildAddressDetailsPath(address, "txs", evmHistoryPage, evmHistoryPageSize), &page1)
|
|
|
|
assertAddressMatches(t, page1.Address, address, "GetAddressTxsPaginationEVM.page1.address")
|
|
assertPageMeta(t, page1.Page, page1.ItemsOnPage, page1.TotalPages, page1.Txs, "GetAddressTxsPaginationEVM.page1")
|
|
assertPageSizeUpperBound(t, len(page1.Transactions), page1.ItemsOnPage, evmHistoryPageSize, "GetAddressTxsPaginationEVM.page1.transactions")
|
|
if len(page1.Transactions) == 0 {
|
|
t.Fatalf("GetAddressTxsPaginationEVM page 1 returned no transactions")
|
|
}
|
|
txIDsFromTransactions(t, page1.Transactions, "GetAddressTxsPaginationEVM.page1")
|
|
|
|
if page1.TotalPages <= 1 || page1.Txs <= evmHistoryPageSize {
|
|
t.Skipf("Skipping pagination check, address %s has %d txs and %d page(s)", address, page1.Txs, page1.TotalPages)
|
|
}
|
|
|
|
var page2 addressTxsResponse
|
|
h.mustGetJSON(t, buildAddressDetailsPath(address, "txs", evmHistoryPage+1, evmHistoryPageSize), &page2)
|
|
|
|
assertAddressMatches(t, page2.Address, address, "GetAddressTxsPaginationEVM.page2.address")
|
|
assertPageMeta(t, page2.Page, page2.ItemsOnPage, page2.TotalPages, page2.Txs, "GetAddressTxsPaginationEVM.page2")
|
|
assertPageSizeUpperBound(t, len(page2.Transactions), page2.ItemsOnPage, evmHistoryPageSize, "GetAddressTxsPaginationEVM.page2.transactions")
|
|
if page2.Page != evmHistoryPage+1 {
|
|
t.Fatalf("GetAddressTxsPaginationEVM page mismatch: got %d, want %d", page2.Page, evmHistoryPage+1)
|
|
}
|
|
if len(page2.Transactions) == 0 {
|
|
t.Fatalf("GetAddressTxsPaginationEVM page 2 returned no transactions")
|
|
}
|
|
page2Txids := txIDsFromTransactions(t, page2.Transactions, "GetAddressTxsPaginationEVM.page2")
|
|
_ = page2Txids
|
|
}
|
|
|
|
func testGetAddressTokensEVM(t *testing.T, h *TestHandler) {
|
|
address := h.sampleEVMAddressOrSkip(t)
|
|
|
|
path := buildAddressDetailsPath(address, "tokens", addressPage, addressPageSize)
|
|
var resp evmAddressTokenBalanceResponse
|
|
h.mustGetJSON(t, path, &resp)
|
|
|
|
assertEVMBasicAddressPayload(t, &resp, address, "GetAddressTokensEVM")
|
|
for i := range resp.Tokens {
|
|
tokenContext := fmt.Sprintf("GetAddressTokensEVM.tokens[%d]", i)
|
|
assertNonEmptyString(t, resp.Tokens[i].Type, tokenContext+".type")
|
|
assertNonEmptyString(t, resp.Tokens[i].Contract, tokenContext+".contract")
|
|
}
|
|
}
|
|
|
|
func testGetAddressTokenBalances(t *testing.T, h *TestHandler) {
|
|
address := h.sampleEVMAddressOrSkip(t)
|
|
|
|
path := buildAddressDetailsPath(address, "tokenBalances", addressPage, addressPageSize)
|
|
var resp evmAddressTokenBalanceResponse
|
|
h.mustGetJSON(t, path, &resp)
|
|
|
|
assertEVMTokenBalancesPayload(t, &resp, address, "GetAddressTokenBalances")
|
|
assertEVMTokenBalancesHaveHoldingsFields(t, &resp, address, "GetAddressTokenBalances")
|
|
}
|
|
|
|
func testGetAddressContractFilterEVM(t *testing.T, h *TestHandler) {
|
|
address := h.sampleEVMAddressOrSkip(t)
|
|
contract := h.sampleEVMContractOrSkip(t)
|
|
|
|
path := buildAddressDetailsPath(address, "tokenBalances", addressPage, addressPageSize) + "&contract=" + url.QueryEscape(contract)
|
|
var resp evmAddressTokenBalanceResponse
|
|
h.mustGetJSON(t, path, &resp)
|
|
|
|
assertEVMTokenBalancesPayload(t, &resp, address, "GetAddressContractFilterEVM")
|
|
assertEVMTokenBalancesHaveHoldingsFields(t, &resp, address, "GetAddressContractFilterEVM")
|
|
assertEVMTokenListContractsMatch(t, resp.Tokens, contract, "GetAddressContractFilterEVM")
|
|
}
|
|
|
|
func testGetTransactionEVMShape(t *testing.T, h *TestHandler) {
|
|
txid := h.sampleEVMTxIDOrSkip(t)
|
|
|
|
path := "/api/v2/tx/" + url.PathEscape(txid)
|
|
var tx evmTxShapeResponse
|
|
h.mustGetJSON(t, path, &tx)
|
|
|
|
assertEqualString(t, tx.Txid, txid, "GetTransactionEVMShape.txid")
|
|
if !isEVMTxID(tx.Txid) {
|
|
t.Fatalf("GetTransactionEVMShape txid is not EVM-like: %s", tx.Txid)
|
|
}
|
|
if len(tx.Vin) != 1 {
|
|
t.Fatalf("GetTransactionEVMShape expected exactly 1 vin entry, got %d", len(tx.Vin))
|
|
}
|
|
if len(tx.Vout) != 1 {
|
|
t.Fatalf("GetTransactionEVMShape expected exactly 1 vout entry, got %d", len(tx.Vout))
|
|
}
|
|
if !hasNonEmptyObject(tx.EthereumSpecific) {
|
|
t.Fatalf("GetTransactionEVMShape missing ethereumSpecific object for %s", txid)
|
|
}
|
|
}
|
|
|
|
func testWsGetAccountInfoBasicEVM(t *testing.T, h *TestHandler) {
|
|
address := h.sampleEVMAddressOrSkip(t)
|
|
|
|
resp := h.wsCall(t, "getAccountInfo", map[string]interface{}{
|
|
"descriptor": address,
|
|
"details": "basic",
|
|
"page": addressPage,
|
|
"pageSize": addressPageSize,
|
|
})
|
|
|
|
var info evmAddressTokenBalanceResponse
|
|
if err := json.Unmarshal(resp.Data, &info); err != nil {
|
|
t.Fatalf("decode websocket getAccountInfo EVM basic response: %v", err)
|
|
}
|
|
|
|
assertEVMBasicAddressPayload(t, &info, address, "WsGetAccountInfoBasicEVM")
|
|
}
|
|
|
|
func testWsGetAccountInfoEVM(t *testing.T, h *TestHandler) {
|
|
address := h.sampleEVMAddressOrSkip(t)
|
|
|
|
resp := h.wsCall(t, "getAccountInfo", map[string]interface{}{
|
|
"descriptor": address,
|
|
"details": "tokenBalances",
|
|
"page": addressPage,
|
|
"pageSize": addressPageSize,
|
|
})
|
|
|
|
var info evmAddressTokenBalanceResponse
|
|
if err := json.Unmarshal(resp.Data, &info); err != nil {
|
|
t.Fatalf("decode websocket getAccountInfo EVM response: %v", err)
|
|
}
|
|
|
|
assertEVMTokenBalancesPayload(t, &info, address, "WsGetAccountInfoEVM")
|
|
assertEVMTokenBalancesHaveHoldingsFields(t, &info, address, "WsGetAccountInfoEVM")
|
|
}
|
|
|
|
func testWsGetAccountInfoTxidsConsistencyEVM(t *testing.T, h *TestHandler) {
|
|
address := h.sampleEVMAddressOrSkip(t)
|
|
bestHeight := h.getStatus(t).BestHeight
|
|
|
|
var httpResp addressTxidsResponse
|
|
h.mustGetJSON(t, buildAddressDetailsPathWithTo(address, "txids", evmHistoryPage, evmHistoryPageSize, bestHeight), &httpResp)
|
|
assertAddressMatches(t, httpResp.Address, address, "WsGetAccountInfoTxidsConsistencyEVM.http.address")
|
|
assertPageMetaAllowUnknownTotal(t, httpResp.Page, httpResp.ItemsOnPage, httpResp.TotalPages, httpResp.Txs, "WsGetAccountInfoTxidsConsistencyEVM.http")
|
|
|
|
wsRaw := h.wsCall(t, "getAccountInfo", map[string]interface{}{
|
|
"descriptor": address,
|
|
"details": "txids",
|
|
"page": evmHistoryPage,
|
|
"pageSize": evmHistoryPageSize,
|
|
"to": bestHeight,
|
|
})
|
|
var wsResp addressTxidsResponse
|
|
if err := json.Unmarshal(wsRaw.Data, &wsResp); err != nil {
|
|
t.Fatalf("decode websocket getAccountInfo txids EVM response: %v", err)
|
|
}
|
|
assertAddressMatches(t, wsResp.Address, address, "WsGetAccountInfoTxidsConsistencyEVM.ws.address")
|
|
assertPageMetaAllowUnknownTotal(t, wsResp.Page, wsResp.ItemsOnPage, wsResp.TotalPages, wsResp.Txs, "WsGetAccountInfoTxidsConsistencyEVM.ws")
|
|
|
|
if wsResp.Page != httpResp.Page || wsResp.ItemsOnPage != httpResp.ItemsOnPage {
|
|
t.Fatalf("WsGetAccountInfoTxidsConsistencyEVM page meta mismatch: ws(page=%d items=%d totalPages=%d txs=%d) http(page=%d items=%d totalPages=%d txs=%d)",
|
|
wsResp.Page, wsResp.ItemsOnPage, wsResp.TotalPages, wsResp.Txs,
|
|
httpResp.Page, httpResp.ItemsOnPage, httpResp.TotalPages, httpResp.Txs)
|
|
}
|
|
if wsResp.TotalPages != httpResp.TotalPages {
|
|
t.Fatalf("WsGetAccountInfoTxidsConsistencyEVM totalPages mismatch: ws=%d http=%d", wsResp.TotalPages, httpResp.TotalPages)
|
|
}
|
|
if wsResp.TotalPages >= 0 && wsResp.Txs != httpResp.Txs {
|
|
t.Fatalf("WsGetAccountInfoTxidsConsistencyEVM tx count mismatch: ws=%d http=%d", wsResp.Txs, httpResp.Txs)
|
|
}
|
|
assertStringSlicesEqual(t, wsResp.Txids, httpResp.Txids, "WsGetAccountInfoTxidsConsistencyEVM.txids")
|
|
}
|
|
|
|
func testWsGetAccountInfoTxsConsistencyEVM(t *testing.T, h *TestHandler) {
|
|
address := h.sampleEVMAddressOrSkip(t)
|
|
bestHeight := h.getStatus(t).BestHeight
|
|
|
|
var httpResp addressTxsResponse
|
|
h.mustGetJSON(t, buildAddressDetailsPathWithTo(address, "txs", evmHistoryPage, evmHistoryPageSize, bestHeight), &httpResp)
|
|
assertAddressMatches(t, httpResp.Address, address, "WsGetAccountInfoTxsConsistencyEVM.http.address")
|
|
assertPageMetaAllowUnknownTotal(t, httpResp.Page, httpResp.ItemsOnPage, httpResp.TotalPages, httpResp.Txs, "WsGetAccountInfoTxsConsistencyEVM.http")
|
|
httpTxids := txIDsFromTransactions(t, httpResp.Transactions, "WsGetAccountInfoTxsConsistencyEVM.http")
|
|
|
|
wsRaw := h.wsCall(t, "getAccountInfo", map[string]interface{}{
|
|
"descriptor": address,
|
|
"details": "txs",
|
|
"page": evmHistoryPage,
|
|
"pageSize": evmHistoryPageSize,
|
|
"to": bestHeight,
|
|
})
|
|
var wsResp addressTxsResponse
|
|
if err := json.Unmarshal(wsRaw.Data, &wsResp); err != nil {
|
|
t.Fatalf("decode websocket getAccountInfo txs EVM response: %v", err)
|
|
}
|
|
assertAddressMatches(t, wsResp.Address, address, "WsGetAccountInfoTxsConsistencyEVM.ws.address")
|
|
assertPageMetaAllowUnknownTotal(t, wsResp.Page, wsResp.ItemsOnPage, wsResp.TotalPages, wsResp.Txs, "WsGetAccountInfoTxsConsistencyEVM.ws")
|
|
wsTxids := txIDsFromTransactions(t, wsResp.Transactions, "WsGetAccountInfoTxsConsistencyEVM.ws")
|
|
|
|
if wsResp.Page != httpResp.Page || wsResp.ItemsOnPage != httpResp.ItemsOnPage {
|
|
t.Fatalf("WsGetAccountInfoTxsConsistencyEVM page meta mismatch: ws(page=%d items=%d totalPages=%d txs=%d) http(page=%d items=%d totalPages=%d txs=%d)",
|
|
wsResp.Page, wsResp.ItemsOnPage, wsResp.TotalPages, wsResp.Txs,
|
|
httpResp.Page, httpResp.ItemsOnPage, httpResp.TotalPages, httpResp.Txs)
|
|
}
|
|
if wsResp.TotalPages != httpResp.TotalPages {
|
|
t.Fatalf("WsGetAccountInfoTxsConsistencyEVM totalPages mismatch: ws=%d http=%d", wsResp.TotalPages, httpResp.TotalPages)
|
|
}
|
|
if wsResp.TotalPages >= 0 && wsResp.Txs != httpResp.Txs {
|
|
t.Fatalf("WsGetAccountInfoTxsConsistencyEVM tx count mismatch: ws=%d http=%d", wsResp.Txs, httpResp.Txs)
|
|
}
|
|
assertStringSlicesEqual(t, wsTxids, httpTxids, "WsGetAccountInfoTxsConsistencyEVM.txids")
|
|
}
|
|
|
|
func testWsGetAccountInfoContractFilterEVM(t *testing.T, h *TestHandler) {
|
|
address := h.sampleEVMAddressOrSkip(t)
|
|
contract := h.sampleEVMContractOrSkip(t)
|
|
|
|
resp := h.wsCall(t, "getAccountInfo", map[string]interface{}{
|
|
"descriptor": address,
|
|
"details": "tokenBalances",
|
|
"contractFilter": contract,
|
|
"page": addressPage,
|
|
"pageSize": addressPageSize,
|
|
})
|
|
|
|
var info evmAddressTokenBalanceResponse
|
|
if err := json.Unmarshal(resp.Data, &info); err != nil {
|
|
t.Fatalf("decode websocket getAccountInfo EVM contractFilter response: %v", err)
|
|
}
|
|
|
|
assertEVMTokenBalancesPayload(t, &info, address, "WsGetAccountInfoContractFilterEVM")
|
|
assertEVMTokenBalancesHaveHoldingsFields(t, &info, address, "WsGetAccountInfoContractFilterEVM")
|
|
assertEVMTokenListContractsMatch(t, info.Tokens, contract, "WsGetAccountInfoContractFilterEVM")
|
|
}
|