github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/database/sqlcommon/message_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  	"database/sql/driver"
    22  	"encoding/json"
    23  	"fmt"
    24  	"testing"
    25  
    26  	"github.com/DATA-DOG/go-sqlmock"
    27  	"github.com/kaleido-io/firefly/internal/log"
    28  	"github.com/kaleido-io/firefly/pkg/database"
    29  	"github.com/kaleido-io/firefly/pkg/fftypes"
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/mock"
    32  )
    33  
    34  func TestUpsertE2EWithDB(t *testing.T) {
    35  	log.SetLevel("trace")
    36  
    37  	s := newQLTestProvider(t)
    38  	defer s.Close()
    39  	ctx := context.Background()
    40  
    41  	s.callbacks.On("MessageCreated", mock.Anything).Return()
    42  
    43  	// Create a new message
    44  	msgID := fftypes.NewUUID()
    45  	dataID1 := fftypes.NewUUID()
    46  	dataID2 := fftypes.NewUUID()
    47  	rand1 := fftypes.NewRandB32()
    48  	rand2 := fftypes.NewRandB32()
    49  	msg := &fftypes.Message{
    50  		Header: fftypes.MessageHeader{
    51  			ID:        msgID,
    52  			CID:       nil,
    53  			Type:      fftypes.MessageTypeBroadcast,
    54  			Author:    "0x12345",
    55  			Created:   fftypes.Now(),
    56  			Namespace: "ns12345",
    57  			Topics:    []string{"test1"},
    58  			Group:     nil,
    59  			DataHash:  fftypes.NewRandB32(),
    60  			TxType:    fftypes.TransactionTypeNone,
    61  		},
    62  		Hash:      fftypes.NewRandB32(),
    63  		Confirmed: nil,
    64  		Data: []*fftypes.DataRef{
    65  			{ID: dataID1, Hash: rand1},
    66  			{ID: dataID2, Hash: rand2},
    67  		},
    68  	}
    69  	err := s.InsertMessageLocal(ctx, msg)
    70  	assert.NoError(t, err)
    71  
    72  	// Check we get the exact same message back
    73  	msgRead, err := s.GetMessageByID(ctx, msgID)
    74  	assert.NoError(t, err)
    75  	assert.True(t, msgRead.Local)
    76  	// The generated sequence will have been added
    77  	msg.Sequence = msgRead.Sequence
    78  	assert.NoError(t, err)
    79  	msgJson, _ := json.Marshal(&msg)
    80  	msgReadJson, _ := json.Marshal(&msgRead)
    81  	assert.Equal(t, string(msgJson), string(msgReadJson))
    82  
    83  	// Update the message (this is testing what's possible at the database layer,
    84  	// and does not account for the verification that happens at the higher level)
    85  	dataID3 := fftypes.NewUUID()
    86  	rand3 := fftypes.NewRandB32()
    87  	cid := fftypes.NewUUID()
    88  	gid := fftypes.NewRandB32()
    89  	bid := fftypes.NewUUID()
    90  	msgUpdated := &fftypes.Message{
    91  		Header: fftypes.MessageHeader{
    92  			ID:        msgID,
    93  			CID:       cid,
    94  			Type:      fftypes.MessageTypeBroadcast,
    95  			Author:    "0x12345",
    96  			Created:   fftypes.Now(),
    97  			Namespace: "ns12345",
    98  			Topics:    []string{"topic1", "topic2"},
    99  			Tag:       "tag1",
   100  			Group:     gid,
   101  			DataHash:  fftypes.NewRandB32(),
   102  			TxType:    fftypes.TransactionTypeBatchPin,
   103  		},
   104  		Hash:      fftypes.NewRandB32(),
   105  		Pins:      []string{fftypes.NewRandB32().String(), fftypes.NewRandB32().String()},
   106  		Confirmed: fftypes.Now(),
   107  		BatchID:   bid,
   108  		Data: []*fftypes.DataRef{
   109  			{ID: dataID2, Hash: rand2},
   110  			{ID: dataID3, Hash: rand3},
   111  		},
   112  		Local: false, // must be ignored
   113  	}
   114  
   115  	// Ensure hash change rejected
   116  	err = s.UpsertMessage(context.Background(), msgUpdated, true, false)
   117  	assert.Equal(t, database.HashMismatch, err)
   118  
   119  	err = s.UpsertMessage(context.Background(), msgUpdated, true, true)
   120  	assert.NoError(t, err)
   121  
   122  	// Check we get the exact same message back - note the removal of one of the data elements
   123  	msgRead, err = s.GetMessageByID(ctx, msgID)
   124  	assert.True(t, msgRead.Local) // Must not have been overridden with the update
   125  	// The generated sequence will have been added
   126  	msgUpdated.Sequence = msgRead.Sequence
   127  	msgUpdated.Local = true // retained
   128  	assert.NoError(t, err)
   129  	msgJson, _ = json.Marshal(&msgUpdated)
   130  	msgReadJson, _ = json.Marshal(&msgRead)
   131  	assert.Equal(t, string(msgJson), string(msgReadJson))
   132  
   133  	// Query back the message
   134  	fb := database.MessageQueryFactory.NewFilter(ctx)
   135  	filter := fb.And(
   136  		fb.Eq("id", msgUpdated.Header.ID.String()),
   137  		fb.Eq("namespace", msgUpdated.Header.Namespace),
   138  		fb.Eq("type", string(msgUpdated.Header.Type)),
   139  		fb.Eq("author", msgUpdated.Header.Author),
   140  		fb.Eq("topics", msgUpdated.Header.Topics),
   141  		fb.Eq("group", msgUpdated.Header.Group),
   142  		fb.Eq("cid", msgUpdated.Header.CID),
   143  		fb.Eq("local", true),
   144  		fb.Gt("created", "0"),
   145  		fb.Gt("confirmed", "0"),
   146  	)
   147  	msgs, err := s.GetMessages(ctx, filter)
   148  	assert.NoError(t, err)
   149  	assert.Equal(t, 1, len(msgs))
   150  	msgReadJson, _ = json.Marshal(msgs[0])
   151  	assert.Equal(t, string(msgJson), string(msgReadJson))
   152  
   153  	// Check just getting hte refs
   154  	msgRefs, err := s.GetMessageRefs(ctx, filter)
   155  	assert.NoError(t, err)
   156  	assert.Equal(t, 1, len(msgs))
   157  	assert.Equal(t, msgUpdated.Header.ID, msgRefs[0].ID)
   158  	assert.Equal(t, msgUpdated.Hash, msgRefs[0].Hash)
   159  	assert.Equal(t, msgUpdated.Sequence, msgRefs[0].Sequence)
   160  
   161  	// Check we can get it with a filter on only mesasges with a particular data ref
   162  	msgs, err = s.GetMessagesForData(ctx, dataID3, filter)
   163  	assert.NoError(t, err)
   164  	assert.Equal(t, 1, len(msgs))
   165  	msgReadJson, _ = json.Marshal(msgs[0])
   166  	assert.Equal(t, string(msgJson), string(msgReadJson))
   167  
   168  	// Negative test on filter
   169  	filter = fb.And(
   170  		fb.Eq("id", msgUpdated.Header.ID.String()),
   171  		fb.Eq("created", "0"),
   172  	)
   173  	msgs, err = s.GetMessages(ctx, filter)
   174  	assert.NoError(t, err)
   175  	assert.Equal(t, 0, len(msgs))
   176  
   177  	// Update
   178  	gid2 := fftypes.NewRandB32()
   179  	bid2 := fftypes.NewUUID()
   180  	up := database.MessageQueryFactory.NewUpdate(ctx).
   181  		Set("group", gid2).
   182  		Set("batch", bid2)
   183  	err = s.UpdateMessage(ctx, msgID, up)
   184  	assert.NoError(t, err)
   185  
   186  	// Test find updated value
   187  	filter = fb.And(
   188  		fb.Eq("id", msgUpdated.Header.ID.String()),
   189  		fb.Eq("group", gid2),
   190  	)
   191  	msgs, err = s.GetMessages(ctx, filter)
   192  	assert.NoError(t, err)
   193  	assert.Equal(t, 1, len(msgs))
   194  	assert.Equal(t, *bid2, *msgs[0].BatchID)
   195  
   196  	s.callbacks.AssertExpectations(t)
   197  }
   198  
   199  func TestUpsertMessageFailBegin(t *testing.T) {
   200  	s, mock := newMockProvider().init()
   201  	mock.ExpectBegin().WillReturnError(fmt.Errorf("pop"))
   202  	err := s.UpsertMessage(context.Background(), &fftypes.Message{}, true, true)
   203  	assert.Regexp(t, "FF10114", err)
   204  	assert.NoError(t, mock.ExpectationsWereMet())
   205  }
   206  
   207  func TestUpsertMessageFailSelect(t *testing.T) {
   208  	s, mock := newMockProvider().init()
   209  	mock.ExpectBegin()
   210  	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
   211  	mock.ExpectRollback()
   212  	msgID := fftypes.NewUUID()
   213  	err := s.UpsertMessage(context.Background(), &fftypes.Message{Header: fftypes.MessageHeader{ID: msgID}}, true, true)
   214  	assert.Regexp(t, "FF10115", err)
   215  	assert.NoError(t, mock.ExpectationsWereMet())
   216  }
   217  
   218  func TestUpsertMessageFailInsert(t *testing.T) {
   219  	s, mock := newMockProvider().init()
   220  	mock.ExpectBegin()
   221  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{}))
   222  	mock.ExpectExec("INSERT .*").WillReturnError(fmt.Errorf("pop"))
   223  	mock.ExpectRollback()
   224  	msgID := fftypes.NewUUID()
   225  	err := s.UpsertMessage(context.Background(), &fftypes.Message{Header: fftypes.MessageHeader{ID: msgID}}, true, true)
   226  	assert.Regexp(t, "FF10116", err)
   227  	assert.NoError(t, mock.ExpectationsWereMet())
   228  }
   229  
   230  func TestUpsertMessageFailUpdate(t *testing.T) {
   231  	s, mock := newMockProvider().init()
   232  	msgID := fftypes.NewUUID()
   233  	mock.ExpectBegin()
   234  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(msgID.String()))
   235  	mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop"))
   236  	mock.ExpectRollback()
   237  	err := s.UpsertMessage(context.Background(), &fftypes.Message{Header: fftypes.MessageHeader{ID: msgID}}, true, true)
   238  	assert.Regexp(t, "FF10117", err)
   239  	assert.NoError(t, mock.ExpectationsWereMet())
   240  }
   241  
   242  func TestUpsertMessageFailUpdateRefs(t *testing.T) {
   243  	s, mock := newMockProvider().init()
   244  	msgID := fftypes.NewUUID()
   245  	mock.ExpectBegin()
   246  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(msgID.String()))
   247  	mock.ExpectExec("UPDATE .*").WillReturnResult(driver.ResultNoRows)
   248  	mock.ExpectExec("DELETE .*").WillReturnError(fmt.Errorf("pop"))
   249  	mock.ExpectRollback()
   250  	err := s.UpsertMessage(context.Background(), &fftypes.Message{Header: fftypes.MessageHeader{ID: msgID}}, true, true)
   251  	assert.Regexp(t, "FF10118", err)
   252  	assert.NoError(t, mock.ExpectationsWereMet())
   253  }
   254  
   255  func TestUpsertMessageFailCommit(t *testing.T) {
   256  	s, mock := newMockProvider().init()
   257  	msgID := fftypes.NewUUID()
   258  	mock.ExpectBegin()
   259  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}))
   260  	mock.ExpectExec("INSERT .*").WillReturnResult(sqlmock.NewResult(1, 1))
   261  	mock.ExpectCommit().WillReturnError(fmt.Errorf("pop"))
   262  	err := s.UpsertMessage(context.Background(), &fftypes.Message{Header: fftypes.MessageHeader{ID: msgID}}, true, true)
   263  	assert.Regexp(t, "FF10119", err)
   264  	assert.NoError(t, mock.ExpectationsWereMet())
   265  }
   266  
   267  func TestUpdateMessageDataRefsNilID(t *testing.T) {
   268  	s, mock := newMockProvider().init()
   269  	msgID := fftypes.NewUUID()
   270  	mock.ExpectBegin()
   271  	tx, _ := s.db.Begin()
   272  	err := s.updateMessageDataRefs(context.Background(), &txWrapper{sqlTX: tx}, &fftypes.Message{
   273  		Header: fftypes.MessageHeader{ID: msgID},
   274  		Data:   []*fftypes.DataRef{{ID: nil}},
   275  	}, false)
   276  	assert.Regexp(t, "FF10123", err)
   277  	assert.NoError(t, mock.ExpectationsWereMet())
   278  }
   279  
   280  func TestUpdateMessageDataRefsNilHash(t *testing.T) {
   281  	s, mock := newMockProvider().init()
   282  	msgID := fftypes.NewUUID()
   283  	mock.ExpectBegin()
   284  	tx, _ := s.db.Begin()
   285  	err := s.updateMessageDataRefs(context.Background(), &txWrapper{sqlTX: tx}, &fftypes.Message{
   286  		Header: fftypes.MessageHeader{ID: msgID},
   287  		Data:   []*fftypes.DataRef{{ID: fftypes.NewUUID()}},
   288  	}, false)
   289  	assert.Regexp(t, "FF10139", err)
   290  	assert.NoError(t, mock.ExpectationsWereMet())
   291  }
   292  
   293  func TestUpdateMessageDataDeleteFail(t *testing.T) {
   294  	s, mock := newMockProvider().init()
   295  	msgID := fftypes.NewUUID()
   296  	mock.ExpectBegin()
   297  	tx, _ := s.db.Begin()
   298  	mock.ExpectExec("DELETE .*").WillReturnError(fmt.Errorf("pop"))
   299  	err := s.updateMessageDataRefs(context.Background(), &txWrapper{sqlTX: tx}, &fftypes.Message{
   300  		Header: fftypes.MessageHeader{ID: msgID},
   301  	}, true)
   302  	assert.Regexp(t, "FF10118", err)
   303  	assert.NoError(t, mock.ExpectationsWereMet())
   304  }
   305  
   306  func TestUpdateMessageDataAddFail(t *testing.T) {
   307  	s, mock := newMockProvider().init()
   308  	msgID := fftypes.NewUUID()
   309  	dataID := fftypes.NewUUID()
   310  	dataHash := fftypes.NewRandB32()
   311  	mock.ExpectBegin()
   312  	tx, _ := s.db.Begin()
   313  	mock.ExpectExec("INSERT .*").WillReturnError(fmt.Errorf("pop"))
   314  	err := s.updateMessageDataRefs(context.Background(), &txWrapper{sqlTX: tx}, &fftypes.Message{
   315  		Header: fftypes.MessageHeader{ID: msgID},
   316  		Data:   []*fftypes.DataRef{{ID: dataID, Hash: dataHash}},
   317  	}, false)
   318  	assert.Regexp(t, "FF10116", err)
   319  	assert.NoError(t, mock.ExpectationsWereMet())
   320  }
   321  
   322  func TestLoadMessageDataRefsQueryFail(t *testing.T) {
   323  	s, mock := newMockProvider().init()
   324  	msgID := fftypes.NewUUID()
   325  	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
   326  	err := s.loadDataRefs(context.Background(), []*fftypes.Message{
   327  		{
   328  			Header: fftypes.MessageHeader{ID: msgID},
   329  		},
   330  	})
   331  	assert.Regexp(t, "FF10115", err)
   332  	assert.NoError(t, mock.ExpectationsWereMet())
   333  }
   334  
   335  func TestLoadMessageDataRefsScanFail(t *testing.T) {
   336  	s, mock := newMockProvider().init()
   337  	msgID := fftypes.NewUUID()
   338  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"data_id"}).AddRow("only one"))
   339  	err := s.loadDataRefs(context.Background(), []*fftypes.Message{
   340  		{
   341  			Header: fftypes.MessageHeader{ID: msgID},
   342  		},
   343  	})
   344  	assert.Regexp(t, "FF10121", err)
   345  	assert.NoError(t, mock.ExpectationsWereMet())
   346  }
   347  
   348  func TestLoadMessageDataRefsEmpty(t *testing.T) {
   349  	s, mock := newMockProvider().init()
   350  	msgID := fftypes.NewUUID()
   351  	msg := &fftypes.Message{Header: fftypes.MessageHeader{ID: msgID}}
   352  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"data_id", "data_hash"}))
   353  	err := s.loadDataRefs(context.Background(), []*fftypes.Message{msg})
   354  	assert.NoError(t, err)
   355  	assert.Equal(t, fftypes.DataRefs{}, msg.Data)
   356  	assert.NoError(t, mock.ExpectationsWereMet())
   357  }
   358  
   359  func TestGetMessageByIDSelectFail(t *testing.T) {
   360  	s, mock := newMockProvider().init()
   361  	msgID := fftypes.NewUUID()
   362  	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
   363  	_, err := s.GetMessageByID(context.Background(), msgID)
   364  	assert.Regexp(t, "FF10115", err)
   365  	assert.NoError(t, mock.ExpectationsWereMet())
   366  }
   367  
   368  func TestGetMessageByIDNotFound(t *testing.T) {
   369  	s, mock := newMockProvider().init()
   370  	msgID := fftypes.NewUUID()
   371  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}))
   372  	msg, err := s.GetMessageByID(context.Background(), msgID)
   373  	assert.NoError(t, err)
   374  	assert.Nil(t, msg)
   375  	assert.NoError(t, mock.ExpectationsWereMet())
   376  }
   377  
   378  func TestGetMessageByIDScanFail(t *testing.T) {
   379  	s, mock := newMockProvider().init()
   380  	msgID := fftypes.NewUUID()
   381  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("only one"))
   382  	_, err := s.GetMessageByID(context.Background(), msgID)
   383  	assert.Regexp(t, "FF10121", err)
   384  	assert.NoError(t, mock.ExpectationsWereMet())
   385  }
   386  
   387  func TestGetMessageByIDLoadRefsFail(t *testing.T) {
   388  	s, mock := newMockProvider().init()
   389  	msgID := fftypes.NewUUID()
   390  	b32 := fftypes.NewRandB32()
   391  	cols := append([]string{}, msgColumns...)
   392  	cols = append(cols, "id()")
   393  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows(cols).
   394  		AddRow(msgID.String(), nil, fftypes.MessageTypeBroadcast, "0x12345", 0, "ns1", "t1", "c1", nil, b32.String(), b32.String(), b32.String(), 0, "pin", nil, false, 0))
   395  	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
   396  	_, err := s.GetMessageByID(context.Background(), msgID)
   397  	assert.Regexp(t, "FF10115", err)
   398  	assert.NoError(t, mock.ExpectationsWereMet())
   399  }
   400  
   401  func TestGetMessagesBuildQueryFail(t *testing.T) {
   402  	s, _ := newMockProvider().init()
   403  	f := database.MessageQueryFactory.NewFilter(context.Background()).Eq("id", map[bool]bool{true: false})
   404  	_, err := s.GetMessages(context.Background(), f)
   405  	assert.Regexp(t, "FF10149.*id", err)
   406  }
   407  
   408  func TestGetMessagesQueryFail(t *testing.T) {
   409  	s, mock := newMockProvider().init()
   410  	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
   411  	f := database.MessageQueryFactory.NewFilter(context.Background()).Eq("id", "")
   412  	_, err := s.GetMessages(context.Background(), f)
   413  	assert.Regexp(t, "FF10115", err)
   414  	assert.NoError(t, mock.ExpectationsWereMet())
   415  }
   416  
   417  func TestGetMessagesForDataBadQuery(t *testing.T) {
   418  	s, mock := newMockProvider().init()
   419  	f := database.MessageQueryFactory.NewFilter(context.Background()).Eq("!wrong", "")
   420  	_, err := s.GetMessagesForData(context.Background(), fftypes.NewUUID(), f)
   421  	assert.Regexp(t, "FF10148", err)
   422  	assert.NoError(t, mock.ExpectationsWereMet())
   423  }
   424  
   425  func TestGetMessagesReadMessageFail(t *testing.T) {
   426  	s, mock := newMockProvider().init()
   427  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("only one"))
   428  	f := database.MessageQueryFactory.NewFilter(context.Background()).Eq("id", "")
   429  	_, err := s.GetMessages(context.Background(), f)
   430  	assert.Regexp(t, "FF10121", err)
   431  	assert.NoError(t, mock.ExpectationsWereMet())
   432  }
   433  
   434  func TestGetMessagesLoadRefsFail(t *testing.T) {
   435  	s, mock := newMockProvider().init()
   436  	msgID := fftypes.NewUUID()
   437  	b32 := fftypes.NewRandB32()
   438  	cols := append([]string{}, msgColumns...)
   439  	cols = append(cols, "id()")
   440  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows(cols).
   441  		AddRow(msgID.String(), nil, fftypes.MessageTypeBroadcast, "0x12345", 0, "ns1", "t1", "c1", nil, b32.String(), b32.String(), b32.String(), 0, "pin", nil, false, 0))
   442  	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
   443  	f := database.MessageQueryFactory.NewFilter(context.Background()).Gt("confirmed", "0")
   444  	_, err := s.GetMessages(context.Background(), f)
   445  	assert.Regexp(t, "FF10115", err)
   446  	assert.NoError(t, mock.ExpectationsWereMet())
   447  }
   448  
   449  func TestGetMessageRefsBuildQueryFail(t *testing.T) {
   450  	s, _ := newMockProvider().init()
   451  	f := database.MessageQueryFactory.NewFilter(context.Background()).Eq("id", map[bool]bool{true: false})
   452  	_, err := s.GetMessageRefs(context.Background(), f)
   453  	assert.Regexp(t, "FF10149.*id", err)
   454  }
   455  
   456  func TestGetMessageRefsQueryFail(t *testing.T) {
   457  	s, mock := newMockProvider().init()
   458  	mock.ExpectQuery("SELECT .*").WillReturnError(fmt.Errorf("pop"))
   459  	f := database.MessageQueryFactory.NewFilter(context.Background()).Eq("id", "")
   460  	_, err := s.GetMessageRefs(context.Background(), f)
   461  	assert.Regexp(t, "FF10115", err)
   462  	assert.NoError(t, mock.ExpectationsWereMet())
   463  }
   464  
   465  func TestGetMessageRefsReadMessageFail(t *testing.T) {
   466  	s, mock := newMockProvider().init()
   467  	mock.ExpectQuery("SELECT .*").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("only one"))
   468  	f := database.MessageQueryFactory.NewFilter(context.Background()).Eq("id", "")
   469  	_, err := s.GetMessageRefs(context.Background(), f)
   470  	assert.Regexp(t, "FF10121", err)
   471  	assert.NoError(t, mock.ExpectationsWereMet())
   472  }
   473  
   474  func TestMessageUpdateBeginFail(t *testing.T) {
   475  	s, mock := newMockProvider().init()
   476  	mock.ExpectBegin().WillReturnError(fmt.Errorf("pop"))
   477  	u := database.MessageQueryFactory.NewUpdate(context.Background()).Set("id", "anything")
   478  	err := s.UpdateMessage(context.Background(), fftypes.NewUUID(), u)
   479  	assert.Regexp(t, "FF10114", err)
   480  }
   481  
   482  func TestMessageUpdateBuildQueryFail(t *testing.T) {
   483  	s, mock := newMockProvider().init()
   484  	mock.ExpectBegin()
   485  	u := database.MessageQueryFactory.NewUpdate(context.Background()).Set("id", map[bool]bool{true: false})
   486  	err := s.UpdateMessage(context.Background(), fftypes.NewUUID(), u)
   487  	assert.Regexp(t, "FF10149.*id", err)
   488  }
   489  
   490  func TestMessagesUpdateBuildFilterFail(t *testing.T) {
   491  	s, mock := newMockProvider().init()
   492  	mock.ExpectBegin()
   493  	f := database.MessageQueryFactory.NewFilter(context.Background()).Eq("id", map[bool]bool{true: false})
   494  	u := database.MessageQueryFactory.NewUpdate(context.Background()).Set("type", fftypes.MessageTypeBroadcast)
   495  	err := s.UpdateMessages(context.Background(), f, u)
   496  	assert.Regexp(t, "FF10149.*id", err)
   497  }
   498  
   499  func TestMessageUpdateFail(t *testing.T) {
   500  	s, mock := newMockProvider().init()
   501  	mock.ExpectBegin()
   502  	mock.ExpectExec("UPDATE .*").WillReturnError(fmt.Errorf("pop"))
   503  	mock.ExpectRollback()
   504  	u := database.MessageQueryFactory.NewUpdate(context.Background()).Set("group", fftypes.NewRandB32())
   505  	err := s.UpdateMessage(context.Background(), fftypes.NewUUID(), u)
   506  	assert.Regexp(t, "FF10117", err)
   507  }