127 lines
2.9 KiB
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)
|
|
}
|