- go.mod with github.com/dailz/go-kv, Go 1.26.3, testify - config/config.go with WalConfig, Validate() with checked arithmetic - errors.go with sentinel errors (ErrCommitUnknown, ErrWriteStopped, etc.) - wal/constants.go with all WAL format constants and enums - wal/header.go with WAL File Header encode/decode (CRC32 IEEE) - wal/record.go with Physical Record codec, block boundary, SplitIntoRecords - wal/entry.go with WAL Entry codec (varint keys/values, OpType, ValueKind) - wal/sequence.go with SequenceManager (atomic, CAS, overflow-safe) - manifest/manifest.go with MANIFEST stub (Load/Save atomic) - manifest/current.go with CURRENT file (WriteCurrent/ReadCurrent) - Comprehensive tests for all modules - .golangci.yml configuration
88 lines
2.4 KiB
Go
88 lines
2.4 KiB
Go
package wal
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
)
|
|
|
|
func TestHeaderRoundtrip(t *testing.T) {
|
|
orig := &WalFileHeader{
|
|
BlockSize: 32 * 1024, // 32 KB
|
|
SegmentID: 5,
|
|
StartSequence: 1000,
|
|
}
|
|
|
|
encoded := EncodeWalHeader(orig)
|
|
decoded, err := DecodeWalHeader(encoded[:])
|
|
if err != nil {
|
|
t.Fatalf("DecodeWalHeader returned error: %v", err)
|
|
}
|
|
|
|
if decoded.Magic != walMagic {
|
|
t.Errorf("Magic = %x, want %x", decoded.Magic, walMagic)
|
|
}
|
|
if decoded.FormatVersion != walFormatVersion {
|
|
t.Errorf("FormatVersion = %d, want %d", decoded.FormatVersion, walFormatVersion)
|
|
}
|
|
if decoded.HeaderSize != walFileHeaderSize {
|
|
t.Errorf("HeaderSize = %d, want %d", decoded.HeaderSize, walFileHeaderSize)
|
|
}
|
|
if decoded.BlockSize != orig.BlockSize {
|
|
t.Errorf("BlockSize = %d, want %d", decoded.BlockSize, orig.BlockSize)
|
|
}
|
|
if decoded.SegmentID != orig.SegmentID {
|
|
t.Errorf("SegmentID = %d, want %d", decoded.SegmentID, orig.SegmentID)
|
|
}
|
|
if decoded.StartSequence != orig.StartSequence {
|
|
t.Errorf("StartSequence = %d, want %d", decoded.StartSequence, orig.StartSequence)
|
|
}
|
|
if decoded.HeaderCRC != orig.HeaderCRC {
|
|
t.Errorf("HeaderCRC = %x, want %x", decoded.HeaderCRC, orig.HeaderCRC)
|
|
}
|
|
}
|
|
|
|
func TestHeaderCRC(t *testing.T) {
|
|
h := &WalFileHeader{
|
|
BlockSize: 32 * 1024,
|
|
SegmentID: 1,
|
|
StartSequence: 0,
|
|
}
|
|
encoded := EncodeWalHeader(h)
|
|
|
|
// Flip a byte in the magic field (bytes 0-3)
|
|
encoded[0] ^= 0xFF
|
|
|
|
_, err := DecodeWalHeader(encoded[:])
|
|
if !errors.Is(err, errCRCMismatch) && !errors.Is(err, errBadMagic) {
|
|
// Flipping magic may fail on magic check first or CRC check
|
|
// Either way, decoding must fail
|
|
t.Fatalf("expected CRC or magic error, got: %v", err)
|
|
}
|
|
|
|
// Restore magic and flip a byte in the payload instead
|
|
encoded[0] = byte(walMagic & 0xFF)
|
|
encoded[12] ^= 0x01 // flip byte in SegmentID
|
|
|
|
_, err = DecodeWalHeader(encoded[:])
|
|
if !errors.Is(err, errCRCMismatch) {
|
|
t.Fatalf("expected errCRCMismatch, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestHeaderBadMagic(t *testing.T) {
|
|
data := make([]byte, 32)
|
|
// All zeros — magic won't match
|
|
_, err := DecodeWalHeader(data)
|
|
if !errors.Is(err, errBadMagic) {
|
|
t.Fatalf("expected errBadMagic, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestHeaderShortData(t *testing.T) {
|
|
data := make([]byte, 16) // too short
|
|
_, err := DecodeWalHeader(data)
|
|
if !errors.Is(err, errHeaderTooShort) {
|
|
t.Fatalf("expected errHeaderTooShort, got: %v", err)
|
|
}
|
|
}
|