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

     1  package routes
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"strings"
     8  	"testing"
     9  
    10  	"google.golang.org/grpc/codes"
    11  	"google.golang.org/grpc/status"
    12  
    13  	"github.com/onflow/flow-go/model/flow"
    14  
    15  	"github.com/stretchr/testify/assert"
    16  	mocks "github.com/stretchr/testify/mock"
    17  
    18  	"github.com/onflow/flow-go/access/mock"
    19  	"github.com/onflow/flow-go/utils/unittest"
    20  )
    21  
    22  func getCollectionReq(id string, expandTransactions bool) *http.Request {
    23  	url := fmt.Sprintf("/v1/collections/%s", id)
    24  	if expandTransactions {
    25  		url = fmt.Sprintf("%s?expand=transactions", url)
    26  	}
    27  
    28  	req, _ := http.NewRequest("GET", url, nil)
    29  	return req
    30  }
    31  
    32  func TestGetCollections(t *testing.T) {
    33  	backend := &mock.API{}
    34  
    35  	t.Run("get by ID", func(t *testing.T) {
    36  		inputs := []flow.LightCollection{
    37  			unittest.CollectionFixture(1).Light(),
    38  			unittest.CollectionFixture(10).Light(),
    39  			unittest.CollectionFixture(100).Light(),
    40  		}
    41  
    42  		for _, col := range inputs {
    43  			backend.Mock.
    44  				On("GetCollectionByID", mocks.Anything, col.ID()).
    45  				Return(&col, nil).
    46  				Once()
    47  
    48  			txs := make([]string, len(col.Transactions))
    49  			for i, tx := range col.Transactions {
    50  				txs[i] = fmt.Sprintf("\"/v1/transactions/%s\"", tx.String())
    51  			}
    52  			transactionsStr := fmt.Sprintf("[%s]", strings.Join(txs, ","))
    53  
    54  			expected := fmt.Sprintf(`{
    55  				"id":"%s",	
    56  				"_links": {
    57  					"_self": "/v1/collections/%s"
    58  				},
    59                  "_expandable": {
    60                     "transactions": %s
    61                  }
    62  			}`, col.ID(), col.ID(), transactionsStr)
    63  
    64  			req := getCollectionReq(col.ID().String(), false)
    65  			assertOKResponse(t, req, expected, backend)
    66  			mocks.AssertExpectationsForObjects(t, backend)
    67  		}
    68  	})
    69  
    70  	t.Run("get by ID expand transactions", func(t *testing.T) {
    71  		col := unittest.CollectionFixture(3).Light()
    72  
    73  		transactions := make([]flow.TransactionBody, len(col.Transactions))
    74  		for i := range col.Transactions {
    75  			transactions[i] = unittest.TransactionBodyFixture()
    76  			col.Transactions[i] = transactions[i].ID() // overwrite tx ids
    77  
    78  			backend.Mock.
    79  				On("GetTransaction", mocks.Anything, transactions[i].ID()).
    80  				Return(&transactions[i], nil).
    81  				Once()
    82  		}
    83  
    84  		backend.Mock.
    85  			On("GetCollectionByID", mocks.Anything, col.ID()).
    86  			Return(&col, nil).
    87  			Once()
    88  
    89  		req := getCollectionReq(col.ID().String(), true)
    90  		rr := executeRequest(req, backend)
    91  
    92  		assert.Equal(t, http.StatusOK, rr.Code)
    93  		// really hacky but we can't build whole response since it's really complex
    94  		// so we just make sure the transactions are included and have defined values
    95  		// anyhow we already test transaction responses in transaction tests
    96  		var res map[string]interface{}
    97  		err := json.Unmarshal(rr.Body.Bytes(), &res)
    98  		assert.NoError(t, err)
    99  		resTx := res["transactions"].([]interface{})
   100  		for i, r := range resTx {
   101  			c := r.(map[string]interface{})
   102  			assert.Equal(t, transactions[i].ID().String(), c["id"])
   103  			assert.NotNil(t, c["envelope_signatures"])
   104  		}
   105  
   106  		mocks.AssertExpectationsForObjects(t, backend)
   107  	})
   108  
   109  	t.Run("get by ID errors out", func(t *testing.T) {
   110  		testID := unittest.IdentifierFixture()
   111  		tests := []struct {
   112  			id        string
   113  			mockValue *flow.LightCollection
   114  			mockErr   error
   115  			response  string
   116  			status    int
   117  		}{{
   118  			testID.String(),
   119  			nil,
   120  			status.Error(codes.NotFound, "not found"),
   121  			`{"code":404,"message":"Flow resource not found: not found"}`,
   122  			http.StatusNotFound,
   123  		}, {
   124  			"invalidID",
   125  			nil,
   126  			nil,
   127  			`{"code":400,"message":"invalid ID format"}`,
   128  			http.StatusBadRequest,
   129  		},
   130  			{
   131  				unittest.IdentifierFixture().String(),
   132  				nil,
   133  				status.Errorf(codes.Internal, "block not found"),
   134  				`{"code":400,"message":"Invalid Flow request: block not found"}`,
   135  				http.StatusBadRequest,
   136  			},
   137  		}
   138  
   139  		for _, test := range tests {
   140  			id, err := flow.HexStringToIdentifier(test.id)
   141  			if err == nil {
   142  				// setup the backend mock ti return a not found error if this is a valid id
   143  				backend.Mock.
   144  					On("GetCollectionByID", mocks.Anything, id).
   145  					Return(test.mockValue, test.mockErr)
   146  			}
   147  			req := getCollectionReq(test.id, false)
   148  			assertResponse(t, req, test.status, test.response, backend)
   149  		}
   150  	})
   151  }