github.com/binyushen/fabric@v2.1.1+incompatible/core/chaincode/query_response_generator_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package chaincode_test 8 9 import ( 10 "errors" 11 "fmt" 12 "math" 13 "testing" 14 15 "github.com/hyperledger/fabric-protos-go/ledger/queryresult" 16 "github.com/hyperledger/fabric/core/chaincode" 17 "github.com/hyperledger/fabric/core/chaincode/mock" 18 "github.com/stretchr/testify/assert" 19 ) 20 21 const totalQueryLimit = 103 22 23 func TestBuildQueryResponse(t *testing.T) { 24 queryResult := &queryresult.KV{ 25 Key: "key", 26 Namespace: "namespace", 27 Value: []byte("value"), 28 } 29 30 // test various boundry cases around maxResultLimit 31 const maxResultLimit = 10 32 testCases := []struct { 33 recordCount int 34 expectedResultCount int 35 expectedHasMoreCount int 36 isPaginated bool 37 maxResultLimit int 38 totalQueryLimit int 39 }{ 40 {0, 0, 0, false, maxResultLimit, totalQueryLimit}, 41 {1, 1, 0, false, maxResultLimit, totalQueryLimit}, 42 {10, 10, 0, false, maxResultLimit, totalQueryLimit}, 43 {maxResultLimit - 2, maxResultLimit - 2, 0, false, maxResultLimit, totalQueryLimit}, 44 {maxResultLimit - 1, maxResultLimit - 1, 0, false, maxResultLimit, totalQueryLimit}, 45 {maxResultLimit, maxResultLimit, 0, false, maxResultLimit, totalQueryLimit}, 46 {maxResultLimit + 1, maxResultLimit + 1, 1, false, maxResultLimit, totalQueryLimit}, 47 {maxResultLimit + 2, maxResultLimit + 2, 1, false, maxResultLimit, totalQueryLimit}, 48 {int(math.Floor(maxResultLimit * 1.5)), int(math.Floor(maxResultLimit * 1.5)), 1, false, maxResultLimit, totalQueryLimit}, 49 {maxResultLimit * 2, maxResultLimit * 2, 1, false, maxResultLimit, totalQueryLimit}, 50 {10*maxResultLimit - 2, 10*maxResultLimit - 2, 9, false, maxResultLimit, totalQueryLimit}, 51 {10*maxResultLimit - 1, 10*maxResultLimit - 1, 9, false, maxResultLimit, totalQueryLimit}, 52 {10 * maxResultLimit, 10 * maxResultLimit, 9, false, maxResultLimit, totalQueryLimit}, 53 {10*maxResultLimit + 1, 10*maxResultLimit + 1, 10, false, maxResultLimit, totalQueryLimit}, 54 {10*maxResultLimit + 2, 10*maxResultLimit + 2, 10, false, maxResultLimit, totalQueryLimit}, 55 {10*maxResultLimit + 3, 10*maxResultLimit + 3, 10, false, maxResultLimit, totalQueryLimit}, 56 {10*maxResultLimit + 5, 10*maxResultLimit + 3, 10, false, maxResultLimit, totalQueryLimit}, 57 {10, 5, 1, false, 4, 5}, 58 {10, 5, 0, false, 5, 5}, 59 {10, 5, 0, false, 6, 5}, 60 {0, 0, 0, true, maxResultLimit, totalQueryLimit}, 61 {1, 1, 0, true, maxResultLimit, totalQueryLimit}, 62 {10, 10, 0, true, maxResultLimit, totalQueryLimit}, 63 {maxResultLimit, maxResultLimit, 0, true, maxResultLimit, totalQueryLimit}, 64 {maxResultLimit + 1, maxResultLimit + 1, 0, true, maxResultLimit, totalQueryLimit}, 65 {10*maxResultLimit + 2, 10*maxResultLimit + 2, 0, true, maxResultLimit, totalQueryLimit}, 66 {10*maxResultLimit + 3, totalQueryLimit, 0, true, maxResultLimit, totalQueryLimit}, 67 {10*maxResultLimit + 4, totalQueryLimit, 0, true, maxResultLimit, totalQueryLimit}, 68 {10, 5, 0, true, 4, 5}, 69 {10, 5, 0, false, 5, 5}, 70 {10, 5, 0, false, 6, 5}, 71 } 72 73 for _, tc := range testCases { 74 t.Run(fmt.Sprintf("%d", tc.expectedResultCount), func(t *testing.T) { 75 txSimulator := &mock.TxSimulator{} 76 transactionContext := &chaincode.TransactionContext{ 77 TXSimulator: txSimulator, 78 } 79 80 resultsIterator := &mock.QueryResultsIterator{} 81 transactionContext.InitializeQueryContext("query-id", resultsIterator) 82 for i := 0; i < tc.recordCount; i++ { 83 resultsIterator.NextReturnsOnCall(i, queryResult, nil) 84 } 85 resultsIterator.NextReturnsOnCall(tc.recordCount, nil, nil) 86 responseGenerator := &chaincode.QueryResponseGenerator{ 87 MaxResultLimit: tc.maxResultLimit, 88 } 89 totalResultCount := 0 90 for hasMoreCount := 0; hasMoreCount <= tc.expectedHasMoreCount; hasMoreCount++ { 91 queryResponse, err := responseGenerator.BuildQueryResponse(transactionContext, resultsIterator, "query-id", tc.isPaginated, int32(tc.totalQueryLimit)) 92 assert.NoError(t, err) 93 94 switch { 95 case hasMoreCount < tc.expectedHasMoreCount: 96 // max limit sized batch retrieved, more expected 97 assert.True(t, queryResponse.GetHasMore()) 98 assert.Len(t, queryResponse.GetResults(), tc.maxResultLimit) 99 default: 100 // remainder retrieved, no more expected 101 assert.Len(t, queryResponse.GetResults(), tc.expectedResultCount-totalResultCount) 102 assert.False(t, queryResponse.GetHasMore()) 103 104 } 105 totalResultCount += len(queryResponse.GetResults()) 106 } 107 108 // assert the total number of records is correct 109 assert.Equal(t, tc.expectedResultCount, totalResultCount) 110 111 if tc.isPaginated { 112 // this case checks if the expected method was called to close the recordset 113 assert.Equal(t, 1, resultsIterator.GetBookmarkAndCloseCallCount()) 114 } else { 115 assert.Equal(t, 1, resultsIterator.CloseCallCount()) 116 } 117 }) 118 } 119 } 120 121 func TestBuildQueryResponseErrors(t *testing.T) { 122 validResult := &queryresult.KV{Key: "key-name"} 123 invalidResult := brokenProto{} 124 125 tests := []struct { 126 errorOnNextCall int 127 brokenResultOnNextCall int 128 expectedErrValue string 129 }{ 130 {-1, 2, "marshal-failed"}, 131 {-1, 3, "marshal-failed"}, 132 {2, -1, "next-failed"}, 133 } 134 135 for i, tc := range tests { 136 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { 137 txSimulator := &mock.TxSimulator{} 138 transactionContext := &chaincode.TransactionContext{TXSimulator: txSimulator} 139 resultsIterator := &mock.QueryResultsIterator{} 140 resultsIterator.NextReturns(validResult, nil) 141 if tc.errorOnNextCall >= 0 { 142 resultsIterator.NextReturnsOnCall(tc.errorOnNextCall, nil, errors.New("next-failed")) 143 } 144 if tc.brokenResultOnNextCall >= 0 { 145 resultsIterator.NextReturnsOnCall(tc.brokenResultOnNextCall, invalidResult, nil) 146 } 147 148 transactionContext.InitializeQueryContext("query-id", resultsIterator) 149 responseGenerator := &chaincode.QueryResponseGenerator{ 150 MaxResultLimit: 3, 151 } 152 153 resp, err := responseGenerator.BuildQueryResponse(transactionContext, resultsIterator, "query-id", false, totalQueryLimit) 154 if tc.expectedErrValue == "" { 155 assert.NoError(t, err) 156 } else { 157 assert.EqualError(t, err, tc.expectedErrValue) 158 } 159 assert.Nil(t, resp) 160 assert.Equal(t, 1, resultsIterator.CloseCallCount()) 161 }) 162 } 163 }