github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/database/sqlcommon/transaction_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 TestTransactionE2EWithDB(t *testing.T) {
    32  
    33  	s := newQLTestProvider(t)
    34  	defer s.Close()
    35  	ctx := context.Background()
    36  
    37  	// Create a new transaction entry
    38  	transactionID := fftypes.NewUUID()
    39  	transaction := &fftypes.Transaction{
    40  		ID:   transactionID,
    41  		Hash: fftypes.NewRandB32(),
    42  		Subject: fftypes.TransactionSubject{
    43  			Type:      fftypes.TransactionTypeBatchPin,
    44  			Namespace: "ns1",
    45  			Signer:    "0x12345",
    46  			Reference: fftypes.NewUUID(),
    47  		},
    48  		Created: fftypes.Now(),
    49  		Status:  fftypes.OpStatusPending,
    50  	}
    51  	err := s.UpsertTransaction(ctx, transaction, true, false)
    52  	assert.NoError(t, err)
    53  
    54  	// Check we get the exact same transaction back
    55  	transactionRead, err := s.GetTransactionByID(ctx, transactionID)
    56  	// The generated sequence will have been added
    57  	transaction.Sequence = transactionRead.Sequence
    58  	assert.NoError(t, err)
    59  	assert.NotNil(t, transactionRead)
    60  	transactionJson, _ := json.Marshal(&transaction)
    61  	transactionReadJson, _ := json.Marshal(&transactionRead)
    62  	assert.Equal(t, string(transactionJson), string(transactionReadJson))
    63  
    64  	// Update the transaction (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  	transactionUpdated := &fftypes.Transaction{
    67  		ID:   transactionID,
    68  		Hash: fftypes.NewRandB32(),
    69  		Subject: fftypes.TransactionSubject{
    70  			Type:      fftypes.TransactionTypeBatchPin,
    71  			Namespace: "ns1",
    72  			Signer:    "0x12345",
    73  			Reference: fftypes.NewUUID(),
    74  		},
    75  		Created:    fftypes.Now(),
    76  		ProtocolID: "0x33333",
    77  		Status:     fftypes.OpStatusFailed,
    78  		Info: fftypes.JSONObject{
    79  			"some": "data",
    80  		},
    81  	}
    82  
    83  	// Check reject hash update
    84  	err = s.UpsertTransaction(context.Background(), transactionUpdated, true, false)
    85  	assert.Equal(t, database.HashMismatch, err)
    86  
    87  	err = s.UpsertTransaction(context.Background(), transactionUpdated, true, true)
    88  	assert.NoError(t, err)
    89  
    90  	// Check we get the exact same message back - note the removal of one of the transaction elements
    91  	transactionRead, err = s.GetTransactionByID(ctx, transactionID)
    92  	assert.NoError(t, err)
    93  	// The generated sequence will have been added
    94  	transactionUpdated.Sequence = transaction.Sequence
    95  	transactionJson, _ = json.Marshal(&transactionUpdated)
    96  	transactionReadJson, _ = json.Marshal(&transactionRead)
    97  	assert.Equal(t, string(transactionJson), string(transactionReadJson))
    98  
    99  	// Query back the transaction
   100  	fb := database.TransactionQueryFactory.NewFilter(ctx)
   101  	filter := fb.And(
   102  		fb.Eq("id", transactionUpdated.ID.String()),
   103  		fb.Eq("protocolid", transactionUpdated.ProtocolID),
   104  		fb.Eq("signer", transactionUpdated.Subject.Signer),
   105  		fb.Gt("created", "0"),
   106  	)
   107  	transactions, err := s.GetTransactions(ctx, filter)
   108  	assert.NoError(t, err)
   109  	assert.Equal(t, 1, len(transactions))
   110  	transactionReadJson, _ = json.Marshal(transactions[0])
   111  	assert.Equal(t, string(transactionJson), string(transactionReadJson))
   112  
   113  	// Negative test on filter
   114  	filter = fb.And(
   115  		fb.Eq("id", transactionUpdated.ID.String()),
   116  		fb.Eq("created", "0"),
   117  	)
   118  	transactions, err = s.GetTransactions(ctx, filter)
   119  	assert.NoError(t, err)
   120  	assert.Equal(t, 0, len(transactions))
   121  
   122  	// Update
   123  	up := database.TransactionQueryFactory.NewUpdate(ctx).
   124  		Set("status", fftypes.OpStatusSucceeded)
   125  	err = s.UpdateTransaction(ctx, transactionUpdated.ID, up)
   126  	assert.NoError(t, err)
   127  
   128  	// Test find updated value
   129  	filter = fb.And(
   130  		fb.Eq("id", transactionUpdated.ID.String()),
   131  		fb.Eq("status", fftypes.OpStatusSucceeded),
   132  	)
   133  	transactions, err = s.GetTransactions(ctx, filter)
   134  	assert.NoError(t, err)
   135  	assert.Equal(t, 1, len(transactions))
   136  }
   137  
   138  func TestUpsertTransactionFailBegin(t *testing.T) {
   139  	s, mock := newMockProvider().init()
   140  	mock.ExpectBegin().WillReturnError(fmt.Errorf("pop"))
   141  	err := s.UpsertTransaction(context.Background(), &fftypes.Transaction{}, true, true)
   142  	assert.Regexp(t, "FF10114", err)
   143  	assert.NoError(t, mock.ExpectationsWereMet())
   144  }
   145  
   146  func TestUpsertTransactionFailSelect(t *testing.T) {
   147  	s, mock := newMockProvider().init()
   148  	mock.ExpectBegin()
   149  	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
   150  	mock.ExpectRollback()
   151  	transactionID := fftypes.NewUUID()
   152  	err := s.UpsertTransaction(context.Background(), &fftypes.Transaction{ID: transactionID}, true, true)
   153  	assert.Regexp(t, "FF10115", err)
   154  	assert.NoError(t, mock.ExpectationsWereMet())
   155  }
   156  
   157  func TestUpsertTransactionFailInsert(t *testing.T) {
   158  	s, mock := newMockProvider().init()
   159  	mock.ExpectBegin()
   160  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{}))
   161  	mock.ExpectExec("INSERT .*").WillReturnError(fmt.Errorf("pop"))
   162  	mock.ExpectRollback()
   163  	transactionID := fftypes.NewUUID()
   164  	err := s.UpsertTransaction(context.Background(), &fftypes.Transaction{ID: transactionID}, true, true)
   165  	assert.Regexp(t, "FF10116", err)
   166  	assert.NoError(t, mock.ExpectationsWereMet())
   167  }
   168  
   169  func TestUpsertTransactionFailUpdate(t *testing.T) {
   170  	s, mock := newMockProvider().init()
   171  	transactionID := fftypes.NewUUID()
   172  	mock.ExpectBegin()
   173  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(transactionID.String()))
   174  	mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop"))
   175  	mock.ExpectRollback()
   176  	err := s.UpsertTransaction(context.Background(), &fftypes.Transaction{ID: transactionID}, true, true)
   177  	assert.Regexp(t, "FF10117", err)
   178  	assert.NoError(t, mock.ExpectationsWereMet())
   179  }
   180  
   181  func TestUpsertTransactionFailCommit(t *testing.T) {
   182  	s, mock := newMockProvider().init()
   183  	transactionID := fftypes.NewUUID()
   184  	mock.ExpectBegin()
   185  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}))
   186  	mock.ExpectExec("INSERT .*").WillReturnResult(sqlmock.NewResult(1, 1))
   187  	mock.ExpectCommit().WillReturnError(fmt.Errorf("pop"))
   188  	err := s.UpsertTransaction(context.Background(), &fftypes.Transaction{ID: transactionID}, true, true)
   189  	assert.Regexp(t, "FF10119", err)
   190  	assert.NoError(t, mock.ExpectationsWereMet())
   191  }
   192  
   193  func TestGetTransactionByIDSelectFail(t *testing.T) {
   194  	s, mock := newMockProvider().init()
   195  	transactionID := fftypes.NewUUID()
   196  	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
   197  	_, err := s.GetTransactionByID(context.Background(), transactionID)
   198  	assert.Regexp(t, "FF10115", err)
   199  	assert.NoError(t, mock.ExpectationsWereMet())
   200  }
   201  
   202  func TestGetTransactionByIDNotFound(t *testing.T) {
   203  	s, mock := newMockProvider().init()
   204  	transactionID := fftypes.NewUUID()
   205  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}))
   206  	msg, err := s.GetTransactionByID(context.Background(), transactionID)
   207  	assert.NoError(t, err)
   208  	assert.Nil(t, msg)
   209  	assert.NoError(t, mock.ExpectationsWereMet())
   210  }
   211  
   212  func TestGetTransactionByIDScanFail(t *testing.T) {
   213  	s, mock := newMockProvider().init()
   214  	transactionID := fftypes.NewUUID()
   215  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("only one"))
   216  	_, err := s.GetTransactionByID(context.Background(), transactionID)
   217  	assert.Regexp(t, "FF10121", err)
   218  	assert.NoError(t, mock.ExpectationsWereMet())
   219  }
   220  
   221  func TestGetTransactionsQueryFail(t *testing.T) {
   222  	s, mock := newMockProvider().init()
   223  	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
   224  	f := database.TransactionQueryFactory.NewFilter(context.Background()).Eq("id", "")
   225  	_, err := s.GetTransactions(context.Background(), f)
   226  	assert.Regexp(t, "FF10115", err)
   227  	assert.NoError(t, mock.ExpectationsWereMet())
   228  }
   229  
   230  func TestGetTransactionsBuildQueryFail(t *testing.T) {
   231  	s, _ := newMockProvider().init()
   232  	f := database.TransactionQueryFactory.NewFilter(context.Background()).Eq("id", map[bool]bool{true: false})
   233  	_, err := s.GetTransactions(context.Background(), f)
   234  	assert.Regexp(t, "FF10149.*id", err)
   235  }
   236  
   237  func TestGettTransactionsReadMessageFail(t *testing.T) {
   238  	s, mock := newMockProvider().init()
   239  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("only one"))
   240  	f := database.TransactionQueryFactory.NewFilter(context.Background()).Eq("id", "")
   241  	_, err := s.GetTransactions(context.Background(), f)
   242  	assert.Regexp(t, "FF10121", err)
   243  	assert.NoError(t, mock.ExpectationsWereMet())
   244  }
   245  
   246  func TestTxSubmissionUpdateBeginFail(t *testing.T) {
   247  	s, mock := newMockProvider().init()
   248  	mock.ExpectBegin().WillReturnError(fmt.Errorf("pop"))
   249  	u := database.TransactionQueryFactory.NewUpdate(context.Background()).Set("id", "anything")
   250  	err := s.UpdateTransaction(context.Background(), fftypes.NewUUID(), u)
   251  	assert.Regexp(t, "FF10114", err)
   252  }
   253  
   254  func TestTxSubmissionUpdateBuildQueryFail(t *testing.T) {
   255  	s, mock := newMockProvider().init()
   256  	mock.ExpectBegin()
   257  	u := database.TransactionQueryFactory.NewUpdate(context.Background()).Set("id", map[bool]bool{true: false})
   258  	err := s.UpdateTransaction(context.Background(), fftypes.NewUUID(), u)
   259  	assert.Regexp(t, "FF10149.*id", err)
   260  }
   261  
   262  func TestTxSubmissionUpdateFail(t *testing.T) {
   263  	s, mock := newMockProvider().init()
   264  	mock.ExpectBegin()
   265  	mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop"))
   266  	mock.ExpectRollback()
   267  	u := database.TransactionQueryFactory.NewUpdate(context.Background()).Set("id", fftypes.NewUUID())
   268  	err := s.UpdateTransaction(context.Background(), fftypes.NewUUID(), u)
   269  	assert.Regexp(t, "FF10117", err)
   270  }