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 }