github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/database/sqlcommon/data_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/internal/log" 27 "github.com/kaleido-io/firefly/pkg/database" 28 "github.com/kaleido-io/firefly/pkg/fftypes" 29 "github.com/stretchr/testify/assert" 30 ) 31 32 func TestDataE2EWithDB(t *testing.T) { 33 log.SetLevel("debug") 34 35 s := newQLTestProvider(t) 36 defer s.Close() 37 ctx := context.Background() 38 39 // Create a new data entry 40 dataID := fftypes.NewUUID() 41 val := fftypes.JSONObject{ 42 "some": "data", 43 "with": map[string]interface{}{ 44 "nesting": 12345, 45 }, 46 } 47 data := &fftypes.Data{ 48 ID: dataID, 49 Validator: fftypes.ValidatorTypeSystemDefinition, 50 Namespace: "ns1", 51 Hash: fftypes.NewRandB32(), 52 Created: fftypes.Now(), 53 Value: []byte(val.String()), 54 } 55 err := s.UpsertData(ctx, data, true, false) 56 assert.NoError(t, err) 57 58 // Check we get the exact same data back - we should not to return the value first 59 dataRead, err := s.GetDataByID(ctx, dataID, false) 60 assert.NoError(t, err) 61 assert.Equal(t, *dataID, *dataRead.ID) 62 assert.Nil(t, dataRead.Value) 63 64 // Now with value 65 dataRead, err = s.GetDataByID(ctx, dataID, true) 66 assert.NotNil(t, dataRead) 67 dataJson, _ := json.Marshal(&data) 68 dataReadJson, _ := json.Marshal(&dataRead) 69 assert.Equal(t, string(dataJson), string(dataReadJson)) 70 71 // Update the data (this is testing what's possible at the database layer, 72 // and does not account for the verification that happens at the higher level) 73 val2 := fftypes.JSONObject{ 74 "another": "set", 75 "of": map[string]interface{}{ 76 "data": 12345, 77 "and": "stuff", 78 }, 79 } 80 dataUpdated := &fftypes.Data{ 81 ID: dataID, 82 Validator: fftypes.ValidatorTypeJSON, 83 Namespace: "ns2", 84 Datatype: &fftypes.DatatypeRef{ 85 Name: "customer", 86 Version: "0.0.1", 87 }, 88 Hash: fftypes.NewRandB32(), 89 Created: fftypes.Now(), 90 Value: []byte(val2.String()), 91 } 92 93 // Check disallows hash update 94 err = s.UpsertData(context.Background(), dataUpdated, true, false) 95 assert.Equal(t, database.HashMismatch, err) 96 97 err = s.UpsertData(context.Background(), dataUpdated, true, true) 98 assert.NoError(t, err) 99 100 // Check we get the exact same message back - note the removal of one of the data elements 101 dataRead, err = s.GetDataByID(ctx, dataID, true) 102 assert.NoError(t, err) 103 dataJson, _ = json.Marshal(&dataUpdated) 104 dataReadJson, _ = json.Marshal(&dataRead) 105 assert.Equal(t, string(dataJson), string(dataReadJson)) 106 107 valRestored, ok := dataRead.Value.JSONObjectOk() 108 assert.True(t, ok) 109 assert.Equal(t, "stuff", valRestored.GetObject("of").GetString("and")) 110 111 // Query back the data 112 fb := database.DataQueryFactory.NewFilter(ctx) 113 filter := fb.And( 114 fb.Eq("id", dataUpdated.ID.String()), 115 fb.Eq("namespace", dataUpdated.Namespace), 116 fb.Eq("validator", string(dataUpdated.Validator)), 117 fb.Eq("datatype.name", dataUpdated.Datatype.Name), 118 fb.Eq("datatype.version", dataUpdated.Datatype.Version), 119 fb.Eq("hash", dataUpdated.Hash), 120 fb.Gt("created", 0), 121 ) 122 dataRes, err := s.GetData(ctx, filter) 123 assert.NoError(t, err) 124 assert.Equal(t, 1, len(dataRes)) 125 dataReadJson, _ = json.Marshal(dataRes[0]) 126 assert.Equal(t, string(dataJson), string(dataReadJson)) 127 128 dataRefRes, err := s.GetDataRefs(ctx, filter) 129 assert.NoError(t, err) 130 assert.Equal(t, 1, len(dataRefRes)) 131 assert.Equal(t, *dataUpdated.ID, *dataRefRes[0].ID) 132 assert.Equal(t, dataUpdated.Hash, dataRefRes[0].Hash) 133 134 // Update 135 v2 := "2.0.0" 136 up := database.DataQueryFactory.NewUpdate(ctx).Set("datatype.version", v2) 137 err = s.UpdateData(ctx, dataID, up) 138 assert.NoError(t, err) 139 140 // Test find updated value 141 filter = fb.And( 142 fb.Eq("id", dataUpdated.ID.String()), 143 fb.Eq("datatype.version", v2), 144 ) 145 dataRes, err = s.GetData(ctx, filter) 146 assert.NoError(t, err) 147 assert.Equal(t, 1, len(dataRes)) 148 149 } 150 151 func TestUpsertDataFailBegin(t *testing.T) { 152 s, mock := newMockProvider().init() 153 mock.ExpectBegin().WillReturnError(fmt.Errorf("pop")) 154 err := s.UpsertData(context.Background(), &fftypes.Data{}, true, true) 155 assert.Regexp(t, "FF10114", err) 156 assert.NoError(t, mock.ExpectationsWereMet()) 157 } 158 159 func TestUpsertDataFailSelect(t *testing.T) { 160 s, mock := newMockProvider().init() 161 mock.ExpectBegin() 162 mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) 163 mock.ExpectRollback() 164 dataID := fftypes.NewUUID() 165 err := s.UpsertData(context.Background(), &fftypes.Data{ID: dataID}, true, true) 166 assert.Regexp(t, "FF10115", err) 167 assert.NoError(t, mock.ExpectationsWereMet()) 168 } 169 170 func TestUpsertDataFailInsert(t *testing.T) { 171 s, mock := newMockProvider().init() 172 mock.ExpectBegin() 173 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{})) 174 mock.ExpectExec("INSERT .*").WillReturnError(fmt.Errorf("pop")) 175 mock.ExpectRollback() 176 dataID := fftypes.NewUUID() 177 err := s.UpsertData(context.Background(), &fftypes.Data{ID: dataID}, true, true) 178 assert.Regexp(t, "FF10116", err) 179 assert.NoError(t, mock.ExpectationsWereMet()) 180 } 181 182 func TestUpsertDataFailUpdate(t *testing.T) { 183 s, mock := newMockProvider().init() 184 dataID := fftypes.NewUUID() 185 mock.ExpectBegin() 186 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(dataID.String())) 187 mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop")) 188 mock.ExpectRollback() 189 err := s.UpsertData(context.Background(), &fftypes.Data{ID: dataID}, true, true) 190 assert.Regexp(t, "FF10117", err) 191 assert.NoError(t, mock.ExpectationsWereMet()) 192 } 193 194 func TestUpsertDataFailCommit(t *testing.T) { 195 s, mock := newMockProvider().init() 196 dataID := fftypes.NewUUID() 197 mock.ExpectBegin() 198 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"})) 199 mock.ExpectExec("INSERT .*").WillReturnResult(sqlmock.NewResult(1, 1)) 200 mock.ExpectCommit().WillReturnError(fmt.Errorf("pop")) 201 err := s.UpsertData(context.Background(), &fftypes.Data{ID: dataID}, true, true) 202 assert.Regexp(t, "FF10119", err) 203 assert.NoError(t, mock.ExpectationsWereMet()) 204 } 205 206 func TestGetDataByIDSelectFail(t *testing.T) { 207 s, mock := newMockProvider().init() 208 dataID := fftypes.NewUUID() 209 mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) 210 _, err := s.GetDataByID(context.Background(), dataID, false) 211 assert.Regexp(t, "FF10115", err) 212 assert.NoError(t, mock.ExpectationsWereMet()) 213 } 214 215 func TestGetDataByIDNotFound(t *testing.T) { 216 s, mock := newMockProvider().init() 217 dataID := fftypes.NewUUID() 218 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"})) 219 msg, err := s.GetDataByID(context.Background(), dataID, true) 220 assert.NoError(t, err) 221 assert.Nil(t, msg) 222 assert.NoError(t, mock.ExpectationsWereMet()) 223 } 224 225 func TestGetDataByIDScanFail(t *testing.T) { 226 s, mock := newMockProvider().init() 227 dataID := fftypes.NewUUID() 228 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("only one")) 229 _, err := s.GetDataByID(context.Background(), dataID, true) 230 assert.Regexp(t, "FF10121", err) 231 assert.NoError(t, mock.ExpectationsWereMet()) 232 } 233 234 func TestGetDataQueryFail(t *testing.T) { 235 s, mock := newMockProvider().init() 236 mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) 237 f := database.DataQueryFactory.NewFilter(context.Background()).Eq("id", "") 238 _, err := s.GetData(context.Background(), f) 239 assert.Regexp(t, "FF10115", err) 240 assert.NoError(t, mock.ExpectationsWereMet()) 241 } 242 243 func TestGetDataBuildQueryFail(t *testing.T) { 244 s, _ := newMockProvider().init() 245 f := database.DataQueryFactory.NewFilter(context.Background()).Eq("id", map[bool]bool{true: false}) 246 _, err := s.GetData(context.Background(), f) 247 assert.Regexp(t, "FF10149.*id", err) 248 } 249 250 func TestGetDataReadMessageFail(t *testing.T) { 251 s, mock := newMockProvider().init() 252 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("only one")) 253 f := database.DataQueryFactory.NewFilter(context.Background()).Eq("id", "") 254 _, err := s.GetData(context.Background(), f) 255 assert.Regexp(t, "FF10121", err) 256 assert.NoError(t, mock.ExpectationsWereMet()) 257 } 258 259 func TestGetDataRefsQueryFail(t *testing.T) { 260 s, mock := newMockProvider().init() 261 mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop")) 262 f := database.DataQueryFactory.NewFilter(context.Background()).Eq("id", "") 263 _, err := s.GetDataRefs(context.Background(), f) 264 assert.Regexp(t, "FF10115", err) 265 assert.NoError(t, mock.ExpectationsWereMet()) 266 } 267 268 func TestGetDataRefsBuildQueryFail(t *testing.T) { 269 s, _ := newMockProvider().init() 270 f := database.DataQueryFactory.NewFilter(context.Background()).Eq("id", map[bool]bool{true: false}) 271 _, err := s.GetDataRefs(context.Background(), f) 272 assert.Regexp(t, "FF10149.*id", err) 273 } 274 275 func TestGetDataRefsReadMessageFail(t *testing.T) { 276 s, mock := newMockProvider().init() 277 mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("only one")) 278 f := database.DataQueryFactory.NewFilter(context.Background()).Eq("id", "") 279 _, err := s.GetDataRefs(context.Background(), f) 280 assert.Regexp(t, "FF10121", err) 281 assert.NoError(t, mock.ExpectationsWereMet()) 282 } 283 284 func TestDataUpdateBeginFail(t *testing.T) { 285 s, mock := newMockProvider().init() 286 mock.ExpectBegin().WillReturnError(fmt.Errorf("pop")) 287 u := database.DataQueryFactory.NewUpdate(context.Background()).Set("id", "anything") 288 err := s.UpdateData(context.Background(), fftypes.NewUUID(), u) 289 assert.Regexp(t, "FF10114", err) 290 } 291 292 func TestDataUpdateBuildQueryFail(t *testing.T) { 293 s, mock := newMockProvider().init() 294 mock.ExpectBegin() 295 u := database.DataQueryFactory.NewUpdate(context.Background()).Set("id", map[bool]bool{true: false}) 296 err := s.UpdateData(context.Background(), fftypes.NewUUID(), u) 297 assert.Regexp(t, "FF10149.*id", err) 298 } 299 300 func TestDataUpdateFail(t *testing.T) { 301 s, mock := newMockProvider().init() 302 mock.ExpectBegin() 303 mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop")) 304 mock.ExpectRollback() 305 u := database.DataQueryFactory.NewUpdate(context.Background()).Set("id", fftypes.NewUUID()) 306 err := s.UpdateData(context.Background(), fftypes.NewUUID(), u) 307 assert.Regexp(t, "FF10117", err) 308 }