github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/database/sqlcommon/transaction_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 TestTransactionE2EWithDB(t *testing.T) { 32 33 s := newQLTestProvider(t) 34 defer s.Close() 35 ctx := context.Background() 36 37 // Create a new transaction entry 38 transactionID := fftypes.NewUUID() 39 transaction := &fftypes.Transaction{ 40 ID: transactionID, 41 Hash: fftypes.NewRandB32(), 42 Subject: fftypes.TransactionSubject{ 43 Type: fftypes.TransactionTypeBatchPin, 44 Namespace: "ns1", 45 Signer: "0x12345", 46 Reference: fftypes.NewUUID(), 47 }, 48 Created: fftypes.Now(), 49 Status: fftypes.OpStatusPending, 50 } 51 err := s.UpsertTransaction(ctx, transaction, true, false) 52 assert.NoError(t, err) 53 54 // Check we get the exact same transaction back 55 transactionRead, err := s.GetTransactionByID(ctx, transactionID) 56 // The generated sequence will have been added 57 transaction.Sequence = transactionRead.Sequence 58 assert.NoError(t, err) 59 assert.NotNil(t, transactionRead) 60 transactionJson, _ := json.Marshal(&transaction) 61 transactionReadJson, _ := json.Marshal(&transactionRead) 62 assert.Equal(t, string(transactionJson), string(transactionReadJson)) 63 64 // Update the transaction (this is testing what's possible at the database layer, 65 // and does not account for the verification that happens at the higher level) 66 transactionUpdated := &fftypes.Transaction{ 67 ID: transactionID, 68 Hash: fftypes.NewRandB32(), 69 Subject: fftypes.TransactionSubject{ 70 Type: fftypes.TransactionTypeBatchPin, 71 Namespace: "ns1", 72 Signer: "0x12345", 73 Reference: fftypes.NewUUID(), 74 }, 75 Created: fftypes.Now(), 76 ProtocolID: "0x33333", 77 Status: fftypes.OpStatusFailed, 78 Info: fftypes.JSONObject{ 79 "some": "data", 80 }, 81 } 82 83 // Check reject hash update 84 err = s.UpsertTransaction(context.Background(), transactionUpdated, true, false) 85 assert.Equal(t, database.HashMismatch, err) 86 87 err = s.UpsertTransaction(context.Background(), transactionUpdated, true, true) 88 assert.NoError(t, err) 89 90 // Check we get the exact same message back - note the removal of one of the transaction elements 91 transactionRead, err = s.GetTransactionByID(ctx, transactionID) 92 assert.NoError(t, err) 93 // The generated sequence will have been added 94 transactionUpdated.Sequence = transaction.Sequence 95 transactionJson, _ = json.Marshal(&transactionUpdated) 96 transactionReadJson, _ = json.Marshal(&transactionRead) 97 assert.Equal(t, string(transactionJson), string(transactionReadJson)) 98 99 // Query back the transaction 100 fb := database.TransactionQueryFactory.NewFilter(ctx) 101 filter := fb.And( 102 fb.Eq("id", transactionUpdated.ID.String()), 103 fb.Eq("protocolid", transactionUpdated.ProtocolID), 104 fb.Eq("signer", transactionUpdated.Subject.Signer), 105 fb.Gt("created", "0"), 106 ) 107 transactions, err := s.GetTransactions(ctx, filter) 108 assert.NoError(t, err) 109 assert.Equal(t, 1, len(transactions)) 110 transactionReadJson, _ = json.Marshal(transactions[0]) 111 assert.Equal(t, string(transactionJson), string(transactionReadJson)) 112 113 // Negative test on filter 114 filter = fb.And( 115 fb.Eq("id", transactionUpdated.ID.String()), 116 fb.Eq("created", "0"), 117 ) 118 transactions, err = s.GetTransactions(ctx, filter) 119 assert.NoError(t, err) 120 assert.Equal(t, 0, len(transactions)) 121 122 // Update 123 up := database.TransactionQueryFactory.NewUpdate(ctx). 124 Set("status", fftypes.OpStatusSucceeded) 125 err = s.UpdateTransaction(ctx, transactionUpdated.ID, up) 126 assert.NoError(t, err) 127 128 // Test find updated value 129 filter = fb.And( 130 fb.Eq("id", transactionUpdated.ID.String()), 131 fb.Eq("status", fftypes.OpStatusSucceeded), 132 ) 133 transactions, err = s.GetTransactions(ctx, filter) 134 assert.NoError(t, err) 135 assert.Equal(t, 1, len(transactions)) 136 } 137 138 func TestUpsertTransactionFailBegin(t *testing.T) { 139 s, mock := newMockProvider().init() 140 mock.ExpectBegin().WillReturnError(fmt.Errorf("pop")) 141 err := s.UpsertTransaction(context.Background(), &fftypes.Transaction{}, true, true) 142 assert.Regexp(t, "FF10114", err) 143 assert.NoError(t, mock.ExpectationsWereMet()) 144 } 145 146 func TestUpsertTransactionFailSelect(t *testing.T) { 147 s, mock := newMockProvider().init() 148 mock.ExpectBegin() 149 mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) 150 mock.ExpectRollback() 151 transactionID := fftypes.NewUUID() 152 err := s.UpsertTransaction(context.Background(), &fftypes.Transaction{ID: transactionID}, true, true) 153 assert.Regexp(t, "FF10115", err) 154 assert.NoError(t, mock.ExpectationsWereMet()) 155 } 156 157 func TestUpsertTransactionFailInsert(t *testing.T) { 158 s, mock := newMockProvider().init() 159 mock.ExpectBegin() 160 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{})) 161 mock.ExpectExec("INSERT .*").WillReturnError(fmt.Errorf("pop")) 162 mock.ExpectRollback() 163 transactionID := fftypes.NewUUID() 164 err := s.UpsertTransaction(context.Background(), &fftypes.Transaction{ID: transactionID}, true, true) 165 assert.Regexp(t, "FF10116", err) 166 assert.NoError(t, mock.ExpectationsWereMet()) 167 } 168 169 func TestUpsertTransactionFailUpdate(t *testing.T) { 170 s, mock := newMockProvider().init() 171 transactionID := fftypes.NewUUID() 172 mock.ExpectBegin() 173 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(transactionID.String())) 174 mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop")) 175 mock.ExpectRollback() 176 err := s.UpsertTransaction(context.Background(), &fftypes.Transaction{ID: transactionID}, true, true) 177 assert.Regexp(t, "FF10117", err) 178 assert.NoError(t, mock.ExpectationsWereMet()) 179 } 180 181 func TestUpsertTransactionFailCommit(t *testing.T) { 182 s, mock := newMockProvider().init() 183 transactionID := fftypes.NewUUID() 184 mock.ExpectBegin() 185 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"})) 186 mock.ExpectExec("INSERT .*").WillReturnResult(sqlmock.NewResult(1, 1)) 187 mock.ExpectCommit().WillReturnError(fmt.Errorf("pop")) 188 err := s.UpsertTransaction(context.Background(), &fftypes.Transaction{ID: transactionID}, true, true) 189 assert.Regexp(t, "FF10119", err) 190 assert.NoError(t, mock.ExpectationsWereMet()) 191 } 192 193 func TestGetTransactionByIDSelectFail(t *testing.T) { 194 s, mock := newMockProvider().init() 195 transactionID := fftypes.NewUUID() 196 mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) 197 _, err := s.GetTransactionByID(context.Background(), transactionID) 198 assert.Regexp(t, "FF10115", err) 199 assert.NoError(t, mock.ExpectationsWereMet()) 200 } 201 202 func TestGetTransactionByIDNotFound(t *testing.T) { 203 s, mock := newMockProvider().init() 204 transactionID := fftypes.NewUUID() 205 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"})) 206 msg, err := s.GetTransactionByID(context.Background(), transactionID) 207 assert.NoError(t, err) 208 assert.Nil(t, msg) 209 assert.NoError(t, mock.ExpectationsWereMet()) 210 } 211 212 func TestGetTransactionByIDScanFail(t *testing.T) { 213 s, mock := newMockProvider().init() 214 transactionID := fftypes.NewUUID() 215 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("only one")) 216 _, err := s.GetTransactionByID(context.Background(), transactionID) 217 assert.Regexp(t, "FF10121", err) 218 assert.NoError(t, mock.ExpectationsWereMet()) 219 } 220 221 func TestGetTransactionsQueryFail(t *testing.T) { 222 s, mock := newMockProvider().init() 223 mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) 224 f := database.TransactionQueryFactory.NewFilter(context.Background()).Eq("id", "") 225 _, err := s.GetTransactions(context.Background(), f) 226 assert.Regexp(t, "FF10115", err) 227 assert.NoError(t, mock.ExpectationsWereMet()) 228 } 229 230 func TestGetTransactionsBuildQueryFail(t *testing.T) { 231 s, _ := newMockProvider().init() 232 f := database.TransactionQueryFactory.NewFilter(context.Background()).Eq("id", map[bool]bool{true: false}) 233 _, err := s.GetTransactions(context.Background(), f) 234 assert.Regexp(t, "FF10149.*id", err) 235 } 236 237 func TestGettTransactionsReadMessageFail(t *testing.T) { 238 s, mock := newMockProvider().init() 239 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("only one")) 240 f := database.TransactionQueryFactory.NewFilter(context.Background()).Eq("id", "") 241 _, err := s.GetTransactions(context.Background(), f) 242 assert.Regexp(t, "FF10121", err) 243 assert.NoError(t, mock.ExpectationsWereMet()) 244 } 245 246 func TestTxSubmissionUpdateBeginFail(t *testing.T) { 247 s, mock := newMockProvider().init() 248 mock.ExpectBegin().WillReturnError(fmt.Errorf("pop")) 249 u := database.TransactionQueryFactory.NewUpdate(context.Background()).Set("id", "anything") 250 err := s.UpdateTransaction(context.Background(), fftypes.NewUUID(), u) 251 assert.Regexp(t, "FF10114", err) 252 } 253 254 func TestTxSubmissionUpdateBuildQueryFail(t *testing.T) { 255 s, mock := newMockProvider().init() 256 mock.ExpectBegin() 257 u := database.TransactionQueryFactory.NewUpdate(context.Background()).Set("id", map[bool]bool{true: false}) 258 err := s.UpdateTransaction(context.Background(), fftypes.NewUUID(), u) 259 assert.Regexp(t, "FF10149.*id", err) 260 } 261 262 func TestTxSubmissionUpdateFail(t *testing.T) { 263 s, mock := newMockProvider().init() 264 mock.ExpectBegin() 265 mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop")) 266 mock.ExpectRollback() 267 u := database.TransactionQueryFactory.NewUpdate(context.Background()).Set("id", fftypes.NewUUID()) 268 err := s.UpdateTransaction(context.Background(), fftypes.NewUUID(), u) 269 assert.Regexp(t, "FF10117", err) 270 }