github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/database/sqlcommon/offset_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  	"crypto/rand"
    22  	"encoding/json"
    23  	"fmt"
    24  	"math/big"
    25  	"testing"
    26  
    27  	"github.com/DATA-DOG/go-sqlmock"
    28  	"github.com/kaleido-io/firefly/internal/log"
    29  	"github.com/kaleido-io/firefly/pkg/database"
    30  	"github.com/kaleido-io/firefly/pkg/fftypes"
    31  	"github.com/stretchr/testify/assert"
    32  )
    33  
    34  func TestOffsetsE2EWithDB(t *testing.T) {
    35  	log.SetLevel("debug")
    36  
    37  	s := newQLTestProvider(t)
    38  	defer s.Close()
    39  	ctx := context.Background()
    40  
    41  	// Create a new offset entry
    42  	rand1, _ := rand.Int(rand.Reader, big.NewInt(10000000000000))
    43  	offset := &fftypes.Offset{
    44  		ID:        fftypes.NewUUID(),
    45  		Type:      fftypes.OffsetTypeBatch,
    46  		Namespace: "ns1",
    47  		Name:      "offset1",
    48  		Current:   rand1.Int64(),
    49  	}
    50  	err := s.UpsertOffset(ctx, offset, true)
    51  	assert.NoError(t, err)
    52  
    53  	// Check we get the exact same offset back
    54  	offsetRead, err := s.GetOffset(ctx, offset.Type, offset.Namespace, offset.Name)
    55  	assert.NoError(t, err)
    56  	assert.NotNil(t, offsetRead)
    57  	offsetJson, _ := json.Marshal(&offset)
    58  	offsetReadJson, _ := json.Marshal(&offsetRead)
    59  	assert.Equal(t, string(offsetJson), string(offsetReadJson))
    60  
    61  	// Update the offset (this is testing what's possible at the database layer,
    62  	// and does not account for the verification that happens at the higher level)
    63  	rand2, _ := rand.Int(rand.Reader, big.NewInt(10000000000000))
    64  	offsetUpdated := &fftypes.Offset{
    65  		ID:        fftypes.NewUUID(),
    66  		Type:      fftypes.OffsetTypeBatch,
    67  		Namespace: "ns1",
    68  		Name:      "offset1",
    69  		Current:   rand2.Int64(),
    70  	}
    71  
    72  	// Attempt with wrong ID
    73  	err = s.UpsertOffset(context.Background(), offsetUpdated, true)
    74  	assert.Equal(t, err, database.IDMismatch)
    75  
    76  	// Remove ID for upsert and retry
    77  	offsetUpdated.ID = nil
    78  	err = s.UpsertOffset(context.Background(), offsetUpdated, true)
    79  	assert.NoError(t, err)
    80  
    81  	// Check we get the exact same data back - note the removal of one of the offset elements
    82  	offsetRead, err = s.GetOffset(ctx, offset.Type, offset.Namespace, offset.Name)
    83  	assert.NoError(t, err)
    84  	offsetJson, _ = json.Marshal(&offsetUpdated)
    85  	offsetReadJson, _ = json.Marshal(&offsetRead)
    86  	assert.Equal(t, string(offsetJson), string(offsetReadJson))
    87  
    88  	// Query back the offset
    89  	fb := database.OffsetQueryFactory.NewFilter(ctx)
    90  	filter := fb.And(
    91  		fb.Eq("type", string(offsetUpdated.Type)),
    92  		fb.Eq("namespace", offsetUpdated.Namespace),
    93  		fb.Eq("name", offsetUpdated.Name),
    94  		fb.Gt("current", 0),
    95  	)
    96  	offsetRes, err := s.GetOffsets(ctx, filter)
    97  	assert.NoError(t, err)
    98  	assert.Equal(t, 1, len(offsetRes))
    99  	offsetReadJson, _ = json.Marshal(offsetRes[0])
   100  	assert.Equal(t, string(offsetJson), string(offsetReadJson))
   101  
   102  	// Update
   103  	rand3, _ := rand.Int(rand.Reader, big.NewInt(10000000000000))
   104  	up := database.OffsetQueryFactory.NewUpdate(ctx).Set("current", rand3.Int64())
   105  	err = s.UpdateOffset(ctx, offsetUpdated.ID, up)
   106  	assert.NoError(t, err)
   107  
   108  	// Test find updated value
   109  	filter = fb.And(
   110  		fb.Eq("name", offsetUpdated.Name),
   111  		fb.Eq("current", rand3.Int64()),
   112  	)
   113  	offsets, err := s.GetOffsets(ctx, filter)
   114  	assert.NoError(t, err)
   115  	assert.Equal(t, 1, len(offsets))
   116  
   117  	// Test delete
   118  	err = s.DeleteOffset(ctx, fftypes.OffsetTypeBatch, offsetUpdated.Namespace, offsetUpdated.Name)
   119  	assert.NoError(t, err)
   120  	offsets, err = s.GetOffsets(ctx, filter)
   121  	assert.NoError(t, err)
   122  	assert.Equal(t, 0, len(offsets))
   123  
   124  }
   125  
   126  func TestUpsertOffsetFailBegin(t *testing.T) {
   127  	s, mock := newMockProvider().init()
   128  	mock.ExpectBegin().WillReturnError(fmt.Errorf("pop"))
   129  	err := s.UpsertOffset(context.Background(), &fftypes.Offset{}, true)
   130  	assert.Regexp(t, "FF10114", err)
   131  	assert.NoError(t, mock.ExpectationsWereMet())
   132  }
   133  
   134  func TestUpsertOffsetFailSelect(t *testing.T) {
   135  	s, mock := newMockProvider().init()
   136  	mock.ExpectBegin()
   137  	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
   138  	mock.ExpectRollback()
   139  	err := s.UpsertOffset(context.Background(), &fftypes.Offset{Name: "name1"}, true)
   140  	assert.Regexp(t, "FF10115", err)
   141  	assert.NoError(t, mock.ExpectationsWereMet())
   142  }
   143  
   144  func TestUpsertOffsetFailInsert(t *testing.T) {
   145  	s, mock := newMockProvider().init()
   146  	mock.ExpectBegin()
   147  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{}))
   148  	mock.ExpectExec("INSERT .*").WillReturnError(fmt.Errorf("pop"))
   149  	mock.ExpectRollback()
   150  	err := s.UpsertOffset(context.Background(), &fftypes.Offset{Name: "name1"}, true)
   151  	assert.Regexp(t, "FF10116", err)
   152  	assert.NoError(t, mock.ExpectationsWereMet())
   153  }
   154  
   155  func TestUpsertOffsetFailUpdate(t *testing.T) {
   156  	s, mock := newMockProvider().init()
   157  	mock.ExpectBegin()
   158  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"otype", "namespace", "name"}).
   159  		AddRow(fftypes.OffsetTypeBatch, "ns1", "name1"))
   160  	mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop"))
   161  	mock.ExpectRollback()
   162  	err := s.UpsertOffset(context.Background(), &fftypes.Offset{Name: "name1"}, true)
   163  	assert.Regexp(t, "FF10117", err)
   164  	assert.NoError(t, mock.ExpectationsWereMet())
   165  }
   166  
   167  func TestUpsertOffsetFailCommit(t *testing.T) {
   168  	s, mock := newMockProvider().init()
   169  	mock.ExpectBegin()
   170  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"otype", "namespace", "name"}))
   171  	mock.ExpectExec("INSERT .*").WillReturnResult(sqlmock.NewResult(1, 1))
   172  	mock.ExpectCommit().WillReturnError(fmt.Errorf("pop"))
   173  	err := s.UpsertOffset(context.Background(), &fftypes.Offset{Name: "name1"}, true)
   174  	assert.Regexp(t, "FF10119", err)
   175  	assert.NoError(t, mock.ExpectationsWereMet())
   176  }
   177  
   178  func TestGetOffsetByIDSelectFail(t *testing.T) {
   179  	s, mock := newMockProvider().init()
   180  	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
   181  	_, err := s.GetOffset(context.Background(), fftypes.OffsetTypeBatch, "ns1", "name1")
   182  	assert.Regexp(t, "FF10115", err)
   183  	assert.NoError(t, mock.ExpectationsWereMet())
   184  }
   185  
   186  func TestGetOffsetByIDNotFound(t *testing.T) {
   187  	s, mock := newMockProvider().init()
   188  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"otype", "namespace", "name"}))
   189  	msg, err := s.GetOffset(context.Background(), fftypes.OffsetTypeBatch, "ns1", "name1")
   190  	assert.NoError(t, err)
   191  	assert.Nil(t, msg)
   192  	assert.NoError(t, mock.ExpectationsWereMet())
   193  }
   194  
   195  func TestGetOffsetByIDScanFail(t *testing.T) {
   196  	s, mock := newMockProvider().init()
   197  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"otype"}).AddRow("only one"))
   198  	_, err := s.GetOffset(context.Background(), fftypes.OffsetTypeBatch, "ns1", "name1")
   199  	assert.Regexp(t, "FF10121", err)
   200  	assert.NoError(t, mock.ExpectationsWereMet())
   201  }
   202  
   203  func TestGetOffsetQueryFail(t *testing.T) {
   204  	s, mock := newMockProvider().init()
   205  	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
   206  	f := database.OffsetQueryFactory.NewFilter(context.Background()).Eq("type", "")
   207  	_, err := s.GetOffsets(context.Background(), f)
   208  	assert.Regexp(t, "FF10115", err)
   209  	assert.NoError(t, mock.ExpectationsWereMet())
   210  }
   211  
   212  func TestGetOffsetBuildQueryFail(t *testing.T) {
   213  	s, _ := newMockProvider().init()
   214  	f := database.OffsetQueryFactory.NewFilter(context.Background()).Eq("type", map[bool]bool{true: false})
   215  	_, err := s.GetOffsets(context.Background(), f)
   216  	assert.Regexp(t, "FF10149.*type", err)
   217  }
   218  
   219  func TestGetOffsetReadMessageFail(t *testing.T) {
   220  	s, mock := newMockProvider().init()
   221  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"otype"}).AddRow("only one"))
   222  	f := database.OffsetQueryFactory.NewFilter(context.Background()).Eq("type", "")
   223  	_, err := s.GetOffsets(context.Background(), f)
   224  	assert.Regexp(t, "FF10121", err)
   225  	assert.NoError(t, mock.ExpectationsWereMet())
   226  }
   227  
   228  func TestOffsetUpdateBeginFail(t *testing.T) {
   229  	s, mock := newMockProvider().init()
   230  	mock.ExpectBegin().WillReturnError(fmt.Errorf("pop"))
   231  	u := database.OffsetQueryFactory.NewUpdate(context.Background()).Set("name", "anything")
   232  	err := s.UpdateOffset(context.Background(), fftypes.NewUUID(), u)
   233  	assert.Regexp(t, "FF10114", err)
   234  }
   235  
   236  func TestOffsetUpdateBuildQueryFail(t *testing.T) {
   237  	s, mock := newMockProvider().init()
   238  	mock.ExpectBegin()
   239  	u := database.OffsetQueryFactory.NewUpdate(context.Background()).Set("name", map[bool]bool{true: false})
   240  	err := s.UpdateOffset(context.Background(), fftypes.NewUUID(), u)
   241  	assert.Regexp(t, "FF10149.*name", err)
   242  }
   243  
   244  func TestOffsetUpdateFail(t *testing.T) {
   245  	s, mock := newMockProvider().init()
   246  	mock.ExpectBegin()
   247  	mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop"))
   248  	mock.ExpectRollback()
   249  	u := database.OffsetQueryFactory.NewUpdate(context.Background()).Set("name", fftypes.NewUUID())
   250  	err := s.UpdateOffset(context.Background(), fftypes.NewUUID(), u)
   251  	assert.Regexp(t, "FF10117", err)
   252  }
   253  
   254  func TestOffsetDeleteBeginFail(t *testing.T) {
   255  	s, mock := newMockProvider().init()
   256  	mock.ExpectBegin().WillReturnError(fmt.Errorf("pop"))
   257  	err := s.DeleteOffset(context.Background(), fftypes.OffsetTypeSubscription, "ns1", "sub1")
   258  	assert.Regexp(t, "FF10114", err)
   259  }
   260  
   261  func TestOffsetDeleteFail(t *testing.T) {
   262  	s, mock := newMockProvider().init()
   263  	mock.ExpectBegin()
   264  	mock.ExpectExec("DELETE .*").WillReturnError(fmt.Errorf("pop"))
   265  	mock.ExpectRollback()
   266  	err := s.DeleteOffset(context.Background(), fftypes.OffsetTypeSubscription, "ns1", "sub1")
   267  	assert.Regexp(t, "FF10118", err)
   268  }