more checkout
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
package idempotency
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPutGet(t *testing.T) {
|
||||
s, err := Open(filepath.Join(t.TempDir(), "idem.log"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
if _, ok := s.Get("a"); ok {
|
||||
t.Fatal("unexpected hit on empty store")
|
||||
}
|
||||
if err := s.Put("a", 42); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if id, ok := s.Get("a"); !ok || id != 42 {
|
||||
t.Fatalf("Get(a) = %d,%v; want 42,true", id, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFirstWriterWins(t *testing.T) {
|
||||
s, _ := Open(filepath.Join(t.TempDir(), "idem.log"))
|
||||
defer s.Close()
|
||||
_ = s.Put("a", 1)
|
||||
_ = s.Put("a", 2) // ignored
|
||||
if id, _ := s.Get("a"); id != 1 {
|
||||
t.Fatalf("got %d, want first value 1", id)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDurableAcrossReopen(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "idem.log")
|
||||
s, _ := Open(path)
|
||||
_ = s.Put("checkout-x", 1001)
|
||||
_ = s.Put("checkout-y", 1002)
|
||||
_ = s.Close()
|
||||
|
||||
// A fresh process (the restart case the in-memory map failed) must still see
|
||||
// the recorded keys — this is the duplicate-order bug the durable store fixes.
|
||||
s2, err := Open(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer s2.Close()
|
||||
if id, ok := s2.Get("checkout-x"); !ok || id != 1001 {
|
||||
t.Fatalf("after reopen Get(checkout-x) = %d,%v; want 1001,true", id, ok)
|
||||
}
|
||||
if id, ok := s2.Get("checkout-y"); !ok || id != 1002 {
|
||||
t.Fatalf("after reopen Get(checkout-y) = %d,%v; want 1002,true", id, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCorruptTrailingLineSkipped(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "idem.log")
|
||||
s, _ := Open(path)
|
||||
_ = s.Put("good", 7)
|
||||
_ = s.Close()
|
||||
|
||||
// Simulate a torn write from a crash mid-append.
|
||||
f, _ := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0o644)
|
||||
_, _ = f.WriteString(`{"k":"bad","id":`)
|
||||
_ = f.Close()
|
||||
|
||||
s2, err := Open(path)
|
||||
if err != nil {
|
||||
t.Fatalf("open with corrupt tail: %v", err)
|
||||
}
|
||||
defer s2.Close()
|
||||
if id, ok := s2.Get("good"); !ok || id != 7 {
|
||||
t.Fatalf("good key lost after corrupt tail: %d,%v", id, ok)
|
||||
}
|
||||
if _, ok := s2.Get("bad"); ok {
|
||||
t.Fatal("corrupt line should not have loaded")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLockSerialisesKey(t *testing.T) {
|
||||
s, _ := Open(filepath.Join(t.TempDir(), "idem.log"))
|
||||
defer s.Close()
|
||||
|
||||
// Two goroutines contend on the same key; the lock must serialise the
|
||||
// check→create→record so exactly one writes and both agree on the value.
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 2; i++ {
|
||||
wg.Add(1)
|
||||
go func(v uint64) {
|
||||
defer wg.Done()
|
||||
unlock := s.Lock("k")
|
||||
defer unlock()
|
||||
if _, ok := s.Get("k"); !ok {
|
||||
_ = s.Put("k", v)
|
||||
}
|
||||
}(uint64(i + 1))
|
||||
}
|
||||
wg.Wait()
|
||||
if _, ok := s.Get("k"); !ok {
|
||||
t.Fatal("expected a value recorded under contention")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user