Files
go-kv/wal/entry_test.go
T
dailz 56bec62a6e fix: add write-stopped test and remove VKValueLogPointer from test logic
- 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)
2026-06-12 14:39:16 +08:00

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")
}
}