github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/access/rest/routes/accounts_test.go (about)

     1  package routes
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"net/url"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	mocktestify "github.com/stretchr/testify/mock"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/onflow/flow-go/access/mock"
    15  	"github.com/onflow/flow-go/engine/access/rest/middleware"
    16  	"github.com/onflow/flow-go/model/flow"
    17  	"github.com/onflow/flow-go/utils/unittest"
    18  )
    19  
    20  const expandableFieldKeys = "keys"
    21  const expandableFieldContracts = "contracts"
    22  
    23  func accountURL(t *testing.T, address string, height string) string {
    24  	u, err := url.ParseRequestURI(fmt.Sprintf("/v1/accounts/%s", address))
    25  	require.NoError(t, err)
    26  	q := u.Query()
    27  
    28  	if height != "" {
    29  		q.Add("block_height", height)
    30  	}
    31  
    32  	u.RawQuery = q.Encode()
    33  	return u.String()
    34  }
    35  
    36  // TestAccessGetAccount tests local getAccount request.
    37  //
    38  //	Runs the following tests:
    39  //	1. Get account by address at latest sealed block.
    40  //	2. Get account by address at latest finalized block.
    41  //	3. Get account by address at height.
    42  //	4. Get account by address at height condensed.
    43  //	5. Get invalid account.
    44  func TestAccessGetAccount(t *testing.T) {
    45  	backend := &mock.API{}
    46  
    47  	t.Run("get by address at latest sealed block", func(t *testing.T) {
    48  		account := accountFixture(t)
    49  		var height uint64 = 100
    50  		block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height))
    51  
    52  		req := getAccountRequest(t, account, sealedHeightQueryParam, expandableFieldKeys, expandableFieldContracts)
    53  
    54  		backend.Mock.
    55  			On("GetLatestBlockHeader", mocktestify.Anything, true).
    56  			Return(block, flow.BlockStatusSealed, nil)
    57  
    58  		backend.Mock.
    59  			On("GetAccountAtBlockHeight", mocktestify.Anything, account.Address, height).
    60  			Return(account, nil)
    61  
    62  		expected := expectedExpandedResponse(account)
    63  
    64  		assertOKResponse(t, req, expected, backend)
    65  		mocktestify.AssertExpectationsForObjects(t, backend)
    66  	})
    67  
    68  	t.Run("get by address at latest finalized block", func(t *testing.T) {
    69  
    70  		var height uint64 = 100
    71  		block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height))
    72  		account := accountFixture(t)
    73  
    74  		req := getAccountRequest(t, account, finalHeightQueryParam, expandableFieldKeys, expandableFieldContracts)
    75  		backend.Mock.
    76  			On("GetLatestBlockHeader", mocktestify.Anything, false).
    77  			Return(block, flow.BlockStatusFinalized, nil)
    78  		backend.Mock.
    79  			On("GetAccountAtBlockHeight", mocktestify.Anything, account.Address, height).
    80  			Return(account, nil)
    81  
    82  		expected := expectedExpandedResponse(account)
    83  
    84  		assertOKResponse(t, req, expected, backend)
    85  		mocktestify.AssertExpectationsForObjects(t, backend)
    86  	})
    87  
    88  	t.Run("get by address at height", func(t *testing.T) {
    89  		var height uint64 = 1337
    90  		account := accountFixture(t)
    91  		req := getAccountRequest(t, account, fmt.Sprintf("%d", height), expandableFieldKeys, expandableFieldContracts)
    92  
    93  		backend.Mock.
    94  			On("GetAccountAtBlockHeight", mocktestify.Anything, account.Address, height).
    95  			Return(account, nil)
    96  
    97  		expected := expectedExpandedResponse(account)
    98  
    99  		assertOKResponse(t, req, expected, backend)
   100  		mocktestify.AssertExpectationsForObjects(t, backend)
   101  	})
   102  
   103  	t.Run("get by address at height condensed", func(t *testing.T) {
   104  		var height uint64 = 1337
   105  		account := accountFixture(t)
   106  		req := getAccountRequest(t, account, fmt.Sprintf("%d", height))
   107  
   108  		backend.Mock.
   109  			On("GetAccountAtBlockHeight", mocktestify.Anything, account.Address, height).
   110  			Return(account, nil)
   111  
   112  		expected := expectedCondensedResponse(account)
   113  
   114  		assertOKResponse(t, req, expected, backend)
   115  		mocktestify.AssertExpectationsForObjects(t, backend)
   116  	})
   117  
   118  	t.Run("get invalid", func(t *testing.T) {
   119  		tests := []struct {
   120  			url string
   121  			out string
   122  		}{
   123  			{accountURL(t, "123", ""), `{"code":400, "message":"invalid address"}`},
   124  			{accountURL(t, unittest.AddressFixture().String(), "foo"), `{"code":400, "message":"invalid height format"}`},
   125  		}
   126  
   127  		for i, test := range tests {
   128  			req, _ := http.NewRequest("GET", test.url, nil)
   129  			rr := executeRequest(req, backend)
   130  
   131  			assert.Equal(t, http.StatusBadRequest, rr.Code)
   132  			assert.JSONEq(t, test.out, rr.Body.String(), fmt.Sprintf("test #%d failed: %v", i, test))
   133  		}
   134  	})
   135  }
   136  
   137  func expectedExpandedResponse(account *flow.Account) string {
   138  	return fmt.Sprintf(`{
   139  			  "address":"%s",
   140  			  "balance":"100",
   141  			  "keys":[
   142  				  {
   143  					 "index":"0",
   144  					 "public_key":"%s",
   145  					 "signing_algorithm":"ECDSA_P256",
   146  					 "hashing_algorithm":"SHA3_256",
   147  					 "sequence_number":"0",
   148  					 "weight":"1000",
   149  					 "revoked":false
   150  				  }
   151  			  ],
   152                "_links":{"_self":"/v1/accounts/%s" },
   153                "_expandable": {},
   154                "contracts": {"contract1":"Y29udHJhY3Qx", "contract2":"Y29udHJhY3Qy"}
   155  			}`, account.Address, account.Keys[0].PublicKey.String(), account.Address)
   156  }
   157  
   158  func expectedCondensedResponse(account *flow.Account) string {
   159  	return fmt.Sprintf(`{
   160  			  "address":"%s",
   161  			  "balance":"100",
   162                "_links":{"_self":"/v1/accounts/%s" },
   163                "_expandable":{"contracts":"contracts", "keys":"keys"}
   164  			}`, account.Address, account.Address)
   165  }
   166  
   167  func getAccountRequest(t *testing.T, account *flow.Account, height string, expandFields ...string) *http.Request {
   168  	req, err := http.NewRequest("GET", accountURL(t, account.Address.String(), height), nil)
   169  	if len(expandFields) > 0 {
   170  		fieldParam := strings.Join(expandFields, ",")
   171  		q := req.URL.Query()
   172  		q.Add(middleware.ExpandQueryParam, fieldParam)
   173  		req.URL.RawQuery = q.Encode()
   174  	}
   175  
   176  	require.NoError(t, err)
   177  	return req
   178  }
   179  
   180  func accountFixture(t *testing.T) *flow.Account {
   181  	account, err := unittest.AccountFixture()
   182  	require.NoError(t, err)
   183  	return account
   184  }