- Add TestWriteStoppedAfterIOError verifying ErrCommitUnknown → write-stopped transition after I/O failure (DoD requirement) - Replace VKValueLogPointer with VKInline in segment_writer_test.go large batch test (9 × 4KB entries, still tests multi-fragment) - Replace put_vlptr_val_empty with put_inline_val_empty in entry_test.go - VKValueLogPointer remains only in constants_test.go (enum value check)
115 lines
3.4 KiB
Go
115 lines
3.4 KiB
Go
package wal
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"testing"
|
|
)
|
|
|
|
func TestEntryRoundtrip(t *testing.T) {
|
|
maxKey := bytes.Repeat([]byte("k"), int(MaxWalKeyBytes))
|
|
|
|
cases := []struct {
|
|
name string
|
|
e *WalEntry
|
|
}{
|
|
{"put_inline", &WalEntry{OpPut, VKInline, []byte("key1"), []byte("val1")}},
|
|
{"put_inline_empty_value", &WalEntry{OpPut, VKInline, []byte("key2"), []byte{}}},
|
|
{"delete", &WalEntry{OpDelete, VKNone, []byte("key3"), nil}},
|
|
{"put_max_key", &WalEntry{OpPut, VKInline, maxKey, []byte("v")}},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
encoded, err := EncodeEntry(tc.e)
|
|
if err != nil {
|
|
t.Fatalf("encode: %v", err)
|
|
}
|
|
|
|
got, consumed, err := DecodeEntry(encoded)
|
|
if err != nil {
|
|
t.Fatalf("decode: %v", err)
|
|
}
|
|
if consumed != len(encoded) {
|
|
t.Fatalf("consumed %d != encoded len %d", consumed, len(encoded))
|
|
}
|
|
|
|
if got.OpType != tc.e.OpType {
|
|
t.Errorf("OpType: got %d, want %d", got.OpType, tc.e.OpType)
|
|
}
|
|
if got.ValueKind != tc.e.ValueKind {
|
|
t.Errorf("ValueKind: got %d, want %d", got.ValueKind, tc.e.ValueKind)
|
|
}
|
|
if !bytes.Equal(got.Key, tc.e.Key) {
|
|
t.Errorf("Key: got %q, want %q", got.Key, tc.e.Key)
|
|
}
|
|
if !bytes.Equal(got.Value, tc.e.Value) {
|
|
t.Errorf("Value: got %q, want %q", got.Value, tc.e.Value)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestEntryValidation(t *testing.T) {
|
|
bigKey := bytes.Repeat([]byte("k"), int(MaxWalKeyBytes)+1)
|
|
bigVal := bytes.Repeat([]byte("v"), int(MaxWalInlineValueBytes)+1)
|
|
|
|
cases := []struct {
|
|
name string
|
|
e *WalEntry
|
|
wantErr bool
|
|
}{
|
|
{"op_invalid", &WalEntry{OpInvalid, VKNone, []byte("k"), nil}, true},
|
|
{"put_vk_none", &WalEntry{OpPut, VKNone, []byte("k"), nil}, true},
|
|
{"delete_vk_inline", &WalEntry{OpDelete, VKInline, []byte("k"), nil}, true},
|
|
{"key_empty", &WalEntry{OpPut, VKInline, []byte{}, []byte("v")}, true},
|
|
{"key_too_big", &WalEntry{OpPut, VKInline, bigKey, []byte("v")}, true},
|
|
{"put_inline_val_too_big", &WalEntry{OpPut, VKInline, []byte("k"), bigVal}, true},
|
|
{"put_inline_val_empty", &WalEntry{OpPut, VKInline, []byte("k"), []byte{}}, false},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
_, err := EncodeEntry(tc.e)
|
|
if (err != nil) != tc.wantErr {
|
|
t.Errorf("EncodeEntry() error = %v, wantErr %v", err, tc.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestEntryDecodeTruncated(t *testing.T) {
|
|
// Build a valid encoded entry, then truncate mid-varint.
|
|
e := &WalEntry{OpPut, VKInline, []byte("key1"), []byte("value1")}
|
|
full, err := EncodeEntry(e)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Truncate to just 1 byte — not enough for header.
|
|
_, _, err = DecodeEntry(full[:1])
|
|
if err == nil {
|
|
t.Error("expected error for 1-byte data")
|
|
}
|
|
|
|
// Build data with an incomplete varint: opType + valueKind + start of varint (0xFF means more bytes follow).
|
|
trunc := []byte{OpPut, VKInline, 0xFF}
|
|
_, _, err = DecodeEntry(trunc)
|
|
if err == nil {
|
|
t.Error("expected error for truncated varint")
|
|
}
|
|
|
|
// Also test: varint specifies more bytes than available.
|
|
// Encode a large key length varint but don't provide the key bytes.
|
|
varintBuf := make([]byte, binary.MaxVarintLen64)
|
|
n := binary.PutUvarint(varintBuf, 1000) // keyLen = 1000
|
|
data := []byte{OpPut, VKInline}
|
|
data = append(data, varintBuf[:n]...)
|
|
data = append(data, varintBuf[:n]...) // valLen varint (also 1000)
|
|
// Don't append any key/value bytes.
|
|
_, _, err = DecodeEntry(data)
|
|
if err == nil {
|
|
t.Error("expected error for missing key/value bytes")
|
|
}
|
|
}
|