github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/database/sqlcommon/group_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 "database/sql/driver" 22 "encoding/json" 23 "fmt" 24 "testing" 25 26 "github.com/DATA-DOG/go-sqlmock" 27 "github.com/kaleido-io/firefly/internal/log" 28 "github.com/kaleido-io/firefly/pkg/database" 29 "github.com/kaleido-io/firefly/pkg/fftypes" 30 "github.com/stretchr/testify/assert" 31 ) 32 33 func TestUpsertGroupE2EWithDB(t *testing.T) { 34 log.SetLevel("debug") 35 36 s := newQLTestProvider(t) 37 defer s.Close() 38 ctx := context.Background() 39 40 // Create a new group 41 groupHash := fftypes.NewRandB32() 42 group := &fftypes.Group{ 43 GroupIdentity: fftypes.GroupIdentity{ 44 Name: "group1", 45 Namespace: "ns1", 46 Members: fftypes.Members{ 47 {Identity: "0x12345", Node: fftypes.NewUUID()}, 48 {Identity: "0x23456", Node: fftypes.NewUUID()}, 49 }, 50 }, 51 Hash: groupHash, 52 Created: fftypes.Now(), 53 } 54 err := s.UpsertGroup(ctx, group, true) 55 assert.NoError(t, err) 56 57 // Check we get the exact same group back 58 groupRead, err := s.GetGroupByHash(ctx, group.Hash) 59 assert.NoError(t, err) 60 groupJson, _ := json.Marshal(&group) 61 groupReadJson, _ := json.Marshal(&groupRead) 62 assert.Equal(t, string(groupJson), string(groupReadJson)) 63 64 // Update the group (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 groupUpdated := &fftypes.Group{ 67 GroupIdentity: fftypes.GroupIdentity{ 68 Name: "group1", 69 Namespace: "ns1", 70 Members: fftypes.Members{ 71 {Identity: "0x12345", Node: fftypes.NewUUID()}, 72 group.Members[0], 73 }, 74 Ledger: fftypes.NewUUID(), 75 }, 76 Created: fftypes.Now(), 77 Message: fftypes.NewUUID(), 78 Hash: groupHash, 79 } 80 81 err = s.UpsertGroup(context.Background(), groupUpdated, true) 82 assert.NoError(t, err) 83 84 // Check we get the exact same group back - note the removal of one of the data elements 85 groupRead, err = s.GetGroupByHash(ctx, group.Hash) 86 assert.NoError(t, err) 87 groupJson, _ = json.Marshal(&groupUpdated) 88 groupReadJson, _ = json.Marshal(&groupRead) 89 assert.Equal(t, string(groupJson), string(groupReadJson)) 90 91 // Query back the group 92 fb := database.GroupQueryFactory.NewFilter(ctx) 93 filter := fb.And( 94 fb.Eq("hash", groupUpdated.Hash), 95 fb.Eq("namespace", groupUpdated.Namespace), 96 fb.Eq("message", groupUpdated.Message), 97 fb.Eq("ledger", groupUpdated.Ledger), 98 fb.Gt("created", "0"), 99 ) 100 groups, err := s.GetGroups(ctx, filter) 101 assert.NoError(t, err) 102 assert.Equal(t, 1, len(groups)) 103 groupReadJson, _ = json.Marshal(groups[0]) 104 assert.Equal(t, string(groupJson), string(groupReadJson)) 105 106 // Negative test on filter 107 filter = fb.And( 108 fb.Eq("hash", groupUpdated.Hash.String()), 109 fb.Eq("created", "0"), 110 ) 111 groups, err = s.GetGroups(ctx, filter) 112 assert.NoError(t, err) 113 assert.Equal(t, 0, len(groups)) 114 115 // Update (testing what's possible at the DB layer) 116 newHash := fftypes.NewRandB32() 117 up := database.GroupQueryFactory.NewUpdate(ctx). 118 Set("hash", newHash) 119 err = s.UpdateGroup(ctx, group.Hash, up) 120 assert.NoError(t, err) 121 122 // Test find updated value 123 filter = fb.And( 124 fb.Eq("hash", newHash), 125 ) 126 groups, err = s.GetGroups(ctx, filter) 127 assert.NoError(t, err) 128 assert.Equal(t, 1, len(groups)) 129 130 s.callbacks.AssertExpectations(t) 131 } 132 133 func TestUpsertGroupFailBegin(t *testing.T) { 134 s, mock := newMockProvider().init() 135 mock.ExpectBegin().WillReturnError(fmt.Errorf("pop")) 136 err := s.UpsertGroup(context.Background(), &fftypes.Group{}, true) 137 assert.Regexp(t, "FF10114", err) 138 assert.NoError(t, mock.ExpectationsWereMet()) 139 } 140 141 func TestUpsertGroupFailSelect(t *testing.T) { 142 s, mock := newMockProvider().init() 143 mock.ExpectBegin() 144 mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) 145 mock.ExpectRollback() 146 groupID := fftypes.NewRandB32() 147 err := s.UpsertGroup(context.Background(), &fftypes.Group{Hash: groupID}, true) 148 assert.Regexp(t, "FF10115", err) 149 assert.NoError(t, mock.ExpectationsWereMet()) 150 } 151 152 func TestUpsertGroupFailInsert(t *testing.T) { 153 s, mock := newMockProvider().init() 154 mock.ExpectBegin() 155 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{})) 156 mock.ExpectExec("INSERT .*").WillReturnError(fmt.Errorf("pop")) 157 mock.ExpectRollback() 158 groupID := fftypes.NewRandB32() 159 err := s.UpsertGroup(context.Background(), &fftypes.Group{Hash: groupID}, true) 160 assert.Regexp(t, "FF10116", err) 161 assert.NoError(t, mock.ExpectationsWereMet()) 162 } 163 164 func TestUpsertGroupFailUpdate(t *testing.T) { 165 s, mock := newMockProvider().init() 166 groupID := fftypes.NewRandB32() 167 mock.ExpectBegin() 168 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"hash"}).AddRow(groupID.String())) 169 mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop")) 170 mock.ExpectRollback() 171 err := s.UpsertGroup(context.Background(), &fftypes.Group{Hash: groupID}, true) 172 assert.Regexp(t, "FF10117", err) 173 assert.NoError(t, mock.ExpectationsWereMet()) 174 } 175 176 func TestUpsertGroupFailUpdateMembers(t *testing.T) { 177 s, mock := newMockProvider().init() 178 groupID := fftypes.NewRandB32() 179 mock.ExpectBegin() 180 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"hash"}).AddRow(groupID.String())) 181 mock.ExpectExec("UPDATE .*").WillReturnResult(driver.ResultNoRows) 182 mock.ExpectExec("DELETE .*").WillReturnError(fmt.Errorf("pop")) 183 mock.ExpectRollback() 184 err := s.UpsertGroup(context.Background(), &fftypes.Group{Hash: groupID}, true) 185 assert.Regexp(t, "FF10118", err) 186 assert.NoError(t, mock.ExpectationsWereMet()) 187 } 188 189 func TestUpsertGroupFailCommit(t *testing.T) { 190 s, mock := newMockProvider().init() 191 groupID := fftypes.NewRandB32() 192 mock.ExpectBegin() 193 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"hash"})) 194 mock.ExpectExec("INSERT .*").WillReturnResult(sqlmock.NewResult(1, 1)) 195 mock.ExpectCommit().WillReturnError(fmt.Errorf("pop")) 196 err := s.UpsertGroup(context.Background(), &fftypes.Group{Hash: groupID}, true) 197 assert.Regexp(t, "FF10119", err) 198 assert.NoError(t, mock.ExpectationsWereMet()) 199 } 200 201 func TestUpdateMembersMissingOrg(t *testing.T) { 202 s, mock := newMockProvider().init() 203 groupID := fftypes.NewRandB32() 204 mock.ExpectBegin() 205 tx, _ := s.db.Begin() 206 err := s.updateMembers(context.Background(), &txWrapper{sqlTX: tx}, &fftypes.Group{ 207 Hash: groupID, 208 GroupIdentity: fftypes.GroupIdentity{ 209 Members: fftypes.Members{{Node: fftypes.NewUUID()}}, 210 }, 211 }, false) 212 assert.Regexp(t, "FF10220", err) 213 assert.NoError(t, mock.ExpectationsWereMet()) 214 } 215 216 func TestUpdateMembersMissingNode(t *testing.T) { 217 s, mock := newMockProvider().init() 218 groupID := fftypes.NewRandB32() 219 mock.ExpectBegin() 220 tx, _ := s.db.Begin() 221 err := s.updateMembers(context.Background(), &txWrapper{sqlTX: tx}, &fftypes.Group{ 222 Hash: groupID, 223 GroupIdentity: fftypes.GroupIdentity{ 224 Members: fftypes.Members{{Identity: "0x12345"}}, 225 }, 226 }, false) 227 assert.Regexp(t, "FF10221", err) 228 assert.NoError(t, mock.ExpectationsWereMet()) 229 } 230 231 func TestUpdateGroupDataDeleteFail(t *testing.T) { 232 s, mock := newMockProvider().init() 233 groupID := fftypes.NewRandB32() 234 mock.ExpectBegin() 235 tx, _ := s.db.Begin() 236 mock.ExpectExec("DELETE .*").WillReturnError(fmt.Errorf("pop")) 237 err := s.updateMembers(context.Background(), &txWrapper{sqlTX: tx}, &fftypes.Group{ 238 Hash: groupID, 239 }, true) 240 assert.Regexp(t, "FF10118", err) 241 assert.NoError(t, mock.ExpectationsWereMet()) 242 } 243 244 func TestUpdateGroupDataAddFail(t *testing.T) { 245 s, mock := newMockProvider().init() 246 groupID := fftypes.NewRandB32() 247 mock.ExpectBegin() 248 tx, _ := s.db.Begin() 249 mock.ExpectExec("INSERT .*").WillReturnError(fmt.Errorf("pop")) 250 err := s.updateMembers(context.Background(), &txWrapper{sqlTX: tx}, &fftypes.Group{ 251 Hash: groupID, 252 GroupIdentity: fftypes.GroupIdentity{ 253 Members: fftypes.Members{{Identity: "0x12345", Node: fftypes.NewUUID()}}, 254 }, 255 }, false) 256 assert.Regexp(t, "FF10116", err) 257 assert.NoError(t, mock.ExpectationsWereMet()) 258 } 259 260 func TestLoadMembersQueryFail(t *testing.T) { 261 s, mock := newMockProvider().init() 262 groupID := fftypes.NewRandB32() 263 mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) 264 err := s.loadMembers(context.Background(), []*fftypes.Group{{Hash: groupID}}) 265 assert.Regexp(t, "FF10115", err) 266 assert.NoError(t, mock.ExpectationsWereMet()) 267 } 268 269 func TestLoadMembersScanFail(t *testing.T) { 270 s, mock := newMockProvider().init() 271 groupID := fftypes.NewRandB32() 272 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"identity"}).AddRow("only one")) 273 err := s.loadMembers(context.Background(), []*fftypes.Group{{Hash: groupID}}) 274 assert.Regexp(t, "FF10121", err) 275 assert.NoError(t, mock.ExpectationsWereMet()) 276 } 277 278 func TestLoadMembersEmpty(t *testing.T) { 279 s, mock := newMockProvider().init() 280 groupID := fftypes.NewRandB32() 281 group := &fftypes.Group{Hash: groupID} 282 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"identity"})) 283 err := s.loadMembers(context.Background(), []*fftypes.Group{group}) 284 assert.NoError(t, err) 285 assert.Equal(t, fftypes.Members{}, group.Members) 286 assert.NoError(t, mock.ExpectationsWereMet()) 287 } 288 289 func TestGetGroupByIDSelectFail(t *testing.T) { 290 s, mock := newMockProvider().init() 291 groupID := fftypes.NewRandB32() 292 mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) 293 _, err := s.GetGroupByHash(context.Background(), groupID) 294 assert.Regexp(t, "FF10115", err) 295 assert.NoError(t, mock.ExpectationsWereMet()) 296 } 297 298 func TestGetGroupByIDNotFound(t *testing.T) { 299 s, mock := newMockProvider().init() 300 groupID := fftypes.NewRandB32() 301 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"hash"})) 302 group, err := s.GetGroupByHash(context.Background(), groupID) 303 assert.NoError(t, err) 304 assert.Nil(t, group) 305 assert.NoError(t, mock.ExpectationsWereMet()) 306 } 307 308 func TestGetGroupByIDScanFail(t *testing.T) { 309 s, mock := newMockProvider().init() 310 groupID := fftypes.NewRandB32() 311 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"hash"}).AddRow("only one")) 312 _, err := s.GetGroupByHash(context.Background(), groupID) 313 assert.Regexp(t, "FF10121", err) 314 assert.NoError(t, mock.ExpectationsWereMet()) 315 } 316 317 func TestGetGroupByIDLoadMembersFail(t *testing.T) { 318 s, mock := newMockProvider().init() 319 groupID := fftypes.NewRandB32() 320 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows(groupColumns). 321 AddRow(nil, "ns1", "name1", fftypes.NewUUID(), fftypes.NewRandB32(), fftypes.Now())) 322 mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) 323 _, err := s.GetGroupByHash(context.Background(), groupID) 324 assert.Regexp(t, "FF10115", err) 325 assert.NoError(t, mock.ExpectationsWereMet()) 326 } 327 328 func TestGetGroupsBuildQueryFail(t *testing.T) { 329 s, _ := newMockProvider().init() 330 f := database.GroupQueryFactory.NewFilter(context.Background()).Eq("hash", map[bool]bool{true: false}) 331 _, err := s.GetGroups(context.Background(), f) 332 assert.Regexp(t, "FF10149.*hash", err) 333 } 334 335 func TestGetGroupsQueryFail(t *testing.T) { 336 s, mock := newMockProvider().init() 337 mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) 338 f := database.GroupQueryFactory.NewFilter(context.Background()).Eq("hash", "") 339 _, err := s.GetGroups(context.Background(), f) 340 assert.Regexp(t, "FF10115", err) 341 assert.NoError(t, mock.ExpectationsWereMet()) 342 } 343 344 func TestGetGroupsReadGroupFail(t *testing.T) { 345 s, mock := newMockProvider().init() 346 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"hash"}).AddRow("only one")) 347 f := database.GroupQueryFactory.NewFilter(context.Background()).Eq("hash", "") 348 _, err := s.GetGroups(context.Background(), f) 349 assert.Regexp(t, "FF10121", err) 350 assert.NoError(t, mock.ExpectationsWereMet()) 351 } 352 353 func TestGetGroupsLoadMembersFail(t *testing.T) { 354 s, mock := newMockProvider().init() 355 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows(groupColumns). 356 AddRow(nil, "ns1", "group1", fftypes.NewUUID(), fftypes.NewRandB32(), fftypes.Now())) 357 mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) 358 f := database.GroupQueryFactory.NewFilter(context.Background()).Gt("created", "0") 359 _, err := s.GetGroups(context.Background(), f) 360 assert.Regexp(t, "FF10115", err) 361 assert.NoError(t, mock.ExpectationsWereMet()) 362 } 363 364 func TestGroupUpdateBeginFail(t *testing.T) { 365 s, mock := newMockProvider().init() 366 mock.ExpectBegin().WillReturnError(fmt.Errorf("pop")) 367 u := database.GroupQueryFactory.NewUpdate(context.Background()).Set("hash", "anything") 368 err := s.UpdateGroup(context.Background(), fftypes.NewRandB32(), u) 369 assert.Regexp(t, "FF10114", err) 370 } 371 372 func TestGroupUpdateBuildQueryFail(t *testing.T) { 373 s, mock := newMockProvider().init() 374 mock.ExpectBegin() 375 u := database.GroupQueryFactory.NewUpdate(context.Background()).Set("hash", map[bool]bool{true: false}) 376 err := s.UpdateGroup(context.Background(), fftypes.NewRandB32(), u) 377 assert.Regexp(t, "FF10149.*hash", err) 378 } 379 380 func TestGroupsUpdateBuildFilterFail(t *testing.T) { 381 s, mock := newMockProvider().init() 382 mock.ExpectBegin() 383 f := database.GroupQueryFactory.NewFilter(context.Background()).Eq("hash", map[bool]bool{true: false}) 384 u := database.GroupQueryFactory.NewUpdate(context.Background()).Set("description", "my desc") 385 err := s.UpdateGroups(context.Background(), f, u) 386 assert.Regexp(t, "FF10149.*hash", err) 387 } 388 389 func TestGroupUpdateFail(t *testing.T) { 390 s, mock := newMockProvider().init() 391 mock.ExpectBegin() 392 mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop")) 393 mock.ExpectRollback() 394 u := database.GroupQueryFactory.NewUpdate(context.Background()).Set("description", fftypes.NewUUID()) 395 err := s.UpdateGroup(context.Background(), fftypes.NewRandB32(), u) 396 assert.Regexp(t, "FF10117", err) 397 }