github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/database/sqlcommon/batch_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 TestBatch2EWithDB(t *testing.T) {
    32  
    33  	s := newQLTestProvider(t)
    34  	defer s.Close()
    35  	ctx := context.Background()
    36  
    37  	// Create a new batch entry
    38  	batchID := fftypes.NewUUID()
    39  	msgID1 := fftypes.NewUUID()
    40  	batch := &fftypes.Batch{
    41  		ID:      batchID,
    42  		Type:    fftypes.MessageTypeBroadcast,
    43  		Author:  "0x12345",
    44  		Hash:    fftypes.NewRandB32(),
    45  		Created: fftypes.Now(),
    46  		Payload: fftypes.BatchPayload{
    47  			Messages: []*fftypes.Message{
    48  				{Header: fftypes.MessageHeader{ID: msgID1}},
    49  			},
    50  			TX: fftypes.TransactionRef{
    51  				Type: fftypes.TransactionTypeNone,
    52  			},
    53  		},
    54  	}
    55  	err := s.UpsertBatch(ctx, batch, true, true)
    56  	assert.NoError(t, err)
    57  
    58  	// Check we get the exact same batch back
    59  	batchRead, err := s.GetBatchByID(ctx, batchID)
    60  	assert.NoError(t, err)
    61  	assert.NotNil(t, batchRead)
    62  	batchJson, _ := json.Marshal(&batch)
    63  	batchReadJson, _ := json.Marshal(&batchRead)
    64  	assert.Equal(t, string(batchJson), string(batchReadJson))
    65  
    66  	// Update the batch (this is testing what's possible at the database layer,
    67  	// and does not account for the verification that happens at the higher level)
    68  	txid := fftypes.NewUUID()
    69  	msgID2 := fftypes.NewUUID()
    70  	payloadRef := fftypes.NewRandB32()
    71  	batchUpdated := &fftypes.Batch{
    72  		ID:        batchID,
    73  		Type:      fftypes.MessageTypeBroadcast,
    74  		Author:    "0x12345",
    75  		Namespace: "ns1",
    76  		Hash:      fftypes.NewRandB32(),
    77  		Created:   fftypes.Now(),
    78  		Payload: fftypes.BatchPayload{
    79  			TX: fftypes.TransactionRef{
    80  				ID:   txid,
    81  				Type: fftypes.TransactionTypeBatchPin,
    82  			},
    83  			Messages: []*fftypes.Message{
    84  				{Header: fftypes.MessageHeader{ID: msgID1}},
    85  				{Header: fftypes.MessageHeader{ID: msgID2}},
    86  			},
    87  		},
    88  		PayloadRef: payloadRef,
    89  		Confirmed:  fftypes.Now(),
    90  	}
    91  
    92  	// Rejects hash change
    93  	err = s.UpsertBatch(context.Background(), batchUpdated, true, false)
    94  	assert.Equal(t, database.HashMismatch, err)
    95  
    96  	err = s.UpsertBatch(context.Background(), batchUpdated, true, true)
    97  	assert.NoError(t, err)
    98  
    99  	// Check we get the exact same message back - note the removal of one of the batch elements
   100  	batchRead, err = s.GetBatchByID(ctx, batchID)
   101  	assert.NoError(t, err)
   102  	batchJson, _ = json.Marshal(&batchUpdated)
   103  	batchReadJson, _ = json.Marshal(&batchRead)
   104  	assert.Equal(t, string(batchJson), string(batchReadJson))
   105  
   106  	// Query back the batch
   107  	fb := database.BatchQueryFactory.NewFilter(ctx)
   108  	filter := fb.And(
   109  		fb.Eq("id", batchUpdated.ID.String()),
   110  		fb.Eq("namespace", batchUpdated.Namespace),
   111  		fb.Eq("author", batchUpdated.Author),
   112  		fb.Gt("created", "0"),
   113  		fb.Gt("confirmed", "0"),
   114  	)
   115  	batches, err := s.GetBatches(ctx, filter)
   116  	assert.NoError(t, err)
   117  	assert.Equal(t, 1, len(batches))
   118  	batchReadJson, _ = json.Marshal(batches[0])
   119  	assert.Equal(t, string(batchJson), string(batchReadJson))
   120  
   121  	// Negative test on filter
   122  	filter = fb.And(
   123  		fb.Eq("id", batchUpdated.ID.String()),
   124  		fb.Eq("created", "0"),
   125  	)
   126  	batches, err = s.GetBatches(ctx, filter)
   127  	assert.NoError(t, err)
   128  	assert.Equal(t, 0, len(batches))
   129  
   130  	// Update
   131  	author2 := "0x222222"
   132  	up := database.BatchQueryFactory.NewUpdate(ctx).Set("author", author2)
   133  	err = s.UpdateBatch(ctx, batchID, up)
   134  	assert.NoError(t, err)
   135  
   136  	// Test find updated value
   137  	filter = fb.And(
   138  		fb.Eq("id", batchUpdated.ID.String()),
   139  		fb.Eq("author", author2),
   140  	)
   141  	batches, err = s.GetBatches(ctx, filter)
   142  	assert.NoError(t, err)
   143  	assert.Equal(t, 1, len(batches))
   144  }
   145  
   146  func TestUpsertBatchFailBegin(t *testing.T) {
   147  	s, mock := newMockProvider().init()
   148  	mock.ExpectBegin().WillReturnError(fmt.Errorf("pop"))
   149  	err := s.UpsertBatch(context.Background(), &fftypes.Batch{}, true, true)
   150  	assert.Regexp(t, "FF10114", err)
   151  	assert.NoError(t, mock.ExpectationsWereMet())
   152  }
   153  
   154  func TestUpsertBatchFailSelect(t *testing.T) {
   155  	s, mock := newMockProvider().init()
   156  	mock.ExpectBegin()
   157  	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
   158  	mock.ExpectRollback()
   159  	batchID := fftypes.NewUUID()
   160  	err := s.UpsertBatch(context.Background(), &fftypes.Batch{ID: batchID}, true, true)
   161  	assert.Regexp(t, "FF10115", err)
   162  	assert.NoError(t, mock.ExpectationsWereMet())
   163  }
   164  
   165  func TestUpsertBatchFailInsert(t *testing.T) {
   166  	s, mock := newMockProvider().init()
   167  	mock.ExpectBegin()
   168  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{}))
   169  	mock.ExpectExec("INSERT .*").WillReturnError(fmt.Errorf("pop"))
   170  	mock.ExpectRollback()
   171  	batchID := fftypes.NewUUID()
   172  	err := s.UpsertBatch(context.Background(), &fftypes.Batch{ID: batchID}, true, true)
   173  	assert.Regexp(t, "FF10116", err)
   174  	assert.NoError(t, mock.ExpectationsWereMet())
   175  }
   176  
   177  func TestUpsertBatchFailUpdate(t *testing.T) {
   178  	s, mock := newMockProvider().init()
   179  	batchID := fftypes.NewUUID()
   180  	mock.ExpectBegin()
   181  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(batchID.String()))
   182  	mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop"))
   183  	mock.ExpectRollback()
   184  	err := s.UpsertBatch(context.Background(), &fftypes.Batch{ID: batchID}, true, true)
   185  	assert.Regexp(t, "FF10117", err)
   186  	assert.NoError(t, mock.ExpectationsWereMet())
   187  }
   188  
   189  func TestUpsertBatchFailCommit(t *testing.T) {
   190  	s, mock := newMockProvider().init()
   191  	batchID := fftypes.NewUUID()
   192  	mock.ExpectBegin()
   193  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}))
   194  	mock.ExpectExec("INSERT .*").WillReturnResult(sqlmock.NewResult(1, 1))
   195  	mock.ExpectCommit().WillReturnError(fmt.Errorf("pop"))
   196  	err := s.UpsertBatch(context.Background(), &fftypes.Batch{ID: batchID}, true, true)
   197  	assert.Regexp(t, "FF10119", err)
   198  	assert.NoError(t, mock.ExpectationsWereMet())
   199  }
   200  
   201  func TestGetBatchByIDSelectFail(t *testing.T) {
   202  	s, mock := newMockProvider().init()
   203  	batchID := fftypes.NewUUID()
   204  	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
   205  	_, err := s.GetBatchByID(context.Background(), batchID)
   206  	assert.Regexp(t, "FF10115", err)
   207  	assert.NoError(t, mock.ExpectationsWereMet())
   208  }
   209  
   210  func TestGetBatchByIDNotFound(t *testing.T) {
   211  	s, mock := newMockProvider().init()
   212  	batchID := fftypes.NewUUID()
   213  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}))
   214  	msg, err := s.GetBatchByID(context.Background(), batchID)
   215  	assert.NoError(t, err)
   216  	assert.Nil(t, msg)
   217  	assert.NoError(t, mock.ExpectationsWereMet())
   218  }
   219  
   220  func TestGetBatchByIDScanFail(t *testing.T) {
   221  	s, mock := newMockProvider().init()
   222  	batchID := fftypes.NewUUID()
   223  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("only one"))
   224  	_, err := s.GetBatchByID(context.Background(), batchID)
   225  	assert.Regexp(t, "FF10121", err)
   226  	assert.NoError(t, mock.ExpectationsWereMet())
   227  }
   228  
   229  func TestGetBatchesQueryFail(t *testing.T) {
   230  	s, mock := newMockProvider().init()
   231  	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
   232  	f := database.BatchQueryFactory.NewFilter(context.Background()).Eq("id", "")
   233  	_, err := s.GetBatches(context.Background(), f)
   234  	assert.Regexp(t, "FF10115", err)
   235  	assert.NoError(t, mock.ExpectationsWereMet())
   236  }
   237  
   238  func TestGetBatchesBuildQueryFail(t *testing.T) {
   239  	s, _ := newMockProvider().init()
   240  	f := database.BatchQueryFactory.NewFilter(context.Background()).Eq("id", map[bool]bool{true: false})
   241  	_, err := s.GetBatches(context.Background(), f)
   242  	assert.Regexp(t, "FF10149.*id", err)
   243  }
   244  
   245  func TestGetBatchesReadMessageFail(t *testing.T) {
   246  	s, mock := newMockProvider().init()
   247  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("only one"))
   248  	f := database.BatchQueryFactory.NewFilter(context.Background()).Eq("id", "")
   249  	_, err := s.GetBatches(context.Background(), f)
   250  	assert.Regexp(t, "FF10121", err)
   251  	assert.NoError(t, mock.ExpectationsWereMet())
   252  }
   253  
   254  func TestBatchUpdateBeginFail(t *testing.T) {
   255  	s, mock := newMockProvider().init()
   256  	mock.ExpectBegin().WillReturnError(fmt.Errorf("pop"))
   257  	u := database.BatchQueryFactory.NewUpdate(context.Background()).Set("id", "anything")
   258  	err := s.UpdateBatch(context.Background(), fftypes.NewUUID(), u)
   259  	assert.Regexp(t, "FF10114", err)
   260  }
   261  
   262  func TestBatchUpdateBuildQueryFail(t *testing.T) {
   263  	s, mock := newMockProvider().init()
   264  	mock.ExpectBegin()
   265  	u := database.BatchQueryFactory.NewUpdate(context.Background()).Set("id", map[bool]bool{true: false})
   266  	err := s.UpdateBatch(context.Background(), fftypes.NewUUID(), u)
   267  	assert.Regexp(t, "FF10149.*id", err)
   268  }
   269  
   270  func TestBatchUpdateFail(t *testing.T) {
   271  	s, mock := newMockProvider().init()
   272  	mock.ExpectBegin()
   273  	mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop"))
   274  	mock.ExpectRollback()
   275  	u := database.BatchQueryFactory.NewUpdate(context.Background()).Set("id", fftypes.NewUUID())
   276  	err := s.UpdateBatch(context.Background(), fftypes.NewUUID(), u)
   277  	assert.Regexp(t, "FF10117", err)
   278  }