github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/privatemessaging/privatemessaging_test.go (about)

     1  // Copyright © 2021 Kaleido, Inc.
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  //
     5  // Licensed under the Apache License, Version 2.0 (the "License");
     6  // you may not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing, software
    12  // distributed under the License is distributed on an "AS IS" BASIS,
    13  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  // See the License for the specific language governing permissions and
    15  // limitations under the License.
    16  
    17  package privatemessaging
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"testing"
    23  
    24  	"github.com/kaleido-io/firefly/internal/config"
    25  	"github.com/kaleido-io/firefly/mocks/batchmocks"
    26  	"github.com/kaleido-io/firefly/mocks/blockchainmocks"
    27  	"github.com/kaleido-io/firefly/mocks/databasemocks"
    28  	"github.com/kaleido-io/firefly/mocks/dataexchangemocks"
    29  	"github.com/kaleido-io/firefly/mocks/datamocks"
    30  	"github.com/kaleido-io/firefly/mocks/identitymocks"
    31  	"github.com/kaleido-io/firefly/pkg/blockchain"
    32  	"github.com/kaleido-io/firefly/pkg/fftypes"
    33  	"github.com/stretchr/testify/assert"
    34  	"github.com/stretchr/testify/mock"
    35  )
    36  
    37  func newTestPrivateMessaging(t *testing.T) (*privateMessaging, func()) {
    38  	config.Reset()
    39  	config.Set(config.NodeName, "node1")
    40  	config.Set(config.OrgIdentity, "localorg")
    41  	config.Set(config.GroupCacheTTL, "1m")
    42  	config.Set(config.GroupCacheSize, "1m")
    43  
    44  	mdi := &databasemocks.Plugin{}
    45  	mii := &identitymocks.Plugin{}
    46  	mdx := &dataexchangemocks.Plugin{}
    47  	mbi := &blockchainmocks.Plugin{}
    48  	mba := &batchmocks.Manager{}
    49  	mdm := &datamocks.Manager{}
    50  
    51  	mba.On("RegisterDispatcher", []fftypes.MessageType{fftypes.MessageTypeGroupInit, fftypes.MessageTypePrivate}, mock.Anything, mock.Anything).Return()
    52  
    53  	ctx, cancel := context.WithCancel(context.Background())
    54  	pm, err := NewPrivateMessaging(ctx, mdi, mii, mdx, mbi, mba, mdm)
    55  	assert.NoError(t, err)
    56  
    57  	// Default mocks to save boilerplate in the tests
    58  	mdx.On("Name").Return("utdx").Maybe()
    59  	mbi.On("Name").Return("utblk").Maybe()
    60  	mii.On("Resolve", ctx, "org1").Return(&fftypes.Identity{
    61  		Identifier: "org1", OnChain: "0x12345",
    62  	}, nil).Maybe()
    63  	mbi.On("VerifyIdentitySyntax", ctx, mock.MatchedBy(func(i *fftypes.Identity) bool { return i.OnChain == "0x12345" })).Return(nil).Maybe()
    64  	mii.On("Resolve", ctx, "org1").Return(&fftypes.Identity{
    65  		Identifier: "org1", OnChain: "0x23456",
    66  	}, nil).Maybe()
    67  	mbi.On("VerifyIdentitySyntax", ctx, mock.MatchedBy(func(i *fftypes.Identity) bool { return i.OnChain == "0x23456" })).Return(nil).Maybe()
    68  
    69  	return pm.(*privateMessaging), cancel
    70  }
    71  
    72  func uuidMatches(id1 *fftypes.UUID) interface{} {
    73  	return mock.MatchedBy(func(id2 *fftypes.UUID) bool { return id1.Equals(id2) })
    74  }
    75  
    76  func TestDispatchBatch(t *testing.T) {
    77  
    78  	pm, cancel := newTestPrivateMessaging(t)
    79  	defer cancel()
    80  
    81  	batchID := fftypes.NewUUID()
    82  	groupID := fftypes.NewRandB32()
    83  	pin1 := fftypes.NewRandB32()
    84  	pin2 := fftypes.NewRandB32()
    85  	node1 := fftypes.NewUUID()
    86  	node2 := fftypes.NewUUID()
    87  	txID := fftypes.NewUUID()
    88  	batchHash := fftypes.NewRandB32()
    89  
    90  	mdi := pm.database.(*databasemocks.Plugin)
    91  	mbi := pm.blockchain.(*blockchainmocks.Plugin)
    92  	mdx := pm.exchange.(*dataexchangemocks.Plugin)
    93  
    94  	rag := mdi.On("RunAsGroup", pm.ctx, mock.Anything).Maybe()
    95  	rag.RunFn = func(a mock.Arguments) {
    96  		rag.ReturnArguments = mock.Arguments{
    97  			a[1].(func(context.Context) error)(a[0].(context.Context)),
    98  		}
    99  	}
   100  
   101  	mdi.On("GetGroupByHash", pm.ctx, groupID).Return(&fftypes.Group{
   102  		Hash: fftypes.NewRandB32(),
   103  		GroupIdentity: fftypes.GroupIdentity{
   104  			Name: "group1",
   105  			Members: fftypes.Members{
   106  				{Identity: "org1", Node: node1},
   107  				{Identity: "org2", Node: node2},
   108  			},
   109  		},
   110  	}, nil)
   111  	mdi.On("GetNodeByID", pm.ctx, uuidMatches(node1)).Return(&fftypes.Node{
   112  		ID: node1,
   113  		DX: fftypes.DXInfo{
   114  			Peer:     "node1",
   115  			Endpoint: fftypes.JSONObject{"url": "https://node1.example.com"},
   116  		},
   117  	}, nil).Once()
   118  	mdi.On("GetNodeByID", pm.ctx, uuidMatches(node2)).Return(&fftypes.Node{
   119  		ID: node2,
   120  		DX: fftypes.DXInfo{
   121  			Peer:     "node2",
   122  			Endpoint: fftypes.JSONObject{"url": "https://node2.example.com"},
   123  		},
   124  	}, nil).Once()
   125  
   126  	mdx.On("SendMessage", pm.ctx, mock.Anything, mock.Anything).Return("tracking1", nil).Once()
   127  	mdi.On("UpsertOperation", pm.ctx, mock.MatchedBy(func(op *fftypes.Operation) bool {
   128  		return op.BackendID == "tracking1" && op.Type == fftypes.OpTypeDataExchangeBatchSend
   129  	}), false).Return(nil, nil)
   130  	mdx.On("SendMessage", pm.ctx, mock.Anything, mock.Anything).Return("tracking2", nil).Once()
   131  	mdi.On("UpsertOperation", pm.ctx, mock.MatchedBy(func(op *fftypes.Operation) bool {
   132  		return op.BackendID == "tracking2" && op.Type == fftypes.OpTypeDataExchangeBatchSend
   133  	}), false).Return(nil, nil)
   134  
   135  	mdi.On("UpsertTransaction", pm.ctx, mock.MatchedBy(func(tx *fftypes.Transaction) bool {
   136  		return tx.Subject.Type == fftypes.TransactionTypeBatchPin && tx.ID.Equals(txID)
   137  	}), true, false).Return(nil, nil)
   138  	mbi.On("SubmitBatchPin", pm.ctx, mock.Anything, mock.Anything, mock.MatchedBy(func(bp *blockchain.BatchPin) bool {
   139  		assert.Equal(t, txID, bp.TransactionID)
   140  		assert.Equal(t, batchID, bp.BatchID)
   141  		assert.Equal(t, batchHash, bp.BatchHash)
   142  		assert.Equal(t, "ns1", bp.Namespace)
   143  		assert.Equal(t, []*fftypes.Bytes32{pin1, pin2}, bp.Contexts)
   144  		return true
   145  	})).Return("tracking3", nil)
   146  	mdi.On("UpsertOperation", pm.ctx, mock.MatchedBy(func(op *fftypes.Operation) bool {
   147  		return op.BackendID == "tracking3" && op.Type == fftypes.OpTypeBlockchainBatchPin
   148  	}), false).Return(nil, nil)
   149  
   150  	err := pm.dispatchBatch(pm.ctx, &fftypes.Batch{
   151  		ID:        batchID,
   152  		Author:    "org1",
   153  		Group:     groupID,
   154  		Namespace: "ns1",
   155  		Payload: fftypes.BatchPayload{
   156  			TX: fftypes.TransactionRef{
   157  				ID: txID,
   158  			},
   159  		},
   160  		Hash: batchHash,
   161  	}, []*fftypes.Bytes32{pin1, pin2})
   162  	assert.NoError(t, err)
   163  
   164  	mdi.AssertExpectations(t)
   165  	mdx.AssertExpectations(t)
   166  }
   167  
   168  func TestNewPrivateMessagingMissingDeps(t *testing.T) {
   169  	_, err := NewPrivateMessaging(context.Background(), nil, nil, nil, nil, nil, nil)
   170  	assert.Regexp(t, "FF10128", err)
   171  }
   172  
   173  func TestDispatchBatchBadData(t *testing.T) {
   174  	pm, cancel := newTestPrivateMessaging(t)
   175  	defer cancel()
   176  
   177  	err := pm.dispatchBatch(pm.ctx, &fftypes.Batch{
   178  		Payload: fftypes.BatchPayload{
   179  			Data: []*fftypes.Data{
   180  				{Value: fftypes.Byteable(`{!json}`)},
   181  			},
   182  		},
   183  	}, []*fftypes.Bytes32{})
   184  	assert.Regexp(t, "FF10137", err)
   185  }
   186  
   187  func TestDispatchErrorFindingGroup(t *testing.T) {
   188  	pm, cancel := newTestPrivateMessaging(t)
   189  	defer cancel()
   190  
   191  	mdi := pm.database.(*databasemocks.Plugin)
   192  	mdi.On("GetGroupByHash", pm.ctx, mock.Anything).Return(nil, fmt.Errorf("pop"))
   193  
   194  	err := pm.dispatchBatch(pm.ctx, &fftypes.Batch{}, []*fftypes.Bytes32{})
   195  	assert.Regexp(t, "pop", err)
   196  }
   197  
   198  func TestSendAndSubmitBatchBadID(t *testing.T) {
   199  	pm, cancel := newTestPrivateMessaging(t)
   200  	defer cancel()
   201  
   202  	mdi := pm.database.(*databasemocks.Plugin)
   203  	mdi.On("GetGroupByHash", pm.ctx, mock.Anything).Return(nil, fmt.Errorf("pop"))
   204  
   205  	mii := pm.identity.(*identitymocks.Plugin)
   206  	mii.On("Resolve", pm.ctx, "badauthor").Return(&fftypes.Identity{OnChain: "!badaddress"}, nil)
   207  
   208  	mbi := pm.blockchain.(*blockchainmocks.Plugin)
   209  	mbi.On("VerifyIdentitySyntax", pm.ctx, mock.Anything).Return(fmt.Errorf("pop"))
   210  
   211  	err := pm.sendAndSubmitBatch(pm.ctx, &fftypes.Batch{
   212  		Author: "badauthor",
   213  	}, []*fftypes.Node{}, fftypes.Byteable(`{}`), []*fftypes.Bytes32{})
   214  	assert.Regexp(t, "pop", err)
   215  }
   216  
   217  func TestSendImmediateFail(t *testing.T) {
   218  	pm, cancel := newTestPrivateMessaging(t)
   219  	defer cancel()
   220  
   221  	mdx := pm.exchange.(*dataexchangemocks.Plugin)
   222  	mdx.On("SendMessage", pm.ctx, mock.Anything, mock.Anything).Return("", fmt.Errorf("pop"))
   223  
   224  	err := pm.sendAndSubmitBatch(pm.ctx, &fftypes.Batch{
   225  		Author: "org1",
   226  	}, []*fftypes.Node{
   227  		{
   228  			DX: fftypes.DXInfo{
   229  				Peer:     "node1",
   230  				Endpoint: fftypes.JSONObject{"url": "https://node1.example.com"},
   231  			},
   232  		},
   233  	}, fftypes.Byteable(`{}`), []*fftypes.Bytes32{})
   234  	assert.Regexp(t, "pop", err)
   235  }
   236  
   237  func TestSendSubmitUpsertOperationFail(t *testing.T) {
   238  	pm, cancel := newTestPrivateMessaging(t)
   239  	defer cancel()
   240  
   241  	mdx := pm.exchange.(*dataexchangemocks.Plugin)
   242  	mdx.On("SendMessage", pm.ctx, mock.Anything, mock.Anything).Return("tracking1", nil)
   243  
   244  	mdi := pm.database.(*databasemocks.Plugin)
   245  	mdi.On("UpsertOperation", pm.ctx, mock.Anything, false).Return(fmt.Errorf("pop"))
   246  
   247  	err := pm.sendAndSubmitBatch(pm.ctx, &fftypes.Batch{
   248  		Author: "org1",
   249  	}, []*fftypes.Node{
   250  		{
   251  			DX: fftypes.DXInfo{
   252  				Peer:     "node1",
   253  				Endpoint: fftypes.JSONObject{"url": "https://node1.example.com"},
   254  			},
   255  		},
   256  	}, fftypes.Byteable(`{}`), []*fftypes.Bytes32{})
   257  	assert.Regexp(t, "pop", err)
   258  }
   259  
   260  func TestWriteTransactionUpsertFail(t *testing.T) {
   261  	pm, cancel := newTestPrivateMessaging(t)
   262  	defer cancel()
   263  
   264  	mdi := pm.database.(*databasemocks.Plugin)
   265  	mdi.On("UpsertTransaction", pm.ctx, mock.Anything, true, false).Return(fmt.Errorf("pop"))
   266  
   267  	err := pm.writeTransaction(pm.ctx, &fftypes.Identity{OnChain: "0x12345"}, &fftypes.Batch{}, []*fftypes.Bytes32{})
   268  	assert.Regexp(t, "pop", err)
   269  }
   270  
   271  func TestWriteTransactionSubmitBatchPinFail(t *testing.T) {
   272  	pm, cancel := newTestPrivateMessaging(t)
   273  	defer cancel()
   274  
   275  	mdi := pm.database.(*databasemocks.Plugin)
   276  	mdi.On("UpsertTransaction", pm.ctx, mock.Anything, true, false).Return(nil)
   277  
   278  	mbi := pm.blockchain.(*blockchainmocks.Plugin)
   279  	mbi.On("SubmitBatchPin", pm.ctx, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", fmt.Errorf("pop"))
   280  
   281  	err := pm.writeTransaction(pm.ctx, &fftypes.Identity{OnChain: "0x12345"}, &fftypes.Batch{}, []*fftypes.Bytes32{})
   282  	assert.Regexp(t, "pop", err)
   283  }
   284  
   285  func TestWriteTransactionUpsertOpFail(t *testing.T) {
   286  	pm, cancel := newTestPrivateMessaging(t)
   287  	defer cancel()
   288  
   289  	mdi := pm.database.(*databasemocks.Plugin)
   290  	mdi.On("UpsertTransaction", pm.ctx, mock.Anything, true, false).Return(nil)
   291  
   292  	mbi := pm.blockchain.(*blockchainmocks.Plugin)
   293  	mbi.On("SubmitBatchPin", pm.ctx, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("tracking1", nil)
   294  
   295  	mdi.On("UpsertOperation", pm.ctx, mock.Anything, false).Return(fmt.Errorf("pop"))
   296  
   297  	err := pm.writeTransaction(pm.ctx, &fftypes.Identity{OnChain: "0x12345"}, &fftypes.Batch{}, []*fftypes.Bytes32{})
   298  	assert.Regexp(t, "pop", err)
   299  }
   300  
   301  func TestStart(t *testing.T) {
   302  	pm, cancel := newTestPrivateMessaging(t)
   303  	defer cancel()
   304  
   305  	mdx := pm.exchange.(*dataexchangemocks.Plugin)
   306  	mdx.On("Start").Return(nil)
   307  
   308  	err := pm.Start()
   309  	assert.NoError(t, err)
   310  }