proglog/internal/log/log_test.go

127 lines
2.9 KiB
Go

package log
import (
"io"
"io/ioutil"
"os"
"testing"
api "github.com/AYM1607/proglog/api/v1"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
)
func TestLog(t *testing.T) {
for scenario, fn := range map[string]func(
t *testing.T, log *Log,
){
"append and rea a record succeeds": testAppendRead,
"offset out of range error": testOutOfRangeErr,
"init with existing segments": testInitExisting,
"reader": testReader,
"truncate": testTruncate,
} {
t.Run(scenario, func(t *testing.T) {
dir, err := ioutil.TempDir("", "log-test")
require.NoError(t, err)
defer os.RemoveAll(dir)
c := Config{}
// This ensures that each segment can only hold one record.
c.Segment.MaxIndexBytes = entWidth
log, err := NewLog(dir, c)
require.NoError(t, err)
fn(t, log)
})
}
}
func testAppendRead(t *testing.T, log *Log) {
apnd := &api.Record{
Value: []byte("hello world"),
}
off, err := log.Append(apnd)
require.NoError(t, err)
require.Equal(t, uint64(0), off)
read, err := log.Read(off)
require.NoError(t, err)
require.Equal(t, apnd.Value, read.Value)
}
func testOutOfRangeErr(t *testing.T, log *Log) {
read, err := log.Read(1)
require.Nil(t, read)
apiErr := err.(api.ErrOffsetOutOfRange)
require.Equal(t, uint64(1), apiErr.Offset)
}
func testInitExisting(t *testing.T, o *Log) {
apnd := &api.Record{
Value: []byte("hello world"),
}
for i := 0; i < 3; i++ {
_, err := o.Append(apnd)
require.NoError(t, err)
}
require.NoError(t, o.Close())
off, err := o.LowestOffset()
require.NoError(t, err)
require.Equal(t, uint64(0), off)
off, err = o.HighestOffset()
require.NoError(t, err)
require.Equal(t, uint64(2), off)
// Create a new log from the directory and config of the old one.
n, err := NewLog(o.Dir, o.Config)
require.NoError(t, err)
off, err = n.LowestOffset()
require.NoError(t, err)
require.Equal(t, uint64(0), off)
off, err = n.HighestOffset()
require.NoError(t, err)
require.Equal(t, uint64(2), off)
}
func testReader(t *testing.T, log *Log) {
apnd := &api.Record{
Value: []byte("hello world"),
}
off, err := log.Append(apnd)
require.NoError(t, err)
require.Equal(t, uint64(0), off)
reader := log.Reader()
b, err := io.ReadAll(reader)
require.NoError(t, err)
read := &api.Record{}
// Store writes the length as a prefix to the binary content so we have to skip it.
err = proto.Unmarshal(b[lenWidth:], read)
require.NoError(t, err)
require.Equal(t, apnd.Value, read.Value)
}
func testTruncate(t *testing.T, log *Log) {
apnd := &api.Record{
Value: []byte("hello world"),
}
// Because of the configured store limit, each segment should only contain a single record.
for i := 0; i < 3; i++ {
_, err := log.Append(apnd)
require.NoError(t, err)
}
err := log.Truncate(1)
require.NoError(t, err)
_, err = log.Read(0)
require.Error(t, err)
}