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  }