github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/database/sqlcommon/operation_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 TestOperationE2EWithDB(t *testing.T) { 32 33 s := newQLTestProvider(t) 34 defer s.Close() 35 ctx := context.Background() 36 37 // Create a new operation entry 38 operationID := fftypes.NewUUID() 39 operation := &fftypes.Operation{ 40 ID: operationID, 41 Type: fftypes.OpTypeBlockchainBatchPin, 42 Transaction: fftypes.NewUUID(), 43 Status: fftypes.OpStatusPending, 44 Created: fftypes.Now(), 45 } 46 err := s.UpsertOperation(ctx, operation, true) 47 assert.NoError(t, err) 48 49 // Check we get the exact same operation back 50 operationRead, err := s.GetOperationByID(ctx, operationID) 51 assert.NoError(t, err) 52 assert.NotNil(t, operationRead) 53 operationJson, _ := json.Marshal(&operation) 54 operationReadJson, _ := json.Marshal(&operationRead) 55 assert.Equal(t, string(operationJson), string(operationReadJson)) 56 57 // Update the operation (this is testing what's possible at the database layer, 58 // and does not account for the verification that happens at the higher level) 59 operationUpdated := &fftypes.Operation{ 60 ID: operationID, 61 Type: fftypes.OpTypeBlockchainBatchPin, 62 Transaction: fftypes.NewUUID(), 63 Status: fftypes.OpStatusFailed, 64 Member: "sally", 65 Plugin: "ethereum", 66 BackendID: fftypes.NewRandB32().String(), 67 Error: "pop", 68 Info: fftypes.JSONObject{"some": "info"}, 69 Created: fftypes.Now(), 70 Updated: fftypes.Now(), 71 } 72 err = s.UpsertOperation(context.Background(), operationUpdated, true) 73 assert.NoError(t, err) 74 75 // Check we get the exact same message back - note the removal of one of the operation elements 76 operationRead, err = s.GetOperationByID(ctx, operationID) 77 assert.NoError(t, err) 78 operationJson, _ = json.Marshal(&operationUpdated) 79 operationReadJson, _ = json.Marshal(&operationRead) 80 assert.Equal(t, string(operationJson), string(operationReadJson)) 81 82 // Query back the operation 83 fb := database.OperationQueryFactory.NewFilter(ctx) 84 filter := fb.And( 85 fb.Eq("id", operationUpdated.ID.String()), 86 fb.Eq("tx", operationUpdated.Transaction), 87 fb.Eq("type", operationUpdated.Type), 88 fb.Eq("member", operationUpdated.Member), 89 fb.Eq("status", operationUpdated.Status), 90 fb.Eq("error", operationUpdated.Error), 91 fb.Eq("plugin", operationUpdated.Plugin), 92 fb.Eq("backendid", operationUpdated.BackendID), 93 fb.Gt("created", 0), 94 fb.Gt("updated", 0), 95 ) 96 97 operations, err := s.GetOperations(ctx, filter) 98 assert.NoError(t, err) 99 assert.Equal(t, 1, len(operations)) 100 operationReadJson, _ = json.Marshal(operations[0]) 101 assert.Equal(t, string(operationJson), string(operationReadJson)) 102 103 // Negative test on filter 104 filter = fb.And( 105 fb.Eq("id", operationUpdated.ID.String()), 106 fb.Eq("updated", "0"), 107 ) 108 operations, err = s.GetOperations(ctx, filter) 109 assert.NoError(t, err) 110 assert.Equal(t, 0, len(operations)) 111 112 // Update 113 updateTime := fftypes.Now() 114 up := database.OperationQueryFactory.NewUpdate(ctx). 115 Set("status", fftypes.OpStatusSucceeded). 116 Set("updated", updateTime). 117 Set("error", "") 118 err = s.UpdateOperation(ctx, operationUpdated.ID, up) 119 assert.NoError(t, err) 120 121 // Test find updated value 122 filter = fb.And( 123 fb.Eq("id", operationUpdated.ID.String()), 124 fb.Eq("status", fftypes.OpStatusSucceeded), 125 fb.Eq("error", ""), 126 ) 127 operations, err = s.GetOperations(ctx, filter) 128 assert.NoError(t, err) 129 assert.Equal(t, 1, len(operations)) 130 } 131 132 func TestUpsertOperationFailBegin(t *testing.T) { 133 s, mock := newMockProvider().init() 134 mock.ExpectBegin().WillReturnError(fmt.Errorf("pop")) 135 err := s.UpsertOperation(context.Background(), &fftypes.Operation{}, true) 136 assert.Regexp(t, "FF10114", err) 137 assert.NoError(t, mock.ExpectationsWereMet()) 138 } 139 140 func TestUpsertOperationFailSelect(t *testing.T) { 141 s, mock := newMockProvider().init() 142 mock.ExpectBegin() 143 mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) 144 mock.ExpectRollback() 145 operationID := fftypes.NewUUID() 146 err := s.UpsertOperation(context.Background(), &fftypes.Operation{ID: operationID}, true) 147 assert.Regexp(t, "FF10115", err) 148 assert.NoError(t, mock.ExpectationsWereMet()) 149 } 150 151 func TestUpsertOperationFailInsert(t *testing.T) { 152 s, mock := newMockProvider().init() 153 mock.ExpectBegin() 154 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{})) 155 mock.ExpectExec("INSERT .*").WillReturnError(fmt.Errorf("pop")) 156 mock.ExpectRollback() 157 operationID := fftypes.NewUUID() 158 err := s.UpsertOperation(context.Background(), &fftypes.Operation{ID: operationID}, true) 159 assert.Regexp(t, "FF10116", err) 160 assert.NoError(t, mock.ExpectationsWereMet()) 161 } 162 163 func TestUpsertOperationFailUpdate(t *testing.T) { 164 s, mock := newMockProvider().init() 165 operationID := fftypes.NewUUID() 166 mock.ExpectBegin() 167 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(operationID.String())) 168 mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop")) 169 mock.ExpectRollback() 170 err := s.UpsertOperation(context.Background(), &fftypes.Operation{ID: operationID}, true) 171 assert.Regexp(t, "FF10117", err) 172 assert.NoError(t, mock.ExpectationsWereMet()) 173 } 174 175 func TestUpsertOperationFailCommit(t *testing.T) { 176 s, mock := newMockProvider().init() 177 operationID := fftypes.NewUUID() 178 mock.ExpectBegin() 179 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"})) 180 mock.ExpectExec("INSERT .*").WillReturnResult(sqlmock.NewResult(1, 1)) 181 mock.ExpectCommit().WillReturnError(fmt.Errorf("pop")) 182 err := s.UpsertOperation(context.Background(), &fftypes.Operation{ID: operationID}, true) 183 assert.Regexp(t, "FF10119", err) 184 assert.NoError(t, mock.ExpectationsWereMet()) 185 } 186 187 func TestGetOperationByIDSelectFail(t *testing.T) { 188 s, mock := newMockProvider().init() 189 operationID := fftypes.NewUUID() 190 mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) 191 _, err := s.GetOperationByID(context.Background(), operationID) 192 assert.Regexp(t, "FF10115", err) 193 assert.NoError(t, mock.ExpectationsWereMet()) 194 } 195 196 func TestGetOperationByIDNotFound(t *testing.T) { 197 s, mock := newMockProvider().init() 198 operationID := fftypes.NewUUID() 199 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"})) 200 msg, err := s.GetOperationByID(context.Background(), operationID) 201 assert.NoError(t, err) 202 assert.Nil(t, msg) 203 assert.NoError(t, mock.ExpectationsWereMet()) 204 } 205 206 func TestGetOperationByIDScanFail(t *testing.T) { 207 s, mock := newMockProvider().init() 208 operationID := fftypes.NewUUID() 209 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("only one")) 210 _, err := s.GetOperationByID(context.Background(), operationID) 211 assert.Regexp(t, "FF10121", err) 212 assert.NoError(t, mock.ExpectationsWereMet()) 213 } 214 215 func TestGetOperationsQueryFail(t *testing.T) { 216 s, mock := newMockProvider().init() 217 mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) 218 f := database.OperationQueryFactory.NewFilter(context.Background()).Eq("id", "") 219 _, err := s.GetOperations(context.Background(), f) 220 assert.Regexp(t, "FF10115", err) 221 assert.NoError(t, mock.ExpectationsWereMet()) 222 } 223 224 func TestGetOperationsBuildQueryFail(t *testing.T) { 225 s, _ := newMockProvider().init() 226 f := database.OperationQueryFactory.NewFilter(context.Background()).Eq("id", map[bool]bool{true: false}) 227 _, err := s.GetOperations(context.Background(), f) 228 assert.Regexp(t, "FF10149.*id", err) 229 } 230 231 func TestGettOperationsReadMessageFail(t *testing.T) { 232 s, mock := newMockProvider().init() 233 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("only one")) 234 f := database.OperationQueryFactory.NewFilter(context.Background()).Eq("id", "") 235 _, err := s.GetOperations(context.Background(), f) 236 assert.Regexp(t, "FF10121", err) 237 assert.NoError(t, mock.ExpectationsWereMet()) 238 } 239 240 func TestOperationUpdateBeginFail(t *testing.T) { 241 s, mock := newMockProvider().init() 242 mock.ExpectBegin().WillReturnError(fmt.Errorf("pop")) 243 u := database.OperationQueryFactory.NewUpdate(context.Background()).Set("id", fftypes.NewUUID()) 244 err := s.UpdateOperation(context.Background(), fftypes.NewUUID(), u) 245 assert.Regexp(t, "FF10114", err) 246 } 247 248 func TestOperationUpdateBuildQueryFail(t *testing.T) { 249 s, mock := newMockProvider().init() 250 mock.ExpectBegin() 251 u := database.OperationQueryFactory.NewUpdate(context.Background()).Set("id", map[bool]bool{true: false}) 252 err := s.UpdateOperation(context.Background(), fftypes.NewUUID(), u) 253 assert.Regexp(t, "FF10149.*id", err) 254 } 255 256 func TestOperationUpdateFail(t *testing.T) { 257 s, mock := newMockProvider().init() 258 mock.ExpectBegin() 259 mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop")) 260 mock.ExpectRollback() 261 u := database.OperationQueryFactory.NewUpdate(context.Background()).Set("id", fftypes.NewUUID()) 262 err := s.UpdateOperation(context.Background(), fftypes.NewUUID(), u) 263 assert.Regexp(t, "FF10117", err) 264 }