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  }