github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/database/sqlcommon/data_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/internal/log"
    27  	"github.com/kaleido-io/firefly/pkg/database"
    28  	"github.com/kaleido-io/firefly/pkg/fftypes"
    29  	"github.com/stretchr/testify/assert"
    30  )
    31  
    32  func TestDataE2EWithDB(t *testing.T) {
    33  	log.SetLevel("debug")
    34  
    35  	s := newQLTestProvider(t)
    36  	defer s.Close()
    37  	ctx := context.Background()
    38  
    39  	// Create a new data entry
    40  	dataID := fftypes.NewUUID()
    41  	val := fftypes.JSONObject{
    42  		"some": "data",
    43  		"with": map[string]interface{}{
    44  			"nesting": 12345,
    45  		},
    46  	}
    47  	data := &fftypes.Data{
    48  		ID:        dataID,
    49  		Validator: fftypes.ValidatorTypeSystemDefinition,
    50  		Namespace: "ns1",
    51  		Hash:      fftypes.NewRandB32(),
    52  		Created:   fftypes.Now(),
    53  		Value:     []byte(val.String()),
    54  	}
    55  	err := s.UpsertData(ctx, data, true, false)
    56  	assert.NoError(t, err)
    57  
    58  	// Check we get the exact same data back - we should not to return the value first
    59  	dataRead, err := s.GetDataByID(ctx, dataID, false)
    60  	assert.NoError(t, err)
    61  	assert.Equal(t, *dataID, *dataRead.ID)
    62  	assert.Nil(t, dataRead.Value)
    63  
    64  	// Now with value
    65  	dataRead, err = s.GetDataByID(ctx, dataID, true)
    66  	assert.NotNil(t, dataRead)
    67  	dataJson, _ := json.Marshal(&data)
    68  	dataReadJson, _ := json.Marshal(&dataRead)
    69  	assert.Equal(t, string(dataJson), string(dataReadJson))
    70  
    71  	// Update the data (this is testing what's possible at the database layer,
    72  	// and does not account for the verification that happens at the higher level)
    73  	val2 := fftypes.JSONObject{
    74  		"another": "set",
    75  		"of": map[string]interface{}{
    76  			"data": 12345,
    77  			"and":  "stuff",
    78  		},
    79  	}
    80  	dataUpdated := &fftypes.Data{
    81  		ID:        dataID,
    82  		Validator: fftypes.ValidatorTypeJSON,
    83  		Namespace: "ns2",
    84  		Datatype: &fftypes.DatatypeRef{
    85  			Name:    "customer",
    86  			Version: "0.0.1",
    87  		},
    88  		Hash:    fftypes.NewRandB32(),
    89  		Created: fftypes.Now(),
    90  		Value:   []byte(val2.String()),
    91  	}
    92  
    93  	// Check disallows hash update
    94  	err = s.UpsertData(context.Background(), dataUpdated, true, false)
    95  	assert.Equal(t, database.HashMismatch, err)
    96  
    97  	err = s.UpsertData(context.Background(), dataUpdated, true, true)
    98  	assert.NoError(t, err)
    99  
   100  	// Check we get the exact same message back - note the removal of one of the data elements
   101  	dataRead, err = s.GetDataByID(ctx, dataID, true)
   102  	assert.NoError(t, err)
   103  	dataJson, _ = json.Marshal(&dataUpdated)
   104  	dataReadJson, _ = json.Marshal(&dataRead)
   105  	assert.Equal(t, string(dataJson), string(dataReadJson))
   106  
   107  	valRestored, ok := dataRead.Value.JSONObjectOk()
   108  	assert.True(t, ok)
   109  	assert.Equal(t, "stuff", valRestored.GetObject("of").GetString("and"))
   110  
   111  	// Query back the data
   112  	fb := database.DataQueryFactory.NewFilter(ctx)
   113  	filter := fb.And(
   114  		fb.Eq("id", dataUpdated.ID.String()),
   115  		fb.Eq("namespace", dataUpdated.Namespace),
   116  		fb.Eq("validator", string(dataUpdated.Validator)),
   117  		fb.Eq("datatype.name", dataUpdated.Datatype.Name),
   118  		fb.Eq("datatype.version", dataUpdated.Datatype.Version),
   119  		fb.Eq("hash", dataUpdated.Hash),
   120  		fb.Gt("created", 0),
   121  	)
   122  	dataRes, err := s.GetData(ctx, filter)
   123  	assert.NoError(t, err)
   124  	assert.Equal(t, 1, len(dataRes))
   125  	dataReadJson, _ = json.Marshal(dataRes[0])
   126  	assert.Equal(t, string(dataJson), string(dataReadJson))
   127  
   128  	dataRefRes, err := s.GetDataRefs(ctx, filter)
   129  	assert.NoError(t, err)
   130  	assert.Equal(t, 1, len(dataRefRes))
   131  	assert.Equal(t, *dataUpdated.ID, *dataRefRes[0].ID)
   132  	assert.Equal(t, dataUpdated.Hash, dataRefRes[0].Hash)
   133  
   134  	// Update
   135  	v2 := "2.0.0"
   136  	up := database.DataQueryFactory.NewUpdate(ctx).Set("datatype.version", v2)
   137  	err = s.UpdateData(ctx, dataID, up)
   138  	assert.NoError(t, err)
   139  
   140  	// Test find updated value
   141  	filter = fb.And(
   142  		fb.Eq("id", dataUpdated.ID.String()),
   143  		fb.Eq("datatype.version", v2),
   144  	)
   145  	dataRes, err = s.GetData(ctx, filter)
   146  	assert.NoError(t, err)
   147  	assert.Equal(t, 1, len(dataRes))
   148  
   149  }
   150  
   151  func TestUpsertDataFailBegin(t *testing.T) {
   152  	s, mock := newMockProvider().init()
   153  	mock.ExpectBegin().WillReturnError(fmt.Errorf("pop"))
   154  	err := s.UpsertData(context.Background(), &fftypes.Data{}, true, true)
   155  	assert.Regexp(t, "FF10114", err)
   156  	assert.NoError(t, mock.ExpectationsWereMet())
   157  }
   158  
   159  func TestUpsertDataFailSelect(t *testing.T) {
   160  	s, mock := newMockProvider().init()
   161  	mock.ExpectBegin()
   162  	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
   163  	mock.ExpectRollback()
   164  	dataID := fftypes.NewUUID()
   165  	err := s.UpsertData(context.Background(), &fftypes.Data{ID: dataID}, true, true)
   166  	assert.Regexp(t, "FF10115", err)
   167  	assert.NoError(t, mock.ExpectationsWereMet())
   168  }
   169  
   170  func TestUpsertDataFailInsert(t *testing.T) {
   171  	s, mock := newMockProvider().init()
   172  	mock.ExpectBegin()
   173  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{}))
   174  	mock.ExpectExec("INSERT .*").WillReturnError(fmt.Errorf("pop"))
   175  	mock.ExpectRollback()
   176  	dataID := fftypes.NewUUID()
   177  	err := s.UpsertData(context.Background(), &fftypes.Data{ID: dataID}, true, true)
   178  	assert.Regexp(t, "FF10116", err)
   179  	assert.NoError(t, mock.ExpectationsWereMet())
   180  }
   181  
   182  func TestUpsertDataFailUpdate(t *testing.T) {
   183  	s, mock := newMockProvider().init()
   184  	dataID := fftypes.NewUUID()
   185  	mock.ExpectBegin()
   186  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(dataID.String()))
   187  	mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop"))
   188  	mock.ExpectRollback()
   189  	err := s.UpsertData(context.Background(), &fftypes.Data{ID: dataID}, true, true)
   190  	assert.Regexp(t, "FF10117", err)
   191  	assert.NoError(t, mock.ExpectationsWereMet())
   192  }
   193  
   194  func TestUpsertDataFailCommit(t *testing.T) {
   195  	s, mock := newMockProvider().init()
   196  	dataID := fftypes.NewUUID()
   197  	mock.ExpectBegin()
   198  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}))
   199  	mock.ExpectExec("INSERT .*").WillReturnResult(sqlmock.NewResult(1, 1))
   200  	mock.ExpectCommit().WillReturnError(fmt.Errorf("pop"))
   201  	err := s.UpsertData(context.Background(), &fftypes.Data{ID: dataID}, true, true)
   202  	assert.Regexp(t, "FF10119", err)
   203  	assert.NoError(t, mock.ExpectationsWereMet())
   204  }
   205  
   206  func TestGetDataByIDSelectFail(t *testing.T) {
   207  	s, mock := newMockProvider().init()
   208  	dataID := fftypes.NewUUID()
   209  	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
   210  	_, err := s.GetDataByID(context.Background(), dataID, false)
   211  	assert.Regexp(t, "FF10115", err)
   212  	assert.NoError(t, mock.ExpectationsWereMet())
   213  }
   214  
   215  func TestGetDataByIDNotFound(t *testing.T) {
   216  	s, mock := newMockProvider().init()
   217  	dataID := fftypes.NewUUID()
   218  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}))
   219  	msg, err := s.GetDataByID(context.Background(), dataID, true)
   220  	assert.NoError(t, err)
   221  	assert.Nil(t, msg)
   222  	assert.NoError(t, mock.ExpectationsWereMet())
   223  }
   224  
   225  func TestGetDataByIDScanFail(t *testing.T) {
   226  	s, mock := newMockProvider().init()
   227  	dataID := fftypes.NewUUID()
   228  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("only one"))
   229  	_, err := s.GetDataByID(context.Background(), dataID, true)
   230  	assert.Regexp(t, "FF10121", err)
   231  	assert.NoError(t, mock.ExpectationsWereMet())
   232  }
   233  
   234  func TestGetDataQueryFail(t *testing.T) {
   235  	s, mock := newMockProvider().init()
   236  	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
   237  	f := database.DataQueryFactory.NewFilter(context.Background()).Eq("id", "")
   238  	_, err := s.GetData(context.Background(), f)
   239  	assert.Regexp(t, "FF10115", err)
   240  	assert.NoError(t, mock.ExpectationsWereMet())
   241  }
   242  
   243  func TestGetDataBuildQueryFail(t *testing.T) {
   244  	s, _ := newMockProvider().init()
   245  	f := database.DataQueryFactory.NewFilter(context.Background()).Eq("id", map[bool]bool{true: false})
   246  	_, err := s.GetData(context.Background(), f)
   247  	assert.Regexp(t, "FF10149.*id", err)
   248  }
   249  
   250  func TestGetDataReadMessageFail(t *testing.T) {
   251  	s, mock := newMockProvider().init()
   252  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("only one"))
   253  	f := database.DataQueryFactory.NewFilter(context.Background()).Eq("id", "")
   254  	_, err := s.GetData(context.Background(), f)
   255  	assert.Regexp(t, "FF10121", err)
   256  	assert.NoError(t, mock.ExpectationsWereMet())
   257  }
   258  
   259  func TestGetDataRefsQueryFail(t *testing.T) {
   260  	s, mock := newMockProvider().init()
   261  	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
   262  	f := database.DataQueryFactory.NewFilter(context.Background()).Eq("id", "")
   263  	_, err := s.GetDataRefs(context.Background(), f)
   264  	assert.Regexp(t, "FF10115", err)
   265  	assert.NoError(t, mock.ExpectationsWereMet())
   266  }
   267  
   268  func TestGetDataRefsBuildQueryFail(t *testing.T) {
   269  	s, _ := newMockProvider().init()
   270  	f := database.DataQueryFactory.NewFilter(context.Background()).Eq("id", map[bool]bool{true: false})
   271  	_, err := s.GetDataRefs(context.Background(), f)
   272  	assert.Regexp(t, "FF10149.*id", err)
   273  }
   274  
   275  func TestGetDataRefsReadMessageFail(t *testing.T) {
   276  	s, mock := newMockProvider().init()
   277  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("only one"))
   278  	f := database.DataQueryFactory.NewFilter(context.Background()).Eq("id", "")
   279  	_, err := s.GetDataRefs(context.Background(), f)
   280  	assert.Regexp(t, "FF10121", err)
   281  	assert.NoError(t, mock.ExpectationsWereMet())
   282  }
   283  
   284  func TestDataUpdateBeginFail(t *testing.T) {
   285  	s, mock := newMockProvider().init()
   286  	mock.ExpectBegin().WillReturnError(fmt.Errorf("pop"))
   287  	u := database.DataQueryFactory.NewUpdate(context.Background()).Set("id", "anything")
   288  	err := s.UpdateData(context.Background(), fftypes.NewUUID(), u)
   289  	assert.Regexp(t, "FF10114", err)
   290  }
   291  
   292  func TestDataUpdateBuildQueryFail(t *testing.T) {
   293  	s, mock := newMockProvider().init()
   294  	mock.ExpectBegin()
   295  	u := database.DataQueryFactory.NewUpdate(context.Background()).Set("id", map[bool]bool{true: false})
   296  	err := s.UpdateData(context.Background(), fftypes.NewUUID(), u)
   297  	assert.Regexp(t, "FF10149.*id", err)
   298  }
   299  
   300  func TestDataUpdateFail(t *testing.T) {
   301  	s, mock := newMockProvider().init()
   302  	mock.ExpectBegin()
   303  	mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop"))
   304  	mock.ExpectRollback()
   305  	u := database.DataQueryFactory.NewUpdate(context.Background()).Set("id", fftypes.NewUUID())
   306  	err := s.UpdateData(context.Background(), fftypes.NewUUID(), u)
   307  	assert.Regexp(t, "FF10117", err)
   308  }