63 lines
1.3 KiB
Go
63 lines
1.3 KiB
Go
|
|
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")
|
||
|
|
}
|
||
|
|
}
|