85 lines
2.2 KiB
Go
85 lines
2.2 KiB
Go
package agent
|
|
|
|
import (
|
|
"bytes"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func testSpool(t *testing.T) *Spool {
|
|
t.Helper()
|
|
now := time.Date(2026, 9, 18, 0, 0, 0, 0, time.UTC)
|
|
s, err := NewSpool(t.TempDir(), func() time.Time { return now })
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return s
|
|
}
|
|
|
|
func TestSpoolAtomicStateAndBootUnknown(t *testing.T) {
|
|
s := testSpool(t)
|
|
if _, err := s.Start("exec-1", 1, "session-1"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := s.Update("exec-1", "running", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
report, err := s.MarkUnknownOnBoot()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(report.Unknown) != 1 || report.Unknown[0] != "exec-1" {
|
|
t.Fatalf("recovery report = %+v", report)
|
|
}
|
|
state, err := s.Load("exec-1")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if state.Status != "unknown" || !state.Unknown {
|
|
t.Fatalf("state = %+v", state)
|
|
}
|
|
}
|
|
|
|
func TestSpoolQuarantinesCorruptStateAndNeverDeletesIt(t *testing.T) {
|
|
s := testSpool(t)
|
|
if err := os.MkdirAll(filepath.Join(s.Root(), "broken"), 0o700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(s.Root(), "broken", "state.json"), []byte("{"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
report, err := s.MarkUnknownOnBoot()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(report.Quarantined) != 1 || len(report.Unknown) != 0 {
|
|
t.Fatalf("recovery report = %+v", report)
|
|
}
|
|
matches, err := filepath.Glob(filepath.Join(s.Root(), "broken", "state.json.corrupt-*"))
|
|
if err != nil || len(matches) != 1 {
|
|
t.Fatalf("quarantine files = %v, err=%v", matches, err)
|
|
}
|
|
}
|
|
|
|
func TestSpoolTranscriptAndAssetAreDurableFiles(t *testing.T) {
|
|
s := testSpool(t)
|
|
if _, err := s.Start("exec-2", 1, "session-2"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.AppendTranscript("exec-2", []byte(`{"text":"hello","final":true}`)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
path, n, hash, err := s.WriteAsset("exec-2", "recording.pcm", bytes.NewReader([]byte("pcm")))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n != 3 || hash == "" || path == "" {
|
|
t.Fatalf("asset result path=%q bytes=%d hash=%q", path, n, hash)
|
|
}
|
|
if _, err := os.Stat(filepath.Join(s.Root(), "exec-2", "assets", "recording.pcm.part")); !os.IsNotExist(err) {
|
|
t.Fatalf("temporary asset still exists: %v", err)
|
|
}
|
|
}
|