github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/broadcast/manager_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 broadcast 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/mocks/publicstoragemocks" 32 "github.com/kaleido-io/firefly/pkg/fftypes" 33 "github.com/stretchr/testify/assert" 34 "github.com/stretchr/testify/mock" 35 ) 36 37 func newTestBroadcast(t *testing.T) (*broadcastManager, func()) { 38 config.Reset() 39 config.Set(config.OrgIdentity, "UTNodeID") 40 mdi := &databasemocks.Plugin{} 41 mii := &identitymocks.Plugin{} 42 mdm := &datamocks.Manager{} 43 mbi := &blockchainmocks.Plugin{} 44 mpi := &publicstoragemocks.Plugin{} 45 mba := &batchmocks.Manager{} 46 mdx := &dataexchangemocks.Plugin{} 47 mbi.On("Name").Return("ut_blockchain").Maybe() 48 defaultIdentity := &fftypes.Identity{Identifier: "UTNodeID", OnChain: "0x12345"} 49 mii.On("Resolve", mock.Anything, "UTNodeID").Return(defaultIdentity, nil).Maybe() 50 mbi.On("VerifyIdentitySyntax", mock.Anything, defaultIdentity).Return(nil).Maybe() 51 mba.On("RegisterDispatcher", []fftypes.MessageType{fftypes.MessageTypeBroadcast, fftypes.MessageTypeDefinition}, mock.Anything, mock.Anything).Return() 52 ctx, cancel := context.WithCancel(context.Background()) 53 b, err := NewBroadcastManager(ctx, mdi, mii, mdm, mbi, mdx, mpi, mba) 54 assert.NoError(t, err) 55 return b.(*broadcastManager), cancel 56 } 57 58 func TestInitFail(t *testing.T) { 59 _, err := NewBroadcastManager(context.Background(), nil, nil, nil, nil, nil, nil, nil) 60 assert.Regexp(t, "FF10128", err) 61 } 62 63 func TestBroadcastMessageGood(t *testing.T) { 64 bm, cancel := newTestBroadcast(t) 65 defer cancel() 66 67 msg := &fftypes.Message{} 68 bm.database.(*databasemocks.Plugin).On("InsertMessageLocal", mock.Anything, msg).Return(nil) 69 70 err := bm.broadcastMessageCommon(context.Background(), msg) 71 assert.NoError(t, err) 72 73 bm.Start() 74 bm.WaitStop() 75 } 76 77 func TestBroadcastMessageBad(t *testing.T) { 78 bm, cancel := newTestBroadcast(t) 79 defer cancel() 80 81 dupID := fftypes.NewUUID() 82 msg := &fftypes.Message{ 83 Data: fftypes.DataRefs{ 84 {ID: dupID /* missing hash */}, 85 }, 86 } 87 bm.database.(*databasemocks.Plugin).On("UpsertMessage", mock.Anything, msg, false).Return(nil) 88 89 err := bm.broadcastMessageCommon(context.Background(), msg) 90 assert.Regexp(t, "FF10144", err) 91 92 } 93 94 func TestDispatchBatchInvalidData(t *testing.T) { 95 bm, cancel := newTestBroadcast(t) 96 defer cancel() 97 98 err := bm.dispatchBatch(context.Background(), &fftypes.Batch{ 99 Payload: fftypes.BatchPayload{ 100 Data: []*fftypes.Data{ 101 {Value: fftypes.Byteable(`!json`)}, 102 }, 103 }, 104 }, []*fftypes.Bytes32{fftypes.NewRandB32()}) 105 assert.Regexp(t, "FF10137", err) 106 } 107 108 func TestDispatchBatchUploadFail(t *testing.T) { 109 bm, cancel := newTestBroadcast(t) 110 defer cancel() 111 112 bm.publicstorage.(*publicstoragemocks.Plugin).On("PublishData", mock.Anything, mock.Anything).Return(nil, "", fmt.Errorf("pop")) 113 114 err := bm.dispatchBatch(context.Background(), &fftypes.Batch{}, []*fftypes.Bytes32{fftypes.NewRandB32()}) 115 assert.EqualError(t, err, "pop") 116 } 117 118 func TestDispatchBatchSubmitBatchPinSucceed(t *testing.T) { 119 bm, cancel := newTestBroadcast(t) 120 defer cancel() 121 122 mdi := bm.database.(*databasemocks.Plugin) 123 mdi.On("RunAsGroup", mock.Anything, mock.Anything).Return(nil) 124 125 bm.publicstorage.(*publicstoragemocks.Plugin).On("PublishData", mock.Anything, mock.Anything).Return(fftypes.NewRandB32(), "id1", nil) 126 127 err := bm.dispatchBatch(context.Background(), &fftypes.Batch{}, []*fftypes.Bytes32{fftypes.NewRandB32()}) 128 assert.NoError(t, err) 129 } 130 131 func TestGetOrgIdentityEmpty(t *testing.T) { 132 bm, cancel := newTestBroadcast(t) 133 defer cancel() 134 135 config.Set(config.OrgIdentity, "") 136 mii := bm.identity.(*identitymocks.Plugin) 137 mii.On("Resolve", mock.Anything, "").Return(nil, fmt.Errorf("pop")) 138 _, err := bm.GetNodeSigningIdentity(bm.ctx) 139 assert.Regexp(t, "pop", err) 140 } 141 142 func TestDispatchBatchSubmitBroadcastBadIdentity(t *testing.T) { 143 bm, cancel := newTestBroadcast(t) 144 defer cancel() 145 146 mdi := bm.database.(*databasemocks.Plugin) 147 mps := bm.publicstorage.(*publicstoragemocks.Plugin) 148 mii := bm.identity.(*identitymocks.Plugin) 149 mbi := bm.blockchain.(*blockchainmocks.Plugin) 150 mdi.On("RunAsGroup", mock.Anything, mock.Anything).Return(nil) 151 mps.On("PublishData", mock.Anything, mock.Anything).Return(fftypes.NewRandB32(), "id1", nil) 152 mii.On("Resolve", mock.Anything, "wrong").Return(nil, fmt.Errorf("pop")) 153 mbi.On("VerifyIdentitySyntax", mock.Anything, mock.Anything).Return(nil) 154 155 err := bm.dispatchBatch(context.Background(), &fftypes.Batch{Author: "wrong"}, []*fftypes.Bytes32{fftypes.NewRandB32()}) 156 assert.NoError(t, err) 157 158 mdi.On("UpsertOperation", mock.Anything, mock.Anything, false).Return(nil) 159 fn := mdi.Calls[0].Arguments[1].(func(ctx context.Context) error) 160 err = fn(context.Background()) 161 assert.Regexp(t, "pop", err) 162 } 163 164 func TestDispatchBatchSubmitBroadcastBadOnchainIdentity(t *testing.T) { 165 bm, cancel := newTestBroadcast(t) 166 defer cancel() 167 168 mdi := bm.database.(*databasemocks.Plugin) 169 mps := bm.publicstorage.(*publicstoragemocks.Plugin) 170 mii := bm.identity.(*identitymocks.Plugin) 171 mbi := bm.blockchain.(*blockchainmocks.Plugin) 172 mdi.On("RunAsGroup", mock.Anything, mock.Anything).Return(nil) 173 mps.On("PublishData", mock.Anything, mock.Anything).Return(fftypes.NewRandB32(), "id1", nil) 174 badID := &fftypes.Identity{OnChain: "0x99999"} 175 mii.On("Resolve", mock.Anything, "wrong").Return(badID, nil) 176 mbi.On("VerifyIdentitySyntax", mock.Anything, badID).Return(fmt.Errorf("pop")) 177 178 err := bm.dispatchBatch(context.Background(), &fftypes.Batch{Author: "wrong"}, []*fftypes.Bytes32{fftypes.NewRandB32()}) 179 assert.NoError(t, err) 180 181 mdi.On("UpsertOperation", mock.Anything, mock.Anything, false).Return(nil) 182 fn := mdi.Calls[0].Arguments[1].(func(ctx context.Context) error) 183 err = fn(context.Background()) 184 assert.Regexp(t, "pop", err) 185 } 186 187 func TestDispatchBatchSubmitBatchPinFail(t *testing.T) { 188 bm, cancel := newTestBroadcast(t) 189 defer cancel() 190 191 mdi := bm.database.(*databasemocks.Plugin) 192 mdi.On("RunAsGroup", mock.Anything, mock.Anything).Return(nil) 193 194 bm.publicstorage.(*publicstoragemocks.Plugin).On("PublishData", mock.Anything, mock.Anything).Return(fftypes.NewRandB32(), "id1", nil) 195 196 err := bm.dispatchBatch(context.Background(), &fftypes.Batch{Author: "UTNodeID"}, []*fftypes.Bytes32{fftypes.NewRandB32()}) 197 assert.NoError(t, err) 198 199 mdi.On("UpsertTransaction", mock.Anything, mock.Anything, true, false).Return(fmt.Errorf("pop")) 200 fn := mdi.Calls[0].Arguments[1].(func(ctx context.Context) error) 201 err = fn(context.Background()) 202 assert.Regexp(t, "pop", err) 203 } 204 205 func TestSubmitTXAndUpdateDBUpdateBatchFail(t *testing.T) { 206 bm, cancel := newTestBroadcast(t) 207 defer cancel() 208 209 mdi := bm.database.(*databasemocks.Plugin) 210 mdi.On("UpsertTransaction", mock.Anything, mock.Anything, true, false).Return(nil) 211 mdi.On("UpdateBatch", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) 212 bm.blockchain.(*blockchainmocks.Plugin).On("SubmitBatchPin", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", fmt.Errorf("pop")) 213 214 err := bm.submitTXAndUpdateDB(context.Background(), &fftypes.Batch{Author: "UTNodeID"}, []*fftypes.Bytes32{fftypes.NewRandB32()}, "id1") 215 assert.Regexp(t, "pop", err) 216 } 217 218 func TestSubmitTXAndUpdateDBSubmitFail(t *testing.T) { 219 bm, cancel := newTestBroadcast(t) 220 defer cancel() 221 222 mdi := bm.database.(*databasemocks.Plugin) 223 mdi.On("UpsertTransaction", mock.Anything, mock.Anything, true, false).Return(nil) 224 mdi.On("UpdateBatch", mock.Anything, mock.Anything, mock.Anything).Return(nil) 225 bm.blockchain.(*blockchainmocks.Plugin).On("SubmitBatchPin", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", fmt.Errorf("pop")) 226 227 err := bm.submitTXAndUpdateDB(context.Background(), &fftypes.Batch{Author: "UTNodeID"}, []*fftypes.Bytes32{fftypes.NewRandB32()}, "id1") 228 assert.Regexp(t, "pop", err) 229 } 230 231 func TestSubmitTXAndUpdateDBAddOp1Fail(t *testing.T) { 232 bm, cancel := newTestBroadcast(t) 233 defer cancel() 234 235 mdi := bm.database.(*databasemocks.Plugin) 236 mbi := bm.blockchain.(*blockchainmocks.Plugin) 237 mdi.On("UpsertTransaction", mock.Anything, mock.Anything, true, false).Return(nil) 238 mdi.On("UpdateBatch", mock.Anything, mock.Anything, mock.Anything).Return(nil) 239 mdi.On("UpsertOperation", mock.Anything, mock.Anything, false).Return(fmt.Errorf("pop")) 240 mbi.On("SubmitBatchPin", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("txid", nil) 241 mbi.On("Name").Return("unittest") 242 243 batch := &fftypes.Batch{ 244 Author: "UTNodeID", 245 Payload: fftypes.BatchPayload{ 246 Messages: []*fftypes.Message{ 247 {Header: fftypes.MessageHeader{ 248 ID: fftypes.NewUUID(), 249 }}, 250 }, 251 }, 252 } 253 254 err := bm.submitTXAndUpdateDB(context.Background(), batch, []*fftypes.Bytes32{fftypes.NewRandB32()}, "id1") 255 assert.Regexp(t, "pop", err) 256 } 257 258 func TestSubmitTXAndUpdateDBAddOp2Fail(t *testing.T) { 259 bm, cancel := newTestBroadcast(t) 260 defer cancel() 261 262 mdi := bm.database.(*databasemocks.Plugin) 263 mbi := bm.blockchain.(*blockchainmocks.Plugin) 264 mdi.On("UpsertTransaction", mock.Anything, mock.Anything, true, false).Return(nil) 265 mdi.On("UpdateBatch", mock.Anything, mock.Anything, mock.Anything).Return(nil) 266 mdi.On("UpsertOperation", mock.Anything, mock.Anything, false).Once().Return(nil) 267 mdi.On("UpsertOperation", mock.Anything, mock.Anything, false).Once().Return(fmt.Errorf("pop")) 268 mbi.On("SubmitBatchPin", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("txid", nil) 269 mbi.On("Name").Return("ut_blockchain") 270 271 bm.publicstorage.(*publicstoragemocks.Plugin).On("Name").Return("ut_publicstorage") 272 273 batch := &fftypes.Batch{ 274 Author: "UTNodeID", 275 Payload: fftypes.BatchPayload{ 276 Messages: []*fftypes.Message{ 277 {Header: fftypes.MessageHeader{ 278 ID: fftypes.NewUUID(), 279 }}, 280 }, 281 }, 282 } 283 284 err := bm.submitTXAndUpdateDB(context.Background(), batch, []*fftypes.Bytes32{fftypes.NewRandB32()}, "id1") 285 assert.Regexp(t, "pop", err) 286 } 287 288 func TestSubmitTXAndUpdateDBSucceed(t *testing.T) { 289 bm, cancel := newTestBroadcast(t) 290 defer cancel() 291 292 mdi := bm.database.(*databasemocks.Plugin) 293 mbi := bm.blockchain.(*blockchainmocks.Plugin) 294 mdi.On("UpsertTransaction", mock.Anything, mock.Anything, true, false).Return(nil) 295 mdi.On("UpdateBatch", mock.Anything, mock.Anything, mock.Anything).Return(nil) 296 mdi.On("UpsertOperation", mock.Anything, mock.Anything, false).Once().Return(nil) 297 mdi.On("UpsertOperation", mock.Anything, mock.Anything, false).Once().Return(nil) 298 mbi.On("SubmitBatchPin", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("blockchain_id", nil) 299 300 bm.publicstorage.(*publicstoragemocks.Plugin).On("Name").Return("ut_publicstorage") 301 302 msgID := fftypes.NewUUID() 303 batch := &fftypes.Batch{ 304 Author: "UTNodeID", 305 Payload: fftypes.BatchPayload{ 306 TX: fftypes.TransactionRef{ 307 Type: fftypes.TransactionTypeBatchPin, 308 ID: fftypes.NewUUID(), 309 }, 310 Messages: []*fftypes.Message{ 311 {Header: fftypes.MessageHeader{ 312 ID: msgID, 313 }}, 314 }, 315 }, 316 } 317 318 err := bm.submitTXAndUpdateDB(context.Background(), batch, []*fftypes.Bytes32{fftypes.NewRandB32()}, "ipfs_id") 319 assert.NoError(t, err) 320 321 op1 := mdi.Calls[2].Arguments[1].(*fftypes.Operation) 322 assert.Equal(t, *batch.Payload.TX.ID, *op1.Transaction) 323 assert.Equal(t, "ut_blockchain", op1.Plugin) 324 assert.Equal(t, "blockchain_id", op1.BackendID) 325 assert.Equal(t, fftypes.OpTypeBlockchainBatchPin, op1.Type) 326 327 op2 := mdi.Calls[3].Arguments[1].(*fftypes.Operation) 328 assert.Equal(t, *batch.Payload.TX.ID, *op2.Transaction) 329 assert.Equal(t, "ut_publicstorage", op2.Plugin) 330 assert.Equal(t, "ipfs_id", op2.BackendID) 331 assert.Equal(t, fftypes.OpTypePublicStorageBatchBroadcast, op2.Type) 332 }