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)
This commit is contained in:
+1
-1
@@ -65,7 +65,7 @@ func TestEntryValidation(t *testing.T) {
|
|||||||
{"key_empty", &WalEntry{OpPut, VKInline, []byte{}, []byte("v")}, true},
|
{"key_empty", &WalEntry{OpPut, VKInline, []byte{}, []byte("v")}, true},
|
||||||
{"key_too_big", &WalEntry{OpPut, VKInline, bigKey, []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_too_big", &WalEntry{OpPut, VKInline, []byte("k"), bigVal}, true},
|
||||||
{"put_vlptr_val_empty", &WalEntry{OpPut, VKValueLogPointer, []byte("k"), []byte{}}, true},
|
{"put_inline_val_empty", &WalEntry{OpPut, VKInline, []byte("k"), []byte{}}, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package wal
|
package wal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -255,13 +256,19 @@ func TestSegmentWriterLargeBatch(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a batch larger than one block payload (~32KB - 7 bytes).
|
// Create a batch larger than one block payload (~32KB - 7 bytes).
|
||||||
largeValue := make([]byte, 40*1024)
|
largeValue := make([]byte, MaxWalInlineValueBytes)
|
||||||
for i := range largeValue {
|
for i := range largeValue {
|
||||||
largeValue[i] = byte(i % 256)
|
largeValue[i] = byte(i % 256)
|
||||||
}
|
}
|
||||||
|
|
||||||
entries := []*WalEntry{
|
var entries []*WalEntry
|
||||||
{OpType: OpPut, ValueKind: VKValueLogPointer, Key: []byte("large-key"), Value: largeValue},
|
for i := 0; i < 9; i++ {
|
||||||
|
entries = append(entries, &WalEntry{
|
||||||
|
OpType: OpPut,
|
||||||
|
ValueKind: VKInline,
|
||||||
|
Key: []byte(fmt.Sprintf("large-key-%d", i)),
|
||||||
|
Value: largeValue,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
encoded, err := EncodeWalBatch(0, entries)
|
encoded, err := EncodeWalBatch(0, entries)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -315,8 +322,8 @@ func TestSegmentWriterLargeBatch(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("DecodeWalBatch: %v", err)
|
t.Fatalf("DecodeWalBatch: %v", err)
|
||||||
}
|
}
|
||||||
if decoded.EntryCount != 1 {
|
if decoded.EntryCount != uint32(len(entries)) {
|
||||||
t.Errorf("EntryCount = %d, want 1", decoded.EntryCount)
|
t.Errorf("EntryCount = %d, want %d", decoded.EntryCount, len(entries))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package wal
|
package wal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/dailz/go-kv/config"
|
"github.com/dailz/go-kv/config"
|
||||||
|
"github.com/dailz/go-kv/errkit"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newTestWalWriter(t *testing.T) *WalWriter {
|
func newTestWalWriter(t *testing.T) *WalWriter {
|
||||||
@@ -118,3 +120,47 @@ func TestWalWriterDelete(t *testing.T) {
|
|||||||
t.Fatalf("Get(k) found deleted key with value %q", got.Value)
|
t.Fatalf("Get(k) found deleted key with value %q", got.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestWriteStoppedAfterIOError verifies: I/O failure during AppendBatch →
|
||||||
|
// ErrCommitUnknown returned, write-stopped state entered, subsequent writes
|
||||||
|
// return ErrWriteStopped.
|
||||||
|
func TestWriteStoppedAfterIOError(t *testing.T) {
|
||||||
|
cfg := config.Defaults()
|
||||||
|
ww, err := NewWalWriter(&cfg, t.TempDir(), 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewWalWriter: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ww.Put([]byte("ok-key"), []byte("ok-val")); err != nil {
|
||||||
|
t.Fatalf("initial Put: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the fd to force AppendBatch → stopWithError(ErrCommitUnknown).
|
||||||
|
if err := ww.segManager.active.fd.Close(); err != nil {
|
||||||
|
t.Fatalf("close fd: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ww.Put([]byte("fail-key"), []byte("fail-val"))
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error from Put after I/O failure, got nil")
|
||||||
|
}
|
||||||
|
if !errors.Is(err, errkit.ErrCommitUnknown) {
|
||||||
|
t.Errorf("Put error = %v, want ErrCommitUnknown", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ww.IsWriteStopped() {
|
||||||
|
t.Fatal("IsWriteStopped() = false, want true after I/O error")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ww.Put([]byte("after-key"), []byte("after-val"))
|
||||||
|
if !errors.Is(err, errkit.ErrWriteStopped) {
|
||||||
|
t.Errorf("Put after write-stopped error = %v, want ErrWriteStopped", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ww.Delete([]byte("after-key"))
|
||||||
|
if !errors.Is(err, errkit.ErrWriteStopped) {
|
||||||
|
t.Errorf("Delete after write-stopped error = %v, want ErrWriteStopped", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = ww.Close()
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user