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  }