go.temporal.io/server@v1.23.0/common/persistence/sql/sqlplugin/tests/queue_v2.go (about)

     1  // The MIT License
     2  //
     3  // Copyright (c) 2020 Temporal Technologies Inc.  All rights reserved.
     4  //
     5  // Copyright (c) 2020 Uber Technologies, Inc.
     6  //
     7  // Permission is hereby granted, free of charge, to any person obtaining a copy
     8  // of this software and associated documentation files (the "Software"), to deal
     9  // in the Software without restriction, including without limitation the rights
    10  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    11  // copies of the Software, and to permit persons to whom the Software is
    12  // furnished to do so, subject to the following conditions:
    13  //
    14  // The above copyright notice and this permission notice shall be included in
    15  // all copies or substantial portions of the Software.
    16  //
    17  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    18  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    19  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    20  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    21  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    22  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    23  // THE SOFTWARE.
    24  
    25  package tests
    26  
    27  import (
    28  	"context"
    29  	"database/sql"
    30  	"errors"
    31  	"testing"
    32  
    33  	"go.temporal.io/api/serviceerror"
    34  
    35  	persistencespb "go.temporal.io/server/api/persistence/v1"
    36  	"go.temporal.io/server/common/persistence/persistencetest"
    37  	"go.temporal.io/server/common/persistence/serialization"
    38  
    39  	"github.com/stretchr/testify/assert"
    40  	"github.com/stretchr/testify/require"
    41  	enumspb "go.temporal.io/api/enums/v1"
    42  
    43  	"go.temporal.io/server/common/log"
    44  	"go.temporal.io/server/common/log/tag"
    45  	"go.temporal.io/server/common/persistence"
    46  	persistencesql "go.temporal.io/server/common/persistence/sql"
    47  	"go.temporal.io/server/common/persistence/sql/sqlplugin"
    48  )
    49  
    50  var (
    51  	ErrGetLastMessageIdFailed = errors.New("getLastMessageId error")
    52  	ErrTxBeginFailed          = errors.New("txBegin error")
    53  	ErrInsertFailed           = errors.New("insert error")
    54  	ErrTxRollbackFailed       = errors.New("txRollBack err")
    55  	ErrTxCommitFailed         = errors.New("txCommit err")
    56  	ErrRangeSelectFailed      = errors.New("rangeSelect err")
    57  	ErrSelectMetadataFailed   = errors.New("selectFromMetadata err")
    58  	ErrInsertMetadataFailed   = errors.New("insertMetadataFailed")
    59  	ErrRangeDeleteFailed      = errors.New("rangeDeleteFailed")
    60  	ErrUpdateMetadataFailed   = errors.New("updateMetadataFailed")
    61  	ErrSelectQueueNames       = errors.New("selectQueueNamesFailed")
    62  )
    63  
    64  type (
    65  	faultyDB struct {
    66  		sqlplugin.DB
    67  		getLastMessageIdErr   error
    68  		txBeginErr            error
    69  		txCommitErr           error
    70  		insertErr             error
    71  		txRollbackErr         error
    72  		rangeSelectError      error
    73  		selectMetadataError   error
    74  		insertMetadataError   error
    75  		rangeDeleteError      error
    76  		updateMetadataError   error
    77  		selectQueueNamesError error
    78  		commitCalls           int
    79  	}
    80  	faultyTx struct {
    81  		db *faultyDB
    82  		sqlplugin.Tx
    83  		commitCalls *int
    84  	}
    85  	logRecorder struct {
    86  		log.Logger
    87  		errMsgs []string
    88  	}
    89  )
    90  
    91  func (db *faultyDB) BeginTx(ctx context.Context) (sqlplugin.Tx, error) {
    92  	if db.txBeginErr != nil {
    93  		return nil, db.txBeginErr
    94  	}
    95  	tx, err := db.DB.BeginTx(ctx)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	return &faultyTx{db: db, commitCalls: &db.commitCalls, Tx: tx}, nil
   100  }
   101  
   102  func (tx *faultyTx) InsertIntoQueueV2Messages(ctx context.Context, row []sqlplugin.QueueV2MessageRow) (sql.Result, error) {
   103  	if tx.db.insertErr != nil {
   104  		return nil, tx.db.insertErr
   105  	}
   106  	return tx.Tx.InsertIntoQueueV2Messages(ctx, row)
   107  }
   108  
   109  func (tx *faultyTx) GetLastEnqueuedMessageIDForUpdateV2(ctx context.Context, filter sqlplugin.QueueV2Filter) (int64, error) {
   110  	if tx.db.getLastMessageIdErr != nil {
   111  		return 0, tx.db.getLastMessageIdErr
   112  	}
   113  	return tx.Tx.GetLastEnqueuedMessageIDForUpdateV2(ctx, filter)
   114  }
   115  
   116  func (db *faultyDB) GetLastEnqueuedMessageIDForUpdateV2(ctx context.Context, filter sqlplugin.QueueV2Filter) (int64, error) {
   117  	if db.getLastMessageIdErr != nil {
   118  		return 0, db.getLastMessageIdErr
   119  	}
   120  	return db.DB.GetLastEnqueuedMessageIDForUpdateV2(ctx, filter)
   121  }
   122  
   123  func (db *faultyDB) RangeSelectFromQueueV2Messages(ctx context.Context, filter sqlplugin.QueueV2MessagesFilter) ([]sqlplugin.QueueV2MessageRow, error) {
   124  	return []sqlplugin.QueueV2MessageRow{}, db.rangeSelectError
   125  
   126  }
   127  
   128  func (db *faultyDB) SelectFromQueueV2Metadata(ctx context.Context, filter sqlplugin.QueueV2MetadataFilter) (*sqlplugin.QueueV2MetadataRow, error) {
   129  	if db.selectMetadataError != nil {
   130  		return &sqlplugin.QueueV2MetadataRow{}, db.selectMetadataError
   131  	}
   132  	return db.DB.SelectFromQueueV2Metadata(ctx, filter)
   133  }
   134  
   135  func (db *faultyDB) SelectNameFromQueueV2Metadata(ctx context.Context, filter sqlplugin.QueueV2MetadataTypeFilter) ([]sqlplugin.QueueV2MetadataRow, error) {
   136  	if db.selectQueueNamesError != nil {
   137  		return nil, db.selectQueueNamesError
   138  	}
   139  	return db.DB.SelectNameFromQueueV2Metadata(ctx, filter)
   140  }
   141  
   142  func (db *faultyDB) SelectFromQueueV2MetadataForUpdate(ctx context.Context, filter sqlplugin.QueueV2MetadataFilter) (*sqlplugin.QueueV2MetadataRow, error) {
   143  	if db.selectMetadataError != nil {
   144  		return &sqlplugin.QueueV2MetadataRow{}, db.selectMetadataError
   145  	}
   146  	return db.DB.SelectFromQueueV2MetadataForUpdate(ctx, filter)
   147  }
   148  
   149  func (tx *faultyTx) SelectFromQueueV2Metadata(ctx context.Context, filter sqlplugin.QueueV2MetadataFilter) (*sqlplugin.QueueV2MetadataRow, error) {
   150  	if tx.db.selectMetadataError != nil {
   151  		return &sqlplugin.QueueV2MetadataRow{}, tx.db.selectMetadataError
   152  	}
   153  	return tx.Tx.SelectFromQueueV2Metadata(ctx, filter)
   154  }
   155  
   156  func (tx *faultyTx) SelectFromQueueV2MetadataForUpdate(ctx context.Context, filter sqlplugin.QueueV2MetadataFilter) (*sqlplugin.QueueV2MetadataRow, error) {
   157  	if tx.db.selectMetadataError != nil {
   158  		return &sqlplugin.QueueV2MetadataRow{}, tx.db.selectMetadataError
   159  	}
   160  	return tx.Tx.SelectFromQueueV2MetadataForUpdate(ctx, filter)
   161  }
   162  
   163  func (db *faultyDB) InsertIntoQueueV2Metadata(ctx context.Context, row *sqlplugin.QueueV2MetadataRow) (sql.Result, error) {
   164  	if db.insertMetadataError != nil {
   165  		return nil, db.insertMetadataError
   166  	}
   167  	return db.DB.InsertIntoQueueV2Metadata(ctx, row)
   168  }
   169  
   170  func (tx *faultyTx) RangeDeleteFromQueueV2Messages(ctx context.Context, filter sqlplugin.QueueV2MessagesFilter) (sql.Result, error) {
   171  	if tx.db.rangeDeleteError != nil {
   172  		return nil, tx.db.rangeDeleteError
   173  	}
   174  	return tx.Tx.RangeDeleteFromQueueV2Messages(ctx, filter)
   175  }
   176  
   177  func (tx *faultyTx) UpdateQueueV2Metadata(ctx context.Context, row *sqlplugin.QueueV2MetadataRow) (sql.Result, error) {
   178  	if tx.db.updateMetadataError != nil {
   179  		return nil, tx.db.updateMetadataError
   180  	}
   181  	return tx.Tx.UpdateQueueV2Metadata(ctx, row)
   182  }
   183  
   184  func (tx *faultyTx) Rollback() error {
   185  	if err := tx.Tx.Rollback(); err != nil {
   186  		return err
   187  	}
   188  	return tx.db.txRollbackErr
   189  }
   190  
   191  func (tx *faultyTx) Commit() error {
   192  	*tx.commitCalls++
   193  	if tx.db.txCommitErr != nil {
   194  		err := tx.Rollback()
   195  		if err != nil {
   196  			return err
   197  		}
   198  		return tx.db.txCommitErr
   199  	}
   200  	return tx.Tx.Commit()
   201  }
   202  
   203  func (l *logRecorder) Error(msg string, _ ...tag.Tag) {
   204  	l.errMsgs = append(l.errMsgs, msg)
   205  }
   206  
   207  func RunSQLQueueV2TestSuite(t *testing.T, baseDB sqlplugin.DB) {
   208  	ctx := context.Background()
   209  	t.Run("TestListQueueFailsToGetLastMessageID", func(t *testing.T) {
   210  		t.Parallel()
   211  		testListQueueFailsToGetLastMessageID(ctx, t, baseDB)
   212  	})
   213  	t.Run("TestListQueueFailsToExtractQueueMetadata", func(t *testing.T) {
   214  		t.Parallel()
   215  		testListQueueFailsToExtractQueueMetadata(ctx, t, baseDB)
   216  	})
   217  	t.Run("GetPartitionFailsForListQueues", func(t *testing.T) {
   218  		t.Parallel()
   219  		testListQueuesGetPartitionFails(ctx, t, baseDB)
   220  	})
   221  	t.Run("QueueInsertFails", func(t *testing.T) {
   222  		t.Parallel()
   223  		testQueueInsertFails(ctx, t, baseDB)
   224  	})
   225  	t.Run("TxBeginFails", func(t *testing.T) {
   226  		t.Parallel()
   227  		testBeginTxFails(ctx, t, baseDB)
   228  	})
   229  	t.Run("TxCommitFails", func(t *testing.T) {
   230  		t.Parallel()
   231  		testCommitTxFails(ctx, t, baseDB)
   232  	})
   233  	t.Run("FailedToGetLastMessageIDFromDB", func(t *testing.T) {
   234  		t.Parallel()
   235  		testGetLastMessageIDFails(ctx, t, baseDB)
   236  	})
   237  	t.Run("FailedToGetLastMessageIDFromDB", func(t *testing.T) {
   238  		t.Parallel()
   239  		testRangeSelectFromQueueV2MessagesFails(ctx, t, baseDB)
   240  	})
   241  	t.Run("InsertIntoQueueV2MetadataFails", func(t *testing.T) {
   242  		t.Parallel()
   243  		testInsertIntoQueueV2MetadataFails(ctx, t, baseDB)
   244  	})
   245  	t.Run("GetPartitionFailsForRangeDelete", func(t *testing.T) {
   246  		t.Parallel()
   247  		testGetPartitionFailsForRangeDelete(ctx, t, baseDB)
   248  	})
   249  	t.Run("GetLastMessageIDForDeleteFails", func(t *testing.T) {
   250  		t.Parallel()
   251  		testGetLastMessageIDForDeleteFails(ctx, t, baseDB)
   252  	})
   253  	t.Run("RangeDeleteMessagesFails", func(t *testing.T) {
   254  		t.Parallel()
   255  		testRangeDeleteMessagesFails(ctx, t, baseDB)
   256  	})
   257  	t.Run("RangeDeleteActuallyDeletes", func(t *testing.T) {
   258  		t.Parallel()
   259  		testRangeDeleteActuallyDeletes(ctx, t, baseDB)
   260  	})
   261  	t.Run("UpdateMetadataFails", func(t *testing.T) {
   262  		t.Parallel()
   263  		testUpdateMetadataFails(ctx, t, baseDB)
   264  	})
   265  	t.Run("InvalidMetadataEncoding", func(t *testing.T) {
   266  		t.Parallel()
   267  		testInvalidMetadataEncoding(ctx, t, baseDB)
   268  	})
   269  	t.Run("InvalidMetadataPayload", func(t *testing.T) {
   270  		t.Parallel()
   271  		testInvalidMetadataPayload(ctx, t, baseDB)
   272  	})
   273  	t.Run("SelectMetadataFails", func(t *testing.T) {
   274  		t.Parallel()
   275  		testSelectMetadataFails(ctx, t, baseDB)
   276  	})
   277  	t.Run("SelectNameFromQueueV2MetadataFails", func(t *testing.T) {
   278  		t.Parallel()
   279  		testSelectNameFromQueueV2MetadataFails(ctx, t, baseDB)
   280  	})
   281  	t.Run("SelectNameFromQueueV2NegativeToken", func(t *testing.T) {
   282  		t.Parallel()
   283  		testSelectNameFromQueueV2NegativeToken(ctx, t, baseDB)
   284  	})
   285  }
   286  
   287  func testQueueInsertFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
   288  	queueType := persistence.QueueTypeHistoryNormal
   289  	queueName := "test-queue-" + t.Name()
   290  	db := &faultyDB{
   291  		DB:            baseDB,
   292  		insertErr:     ErrInsertFailed,
   293  		txRollbackErr: ErrTxRollbackFailed,
   294  	}
   295  	logger := &logRecorder{Logger: log.NewTestLogger()}
   296  	q := persistencesql.NewQueueV2(db, logger)
   297  	_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
   298  		QueueType: queueType,
   299  		QueueName: queueName,
   300  	})
   301  	require.NoError(t, err)
   302  	_, err = persistencetest.EnqueueMessage(context.Background(), q, queueType, queueName)
   303  	require.Error(t, err)
   304  	assert.ErrorContains(t, err, "insert error")
   305  	require.Len(t, logger.errMsgs, 1)
   306  	assert.Contains(t, logger.errMsgs[0], "transaction rollback error")
   307  	assert.Equal(t, db.commitCalls, 0)
   308  }
   309  
   310  func testCommitTxFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
   311  	queueType := persistence.QueueTypeHistoryNormal
   312  	queueName := "test-queue-" + t.Name()
   313  	db := &faultyDB{
   314  		DB:          baseDB,
   315  		txCommitErr: ErrTxCommitFailed,
   316  	}
   317  	logger := &logRecorder{Logger: log.NewTestLogger()}
   318  	q := persistencesql.NewQueueV2(db, logger)
   319  	_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
   320  		QueueType: queueType,
   321  		QueueName: queueName,
   322  	})
   323  	require.NoError(t, err)
   324  	_, err = persistencetest.EnqueueMessage(context.Background(), q, queueType, queueName)
   325  	require.Error(t, err)
   326  	assert.ErrorContains(t, err, "EnqueueMessage failed")
   327  	assert.Equal(t, db.commitCalls, 1)
   328  }
   329  
   330  func testBeginTxFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
   331  	queueType := persistence.QueueTypeHistoryNormal
   332  	queueName := "test-queue-" + t.Name()
   333  	db := &faultyDB{
   334  		DB:         baseDB,
   335  		txBeginErr: ErrTxBeginFailed,
   336  	}
   337  	logger := &logRecorder{Logger: log.NewTestLogger()}
   338  	q := persistencesql.NewQueueV2(db, logger)
   339  	_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
   340  		QueueType: queueType,
   341  		QueueName: queueName,
   342  	})
   343  	require.NoError(t, err)
   344  	_, err = persistencetest.EnqueueMessage(context.Background(), q, queueType, queueName)
   345  	require.Error(t, err)
   346  	assert.ErrorContains(t, err, "txBegin error")
   347  	assert.Equal(t, db.commitCalls, 0)
   348  }
   349  
   350  func testGetLastMessageIDFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
   351  	queueType := persistence.QueueTypeHistoryNormal
   352  	queueName := "test-queue-" + t.Name()
   353  	db := &faultyDB{
   354  		DB:                  baseDB,
   355  		getLastMessageIdErr: ErrGetLastMessageIdFailed,
   356  		txRollbackErr:       ErrTxRollbackFailed,
   357  	}
   358  	logger := &logRecorder{Logger: log.NewTestLogger()}
   359  	q := persistencesql.NewQueueV2(db, logger)
   360  	_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
   361  		QueueType: queueType,
   362  		QueueName: queueName,
   363  	})
   364  	require.NoError(t, err)
   365  	_, err = persistencetest.EnqueueMessage(context.Background(), q, queueType, queueName)
   366  	require.Error(t, err)
   367  	assert.ErrorContains(t, err, "failed to get next messageId")
   368  	assert.Equal(t, db.commitCalls, 0)
   369  }
   370  
   371  func testRangeSelectFromQueueV2MessagesFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
   372  	queueType := persistence.QueueTypeHistoryNormal
   373  	queueName := "test-queue-" + t.Name()
   374  	db := &faultyDB{
   375  		DB:               baseDB,
   376  		rangeSelectError: ErrRangeSelectFailed,
   377  	}
   378  	logger := &logRecorder{Logger: log.NewTestLogger()}
   379  	q := persistencesql.NewQueueV2(db, logger)
   380  	_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
   381  		QueueType: queueType,
   382  		QueueName: queueName,
   383  	})
   384  	require.NoError(t, err)
   385  	_, err = q.ReadMessages(context.Background(), &persistence.InternalReadMessagesRequest{
   386  		QueueType:     queueType,
   387  		QueueName:     queueName,
   388  		PageSize:      1,
   389  		NextPageToken: nil,
   390  	})
   391  	require.Error(t, err)
   392  	assert.ErrorContains(t, err, "RangeSelectFromQueueV2Messages operation failed")
   393  }
   394  
   395  func testInsertIntoQueueV2MetadataFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
   396  	queueType := persistence.QueueTypeHistoryNormal
   397  	queueName := "test-queue-" + t.Name()
   398  	db := &faultyDB{
   399  		DB:                  baseDB,
   400  		insertMetadataError: ErrInsertMetadataFailed,
   401  	}
   402  	logger := &logRecorder{Logger: log.NewTestLogger()}
   403  	q := persistencesql.NewQueueV2(db, logger)
   404  	_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
   405  		QueueType: queueType,
   406  		QueueName: queueName,
   407  	})
   408  	require.Error(t, err)
   409  	assert.ErrorContains(t, err, "InsertIntoQueueV2Metadata operation failed")
   410  }
   411  
   412  func testGetPartitionFailsForRangeDelete(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
   413  	queueType := persistence.QueueTypeHistoryNormal
   414  	queueName := "test-queue-" + t.Name()
   415  	logger := &logRecorder{Logger: log.NewTestLogger()}
   416  	q := persistencesql.NewQueueV2(baseDB, logger)
   417  	queuePB := persistencespb.Queue{
   418  		Partitions: map[int32]*persistencespb.QueuePartition{
   419  			0: {},
   420  			1: {},
   421  		},
   422  	}
   423  	bytes, _ := queuePB.Marshal()
   424  	row := sqlplugin.QueueV2MetadataRow{
   425  		QueueType:        queueType,
   426  		QueueName:        queueName,
   427  		MetadataPayload:  bytes,
   428  		MetadataEncoding: enumspb.ENCODING_TYPE_PROTO3.String(),
   429  	}
   430  	_, err := baseDB.InsertIntoQueueV2Metadata(ctx, &row)
   431  	require.NoError(t, err)
   432  	_, err = q.RangeDeleteMessages(context.Background(), &persistence.InternalRangeDeleteMessagesRequest{
   433  		QueueType:                   persistence.QueueTypeHistoryNormal,
   434  		QueueName:                   "test-queue-" + t.Name(),
   435  		InclusiveMaxMessageMetadata: persistence.MessageMetadata{ID: 0},
   436  	})
   437  	assert.Error(t, err)
   438  	assert.ErrorContains(t, err, "partitions")
   439  }
   440  
   441  func testGetLastMessageIDForDeleteFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
   442  	queueType := persistence.QueueTypeHistoryNormal
   443  	queueName := "test-queue-" + t.Name()
   444  	db := &faultyDB{
   445  		DB:                  baseDB,
   446  		getLastMessageIdErr: ErrGetLastMessageIdFailed,
   447  	}
   448  	logger := &logRecorder{Logger: log.NewTestLogger()}
   449  	q := persistencesql.NewQueueV2(db, logger)
   450  	_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
   451  		QueueType: queueType,
   452  		QueueName: queueName,
   453  	})
   454  	require.NoError(t, err)
   455  	_, err = q.RangeDeleteMessages(context.Background(), &persistence.InternalRangeDeleteMessagesRequest{
   456  		QueueType:                   persistence.QueueTypeHistoryNormal,
   457  		QueueName:                   "test-queue-" + t.Name(),
   458  		InclusiveMaxMessageMetadata: persistence.MessageMetadata{ID: 0},
   459  	})
   460  	assert.Error(t, err)
   461  	assert.ErrorContains(t, err, "getLastMessageId error")
   462  }
   463  
   464  func testRangeDeleteMessagesFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
   465  	queueType := persistence.QueueTypeHistoryNormal
   466  	queueName := "test-queue-" + t.Name()
   467  	db := &faultyDB{
   468  		DB:               baseDB,
   469  		rangeDeleteError: ErrRangeDeleteFailed,
   470  	}
   471  	logger := &logRecorder{Logger: log.NewTestLogger()}
   472  	q := persistencesql.NewQueueV2(db, logger)
   473  	_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
   474  		QueueType: queueType,
   475  		QueueName: queueName,
   476  	})
   477  	require.NoError(t, err)
   478  	persistencetest.EnqueueMessagesForDelete(t, q, queueName, queueType)
   479  	_, err = q.RangeDeleteMessages(context.Background(), &persistence.InternalRangeDeleteMessagesRequest{
   480  		QueueType:                   queueType,
   481  		QueueName:                   queueName,
   482  		InclusiveMaxMessageMetadata: persistence.MessageMetadata{ID: 0},
   483  	})
   484  	assert.Error(t, err)
   485  	assert.ErrorContains(t, err, "rangeDeleteFailed")
   486  }
   487  
   488  func testUpdateMetadataFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
   489  	queueType := persistence.QueueTypeHistoryNormal
   490  	queueName := "test-queue-" + t.Name()
   491  	db := &faultyDB{
   492  		DB:                  baseDB,
   493  		updateMetadataError: ErrUpdateMetadataFailed,
   494  	}
   495  	logger := &logRecorder{Logger: log.NewTestLogger()}
   496  	q := persistencesql.NewQueueV2(db, logger)
   497  	_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
   498  		QueueType: queueType,
   499  		QueueName: queueName,
   500  	})
   501  	require.NoError(t, err)
   502  	persistencetest.EnqueueMessagesForDelete(t, q, queueName, queueType)
   503  	_, err = q.RangeDeleteMessages(context.Background(), &persistence.InternalRangeDeleteMessagesRequest{
   504  		QueueType:                   queueType,
   505  		QueueName:                   queueName,
   506  		InclusiveMaxMessageMetadata: persistence.MessageMetadata{ID: 0},
   507  	})
   508  	assert.Error(t, err)
   509  	assert.ErrorContains(t, err, "updateMetadataFailed")
   510  }
   511  
   512  func testSelectMetadataFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
   513  	queueType := persistence.QueueTypeHistoryNormal
   514  	queueName := "test-queue-" + t.Name()
   515  	db := &faultyDB{
   516  		DB:                  baseDB,
   517  		selectMetadataError: ErrSelectMetadataFailed,
   518  	}
   519  	logger := &logRecorder{Logger: log.NewTestLogger()}
   520  	q := persistencesql.NewQueueV2(db, logger)
   521  	_, err := q.ReadMessages(ctx, &persistence.InternalReadMessagesRequest{
   522  		QueueType: queueType,
   523  		QueueName: queueName,
   524  		PageSize:  10,
   525  	})
   526  	assert.Error(t, err)
   527  	assert.ErrorContains(t, err, ErrSelectMetadataFailed.Error())
   528  	_, err = persistencetest.EnqueueMessage(context.Background(), q, queueType, queueName)
   529  	assert.Error(t, err)
   530  	assert.ErrorContains(t, err, ErrSelectMetadataFailed.Error())
   531  	_, err = q.RangeDeleteMessages(context.Background(), &persistence.InternalRangeDeleteMessagesRequest{
   532  		QueueType:                   queueType,
   533  		QueueName:                   queueName,
   534  		InclusiveMaxMessageMetadata: persistence.MessageMetadata{ID: 0},
   535  	})
   536  	assert.Error(t, err)
   537  	assert.ErrorAs(t, err, new(*serviceerror.Unavailable))
   538  }
   539  
   540  func testInvalidMetadataPayload(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
   541  	queueType := persistence.QueueTypeHistoryNormal
   542  	queueName := "test-queue-" + t.Name()
   543  	logger := &logRecorder{Logger: log.NewTestLogger()}
   544  	q := persistencesql.NewQueueV2(baseDB, logger)
   545  
   546  	row := sqlplugin.QueueV2MetadataRow{
   547  		QueueType:        queueType,
   548  		QueueName:        queueName,
   549  		MetadataPayload:  []byte("invalid_payload"),
   550  		MetadataEncoding: enumspb.ENCODING_TYPE_PROTO3.String(),
   551  	}
   552  	_, err := baseDB.InsertIntoQueueV2Metadata(ctx, &row)
   553  	require.NoError(t, err)
   554  	_, err = q.ReadMessages(context.Background(), &persistence.InternalReadMessagesRequest{
   555  		QueueType: queueType,
   556  		QueueName: queueName,
   557  		PageSize:  10,
   558  	})
   559  	assert.Error(t, err)
   560  	assert.ErrorAs(t, err, new(*serialization.DeserializationError))
   561  }
   562  
   563  func testInvalidMetadataEncoding(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
   564  	queueType := persistence.QueueTypeHistoryNormal
   565  	queueName := "test-queue-" + t.Name()
   566  	logger := &logRecorder{Logger: log.NewTestLogger()}
   567  	q := persistencesql.NewQueueV2(baseDB, logger)
   568  
   569  	row := sqlplugin.QueueV2MetadataRow{
   570  		QueueType:        queueType,
   571  		QueueName:        queueName,
   572  		MetadataPayload:  []byte("test"),
   573  		MetadataEncoding: "invalid_encoding",
   574  	}
   575  	_, err := baseDB.InsertIntoQueueV2Metadata(ctx, &row)
   576  	require.NoError(t, err)
   577  	_, err = q.ReadMessages(context.Background(), &persistence.InternalReadMessagesRequest{
   578  		QueueType: queueType,
   579  		QueueName: queueName,
   580  		PageSize:  10,
   581  	})
   582  	assert.Error(t, err)
   583  	assert.ErrorAs(t, err, new(*serialization.UnknownEncodingTypeError))
   584  	_, err = persistencetest.EnqueueMessage(context.Background(), q, queueType, queueName)
   585  	assert.Error(t, err)
   586  	assert.ErrorAs(t, err, new(*serialization.UnknownEncodingTypeError))
   587  	_, err = q.RangeDeleteMessages(context.Background(), &persistence.InternalRangeDeleteMessagesRequest{
   588  		QueueType:                   queueType,
   589  		QueueName:                   queueName,
   590  		InclusiveMaxMessageMetadata: persistence.MessageMetadata{ID: 0},
   591  	})
   592  	assert.Error(t, err)
   593  	assert.ErrorAs(t, err, new(*serviceerror.Unavailable))
   594  }
   595  
   596  func testRangeDeleteActuallyDeletes(ctx context.Context, t *testing.T, db sqlplugin.DB) {
   597  	queueKey := persistencetest.GetQueueKey(t)
   598  	queueType := persistence.QueueTypeHistoryNormal
   599  	q := persistencesql.NewQueueV2(db, log.NewTestLogger())
   600  	_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
   601  		QueueType: queueType,
   602  		QueueName: queueKey.GetQueueName(),
   603  	})
   604  	require.NoError(t, err)
   605  	for i := 0; i < 3; i++ {
   606  		_, err = persistencetest.EnqueueMessage(context.Background(), q, queueType, queueKey.GetQueueName())
   607  		require.NoError(t, err)
   608  	}
   609  	resp, err := q.RangeDeleteMessages(context.Background(), &persistence.InternalRangeDeleteMessagesRequest{
   610  		QueueType:                   queueType,
   611  		QueueName:                   queueKey.GetQueueName(),
   612  		InclusiveMaxMessageMetadata: persistence.MessageMetadata{ID: persistence.FirstQueueMessageID + 2},
   613  	})
   614  	require.NoError(t, err)
   615  	assert.Equal(t, int64(3), resp.MessagesDeleted)
   616  	result, err := q.ReadMessages(ctx, &persistence.InternalReadMessagesRequest{
   617  		QueueType: queueType,
   618  		QueueName: queueKey.GetQueueName(),
   619  		PageSize:  10,
   620  	})
   621  	require.NoError(t, err)
   622  	assert.Empty(t, result.Messages)
   623  	messages, err := db.RangeSelectFromQueueV2Messages(ctx, sqlplugin.QueueV2MessagesFilter{
   624  		QueueType:    queueType,
   625  		QueueName:    queueKey.GetQueueName(),
   626  		MinMessageID: 0,
   627  		MaxMessageID: 100,
   628  		PageSize:     10,
   629  	})
   630  	require.NoError(t, err)
   631  	if assert.Len(t, messages, 1) {
   632  		assert.Equal(t, int64(persistence.FirstQueueMessageID+2), messages[0].MessageID)
   633  	}
   634  	response, err := persistencetest.EnqueueMessage(context.Background(), q, queueType, queueKey.GetQueueName())
   635  	require.NoError(t, err)
   636  	assert.Equal(t, int64(persistence.FirstQueueMessageID+3), response.Metadata.ID)
   637  }
   638  
   639  func testSelectNameFromQueueV2MetadataFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
   640  	queueType := persistence.QueueTypeHistoryDLQ
   641  	db := &faultyDB{
   642  		DB:                    baseDB,
   643  		selectQueueNamesError: ErrSelectQueueNames,
   644  	}
   645  	logger := &logRecorder{Logger: log.NewTestLogger()}
   646  	q := persistencesql.NewQueueV2(db, logger)
   647  	_, err := q.ListQueues(ctx, &persistence.InternalListQueuesRequest{
   648  		QueueType:     queueType,
   649  		PageSize:      10,
   650  		NextPageToken: nil,
   651  	})
   652  	assert.Error(t, err)
   653  	assert.ErrorContains(t, err, "SelectNameFromQueueV2Metadata operation failed")
   654  }
   655  
   656  func testSelectNameFromQueueV2NegativeToken(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
   657  	queueType := persistence.QueueTypeHistoryDLQ
   658  	logger := &logRecorder{Logger: log.NewTestLogger()}
   659  	q := persistencesql.NewQueueV2(baseDB, logger)
   660  	_, err := q.ListQueues(ctx, &persistence.InternalListQueuesRequest{
   661  		QueueType:     queueType,
   662  		PageSize:      1,
   663  		NextPageToken: persistence.GetNextPageTokenForListQueues(-1),
   664  	})
   665  	require.Error(t, err)
   666  	require.ErrorIs(t, err, persistence.ErrNegativeListQueuesOffset)
   667  }
   668  
   669  func testListQueuesGetPartitionFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
   670  	// Using a different QueueType to prevent this test from failing because of queues created in previous tests.
   671  	queueType := persistence.QueueV2Type(4)
   672  	queueName := "test-queue-" + t.Name()
   673  	logger := &logRecorder{Logger: log.NewTestLogger()}
   674  	q := persistencesql.NewQueueV2(baseDB, logger)
   675  	queuePB := persistencespb.Queue{
   676  		Partitions: map[int32]*persistencespb.QueuePartition{
   677  			0: {},
   678  			1: {},
   679  		},
   680  	}
   681  	bytes, _ := queuePB.Marshal()
   682  	row := sqlplugin.QueueV2MetadataRow{
   683  		QueueType:        queueType,
   684  		QueueName:        queueName,
   685  		MetadataPayload:  bytes,
   686  		MetadataEncoding: enumspb.ENCODING_TYPE_PROTO3.String(),
   687  	}
   688  	_, err := baseDB.InsertIntoQueueV2Metadata(ctx, &row)
   689  	require.NoError(t, err)
   690  	_, err = q.ListQueues(context.Background(), &persistence.InternalListQueuesRequest{
   691  		QueueType: queueType,
   692  		PageSize:  100,
   693  	})
   694  	assert.Error(t, err)
   695  	assert.ErrorContains(t, err, "partitions")
   696  }
   697  
   698  func testListQueueFailsToGetLastMessageID(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
   699  	// Using a different QueueType to prevent this test from failing because of queues created in previous tests.
   700  	queueType := persistence.QueueV2Type(5)
   701  	queueName := "test-queue-" + t.Name()
   702  	db := &faultyDB{
   703  		DB:                  baseDB,
   704  		getLastMessageIdErr: ErrGetLastMessageIdFailed,
   705  	}
   706  	logger := &logRecorder{Logger: log.NewTestLogger()}
   707  	q := persistencesql.NewQueueV2(db, logger)
   708  	_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
   709  		QueueType: queueType,
   710  		QueueName: queueName,
   711  	})
   712  	assert.NoError(t, err)
   713  	_, err = q.ListQueues(ctx, &persistence.InternalListQueuesRequest{
   714  		QueueType: queueType,
   715  		PageSize:  100,
   716  	})
   717  	assert.Error(t, err)
   718  	assert.ErrorContains(t, err, ErrGetLastMessageIdFailed.Error())
   719  }
   720  
   721  func testListQueueFailsToExtractQueueMetadata(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
   722  	// Using a different QueueType to prevent this test from failing because of queues created in previous tests.
   723  	queueType := persistence.QueueV2Type(6)
   724  	queueName := "test-queue-" + t.Name()
   725  	q := persistencesql.NewQueueV2(baseDB, log.NewTestLogger())
   726  	row := sqlplugin.QueueV2MetadataRow{
   727  		QueueType:        queueType,
   728  		QueueName:        queueName,
   729  		MetadataPayload:  []byte("test"),
   730  		MetadataEncoding: "invalid_encoding",
   731  	}
   732  	_, err := baseDB.InsertIntoQueueV2Metadata(ctx, &row)
   733  	assert.NoError(t, err)
   734  	_, err = q.ListQueues(ctx, &persistence.InternalListQueuesRequest{
   735  		QueueType: queueType,
   736  		PageSize:  100,
   737  	})
   738  	assert.Error(t, err)
   739  	assert.ErrorAs(t, err, new(*serialization.UnknownEncodingTypeError))
   740  }