Add daemon-attached goal recovery operator
Resume goals after transient API failures with persisted exponential backoff while respecting pauses and interruptions.
This commit is contained in:
parent
a4522e5ed6
commit
e4b84d9802
1 changed files with 1595 additions and 0 deletions
63
codex-keep/store_test.go
Normal file
63
codex-keep/store_test.go
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func closeStore(t *testing.T, s *store) {
|
||||
t.Helper()
|
||||
if err := s.close(); err != nil && !errors.Is(err, os.ErrClosed) {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckpoint(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "state.json")
|
||||
s, err := openStore(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
w := failedWatch()
|
||||
w.Due = time.Now().Add(time.Minute).UTC()
|
||||
w.Failures = 3
|
||||
s.Watches["thread-1"] = w
|
||||
if err := s.save(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if other, err := openStore(path); err == nil {
|
||||
closeStore(t, other)
|
||||
t.Fatal("two operators acquired one checkpoint")
|
||||
}
|
||||
closeStore(t, s)
|
||||
restored, err := openStore(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer closeStore(t, restored)
|
||||
r := restored.watch("thread-1")
|
||||
if !r.eligible() || !r.Due.Equal(w.Due) || r.Failures != 3 {
|
||||
t.Fatalf("lost recovery: %+v", r)
|
||||
}
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if info.Mode().Perm() != 0600 {
|
||||
t.Fatal(info.Mode())
|
||||
}
|
||||
}
|
||||
|
||||
func TestBadCheckpoint(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "state.json")
|
||||
if err := os.WriteFile(path, []byte(`{"watches":`), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s, err := openStore(path); err == nil {
|
||||
closeStore(t, s)
|
||||
t.Fatal("ignored corrupt state")
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue