Files
go-cart-actor/pkg/idempotency/store_test.go
T
mats 9d420c43fa
Build and Publish / BuildAndDeployArm64 (push) Failing after 6s
Build and Publish / BuildAndDeployAmd64 (push) Has been cancelled
more checkout
2026-06-21 23:43:48 +02:00

107 lines
2.6 KiB
Go

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