Commit b79cb066 authored by Ethen Pociask's avatar Ethen Pociask

[indexer.client] Updated tests

parent fea2d96f
......@@ -135,7 +135,7 @@ func (a *API) startServer(ctx context.Context) error {
}
// Update the port in the config in case the OS chose a different port
// than the one we requested (e.g. port 0)
// than the one we requested (e.g. using port 0 to fetch a random open port)
a.serverConfig.Port = tcpAddr.Port
err = http.Serve(listener, server.Handler)
......
......@@ -7,7 +7,7 @@ import (
"net/http/httptest"
"testing"
"github.com/ethereum-optimism/optimism/indexer/api/routes"
"github.com/ethereum-optimism/optimism/indexer/api/models"
"github.com/ethereum-optimism/optimism/indexer/config"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/op-node/testlog"
......@@ -116,7 +116,7 @@ func TestL1BridgeDepositsHandler(t *testing.T) {
assert.Equal(t, http.StatusOK, responseRecorder.Code)
var resp routes.DepositResponse
var resp models.DepositResponse
err = json.Unmarshal(responseRecorder.Body.Bytes(), &resp)
assert.Nil(t, err)
......@@ -137,7 +137,7 @@ func TestL2BridgeWithdrawalsByAddressHandler(t *testing.T) {
responseRecorder := httptest.NewRecorder()
api.router.ServeHTTP(responseRecorder, request)
var resp routes.WithdrawalResponse
var resp models.WithdrawalResponse
err = json.Unmarshal(responseRecorder.Body.Bytes(), &resp)
assert.Nil(t, err)
......
package models
// DepositItem ... Deposit item model for API responses
type DepositItem struct {
Guid string `json:"guid"`
From string `json:"from"`
To string `json:"to"`
Timestamp uint64 `json:"timestamp"`
L1BlockHash string `json:"l1BlockHash"`
L1TxHash string `json:"l1TxHash"`
L2TxHash string `json:"l2TxHash"`
Amount string `json:"amount"`
L1TokenAddress string `json:"l1TokenAddress"`
L2TokenAddress string `json:"l2TokenAddress"`
}
// DepositResponse ... Data model for API JSON response
type DepositResponse struct {
Cursor string `json:"cursor"`
HasNextPage bool `json:"hasNextPage"`
Items []DepositItem `json:"items"`
}
// WithdrawalItem ... Data model for API JSON response
type WithdrawalItem struct {
Guid string `json:"guid"`
From string `json:"from"`
To string `json:"to"`
TransactionHash string `json:"transactionHash"`
MessageHash string `json:"messageHash"`
Timestamp uint64 `json:"timestamp"`
L2BlockHash string `json:"l2BlockHash"`
Amount string `json:"amount"`
ProofTransactionHash string `json:"proofTransactionHash"`
ClaimTransactionHash string `json:"claimTransactionHash"`
L1TokenAddress string `json:"l1TokenAddress"`
L2TokenAddress string `json:"l2TokenAddress"`
}
// WithdrawalResponse ... Data model for API JSON response
type WithdrawalResponse struct {
Cursor string `json:"cursor"`
HasNextPage bool `json:"hasNextPage"`
Items []WithdrawalItem `json:"items"`
}
......@@ -3,37 +3,17 @@ package routes
import (
"net/http"
"github.com/ethereum-optimism/optimism/indexer/api/models"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum/go-ethereum/common"
"github.com/go-chi/chi/v5"
)
// DepositItem ... Deposit item model for API responses
type DepositItem struct {
Guid string `json:"guid"`
From string `json:"from"`
To string `json:"to"`
Timestamp uint64 `json:"timestamp"`
L1BlockHash string `json:"l1BlockHash"`
L1TxHash string `json:"l1TxHash"`
L2TxHash string `json:"l2TxHash"`
Amount string `json:"amount"`
L1TokenAddress string `json:"l1TokenAddress"`
L2TokenAddress string `json:"l2TokenAddress"`
}
// DepositResponse ... Data model for API JSON response
type DepositResponse struct {
Cursor string `json:"cursor"`
HasNextPage bool `json:"hasNextPage"`
Items []DepositItem `json:"items"`
}
// newDepositResponse ... Converts a database.L1BridgeDepositsResponse to an api.DepositResponse
func newDepositResponse(deposits *database.L1BridgeDepositsResponse) DepositResponse {
items := make([]DepositItem, len(deposits.Deposits))
func newDepositResponse(deposits *database.L1BridgeDepositsResponse) models.DepositResponse {
items := make([]models.DepositItem, len(deposits.Deposits))
for i, deposit := range deposits.Deposits {
item := DepositItem{
item := models.DepositItem{
Guid: deposit.L1BridgeDeposit.TransactionSourceHash.String(),
L1BlockHash: deposit.L1BlockHash.String(),
Timestamp: deposit.L1BridgeDeposit.Tx.Timestamp,
......@@ -48,7 +28,7 @@ func newDepositResponse(deposits *database.L1BridgeDepositsResponse) DepositResp
items[i] = item
}
return DepositResponse{
return models.DepositResponse{
Cursor: deposits.Cursor,
HasNextPage: deposits.HasNextPage,
Items: items,
......@@ -57,10 +37,26 @@ func newDepositResponse(deposits *database.L1BridgeDepositsResponse) DepositResp
// L1DepositsHandler ... Handles /api/v0/deposits/{address} GET requests
func (h Routes) L1DepositsHandler(w http.ResponseWriter, r *http.Request) {
address := common.HexToAddress(chi.URLParam(r, "address"))
addressValue := chi.URLParam(r, "address")
cursor := r.URL.Query().Get("cursor")
limitQuery := r.URL.Query().Get("limit")
address, err := h.v.ParseValidateAddress(addressValue)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
h.logger.Error("Invalid address param", "param", addressValue)
h.logger.Error(err.Error())
return
}
err = h.v.ValidateCursor(cursor)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
h.logger.Error("Invalid cursor param", "param", cursor)
h.logger.Error(err.Error())
return
}
limit, err := h.v.ParseValidateLimit(limitQuery)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
......
......@@ -9,8 +9,7 @@ import (
)
// Validator ... Validates API user request parameters
type Validator struct {
}
type Validator struct{}
// ParseValidateLimit ... Validates and parses the limit query parameters
func (v *Validator) ParseValidateLimit(limit string) (int, error) {
......@@ -33,7 +32,7 @@ func (v *Validator) ParseValidateLimit(limit string) (int, error) {
// ParseValidateAddress ... Validates and parses the address query parameter
func (v *Validator) ParseValidateAddress(addr string) (common.Address, error) {
if common.IsHexAddress(addr) {
if !common.IsHexAddress(addr) {
return common.Address{}, errors.New("address must be represented as a valid hexadecimal string")
}
......@@ -44,3 +43,20 @@ func (v *Validator) ParseValidateAddress(addr string) (common.Address, error) {
return parsedAddr, nil
}
// ValidateCursor ... Validates and parses the cursor query parameter
func (v *Validator) ValidateCursor(cursor string) error {
if cursor == "" {
return nil
}
if len(cursor) != 66 { // 0x + 64 chars
return errors.New("cursor must be a 32 byte hex string")
}
if cursor[:2] != "0x" {
return errors.New("cursor must begin with 0x")
}
return nil
}
......@@ -29,7 +29,7 @@ func Test_ParseValidateAddress(t *testing.T) {
v := Validator{}
// (1) Happy case
addr := "0x1"
addr := "0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5"
_, err := v.ParseValidateAddress(addr)
require.NoError(t, err, "address should be pass")
......@@ -39,7 +39,26 @@ func Test_ParseValidateAddress(t *testing.T) {
require.Error(t, err, "address must be represented as a valid hexadecimal string")
// (3) Zero address
addr = "0x0"
addr = "0x0000000000000000000000000000000000000000"
_, err = v.ParseValidateAddress(addr)
require.Error(t, err, "address cannot be black-hole value")
}
func Test_ParseValidateCursor(t *testing.T) {
v := Validator{}
// (1) Happy case
cursor := "0xf3fd2eb696dab4263550b938726f9b3606e334cce6ebe27446bc26cb700b94e0"
err := v.ValidateCursor(cursor)
require.NoError(t, err, "cursor should be pass")
// (2) Invalid length
cursor = "0x000"
err = v.ValidateCursor(cursor)
require.Error(t, err, "cursor must be 32 byte hex string")
// (3) Invalid hex
cursor = "0🫡"
err = v.ValidateCursor(cursor)
require.Error(t, err, "cursor must start with 0x")
}
......@@ -3,37 +3,17 @@ package routes
import (
"net/http"
"github.com/ethereum-optimism/optimism/indexer/api/models"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum/go-ethereum/common"
"github.com/go-chi/chi/v5"
)
type WithdrawalItem struct {
Guid string `json:"guid"`
From string `json:"from"`
To string `json:"to"`
TransactionHash string `json:"transactionHash"`
MessageHash string `json:"messageHash"`
Timestamp uint64 `json:"timestamp"`
L2BlockHash string `json:"l2BlockHash"`
Amount string `json:"amount"`
ProofTransactionHash string `json:"proofTransactionHash"`
ClaimTransactionHash string `json:"claimTransactionHash"`
L1TokenAddress string `json:"l1TokenAddress"`
L2TokenAddress string `json:"l2TokenAddress"`
}
type WithdrawalResponse struct {
Cursor string `json:"cursor"`
HasNextPage bool `json:"hasNextPage"`
Items []WithdrawalItem `json:"items"`
}
// FIXME make a pure function that returns a struct instead of newWithdrawalResponse
func newWithdrawalResponse(withdrawals *database.L2BridgeWithdrawalsResponse) WithdrawalResponse {
items := make([]WithdrawalItem, len(withdrawals.Withdrawals))
// newWithdrawalResponse ... Converts a database.L2BridgeWithdrawalsResponse to an api.WithdrawalResponse
func newWithdrawalResponse(withdrawals *database.L2BridgeWithdrawalsResponse) models.WithdrawalResponse {
items := make([]models.WithdrawalItem, len(withdrawals.Withdrawals))
for i, withdrawal := range withdrawals.Withdrawals {
item := WithdrawalItem{
item := models.WithdrawalItem{
Guid: withdrawal.L2BridgeWithdrawal.TransactionWithdrawalHash.String(),
L2BlockHash: withdrawal.L2BlockHash.String(),
From: withdrawal.L2BridgeWithdrawal.Tx.FromAddress.String(),
......@@ -48,7 +28,7 @@ func newWithdrawalResponse(withdrawals *database.L2BridgeWithdrawalsResponse) Wi
items[i] = item
}
return WithdrawalResponse{
return models.WithdrawalResponse{
Cursor: withdrawals.Cursor,
HasNextPage: withdrawals.HasNextPage,
Items: items,
......@@ -57,10 +37,26 @@ func newWithdrawalResponse(withdrawals *database.L2BridgeWithdrawalsResponse) Wi
// L2WithdrawalsHandler ... Handles /api/v0/withdrawals/{address} GET requests
func (h Routes) L2WithdrawalsHandler(w http.ResponseWriter, r *http.Request) {
address := common.HexToAddress(chi.URLParam(r, "address"))
addressValue := chi.URLParam(r, "address")
cursor := r.URL.Query().Get("cursor")
limitQuery := r.URL.Query().Get("limit")
address, err := h.v.ParseValidateAddress(addressValue)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
h.logger.Error("Invalid address param", "param", addressValue)
h.logger.Error(err.Error())
return
}
err = h.v.ValidateCursor(cursor)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
h.logger.Error("Invalid cursor param", "param", cursor)
h.logger.Error(err.Error())
return
}
limit, err := h.v.ParseValidateLimit(limitQuery)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
......
......@@ -9,7 +9,7 @@ import (
"encoding/json"
"github.com/ethereum-optimism/optimism/indexer/api"
"github.com/ethereum-optimism/optimism/indexer/api/routes"
"github.com/ethereum-optimism/optimism/indexer/api/models"
"github.com/ethereum-optimism/optimism/indexer/node"
"github.com/ethereum/go-ethereum/common"
)
......@@ -123,8 +123,8 @@ func (c *Client) HealthCheck() error {
}
// GetDepositsByAddress ... Gets a deposit response object provided an L1 address and cursor
func (c *Client) GetDepositsByAddress(l1Address common.Address, cursor string) (*routes.DepositResponse, error) {
var dResponse *routes.DepositResponse
func (c *Client) GetDepositsByAddress(l1Address common.Address, cursor string) (*models.DepositResponse, error) {
var dResponse *models.DepositResponse
url := c.cfg.BaseURL + api.DepositsPath + l1Address.String() + urlParams
endpoint := fmt.Sprintf(url, cursor, c.cfg.PaginationLimit)
......@@ -141,8 +141,8 @@ func (c *Client) GetDepositsByAddress(l1Address common.Address, cursor string) (
}
// GetAllDepositsByAddress ... Gets all deposits provided a L1 address
func (c *Client) GetAllDepositsByAddress(l1Address common.Address) ([]routes.DepositItem, error) {
var deposits []routes.DepositItem
func (c *Client) GetAllDepositsByAddress(l1Address common.Address) ([]models.DepositItem, error) {
var deposits []models.DepositItem
cursor := ""
for {
......@@ -165,8 +165,8 @@ func (c *Client) GetAllDepositsByAddress(l1Address common.Address) ([]routes.Dep
}
// GetAllWithdrawalsByAddress ... Gets all withdrawals provided a L2 address
func (c *Client) GetAllWithdrawalsByAddress(l2Address common.Address) ([]routes.WithdrawalItem, error) {
var withdrawals []routes.WithdrawalItem
func (c *Client) GetAllWithdrawalsByAddress(l2Address common.Address) ([]models.WithdrawalItem, error) {
var withdrawals []models.WithdrawalItem
cursor := ""
for {
......@@ -188,8 +188,8 @@ func (c *Client) GetAllWithdrawalsByAddress(l2Address common.Address) ([]routes.
}
// GetWithdrawalsByAddress ... Gets a withdrawal response object provided an L2 address and cursor
func (c *Client) GetWithdrawalsByAddress(l2Address common.Address, cursor string) (*routes.WithdrawalResponse, error) {
var wResponse *routes.WithdrawalResponse
func (c *Client) GetWithdrawalsByAddress(l2Address common.Address, cursor string) (*models.WithdrawalResponse, error) {
var wResponse *models.WithdrawalResponse
url := c.cfg.BaseURL + api.WithdrawalsPath + l2Address.String() + urlParams
endpoint := fmt.Sprintf(url, cursor, c.cfg.PaginationLimit)
......
......@@ -52,7 +52,7 @@ func TestE2EBridgeTransfersStandardBridgeETHDeposit(t *testing.T) {
}))
cursor := ""
limit := 0
limit := 100
aliceDeposits, err := testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr, cursor, limit)
......@@ -118,7 +118,7 @@ func TestE2EBridgeTransfersOptimismPortalETHReceive(t *testing.T) {
return l1Header != nil && l1Header.Number.Uint64() >= portalDepositReceipt.BlockNumber.Uint64(), nil
}))
aliceDeposits, err := testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr, "", 0)
aliceDeposits, err := testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr, "", 1)
require.NoError(t, err)
require.NotNil(t, aliceDeposits)
require.Len(t, aliceDeposits.Deposits, 1)
......@@ -145,7 +145,7 @@ func TestE2EBridgeTransfersOptimismPortalETHReceive(t *testing.T) {
}))
// Still nil as the withdrawal did not occur through the standard bridge
aliceDeposits, err = testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr, "", 0)
aliceDeposits, err = testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr, "", 1)
require.NoError(t, err)
require.Nil(t, aliceDeposits.Deposits[0].L1BridgeDeposit.CrossDomainMessageHash)
}
......@@ -187,7 +187,7 @@ func TestE2EBridgeTransfersCursoredDeposits(t *testing.T) {
}))
// Get All
aliceDeposits, err := testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr, "", 0)
aliceDeposits, err := testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr, "", 3)
require.NotNil(t, aliceDeposits)
require.NoError(t, err)
require.Len(t, aliceDeposits.Deposits, 3)
......@@ -200,14 +200,14 @@ func TestE2EBridgeTransfersCursoredDeposits(t *testing.T) {
require.Len(t, aliceDeposits.Deposits, 2)
require.True(t, aliceDeposits.HasNextPage)
aliceDeposits, err = testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr, aliceDeposits.Cursor, 2)
aliceDeposits, err = testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr, aliceDeposits.Cursor, 1)
require.NoError(t, err)
require.NotNil(t, aliceDeposits)
require.Len(t, aliceDeposits.Deposits, 1)
require.False(t, aliceDeposits.HasNextPage)
// Returns the results in the right order
aliceDeposits, err = testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr, "", 100)
aliceDeposits, err = testSuite.DB.BridgeTransfers.L1BridgeDepositsByAddress(aliceAddr, "", 3)
require.NotNil(t, aliceDeposits)
require.NoError(t, err)
for i := 0; i < 3; i++ {
......
......@@ -26,6 +26,7 @@ import (
/*
NOTE - Most of the current tests fetch chain data via direct database queries. These could all
be transitioned to use the API client instead to better simulate/validate real-world usage.
Supporting this would potentially require adding new API endpoints for the specific query lookup types.
*/
type E2ETestSuite struct {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment