go_study/fabric-main/msp/cache/cache_test.go

310 lines
10 KiB
Go

/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package cache
import (
"sync"
"testing"
msp2 "github.com/hyperledger/fabric-protos-go/msp"
"github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/msp/mocks"
"github.com/pkg/errors"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
func TestNewCacheMsp(t *testing.T) {
i, err := New(nil)
require.Error(t, err)
require.Nil(t, i)
require.Contains(t, err.Error(), "Invalid passed MSP. It must be different from nil.")
i, err = New(&mocks.MockMSP{})
require.NoError(t, err)
require.NotNil(t, i)
}
func TestSetup(t *testing.T) {
mockMSP := &mocks.MockMSP{}
i, err := New(mockMSP)
require.NoError(t, err)
mockMSP.On("Setup", (*msp2.MSPConfig)(nil)).Return(nil)
err = i.Setup(nil)
require.NoError(t, err)
mockMSP.AssertExpectations(t)
require.Equal(t, 0, i.(*cachedMSP).deserializeIdentityCache.len())
require.Equal(t, 0, i.(*cachedMSP).satisfiesPrincipalCache.len())
require.Equal(t, 0, i.(*cachedMSP).validateIdentityCache.len())
}
func TestGetType(t *testing.T) {
mockMSP := &mocks.MockMSP{}
i, err := New(mockMSP)
require.NoError(t, err)
mockMSP.On("GetType").Return(msp.FABRIC)
require.Equal(t, msp.FABRIC, i.GetType())
mockMSP.AssertExpectations(t)
}
func TestGetIdentifier(t *testing.T) {
mockMSP := &mocks.MockMSP{}
i, err := New(mockMSP)
require.NoError(t, err)
mockMSP.On("GetIdentifier").Return("MSP", nil)
id, err := i.GetIdentifier()
require.NoError(t, err)
require.Equal(t, "MSP", id)
mockMSP.AssertExpectations(t)
}
func TestGetDefaultSigningIdentity(t *testing.T) {
mockMSP := &mocks.MockMSP{}
i, err := New(mockMSP)
require.NoError(t, err)
mockIdentity := &mocks.MockSigningIdentity{Mock: mock.Mock{}, MockIdentity: &mocks.MockIdentity{ID: "Alice"}}
mockMSP.On("GetDefaultSigningIdentity").Return(mockIdentity, nil)
id, err := i.GetDefaultSigningIdentity()
require.NoError(t, err)
require.Equal(t, mockIdentity, id)
mockMSP.AssertExpectations(t)
}
func TestGetTLSRootCerts(t *testing.T) {
mockMSP := &mocks.MockMSP{}
i, err := New(mockMSP)
require.NoError(t, err)
expected := [][]byte{{1}, {2}}
mockMSP.On("GetTLSRootCerts").Return(expected)
certs := i.GetTLSRootCerts()
require.Equal(t, expected, certs)
}
func TestGetTLSIntermediateCerts(t *testing.T) {
mockMSP := &mocks.MockMSP{}
i, err := New(mockMSP)
require.NoError(t, err)
expected := [][]byte{{1}, {2}}
mockMSP.On("GetTLSIntermediateCerts").Return(expected)
certs := i.GetTLSIntermediateCerts()
require.Equal(t, expected, certs)
}
func TestDeserializeIdentity(t *testing.T) {
mockMSP := &mocks.MockMSP{}
wrappedMSP, err := New(mockMSP)
require.NoError(t, err)
// Check id is cached
mockIdentity := &mocks.MockIdentity{ID: "Alice"}
mockIdentity2 := &mocks.MockIdentity{ID: "Bob"}
serializedIdentity := []byte{1, 2, 3}
serializedIdentity2 := []byte{4, 5, 6}
mockMSP.On("DeserializeIdentity", serializedIdentity).Return(mockIdentity, nil)
mockMSP.On("DeserializeIdentity", serializedIdentity2).Return(mockIdentity2, nil)
// Prime the cache
wrappedMSP.DeserializeIdentity(serializedIdentity)
wrappedMSP.DeserializeIdentity(serializedIdentity2)
// Stress the cache and ensure concurrent operations
// do not result in a failure
var wg sync.WaitGroup
wg.Add(100)
for i := 0; i < 100; i++ {
go func(m msp.MSP, i int) {
sIdentity := serializedIdentity
expectedIdentity := mockIdentity
defer wg.Done()
if i%2 == 0 {
sIdentity = serializedIdentity2
expectedIdentity = mockIdentity2
}
id, err := wrappedMSP.DeserializeIdentity(sIdentity)
require.NoError(t, err)
require.Equal(t, expectedIdentity, id.(*cachedIdentity).Identity)
}(wrappedMSP, i)
}
wg.Wait()
mockMSP.AssertExpectations(t)
// Check the cache
_, ok := wrappedMSP.(*cachedMSP).deserializeIdentityCache.get(string(serializedIdentity))
require.True(t, ok)
// Check the same object is returned
id, err := wrappedMSP.DeserializeIdentity(serializedIdentity)
require.NoError(t, err)
require.True(t, mockIdentity == id.(*cachedIdentity).Identity)
mockMSP.AssertExpectations(t)
// Check id is not cached
mockIdentity = &mocks.MockIdentity{ID: "Bob"}
serializedIdentity = []byte{1, 2, 3, 4}
mockMSP.On("DeserializeIdentity", serializedIdentity).Return(mockIdentity, errors.New("Invalid identity"))
_, err = wrappedMSP.DeserializeIdentity(serializedIdentity)
require.Error(t, err)
require.Contains(t, err.Error(), "Invalid identity")
mockMSP.AssertExpectations(t)
_, ok = wrappedMSP.(*cachedMSP).deserializeIdentityCache.get(string(serializedIdentity))
require.False(t, ok)
}
func TestValidate(t *testing.T) {
mockMSP := &mocks.MockMSP{}
i, err := New(mockMSP)
require.NoError(t, err)
// Check validation is cached
mockIdentity := &mocks.MockIdentity{ID: "Alice"}
mockIdentity.On("GetIdentifier").Return(&msp.IdentityIdentifier{Mspid: "MSP", Id: "Alice"})
mockMSP.On("Validate", mockIdentity).Return(nil)
err = i.Validate(mockIdentity)
require.NoError(t, err)
mockIdentity.AssertExpectations(t)
mockMSP.AssertExpectations(t)
// Check the cache
identifier := mockIdentity.GetIdentifier()
key := string(identifier.Mspid + ":" + identifier.Id)
v, ok := i.(*cachedMSP).validateIdentityCache.get(string(key))
require.True(t, ok)
require.True(t, v.(bool))
// Recheck
err = i.Validate(mockIdentity)
require.NoError(t, err)
// Check validation is not cached
mockIdentity = &mocks.MockIdentity{ID: "Bob"}
mockIdentity.On("GetIdentifier").Return(&msp.IdentityIdentifier{Mspid: "MSP", Id: "Bob"})
mockMSP.On("Validate", mockIdentity).Return(errors.New("Invalid identity"))
err = i.Validate(mockIdentity)
require.Error(t, err)
mockIdentity.AssertExpectations(t)
mockMSP.AssertExpectations(t)
// Check the cache
identifier = mockIdentity.GetIdentifier()
key = string(identifier.Mspid + ":" + identifier.Id)
_, ok = i.(*cachedMSP).validateIdentityCache.get(string(key))
require.False(t, ok)
}
func TestSatisfiesValidateIndirectCall(t *testing.T) {
mockMSP := &mocks.MockMSP{}
mockIdentity := &mocks.MockIdentity{ID: "Alice"}
mockIdentity.On("Validate").Run(func(_ mock.Arguments) {
panic("shouldn't have invoked the identity method")
})
mockMSP.On("DeserializeIdentity", mock.Anything).Return(mockIdentity, nil).Once()
mockIdentity.On("GetIdentifier").Return(&msp.IdentityIdentifier{Mspid: "MSP", Id: "Alice"})
cache, err := New(mockMSP)
require.NoError(t, err)
mockMSP.On("Validate", mockIdentity).Return(nil)
// Test that cache returns the correct value, and also use this to prime the cache
err = cache.Validate(mockIdentity)
mockMSP.AssertNumberOfCalls(t, "Validate", 1)
require.NoError(t, err)
// Get the identity we test the caching on
identity, err := cache.DeserializeIdentity([]byte{1, 2, 3})
require.NoError(t, err)
// Ensure the identity returned answers what the cached MSP answers.
err = identity.Validate()
require.NoError(t, err)
// Ensure that although a call to Validate was called, the calls weren't passed on to the backing MSP
mockMSP.AssertNumberOfCalls(t, "Validate", 1)
}
func TestSatisfiesPrincipalIndirectCall(t *testing.T) {
mockMSP := &mocks.MockMSP{}
mockMSPPrincipal := &msp2.MSPPrincipal{PrincipalClassification: msp2.MSPPrincipal_IDENTITY, Principal: []byte{1, 2, 3}}
mockIdentity := &mocks.MockIdentity{ID: "Alice"}
mockIdentity.On("SatisfiesPrincipal", mockMSPPrincipal).Run(func(_ mock.Arguments) {
panic("shouldn't have invoked the identity method")
})
mockMSP.On("DeserializeIdentity", mock.Anything).Return(mockIdentity, nil).Once()
mockIdentity.On("GetIdentifier").Return(&msp.IdentityIdentifier{Mspid: "MSP", Id: "Alice"})
cache, err := New(mockMSP)
require.NoError(t, err)
// First invocation of the SatisfiesPrincipal returns an error
mockMSP.On("SatisfiesPrincipal", mockIdentity, mockMSPPrincipal).Return(errors.New("error: foo")).Once()
// Second invocation returns nil
mockMSP.On("SatisfiesPrincipal", mockIdentity, mockMSPPrincipal).Return(nil).Once()
// Test that cache returns the correct value
err = cache.SatisfiesPrincipal(mockIdentity, mockMSPPrincipal)
require.Equal(t, "error: foo", err.Error())
// Get the identity we test the caching on
identity, err := cache.DeserializeIdentity([]byte{1, 2, 3})
require.NoError(t, err)
// Ensure the identity returned answers what the cached MSP answers.
// If the invocation doesn't hit the cache, it will return nil instead of an error.
err = identity.SatisfiesPrincipal(mockMSPPrincipal)
require.Equal(t, "error: foo", err.Error())
}
func TestSatisfiesPrincipal(t *testing.T) {
mockMSP := &mocks.MockMSP{}
i, err := New(mockMSP)
require.NoError(t, err)
// Check validation is cached
mockIdentity := &mocks.MockIdentity{ID: "Alice"}
mockIdentity.On("GetIdentifier").Return(&msp.IdentityIdentifier{Mspid: "MSP", Id: "Alice"})
mockMSPPrincipal := &msp2.MSPPrincipal{PrincipalClassification: msp2.MSPPrincipal_IDENTITY, Principal: []byte{1, 2, 3}}
mockMSP.On("SatisfiesPrincipal", mockIdentity, mockMSPPrincipal).Return(nil)
mockMSP.SatisfiesPrincipal(mockIdentity, mockMSPPrincipal)
err = i.SatisfiesPrincipal(mockIdentity, mockMSPPrincipal)
require.NoError(t, err)
mockIdentity.AssertExpectations(t)
mockMSP.AssertExpectations(t)
// Check the cache
identifier := mockIdentity.GetIdentifier()
identityKey := string(identifier.Mspid + ":" + identifier.Id)
principalKey := string(mockMSPPrincipal.PrincipalClassification) + string(mockMSPPrincipal.Principal)
key := identityKey + principalKey
v, ok := i.(*cachedMSP).satisfiesPrincipalCache.get(key)
require.True(t, ok)
require.Nil(t, v)
// Recheck
err = i.SatisfiesPrincipal(mockIdentity, mockMSPPrincipal)
require.NoError(t, err)
// Check validation is not cached
mockIdentity = &mocks.MockIdentity{ID: "Bob"}
mockIdentity.On("GetIdentifier").Return(&msp.IdentityIdentifier{Mspid: "MSP", Id: "Bob"})
mockMSPPrincipal = &msp2.MSPPrincipal{PrincipalClassification: msp2.MSPPrincipal_IDENTITY, Principal: []byte{1, 2, 3, 4}}
mockMSP.On("SatisfiesPrincipal", mockIdentity, mockMSPPrincipal).Return(errors.New("Invalid"))
mockMSP.SatisfiesPrincipal(mockIdentity, mockMSPPrincipal)
err = i.SatisfiesPrincipal(mockIdentity, mockMSPPrincipal)
require.Error(t, err)
mockIdentity.AssertExpectations(t)
mockMSP.AssertExpectations(t)
// Check the cache
identifier = mockIdentity.GetIdentifier()
identityKey = string(identifier.Mspid + ":" + identifier.Id)
principalKey = string(mockMSPPrincipal.PrincipalClassification) + string(mockMSPPrincipal.Principal)
key = identityKey + principalKey
v, ok = i.(*cachedMSP).satisfiesPrincipalCache.get(key)
require.True(t, ok)
require.NotNil(t, v)
require.Contains(t, "Invalid", v.(error).Error())
}