// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package state import ( "sort" "testing" "github.com/hashicorp/go-memdb" "github.com/stretchr/testify/require" "github.com/hashicorp/consul/proto/private/pbpeering" ) type indexerTestCase struct { read indexValue write indexValue prefix []indexValue writeMulti indexValueMulti // extra test cases can be added if the indexer has special handling for // specific cases. extra []indexerTestCase } type indexValue struct { source interface{} expected []byte // expectedIndexMissing indicates that this test case should not produce an // expected value. The indexer should report a required value was missing. // This field is only relevant for the writeIndex. expectedIndexMissing bool } type indexValueMulti struct { source interface{} expected [][]byte } func TestNewDBSchema_Indexers(t *testing.T) { schema := newDBSchema() require.NoError(t, schema.Validate()) var testcases = map[string]func() map[string]indexerTestCase{ // acl tableACLBindingRules: testIndexerTableACLBindingRules, tableACLPolicies: testIndexerTableACLPolicies, tableACLRoles: testIndexerTableACLRoles, tableACLTokens: testIndexerTableACLTokens, // catalog tableChecks: testIndexerTableChecks, tableServices: testIndexerTableServices, tableNodes: testIndexerTableNodes, tableCoordinates: testIndexerTableCoordinates, tableMeshTopology: testIndexerTableMeshTopology, tableGatewayServices: testIndexerTableGatewayServices, tableServiceVirtualIPs: testIndexerTableServiceVirtualIPs, tableKindServiceNames: testIndexerTableKindServiceNames, // KV tableKVs: testIndexerTableKVs, tableTombstones: testIndexerTableTombstones, // config tableConfigEntries: testIndexerTableConfigEntries, // peerings tablePeering: testIndexerTablePeering, tablePeeringSecrets: testIndexerTablePeeringSecrets, tablePeeringSecretUUIDs: testIndexerTablePeeringSecretUUIDs, } addEnterpriseIndexerTestCases(testcases) for _, table := range schema.Tables { if testcases[table.Name] == nil { continue } t.Run(table.Name, func(t *testing.T) { tableTCs := testcases[table.Name]() for _, index := range table.Indexes { t.Run(index.Name, func(t *testing.T) { indexer := index.Indexer tc, ok := tableTCs[index.Name] if !ok { t.Skip("TODO: missing test case") } tc.run(t, indexer) }) } }) } } func (tc indexerTestCase) run(t *testing.T, indexer memdb.Indexer) { args := []interface{}{tc.read.source} if s, ok := tc.read.source.([]interface{}); ok { // Indexes using memdb.CompoundIndex must be expanded to multiple args args = s } if tc.read.source != nil { t.Run("readIndex", func(t *testing.T) { actual, err := indexer.FromArgs(args...) require.NoError(t, err) require.Equal(t, tc.read.expected, actual) }) } if i, ok := indexer.(memdb.SingleIndexer); ok { t.Run("writeIndex", func(t *testing.T) { valid, actual, err := i.FromObject(tc.write.source) require.NoError(t, err) if tc.write.expectedIndexMissing { require.False(t, valid, "expected the indexer to produce no index value") } else { require.True(t, valid, "indexer was missing a required value") require.Equal(t, tc.write.expected, actual) } }) } if i, ok := indexer.(memdb.PrefixIndexer); ok { for _, c := range tc.prefix { t.Run("prefixIndex", func(t *testing.T) { actual, err := i.PrefixFromArgs(c.source) require.NoError(t, err) require.Equal(t, c.expected, actual) }) } } sortMultiByteSlice := func(v [][]byte) { sort.Slice(v, func(i, j int) bool { return string(v[i]) < string(v[j]) }) } if i, ok := indexer.(memdb.MultiIndexer); ok { t.Run("writeIndexMulti", func(t *testing.T) { valid, actual, err := i.FromObject(tc.writeMulti.source) require.NoError(t, err) require.True(t, valid) sortMultiByteSlice(actual) sortMultiByteSlice(tc.writeMulti.expected) require.ElementsMatch(t, tc.writeMulti.expected, actual) }) } for _, extra := range tc.extra { t.Run("extra", func(t *testing.T) { extra.run(t, indexer) }) } } func testIndexerTablePeeringSecrets() map[string]indexerTestCase { peerID := "b560e87b-934c-491a-9771-16b9d9ce41f8" encodedPeerID := []byte{0xb5, 0x60, 0xe8, 0x7b, 0x93, 0x4c, 0x49, 0x1a, 0x97, 0x71, 0x16, 0xb9, 0xd9, 0xce, 0x41, 0xf8} obj := &pbpeering.PeeringSecrets{ PeerID: peerID, Establishment: &pbpeering.PeeringSecrets_Establishment{ SecretID: "432feb2f-5476-4ae2-b33c-e43640ca0e86", }, } return map[string]indexerTestCase{ indexID: { read: indexValue{ source: peerID, expected: encodedPeerID, }, write: indexValue{ source: obj, expected: encodedPeerID, }, }, } } func testIndexerTablePeeringSecretUUIDs() map[string]indexerTestCase { secretID := "432feb2f-5476-4ae2-b33c-e43640ca0e86" encodedSecretID := []byte{0x43, 0x2f, 0xeb, 0x2f, 0x54, 0x76, 0x4a, 0xe2, 0xb3, 0x3c, 0xe4, 0x36, 0x40, 0xca, 0xe, 0x86} return map[string]indexerTestCase{ indexID: { read: indexValue{ source: secretID, expected: encodedSecretID, }, write: indexValue{ source: secretID, expected: encodedSecretID, }, }, } }