From c64264bb4ee570396bbb05ecd54941b4cf718039 Mon Sep 17 00:00:00 2001 From: Will Lahti Date: Mon, 22 May 2017 16:27:08 -0400 Subject: [PATCH] [FAB-4057] Improve UT coverage for QSCC This CR refactors the existing unit tests and adds new unit tests, improving the coverage from 62.5% to 88.7%. Change-Id: If2c110a6c5af7d4c8528b91ba3bb16717450e4e7 Signed-off-by: Will Lahti --- core/scc/qscc/query.go | 6 +- core/scc/qscc/query_test.go | 308 ++++++++++++++++++++++-------------- 2 files changed, 188 insertions(+), 126 deletions(-) diff --git a/core/scc/qscc/query.go b/core/scc/qscc/query.go index 539318c6e87..241d276ff70 100644 --- a/core/scc/qscc/query.go +++ b/core/scc/qscc/query.go @@ -21,7 +21,6 @@ import ( "strconv" "github.com/hyperledger/fabric/common/flogging" - "github.com/op/go-logging" "github.com/hyperledger/fabric/common/policies" "github.com/hyperledger/fabric/core/chaincode/shim" @@ -93,9 +92,8 @@ func (e *LedgerQuerier) Invoke(stub shim.ChaincodeStubInterface) pb.Response { if targetLedger == nil { return shim.Error(fmt.Sprintf("Invalid chain ID, %s", cid)) } - if qscclogger.IsEnabledFor(logging.DEBUG) { - qscclogger.Debugf("Invoke function: %s on chain: %s", fname, cid) - } + + qscclogger.Debugf("Invoke function: %s on chain: %s", fname, cid) // Handle ACL: // 1. get the signed proposal diff --git a/core/scc/qscc/query_test.go b/core/scc/qscc/query_test.go index 80a970cf42d..43a69157bf8 100644 --- a/core/scc/qscc/query_test.go +++ b/core/scc/qscc/query_test.go @@ -24,188 +24,252 @@ import ( "strings" + "github.com/hyperledger/fabric/common/ledger/testutil" "github.com/hyperledger/fabric/common/policies" "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/core/peer" "github.com/hyperledger/fabric/core/policy" + "github.com/hyperledger/fabric/protos/common" peer2 "github.com/hyperledger/fabric/protos/peer" "github.com/hyperledger/fabric/protos/utils" "github.com/stretchr/testify/assert" ) -func TestInit(t *testing.T) { - viper.Set("peer.fileSystemPath", "/var/hyperledger/test1/") - defer os.RemoveAll("/var/hyperledger/test1/") +func setupTestLedger(chainid string, path string) (*shim.MockStub, error) { + viper.Set("peer.fileSystemPath", path) peer.MockInitialize() - peer.MockCreateChain("mytestchainid1") - - e := new(LedgerQuerier) - stub := shim.NewMockStub("LedgerQuerier", e) + peer.MockCreateChain(chainid) + lq := new(LedgerQuerier) + stub := shim.NewMockStub("LedgerQuerier", lq) if res := stub.MockInit("1", nil); res.Status != shim.OK { - fmt.Println("Init failed", string(res.Message)) - t.FailNow() + return nil, fmt.Errorf("Init failed for test ledger [%s] with message: %s", chainid, string(res.Message)) } + return stub, nil } func TestQueryGetChainInfo(t *testing.T) { - viper.Set("peer.fileSystemPath", "/var/hyperledger/test2/") - defer os.RemoveAll("/var/hyperledger/test2/") - peer.MockInitialize() - peer.MockCreateChain("mytestchainid2") + chainid := "mytestchainid1" + path := "/var/hyperledger/test1/" + stub, err := setupTestLedger(chainid, path) + defer os.RemoveAll(path) + if err != nil { + t.Fatalf(err.Error()) + } - e := new(LedgerQuerier) - stub := shim.NewMockStub("LedgerQuerier", e) + args := [][]byte{[]byte(GetChainInfo), []byte(chainid)} + res := stub.MockInvoke("1", args) + assert.Equal(t, int32(shim.OK), res.Status, "GetChainInfo failed with err: %s", res.Message) - if res := stub.MockInit("1", nil); res.Status != shim.OK { - fmt.Println("Init failed", string(res.Message)) - t.FailNow() - } + args = [][]byte{[]byte(GetChainInfo)} + res = stub.MockInvoke("2", args) + assert.Equal(t, int32(shim.ERROR), res.Status, "GetChainInfo should have failed because no channel id was provided") - args := [][]byte{[]byte(GetChainInfo), []byte("mytestchainid2")} - if res := stub.MockInvoke("2", args); res.Status != shim.OK { - t.Fatalf("qscc GetChainInfo failed with err: %s", res.Message) - } + args = [][]byte{[]byte(GetChainInfo), []byte("fakechainid")} + res = stub.MockInvoke("3", args) + assert.Equal(t, int32(shim.ERROR), res.Status, "GetChainInfo should have failed because the channel id does not exist") } func TestQueryGetTransactionByID(t *testing.T) { - viper.Set("peer.fileSystemPath", "/var/hyperledger/test3/") - defer os.RemoveAll("/var/hyperledger/test3/") - peer.MockInitialize() - peer.MockCreateChain("mytestchainid3") - - e := new(LedgerQuerier) - stub := shim.NewMockStub("LedgerQuerier", e) - - if res := stub.MockInit("1", nil); res.Status != shim.OK { - fmt.Println("Init failed", string(res.Message)) - t.FailNow() + chainid := "mytestchainid2" + path := "/var/hyperledger/test2/" + stub, err := setupTestLedger(chainid, path) + defer os.RemoveAll(path) + if err != nil { + t.Fatalf(err.Error()) } - args := [][]byte{[]byte(GetTransactionByID), []byte("mytestchainid3"), []byte("1")} - if res := stub.MockInvoke("2", args); res.Status == shim.OK { - t.Fatal("qscc getTransactionByID should have failed with invalid txid: 2") - } -} - -func TestQueryWithWrongParameters(t *testing.T) { - viper.Set("peer.fileSystemPath", "/var/hyperledger/test4/") - defer os.RemoveAll("/var/hyperledger/test4/") - peer.MockInitialize() - peer.MockCreateChain("mytestchainid4") - - e := new(LedgerQuerier) - stub := shim.NewMockStub("LedgerQuerier", e) + args := [][]byte{[]byte(GetTransactionByID), []byte(chainid), []byte("1")} + res := stub.MockInvoke("1", args) + assert.Equal(t, int32(shim.ERROR), res.Status, "GetTransactionByID should have failed with invalid txid: 1") - if res := stub.MockInit("1", nil); res.Status != shim.OK { - fmt.Println("Init failed", string(res.Message)) - t.FailNow() - } + args = [][]byte{[]byte(GetTransactionByID), []byte(chainid), []byte(nil)} + res = stub.MockInvoke("2", args) + assert.Equal(t, int32(shim.ERROR), res.Status, "GetTransactionByID should have failed with invalid txid: nil") // Test with wrong number of parameters - args := [][]byte{[]byte(GetTransactionByID), []byte("mytestchainid4")} - if res := stub.MockInvoke("2", args); res.Status == shim.OK { - t.Fatal("qscc getTransactionByID should have failed with invalid txid: 2") - } + args = [][]byte{[]byte(GetTransactionByID), []byte(chainid)} + res = stub.MockInvoke("3", args) + assert.Equal(t, int32(shim.ERROR), res.Status, "GetTransactionByID should have failed due to incorrect number of arguments") } func TestQueryGetBlockByNumber(t *testing.T) { - //t.Skip() - viper.Set("peer.fileSystemPath", "/var/hyperledger/test5/") - defer os.RemoveAll("/var/hyperledger/test5/") - peer.MockInitialize() - peer.MockCreateChain("mytestchainid5") - - e := new(LedgerQuerier) - stub := shim.NewMockStub("LedgerQuerier", e) - - if res := stub.MockInit("1", nil); res.Status != shim.OK { - fmt.Println("Init failed", string(res.Message)) - t.FailNow() + chainid := "mytestchainid3" + path := "/var/hyperledger/test3/" + stub, err := setupTestLedger(chainid, path) + defer os.RemoveAll(path) + if err != nil { + t.Fatalf(err.Error()) } + // block number 0 (genesis block) would already be present in the ledger - args := [][]byte{[]byte(GetBlockByNumber), []byte("mytestchainid5"), []byte("1")} - if res := stub.MockInvoke("2", args); res.Status == shim.OK { - t.Fatal("qscc GetBlockByNumber should have failed with invalid number: 1") - } + args := [][]byte{[]byte(GetBlockByNumber), []byte(chainid), []byte("0")} + res := stub.MockInvoke("1", args) + assert.Equal(t, int32(shim.OK), res.Status, "GetBlockByNumber should have succeeded for block number: 0") + + // block number 1 should not be present in the ledger + args = [][]byte{[]byte(GetBlockByNumber), []byte(chainid), []byte("1")} + res = stub.MockInvoke("2", args) + assert.Equal(t, int32(shim.ERROR), res.Status, "GetBlockByNumber should have failed with invalid number: 1") + + // block number cannot be nil + args = [][]byte{[]byte(GetBlockByNumber), []byte(chainid), []byte(nil)} + res = stub.MockInvoke("3", args) + assert.Equal(t, int32(shim.ERROR), res.Status, "GetBlockByNumber should have failed with nil block number") } func TestQueryGetBlockByHash(t *testing.T) { - viper.Set("peer.fileSystemPath", "/var/hyperledger/test6/") - defer os.RemoveAll("/var/hyperledger/test6/") - peer.MockInitialize() - peer.MockCreateChain("mytestchainid6") - - e := new(LedgerQuerier) - stub := shim.NewMockStub("LedgerQuerier", e) - - if res := stub.MockInit("1", nil); res.Status != shim.OK { - fmt.Println("Init failed", string(res.Message)) - t.FailNow() + chainid := "mytestchainid4" + path := "/var/hyperledger/test4/" + stub, err := setupTestLedger(chainid, path) + defer os.RemoveAll(path) + if err != nil { + t.Fatalf(err.Error()) } - args := [][]byte{[]byte(GetBlockByHash), []byte("mytestchainid6"), []byte("0")} - if res := stub.MockInvoke("2", args); res.Status == shim.OK { - t.Fatal("qscc GetBlockByHash should have failed with invalid hash: 0") - } + args := [][]byte{[]byte(GetBlockByHash), []byte(chainid), []byte("0")} + res := stub.MockInvoke("1", args) + assert.Equal(t, int32(shim.ERROR), res.Status, "GetBlockByHash should have failed with invalid hash: 0") + + args = [][]byte{[]byte(GetBlockByHash), []byte(chainid), []byte(nil)} + res = stub.MockInvoke("2", args) + assert.Equal(t, int32(shim.ERROR), res.Status, "GetBlockByHash should have failed with nil hash") } func TestQueryGetBlockByTxID(t *testing.T) { - viper.Set("peer.fileSystemPath", "/var/hyperledger/test8/") - defer os.RemoveAll("/var/hyperledger/test8/") - peer.MockInitialize() - peer.MockCreateChain("mytestchainid8") - - e := new(LedgerQuerier) - stub := shim.NewMockStub("LedgerQuerier", e) - - txID := "" - - if res := stub.MockInit("1", nil); res.Status != shim.OK { - fmt.Println("Init failed", string(res.Message)) - t.FailNow() + chainid := "mytestchainid5" + path := "/var/hyperledger/test5/" + stub, err := setupTestLedger(chainid, path) + defer os.RemoveAll(path) + if err != nil { + t.Fatalf(err.Error()) } - args := [][]byte{[]byte(GetBlockByTxID), []byte("mytestchainid8"), []byte(txID)} - if res := stub.MockInvoke("2", args); res.Status == shim.OK { - t.Fatalf("qscc GetBlockByTxID should have failed with invalid txID: %s", txID) - } + args := [][]byte{[]byte(GetBlockByTxID), []byte(chainid), []byte("")} + res := stub.MockInvoke("1", args) + assert.Equal(t, int32(shim.ERROR), res.Status, "GetBlockByTxID should have failed with blank txId.") } func TestFailingAccessControl(t *testing.T) { - viper.Set("peer.fileSystemPath", "/var/hyperledger/test9/") - defer os.RemoveAll("/var/hyperledger/test9/") - peer.MockInitialize() - peer.MockCreateChain("mytestchainid9") - + chainid := "mytestchainid6" + path := "/var/hyperledger/test6/" + _, err := setupTestLedger(chainid, path) + defer os.RemoveAll(path) + if err != nil { + t.Fatalf(err.Error()) + } e := new(LedgerQuerier) // Init the policy checker to have a failure policyManagerGetter := &policy.MockChannelPolicyManagerGetter{ Managers: map[string]policies.Manager{ - "mytestchainid9": &policy.MockChannelPolicyManager{MockPolicy: &policy.MockPolicy{Deserializer: &policy.MockIdentityDeserializer{[]byte("Alice"), []byte("msg1")}}}, + chainid: &policy.MockChannelPolicyManager{MockPolicy: &policy.MockPolicy{Deserializer: &policy.MockIdentityDeserializer{[]byte("Alice"), []byte("msg1")}}}, }, } - e.policyChecker = policy.NewPolicyChecker( policyManagerGetter, &policy.MockIdentityDeserializer{[]byte("Alice"), []byte("msg1")}, &policy.MockMSPPrincipalGetter{Principal: []byte("Alice")}, ) - stub := shim.NewMockStub("LedgerQuerier", e) - args := [][]byte{[]byte(GetChainInfo), []byte("mytestchainid9")} - sProp, _ := utils.MockSignedEndorserProposalOrPanic("mytestchainid9", &peer2.ChaincodeSpec{}, []byte("Alice"), []byte("msg1")) - policyManagerGetter.Managers["mytestchainid9"].(*policy.MockChannelPolicyManager).MockPolicy.(*policy.MockPolicy).Deserializer.(*policy.MockIdentityDeserializer).Msg = sProp.ProposalBytes + args := [][]byte{[]byte(GetChainInfo), []byte(chainid)} + sProp, _ := utils.MockSignedEndorserProposalOrPanic(chainid, &peer2.ChaincodeSpec{}, []byte("Alice"), []byte("msg1")) + policyManagerGetter.Managers[chainid].(*policy.MockChannelPolicyManager).MockPolicy.(*policy.MockPolicy).Deserializer.(*policy.MockIdentityDeserializer).Msg = sProp.ProposalBytes sProp.Signature = sProp.ProposalBytes - if res := stub.MockInvokeWithSignedProposal("2", args, sProp); res.Status != shim.OK { - t.Fatalf("qscc GetChainInfo failed with err: %s", res.Message) + res := stub.MockInvokeWithSignedProposal("2", args, sProp) + assert.Equal(t, int32(shim.OK), res.Status, "GetChainInfo failed with err: %s", res.Message) + + sProp, _ = utils.MockSignedEndorserProposalOrPanic(chainid, &peer2.ChaincodeSpec{}, []byte("Bob"), []byte("msg2")) + res = stub.MockInvokeWithSignedProposal("3", args, sProp) + assert.Equal(t, int32(shim.ERROR), res.Status, "GetChainInfo must fail: %s", res.Message) + assert.True(t, strings.HasPrefix(res.Message, "Authorization request failed")) +} + +func TestQueryNonexistentFunction(t *testing.T) { + chainid := "mytestchainid7" + path := "/var/hyperledger/test7/" + stub, err := setupTestLedger(chainid, path) + defer os.RemoveAll(path) + if err != nil { + t.Fatalf(err.Error()) } - sProp, _ = utils.MockSignedEndorserProposalOrPanic("mytestchainid9", &peer2.ChaincodeSpec{}, []byte("Bob"), []byte("msg2")) - res := stub.MockInvokeWithSignedProposal("3", args, sProp) - if res.Status == shim.OK { - t.Fatalf("qscc GetChainInfo must fail: %s", res.Message) + args := [][]byte{[]byte("GetBlocks"), []byte(chainid), []byte("arg1")} + res := stub.MockInvoke("1", args) + assert.Equal(t, int32(shim.ERROR), res.Status, "GetBlocks should have failed because the function does not exist") +} + +// TestQueryGeneratedBlock tests various queries for a newly generated block +// that contains two transactions +func TestQueryGeneratedBlock(t *testing.T) { + chainid := "mytestchainid8" + path := "/var/hyperledger/test8/" + stub, err := setupTestLedger(chainid, path) + defer os.RemoveAll(path) + if err != nil { + t.Fatalf(err.Error()) } - assert.True(t, strings.HasPrefix(res.Message, "Authorization request failed")) + + block1 := addBlockForTesting(t, chainid) + + // block number 1 should now exist + args := [][]byte{[]byte(GetBlockByNumber), []byte(chainid), []byte("1")} + res := stub.MockInvoke("1", args) + assert.Equal(t, int32(shim.OK), res.Status, "GetBlockByNumber should have succeeded for block number 1") + + // block number 1 + args = [][]byte{[]byte(GetBlockByHash), []byte(chainid), []byte(block1.Header.Hash())} + res = stub.MockInvoke("2", args) + assert.Equal(t, int32(shim.OK), res.Status, "GetBlockByHash should have succeeded for block 1 hash") + + // drill into the block to find the transaction ids it contains + for _, d := range block1.Data.Data { + ebytes := d + if ebytes != nil { + if env, err := utils.GetEnvelopeFromBlock(ebytes); err != nil { + t.Fatalf("error getting envelope from block: %s", err) + } else if env != nil { + payload, err := utils.GetPayload(env) + if err != nil { + t.Fatalf("error extracting payload from envelope: %s", err) + } + chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader) + if err != nil { + t.Fatalf(err.Error()) + } + if common.HeaderType(chdr.Type) == common.HeaderType_ENDORSER_TRANSACTION { + args = [][]byte{[]byte(GetBlockByTxID), []byte(chainid), []byte(chdr.TxId)} + res = stub.MockInvoke("3", args) + assert.Equal(t, int32(shim.OK), res.Status, "GetBlockByTxId should have succeeded for txid: %s", chdr.TxId) + + args = [][]byte{[]byte(GetTransactionByID), []byte(chainid), []byte(chdr.TxId)} + res = stub.MockInvoke("4", args) + assert.Equal(t, int32(shim.OK), res.Status, "GetTransactionById should have succeeded for txid: %s", chdr.TxId) + } + } + } + } +} + +func addBlockForTesting(t *testing.T, chainid string) *common.Block { + bg, _ := testutil.NewBlockGenerator(t, chainid, false) + ledger := peer.GetLedger(chainid) + defer ledger.Close() + + simulator, _ := ledger.NewTxSimulator() + simulator.SetState("ns1", "key1", []byte("value1")) + simulator.SetState("ns1", "key2", []byte("value2")) + simulator.SetState("ns1", "key3", []byte("value3")) + simulator.Done() + simRes1, _ := simulator.GetTxSimulationResults() + simulator, _ = ledger.NewTxSimulator() + simulator.SetState("ns2", "key4", []byte("value4")) + simulator.SetState("ns2", "key5", []byte("value5")) + simulator.SetState("ns2", "key6", []byte("value6")) + simulator.Done() + simRes2, _ := simulator.GetTxSimulationResults() + block1 := bg.NextBlock([][]byte{simRes1, simRes2}) + ledger.Commit(block1) + + return block1 }