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 }