github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/database/sqlcommon/batch_sql_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 sqlcommon 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "testing" 24 25 "github.com/DATA-DOG/go-sqlmock" 26 "github.com/kaleido-io/firefly/pkg/database" 27 "github.com/kaleido-io/firefly/pkg/fftypes" 28 "github.com/stretchr/testify/assert" 29 ) 30 31 func TestBatch2EWithDB(t *testing.T) { 32 33 s := newQLTestProvider(t) 34 defer s.Close() 35 ctx := context.Background() 36 37 // Create a new batch entry 38 batchID := fftypes.NewUUID() 39 msgID1 := fftypes.NewUUID() 40 batch := &fftypes.Batch{ 41 ID: batchID, 42 Type: fftypes.MessageTypeBroadcast, 43 Author: "0x12345", 44 Hash: fftypes.NewRandB32(), 45 Created: fftypes.Now(), 46 Payload: fftypes.BatchPayload{ 47 Messages: []*fftypes.Message{ 48 {Header: fftypes.MessageHeader{ID: msgID1}}, 49 }, 50 TX: fftypes.TransactionRef{ 51 Type: fftypes.TransactionTypeNone, 52 }, 53 }, 54 } 55 err := s.UpsertBatch(ctx, batch, true, true) 56 assert.NoError(t, err) 57 58 // Check we get the exact same batch back 59 batchRead, err := s.GetBatchByID(ctx, batchID) 60 assert.NoError(t, err) 61 assert.NotNil(t, batchRead) 62 batchJson, _ := json.Marshal(&batch) 63 batchReadJson, _ := json.Marshal(&batchRead) 64 assert.Equal(t, string(batchJson), string(batchReadJson)) 65 66 // Update the batch (this is testing what's possible at the database layer, 67 // and does not account for the verification that happens at the higher level) 68 txid := fftypes.NewUUID() 69 msgID2 := fftypes.NewUUID() 70 payloadRef := fftypes.NewRandB32() 71 batchUpdated := &fftypes.Batch{ 72 ID: batchID, 73 Type: fftypes.MessageTypeBroadcast, 74 Author: "0x12345", 75 Namespace: "ns1", 76 Hash: fftypes.NewRandB32(), 77 Created: fftypes.Now(), 78 Payload: fftypes.BatchPayload{ 79 TX: fftypes.TransactionRef{ 80 ID: txid, 81 Type: fftypes.TransactionTypeBatchPin, 82 }, 83 Messages: []*fftypes.Message{ 84 {Header: fftypes.MessageHeader{ID: msgID1}}, 85 {Header: fftypes.MessageHeader{ID: msgID2}}, 86 }, 87 }, 88 PayloadRef: payloadRef, 89 Confirmed: fftypes.Now(), 90 } 91 92 // Rejects hash change 93 err = s.UpsertBatch(context.Background(), batchUpdated, true, false) 94 assert.Equal(t, database.HashMismatch, err) 95 96 err = s.UpsertBatch(context.Background(), batchUpdated, true, true) 97 assert.NoError(t, err) 98 99 // Check we get the exact same message back - note the removal of one of the batch elements 100 batchRead, err = s.GetBatchByID(ctx, batchID) 101 assert.NoError(t, err) 102 batchJson, _ = json.Marshal(&batchUpdated) 103 batchReadJson, _ = json.Marshal(&batchRead) 104 assert.Equal(t, string(batchJson), string(batchReadJson)) 105 106 // Query back the batch 107 fb := database.BatchQueryFactory.NewFilter(ctx) 108 filter := fb.And( 109 fb.Eq("id", batchUpdated.ID.String()), 110 fb.Eq("namespace", batchUpdated.Namespace), 111 fb.Eq("author", batchUpdated.Author), 112 fb.Gt("created", "0"), 113 fb.Gt("confirmed", "0"), 114 ) 115 batches, err := s.GetBatches(ctx, filter) 116 assert.NoError(t, err) 117 assert.Equal(t, 1, len(batches)) 118 batchReadJson, _ = json.Marshal(batches[0]) 119 assert.Equal(t, string(batchJson), string(batchReadJson)) 120 121 // Negative test on filter 122 filter = fb.And( 123 fb.Eq("id", batchUpdated.ID.String()), 124 fb.Eq("created", "0"), 125 ) 126 batches, err = s.GetBatches(ctx, filter) 127 assert.NoError(t, err) 128 assert.Equal(t, 0, len(batches)) 129 130 // Update 131 author2 := "0x222222" 132 up := database.BatchQueryFactory.NewUpdate(ctx).Set("author", author2) 133 err = s.UpdateBatch(ctx, batchID, up) 134 assert.NoError(t, err) 135 136 // Test find updated value 137 filter = fb.And( 138 fb.Eq("id", batchUpdated.ID.String()), 139 fb.Eq("author", author2), 140 ) 141 batches, err = s.GetBatches(ctx, filter) 142 assert.NoError(t, err) 143 assert.Equal(t, 1, len(batches)) 144 } 145 146 func TestUpsertBatchFailBegin(t *testing.T) { 147 s, mock := newMockProvider().init() 148 mock.ExpectBegin().WillReturnError(fmt.Errorf("pop")) 149 err := s.UpsertBatch(context.Background(), &fftypes.Batch{}, true, true) 150 assert.Regexp(t, "FF10114", err) 151 assert.NoError(t, mock.ExpectationsWereMet()) 152 } 153 154 func TestUpsertBatchFailSelect(t *testing.T) { 155 s, mock := newMockProvider().init() 156 mock.ExpectBegin() 157 mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) 158 mock.ExpectRollback() 159 batchID := fftypes.NewUUID() 160 err := s.UpsertBatch(context.Background(), &fftypes.Batch{ID: batchID}, true, true) 161 assert.Regexp(t, "FF10115", err) 162 assert.NoError(t, mock.ExpectationsWereMet()) 163 } 164 165 func TestUpsertBatchFailInsert(t *testing.T) { 166 s, mock := newMockProvider().init() 167 mock.ExpectBegin() 168 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{})) 169 mock.ExpectExec("INSERT .*").WillReturnError(fmt.Errorf("pop")) 170 mock.ExpectRollback() 171 batchID := fftypes.NewUUID() 172 err := s.UpsertBatch(context.Background(), &fftypes.Batch{ID: batchID}, true, true) 173 assert.Regexp(t, "FF10116", err) 174 assert.NoError(t, mock.ExpectationsWereMet()) 175 } 176 177 func TestUpsertBatchFailUpdate(t *testing.T) { 178 s, mock := newMockProvider().init() 179 batchID := fftypes.NewUUID() 180 mock.ExpectBegin() 181 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(batchID.String())) 182 mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop")) 183 mock.ExpectRollback() 184 err := s.UpsertBatch(context.Background(), &fftypes.Batch{ID: batchID}, true, true) 185 assert.Regexp(t, "FF10117", err) 186 assert.NoError(t, mock.ExpectationsWereMet()) 187 } 188 189 func TestUpsertBatchFailCommit(t *testing.T) { 190 s, mock := newMockProvider().init() 191 batchID := fftypes.NewUUID() 192 mock.ExpectBegin() 193 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"})) 194 mock.ExpectExec("INSERT .*").WillReturnResult(sqlmock.NewResult(1, 1)) 195 mock.ExpectCommit().WillReturnError(fmt.Errorf("pop")) 196 err := s.UpsertBatch(context.Background(), &fftypes.Batch{ID: batchID}, true, true) 197 assert.Regexp(t, "FF10119", err) 198 assert.NoError(t, mock.ExpectationsWereMet()) 199 } 200 201 func TestGetBatchByIDSelectFail(t *testing.T) { 202 s, mock := newMockProvider().init() 203 batchID := fftypes.NewUUID() 204 mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) 205 _, err := s.GetBatchByID(context.Background(), batchID) 206 assert.Regexp(t, "FF10115", err) 207 assert.NoError(t, mock.ExpectationsWereMet()) 208 } 209 210 func TestGetBatchByIDNotFound(t *testing.T) { 211 s, mock := newMockProvider().init() 212 batchID := fftypes.NewUUID() 213 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"})) 214 msg, err := s.GetBatchByID(context.Background(), batchID) 215 assert.NoError(t, err) 216 assert.Nil(t, msg) 217 assert.NoError(t, mock.ExpectationsWereMet()) 218 } 219 220 func TestGetBatchByIDScanFail(t *testing.T) { 221 s, mock := newMockProvider().init() 222 batchID := fftypes.NewUUID() 223 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("only one")) 224 _, err := s.GetBatchByID(context.Background(), batchID) 225 assert.Regexp(t, "FF10121", err) 226 assert.NoError(t, mock.ExpectationsWereMet()) 227 } 228 229 func TestGetBatchesQueryFail(t *testing.T) { 230 s, mock := newMockProvider().init() 231 mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) 232 f := database.BatchQueryFactory.NewFilter(context.Background()).Eq("id", "") 233 _, err := s.GetBatches(context.Background(), f) 234 assert.Regexp(t, "FF10115", err) 235 assert.NoError(t, mock.ExpectationsWereMet()) 236 } 237 238 func TestGetBatchesBuildQueryFail(t *testing.T) { 239 s, _ := newMockProvider().init() 240 f := database.BatchQueryFactory.NewFilter(context.Background()).Eq("id", map[bool]bool{true: false}) 241 _, err := s.GetBatches(context.Background(), f) 242 assert.Regexp(t, "FF10149.*id", err) 243 } 244 245 func TestGetBatchesReadMessageFail(t *testing.T) { 246 s, mock := newMockProvider().init() 247 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("only one")) 248 f := database.BatchQueryFactory.NewFilter(context.Background()).Eq("id", "") 249 _, err := s.GetBatches(context.Background(), f) 250 assert.Regexp(t, "FF10121", err) 251 assert.NoError(t, mock.ExpectationsWereMet()) 252 } 253 254 func TestBatchUpdateBeginFail(t *testing.T) { 255 s, mock := newMockProvider().init() 256 mock.ExpectBegin().WillReturnError(fmt.Errorf("pop")) 257 u := database.BatchQueryFactory.NewUpdate(context.Background()).Set("id", "anything") 258 err := s.UpdateBatch(context.Background(), fftypes.NewUUID(), u) 259 assert.Regexp(t, "FF10114", err) 260 } 261 262 func TestBatchUpdateBuildQueryFail(t *testing.T) { 263 s, mock := newMockProvider().init() 264 mock.ExpectBegin() 265 u := database.BatchQueryFactory.NewUpdate(context.Background()).Set("id", map[bool]bool{true: false}) 266 err := s.UpdateBatch(context.Background(), fftypes.NewUUID(), u) 267 assert.Regexp(t, "FF10149.*id", err) 268 } 269 270 func TestBatchUpdateFail(t *testing.T) { 271 s, mock := newMockProvider().init() 272 mock.ExpectBegin() 273 mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop")) 274 mock.ExpectRollback() 275 u := database.BatchQueryFactory.NewUpdate(context.Background()).Set("id", fftypes.NewUUID()) 276 err := s.UpdateBatch(context.Background(), fftypes.NewUUID(), u) 277 assert.Regexp(t, "FF10117", err) 278 }