github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/access/rest/routes/account_keys_test.go (about) 1 package routes 2 3 import ( 4 "fmt" 5 "math" 6 "net/http" 7 "net/url" 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/model/flow" 16 17 "github.com/onflow/flow-go/utils/unittest" 18 ) 19 20 // TestGetAccountKeyByIndex tests local getAccountKeyByIndex request. 21 // 22 // Runs the following tests: 23 // 1. Get key by address and index at latest sealed block. 24 // 2. Get key by address and index at latest finalized block. 25 // 3. Get missing key by address and index at latest sealed block. 26 // 4. Get missing key by address and index at latest finalized block. 27 // 5. Get key by missing address and index at latest sealed block. 28 // 6. Get key by missing address and index at latest finalized block. 29 // 7. Get key by address and index at height. 30 // 8. Get key by address and index at missing block. 31 func TestGetAccountKeyByIndex(t *testing.T) { 32 backend := mock.NewAPI(t) 33 34 t.Run("get key by address and index at latest sealed block", func(t *testing.T) { 35 account := accountFixture(t) 36 var height uint64 = 100 37 block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) 38 39 req := getAccountKeyByIndexRequest(t, account, "0", sealedHeightQueryParam) 40 41 backend.Mock. 42 On("GetLatestBlockHeader", mocktestify.Anything, true). 43 Return(block, flow.BlockStatusSealed, nil) 44 45 backend.Mock. 46 On("GetAccountAtBlockHeight", mocktestify.Anything, account.Address, height). 47 Return(account, nil) 48 49 expected := expectedAccountKeyResponse(account) 50 51 assertOKResponse(t, req, expected, backend) 52 mocktestify.AssertExpectationsForObjects(t, backend) 53 }) 54 55 t.Run("get key by address and index at latest finalized block", func(t *testing.T) { 56 account := accountFixture(t) 57 var height uint64 = 100 58 block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) 59 60 req := getAccountKeyByIndexRequest(t, account, "0", finalHeightQueryParam) 61 62 backend.Mock. 63 On("GetLatestBlockHeader", mocktestify.Anything, false). 64 Return(block, flow.BlockStatusFinalized, nil) 65 66 backend.Mock. 67 On("GetAccountAtBlockHeight", mocktestify.Anything, account.Address, height). 68 Return(account, nil) 69 70 expected := expectedAccountKeyResponse(account) 71 72 assertOKResponse(t, req, expected, backend) 73 mocktestify.AssertExpectationsForObjects(t, backend) 74 }) 75 76 t.Run("get missing key by address and index at latest sealed block", func(t *testing.T) { 77 account := accountFixture(t) 78 var height uint64 = 100 79 index := "2" 80 block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) 81 82 req := getAccountKeyByIndexRequest(t, account, index, sealedHeightQueryParam) 83 84 backend.Mock. 85 On("GetLatestBlockHeader", mocktestify.Anything, true). 86 Return(block, flow.BlockStatusSealed, nil) 87 88 backend.Mock. 89 On("GetAccountAtBlockHeight", mocktestify.Anything, account.Address, height). 90 Return(account, nil) 91 92 statusCode := 404 93 expected := fmt.Sprintf(` 94 { 95 "code": %d, 96 "message": "account key with index: %s does not exist" 97 } 98 `, statusCode, index) 99 100 assertResponse(t, req, statusCode, expected, backend) 101 mocktestify.AssertExpectationsForObjects(t, backend) 102 }) 103 104 t.Run("get missing key by address and index at latest finalized block", func(t *testing.T) { 105 account := accountFixture(t) 106 var height uint64 = 100 107 index := "2" 108 block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) 109 110 req := getAccountKeyByIndexRequest(t, account, index, finalHeightQueryParam) 111 112 backend.Mock. 113 On("GetLatestBlockHeader", mocktestify.Anything, false). 114 Return(block, flow.BlockStatusFinalized, nil) 115 116 backend.Mock. 117 On("GetAccountAtBlockHeight", mocktestify.Anything, account.Address, height). 118 Return(account, nil) 119 120 statusCode := 404 121 expected := fmt.Sprintf(` 122 { 123 "code": %d, 124 "message": "account key with index: %s does not exist" 125 } 126 `, statusCode, index) 127 128 assertResponse(t, req, statusCode, expected, backend) 129 mocktestify.AssertExpectationsForObjects(t, backend) 130 }) 131 132 t.Run("get key by missing address and index at latest sealed block", func(t *testing.T) { 133 account := accountFixture(t) 134 var height uint64 = 100 135 index := "2" 136 block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) 137 138 req := getAccountKeyByIndexRequest(t, account, index, sealedHeightQueryParam) 139 140 backend.Mock. 141 On("GetLatestBlockHeader", mocktestify.Anything, true). 142 Return(block, flow.BlockStatusSealed, nil) 143 144 err := fmt.Errorf("account with address: %s does not exist", account.Address) 145 backend.Mock. 146 On("GetAccountAtBlockHeight", mocktestify.Anything, account.Address, height). 147 Return(nil, err) 148 149 statusCode := 404 150 expected := fmt.Sprintf(` 151 { 152 "code": %d, 153 "message": "account with address: %s does not exist" 154 } 155 `, statusCode, account.Address) 156 157 assertResponse(t, req, statusCode, expected, backend) 158 mocktestify.AssertExpectationsForObjects(t, backend) 159 }) 160 161 t.Run("get key by missing address and index at latest finalized block", func(t *testing.T) { 162 account := accountFixture(t) 163 var height uint64 = 100 164 index := "2" 165 block := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(height)) 166 167 req := getAccountKeyByIndexRequest(t, account, index, finalHeightQueryParam) 168 169 backend.Mock. 170 On("GetLatestBlockHeader", mocktestify.Anything, false). 171 Return(block, flow.BlockStatusFinalized, nil) 172 173 err := fmt.Errorf("account with address: %s does not exist", account.Address) 174 backend.Mock. 175 On("GetAccountAtBlockHeight", mocktestify.Anything, account.Address, height). 176 Return(nil, err) 177 178 statusCode := 404 179 expected := fmt.Sprintf(` 180 { 181 "code": %d, 182 "message": "account with address: %s does not exist" 183 } 184 `, statusCode, account.Address) 185 186 assertResponse(t, req, statusCode, expected, backend) 187 mocktestify.AssertExpectationsForObjects(t, backend) 188 }) 189 190 t.Run("get key by address and index at height", func(t *testing.T) { 191 var height uint64 = 1337 192 account := accountFixture(t) 193 req := getAccountKeyByIndexRequest(t, account, "0", "1337") 194 195 backend.Mock. 196 On("GetAccountAtBlockHeight", mocktestify.Anything, account.Address, height). 197 Return(account, nil) 198 199 expected := expectedAccountKeyResponse(account) 200 201 assertOKResponse(t, req, expected, backend) 202 mocktestify.AssertExpectationsForObjects(t, backend) 203 }) 204 205 t.Run("get key by address and index at missing block", func(t *testing.T) { 206 backend := mock.NewAPI(t) 207 account := accountFixture(t) 208 const finalHeight uint64 = math.MaxUint64 - 2 209 210 req := getAccountKeyByIndexRequest(t, account, "0", finalHeightQueryParam) 211 212 err := fmt.Errorf("block with height: %d does not exist", finalHeight) 213 backend.Mock. 214 On("GetLatestBlockHeader", mocktestify.Anything, false). 215 Return(nil, flow.BlockStatusUnknown, err) 216 217 statusCode := 404 218 expected := fmt.Sprintf(` 219 { 220 "code": %d, 221 "message": "block with height: %d does not exist" 222 } 223 `, statusCode, finalHeight) 224 225 assertResponse(t, req, statusCode, expected, backend) 226 mocktestify.AssertExpectationsForObjects(t, backend) 227 }) 228 229 tests := []struct { 230 name string 231 url string 232 out string 233 }{ 234 { 235 "get key with invalid address", 236 accountKeyURL(t, "123", "3", "100"), 237 `{"code":400, "message":"invalid address"}`, 238 }, 239 { 240 "get key with invalid index", 241 accountKeyURL( 242 t, 243 unittest.AddressFixture().String(), 244 "foo", 245 "100", 246 ), 247 `{"code":400, "message":"invalid key index: value must be an unsigned 64 bit integer"}`, 248 }, 249 { 250 "get key with invalid height", 251 accountKeyURL( 252 t, 253 unittest.AddressFixture().String(), 254 "2", 255 "-100", 256 ), 257 `{"code":400, "message":"invalid height format"}`, 258 }, 259 } 260 261 for _, test := range tests { 262 t.Run(test.name, func(t *testing.T) { 263 req, _ := http.NewRequest("GET", test.url, nil) 264 rr := executeRequest(req, backend) 265 assert.Equal(t, http.StatusBadRequest, rr.Code) 266 assert.JSONEq(t, test.out, rr.Body.String()) 267 }) 268 } 269 } 270 271 func accountKeyURL(t *testing.T, address string, index string, height string) string { 272 u, err := url.ParseRequestURI( 273 fmt.Sprintf("/v1/accounts/%s/keys/%s", address, index), 274 ) 275 require.NoError(t, err) 276 q := u.Query() 277 278 if height != "" { 279 q.Add("block_height", height) 280 } 281 282 u.RawQuery = q.Encode() 283 return u.String() 284 } 285 286 func getAccountKeyByIndexRequest( 287 t *testing.T, 288 account *flow.Account, 289 index string, 290 height string, 291 ) *http.Request { 292 req, err := http.NewRequest( 293 "GET", 294 accountKeyURL(t, account.Address.String(), index, height), 295 nil, 296 ) 297 require.NoError(t, err) 298 299 return req 300 } 301 302 func expectedAccountKeyResponse(account *flow.Account) string { 303 return fmt.Sprintf(` 304 { 305 "index":"0", 306 "public_key":"%s", 307 "signing_algorithm":"ECDSA_P256", 308 "hashing_algorithm":"SHA3_256", 309 "sequence_number":"0", 310 "weight":"1000", 311 "revoked":false 312 }`, 313 account.Keys[0].PublicKey.String(), 314 ) 315 }