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