2023-12-13 15:06:39 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
|
|
|
|
package indexers
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/hashicorp/consul/internal/controller/cache/indexers/indexersmock"
|
|
|
|
"github.com/hashicorp/consul/internal/resource"
|
|
|
|
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
|
|
|
pbdemo "github.com/hashicorp/consul/proto/private/pbdemo/v1"
|
|
|
|
"github.com/hashicorp/consul/proto/private/prototest"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/stretchr/testify/suite"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
fakeTestError = errors.New("fake test error")
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestSingleIndexer(t *testing.T) {
|
|
|
|
suite.Run(t, &decodedSingleIndexerSuite{})
|
|
|
|
}
|
|
|
|
|
|
|
|
type decodedSingleIndexerSuite struct {
|
|
|
|
suite.Suite
|
|
|
|
|
|
|
|
indexer *indexersmock.SingleIndexer[*pbdemo.Album]
|
|
|
|
args *indexersmock.FromArgs
|
|
|
|
|
|
|
|
index *singleIndexer[*pbdemo.Album]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (suite *decodedSingleIndexerSuite) SetupTest() {
|
|
|
|
suite.indexer = indexersmock.NewSingleIndexer[*pbdemo.Album](suite.T())
|
|
|
|
suite.args = indexersmock.NewFromArgs(suite.T())
|
|
|
|
suite.index = &singleIndexer[*pbdemo.Album]{
|
|
|
|
indexArgs: suite.args.Execute,
|
|
|
|
decodedIndexer: suite.indexer.Execute,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (suite *decodedSingleIndexerSuite) TestFromArgs() {
|
|
|
|
suite.args.EXPECT().
|
|
|
|
Execute("blah", 1, true).
|
|
|
|
Return([]byte("foo"), nil).
|
|
|
|
Once()
|
|
|
|
|
|
|
|
val, err := suite.index.FromArgs("blah", 1, true)
|
|
|
|
require.NoError(suite.T(), err)
|
|
|
|
require.Equal(suite.T(), []byte("foo"), val)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (suite *decodedSingleIndexerSuite) TestFromArgs_Error() {
|
|
|
|
suite.args.EXPECT().
|
|
|
|
Execute("blah", 1, true).
|
|
|
|
Return(nil, fakeTestError).
|
|
|
|
Once()
|
|
|
|
|
|
|
|
val, err := suite.index.FromArgs("blah", 1, true)
|
|
|
|
require.ErrorIs(suite.T(), err, fakeTestError)
|
|
|
|
require.Nil(suite.T(), val)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (suite *decodedSingleIndexerSuite) TestFromResource() {
|
|
|
|
res := resourcetest.Resource(pbdemo.AlbumType, "foo").
|
|
|
|
WithData(suite.T(), &pbdemo.Album{
|
|
|
|
Name: "blah",
|
|
|
|
}).
|
|
|
|
Build()
|
|
|
|
|
|
|
|
dec := resourcetest.MustDecode[*pbdemo.Album](suite.T(), res)
|
|
|
|
|
|
|
|
suite.indexer.EXPECT().
|
|
|
|
Execute(dec).
|
|
|
|
Return(true, []byte{1, 2, 3}, nil).
|
|
|
|
Once()
|
|
|
|
|
|
|
|
indexed, val, err := suite.index.FromResource(res)
|
|
|
|
require.True(suite.T(), indexed)
|
|
|
|
require.NoError(suite.T(), err)
|
|
|
|
require.Equal(suite.T(), []byte{1, 2, 3}, val)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (suite *decodedSingleIndexerSuite) TestFromResource_Error() {
|
|
|
|
res := resourcetest.Resource(pbdemo.AlbumType, "foo").
|
|
|
|
WithData(suite.T(), &pbdemo.Album{
|
|
|
|
Name: "blah",
|
|
|
|
}).
|
|
|
|
Build()
|
|
|
|
|
|
|
|
dec := resourcetest.MustDecode[*pbdemo.Album](suite.T(), res)
|
|
|
|
|
|
|
|
suite.indexer.EXPECT().
|
|
|
|
Execute(dec).
|
|
|
|
Return(false, nil, fakeTestError).
|
|
|
|
Once()
|
|
|
|
|
|
|
|
indexed, val, err := suite.index.FromResource(res)
|
|
|
|
require.False(suite.T(), indexed)
|
|
|
|
require.ErrorIs(suite.T(), err, fakeTestError)
|
|
|
|
require.Nil(suite.T(), val)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (suite *decodedSingleIndexerSuite) TestFromResource_DecodeError() {
|
|
|
|
res := resourcetest.Resource(pbdemo.ArtistType, "foo").
|
|
|
|
WithData(suite.T(), &pbdemo.Artist{
|
|
|
|
Name: "blah",
|
|
|
|
}).
|
|
|
|
Build()
|
|
|
|
|
|
|
|
var expectedErr resource.ErrDataParse
|
|
|
|
indexed, val, err := suite.index.FromResource(res)
|
|
|
|
require.False(suite.T(), indexed)
|
|
|
|
require.ErrorAs(suite.T(), err, &expectedErr)
|
|
|
|
require.Nil(suite.T(), val)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (suite *decodedSingleIndexerSuite) TestIntegration() {
|
|
|
|
// This test attempts to do enough to ensure that the
|
|
|
|
// cache Index creator configures all the interfaces/funcs
|
|
|
|
// the correct way. It is not meant to fully test the
|
|
|
|
// Index type itself.
|
|
|
|
res := resourcetest.Resource(pbdemo.AlbumType, "foo").
|
|
|
|
WithData(suite.T(), &pbdemo.Album{
|
|
|
|
Name: "blah",
|
|
|
|
}).
|
|
|
|
Build()
|
|
|
|
|
|
|
|
dec := resourcetest.MustDecode[*pbdemo.Album](suite.T(), res)
|
|
|
|
|
2024-02-09 18:00:21 +00:00
|
|
|
idx := DecodedSingleIndexer("test", suite.args.Execute, suite.indexer.Execute).IndexedData()
|
2023-12-13 15:06:39 +00:00
|
|
|
|
|
|
|
suite.indexer.EXPECT().
|
|
|
|
Execute(dec).
|
|
|
|
Return(true, []byte{1, 2}, nil).
|
|
|
|
Once()
|
|
|
|
|
|
|
|
txn := idx.Txn()
|
|
|
|
require.NoError(suite.T(), txn.Insert(res))
|
|
|
|
txn.Commit()
|
|
|
|
|
|
|
|
suite.args.EXPECT().
|
|
|
|
Execute("fake").
|
|
|
|
Return([]byte{1, 2}, nil).
|
|
|
|
Once()
|
|
|
|
|
|
|
|
r, err := idx.Txn().Get("fake")
|
|
|
|
require.NoError(suite.T(), err)
|
|
|
|
prototest.AssertDeepEqual(suite.T(), res, r)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMultiIndexer(t *testing.T) {
|
|
|
|
suite.Run(t, &decodedMultiIndexerSuite{})
|
|
|
|
}
|
|
|
|
|
|
|
|
type decodedMultiIndexerSuite struct {
|
|
|
|
suite.Suite
|
|
|
|
|
|
|
|
indexer *indexersmock.MultiIndexer[*pbdemo.Album]
|
|
|
|
args *indexersmock.FromArgs
|
|
|
|
|
|
|
|
index *multiIndexer[*pbdemo.Album]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (suite *decodedMultiIndexerSuite) SetupTest() {
|
|
|
|
suite.indexer = indexersmock.NewMultiIndexer[*pbdemo.Album](suite.T())
|
|
|
|
suite.args = indexersmock.NewFromArgs(suite.T())
|
|
|
|
suite.index = &multiIndexer[*pbdemo.Album]{
|
|
|
|
indexArgs: suite.args.Execute,
|
|
|
|
decodedIndexer: suite.indexer.Execute,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (suite *decodedMultiIndexerSuite) TestFromArgs() {
|
|
|
|
suite.args.EXPECT().
|
|
|
|
Execute("blah", 1, true).
|
|
|
|
Return([]byte("foo"), nil).
|
|
|
|
Once()
|
|
|
|
|
|
|
|
val, err := suite.index.FromArgs("blah", 1, true)
|
|
|
|
require.NoError(suite.T(), err)
|
|
|
|
require.Equal(suite.T(), []byte("foo"), val)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (suite *decodedMultiIndexerSuite) TestFromArgs_Error() {
|
|
|
|
suite.args.EXPECT().
|
|
|
|
Execute("blah", 1, true).
|
|
|
|
Return(nil, fakeTestError).
|
|
|
|
Once()
|
|
|
|
|
|
|
|
val, err := suite.index.FromArgs("blah", 1, true)
|
|
|
|
require.ErrorIs(suite.T(), err, fakeTestError)
|
|
|
|
require.Nil(suite.T(), val)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (suite *decodedMultiIndexerSuite) TestFromResource() {
|
|
|
|
res := resourcetest.Resource(pbdemo.AlbumType, "foo").
|
|
|
|
WithData(suite.T(), &pbdemo.Album{
|
|
|
|
Name: "blah",
|
|
|
|
}).
|
|
|
|
Build()
|
|
|
|
|
|
|
|
dec := resourcetest.MustDecode[*pbdemo.Album](suite.T(), res)
|
|
|
|
|
|
|
|
suite.indexer.EXPECT().
|
|
|
|
Execute(dec).
|
|
|
|
Return(true, [][]byte{{1, 2}, {3}}, nil).
|
|
|
|
Once()
|
|
|
|
|
|
|
|
indexed, val, err := suite.index.FromResource(res)
|
|
|
|
require.True(suite.T(), indexed)
|
|
|
|
require.NoError(suite.T(), err)
|
|
|
|
require.Equal(suite.T(), [][]byte{{1, 2}, {3}}, val)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (suite *decodedMultiIndexerSuite) TestFromResource_Error() {
|
|
|
|
res := resourcetest.Resource(pbdemo.AlbumType, "foo").
|
|
|
|
WithData(suite.T(), &pbdemo.Album{
|
|
|
|
Name: "blah",
|
|
|
|
}).
|
|
|
|
Build()
|
|
|
|
|
|
|
|
dec := resourcetest.MustDecode[*pbdemo.Album](suite.T(), res)
|
|
|
|
|
|
|
|
suite.indexer.EXPECT().
|
|
|
|
Execute(dec).
|
|
|
|
Return(false, nil, fakeTestError).
|
|
|
|
Once()
|
|
|
|
|
|
|
|
indexed, val, err := suite.index.FromResource(res)
|
|
|
|
require.False(suite.T(), indexed)
|
|
|
|
require.ErrorIs(suite.T(), err, fakeTestError)
|
|
|
|
require.Nil(suite.T(), val)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (suite *decodedMultiIndexerSuite) TestFromResource_DecodeError() {
|
|
|
|
res := resourcetest.Resource(pbdemo.ArtistType, "foo").
|
|
|
|
WithData(suite.T(), &pbdemo.Artist{
|
|
|
|
Name: "blah",
|
|
|
|
}).
|
|
|
|
Build()
|
|
|
|
|
|
|
|
var expectedErr resource.ErrDataParse
|
|
|
|
indexed, val, err := suite.index.FromResource(res)
|
|
|
|
require.False(suite.T(), indexed)
|
|
|
|
require.ErrorAs(suite.T(), err, &expectedErr)
|
|
|
|
require.Nil(suite.T(), val)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (suite *decodedMultiIndexerSuite) TestIntegration() {
|
|
|
|
// This test attempts to do enough to ensure that the
|
|
|
|
// cache Index creator configures all the interfaces/funcs
|
|
|
|
// the correct way. It is not meant to fully test the
|
|
|
|
// Index type itself.
|
|
|
|
res := resourcetest.Resource(pbdemo.AlbumType, "foo").
|
|
|
|
WithData(suite.T(), &pbdemo.Album{
|
|
|
|
Name: "blah",
|
|
|
|
}).
|
|
|
|
Build()
|
|
|
|
|
|
|
|
dec := resourcetest.MustDecode[*pbdemo.Album](suite.T(), res)
|
|
|
|
|
2024-02-09 18:00:21 +00:00
|
|
|
idx := DecodedMultiIndexer("test", suite.args.Execute, suite.indexer.Execute).IndexedData()
|
2023-12-13 15:06:39 +00:00
|
|
|
|
|
|
|
suite.indexer.EXPECT().
|
|
|
|
Execute(dec).
|
|
|
|
Return(true, [][]byte{{1, 2}, {3}}, nil).
|
|
|
|
Once()
|
|
|
|
|
|
|
|
txn := idx.Txn()
|
|
|
|
require.NoError(suite.T(), txn.Insert(res))
|
|
|
|
txn.Commit()
|
|
|
|
|
|
|
|
suite.args.EXPECT().
|
|
|
|
Execute("fake").
|
|
|
|
Return([]byte{1, 2}, nil).
|
|
|
|
Once()
|
|
|
|
|
|
|
|
suite.args.EXPECT().
|
|
|
|
Execute("fake2").
|
|
|
|
Return([]byte{3}, nil).
|
|
|
|
Once()
|
|
|
|
|
|
|
|
txn = idx.Txn()
|
|
|
|
r, err := txn.Get("fake")
|
|
|
|
require.NoError(suite.T(), err)
|
|
|
|
prototest.AssertDeepEqual(suite.T(), res, r)
|
|
|
|
|
|
|
|
r, err = txn.Get("fake2")
|
|
|
|
require.NoError(suite.T(), err)
|
|
|
|
prototest.AssertDeepEqual(suite.T(), res, r)
|
|
|
|
}
|