Files

176 lines
5.3 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package indexer
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/luxfi/database/memdb"
"github.com/luxfi/ids"
"github.com/luxfi/log"
"github.com/luxfi/math/set"
"github.com/luxfi/runtime"
"github.com/luxfi/timer/mockable"
"github.com/luxfi/utils"
)
// testRuntime creates a minimal runtime.Runtime for testing
func testRuntime(chainID ids.ID) *runtime.Runtime {
return &runtime.Runtime{
NetworkID: 1,
ChainID: chainID,
NodeID: ids.GenerateTestNodeID(),
}
}
func TestIndex(t *testing.T) {
// Setup
pageSize := uint64(64)
require := require.New(t)
baseDB := memdb.New()
// Use a test chain ID
testChainID := ids.GenerateTestID()
rt := testRuntime(testChainID)
idx, err := newIndex(baseDB, log.NoLog{}, &mockable.Clock{})
require.NoError(err)
// Populate "containers" with random IDs/bytes
containers := map[ids.ID][]byte{}
for i := uint64(0); i < 2*pageSize; i++ {
containers[ids.GenerateTestID()] = utils.RandomBytes(32)
}
// Accept each container and after each, make assertions
i := uint64(0)
for containerID, containerBytes := range containers {
require.NoError(idx.Accept(rt, containerID, containerBytes))
lastAcceptedIndex, ok := idx.lastAcceptedIndex()
require.True(ok)
require.Equal(i, lastAcceptedIndex)
require.Equal(i+1, idx.nextAcceptedIndex)
gotContainer, err := idx.GetContainerByID(containerID)
require.NoError(err)
require.Equal(containerBytes, gotContainer.Bytes)
gotIndex, err := idx.GetIndex(containerID)
require.NoError(err)
require.Equal(i, gotIndex)
gotContainer, err = idx.GetContainerByIndex(i)
require.NoError(err)
require.Equal(containerBytes, gotContainer.Bytes)
gotContainer, err = idx.GetLastAccepted()
require.NoError(err)
require.Equal(containerBytes, gotContainer.Bytes)
containers, err := idx.GetContainerRange(i, 1)
require.NoError(err)
require.Len(containers, 1)
require.Equal(containerBytes, containers[0].Bytes)
containers, err = idx.GetContainerRange(i, 2)
require.NoError(err)
require.Len(containers, 1)
require.Equal(containerBytes, containers[0].Bytes)
i++
}
// Create a new index with the same database and ensure contents still there
require.NoError(idx.vDB.Commit())
// Create a new index by directly using the same base database
// Don't close the old index to avoid closing the baseDB
idx, err = newIndex(baseDB, log.NoLog{}, &mockable.Clock{})
require.NoError(err)
// Get all of the containers
containersList, err := idx.GetContainerRange(0, pageSize)
require.NoError(err)
require.Len(containersList, int(pageSize))
containersList2, err := idx.GetContainerRange(pageSize, pageSize)
require.NoError(err)
require.Len(containersList2, int(pageSize))
containersList = append(containersList, containersList2...)
// Ensure that the data is correct
lastTimestamp := int64(0)
sawContainers := make(set.Set[ids.ID])
for _, container := range containersList {
require.False(sawContainers.Contains(container.ID)) // Should only see this container once
require.Contains(containers, container.ID)
require.Equal(containers[container.ID], container.Bytes)
// Timestamps should be non-decreasing
require.GreaterOrEqual(container.Timestamp, lastTimestamp)
lastTimestamp = container.Timestamp
sawContainers.Add(container.ID)
}
}
func TestIndexGetContainerByRangeMaxPageSize(t *testing.T) {
// Setup
require := require.New(t)
db := memdb.New()
// Use a test chain ID
testChainID := ids.GenerateTestID()
rt := testRuntime(testChainID)
idx, err := newIndex(db, log.NoLog{}, &mockable.Clock{})
require.NoError(err)
// Insert [MaxFetchedByRange] + 1 containers
for i := uint64(0); i < MaxFetchedByRange+1; i++ {
require.NoError(idx.Accept(rt, ids.GenerateTestID(), utils.RandomBytes(32)))
}
// Page size too large
_, err = idx.GetContainerRange(0, MaxFetchedByRange+1)
require.ErrorIs(err, errNumToFetchInvalid)
// Make sure data is right
containers, err := idx.GetContainerRange(0, MaxFetchedByRange)
require.NoError(err)
require.Len(containers, MaxFetchedByRange)
containers2, err := idx.GetContainerRange(1, MaxFetchedByRange)
require.NoError(err)
require.Len(containers2, MaxFetchedByRange)
require.Equal(containers[1], containers2[0])
require.Equal(containers[MaxFetchedByRange-1], containers2[MaxFetchedByRange-2])
// Should have last 2 elements
containers, err = idx.GetContainerRange(MaxFetchedByRange-1, MaxFetchedByRange)
require.NoError(err)
require.Len(containers, 2)
require.Equal(containers[1], containers2[MaxFetchedByRange-1])
require.Equal(containers[0], containers2[MaxFetchedByRange-2])
}
func TestDontIndexSameContainerTwice(t *testing.T) {
// Setup
require := require.New(t)
db := memdb.New()
// Use a test chain ID
testChainID := ids.GenerateTestID()
rt := testRuntime(testChainID)
idx, err := newIndex(db, log.NoLog{}, &mockable.Clock{})
require.NoError(err)
// Accept the same container twice
containerID := ids.GenerateTestID()
require.NoError(idx.Accept(rt, containerID, []byte{1, 2, 3}))
require.NoError(idx.Accept(rt, containerID, []byte{4, 5, 6}))
_, err = idx.GetContainerByIndex(1)
require.ErrorIs(err, errNoContainerAtIndex)
gotContainer, err := idx.GetContainerByID(containerID)
require.NoError(err)
require.Equal([]byte{1, 2, 3}, gotContainer.Bytes)
}