dc2d4ec425
executus CI / test (push) Failing after 1m6s
Completes the P4 battery set (squashed onto main from phase-4c-batteries). - checkpoint/: run.Checkpointer durable-resume (CheckpointStore + throttled handle + Memory). - schedule/: generic cron Runner (Tick/Loop; no cron grammar of its own). - critic/: two-tier timeout watchdog (run.Critic) + Escalator policy seam + ExtendOnce default. Includes the verified gadfly #6 fixes (ExtendOnce per-run, Kill-sticky, watch panic-recovery; checkpoint throttle-after-success; schedule Next-before-Run + nil-guard + Loop recovery). P4 battery set complete: audit, budget, persona, skill, checkpoint, schedule, critic — each nil-safe, each with a default, each core-import-clean. Executor wiring for Critic/Checkpointer remains a P2 follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
65 lines
2.1 KiB
Go
65 lines
2.1 KiB
Go
package checkpoint
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"gitea.stevedudenhoeffer.com/steve/executus/run"
|
|
"gitea.stevedudenhoeffer.com/steve/majordomo/llm"
|
|
)
|
|
|
|
func TestHandleSaveCompleteDelete(t *testing.T) {
|
|
ctx := context.Background()
|
|
mem := NewMemory()
|
|
meta := RunCheckpointMeta{RunID: "r1", AgentID: "a1", CallerID: "c1"}
|
|
cp := New(mem, meta, 0, nil) // throttle 0 = save every call
|
|
|
|
if err := cp.Save(ctx, run.RunCheckpointState{Messages: []llm.Message{{Role: "user"}}, Iteration: 2}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, _ := mem.Load(ctx, "r1")
|
|
if got == nil || got.Iteration != 2 || got.Meta.AgentID != "a1" {
|
|
t.Fatalf("checkpoint not persisted: %+v", got)
|
|
}
|
|
if il, _ := mem.ListInterrupted(ctx); len(il) != 1 {
|
|
t.Errorf("ListInterrupted = %d, want 1 (in-flight)", len(il))
|
|
}
|
|
// Complete clears it (no longer a recovery candidate).
|
|
if err := cp.Complete(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if il, _ := mem.ListInterrupted(ctx); len(il) != 0 {
|
|
t.Errorf("after Complete, ListInterrupted = %d, want 0", len(il))
|
|
}
|
|
}
|
|
|
|
func TestHandleThrottle(t *testing.T) {
|
|
ctx := context.Background()
|
|
mem := NewMemory()
|
|
now := time.Now()
|
|
cp := New(mem, RunCheckpointMeta{RunID: "r"}, time.Minute, func() time.Time { return now })
|
|
|
|
cp.Save(ctx, run.RunCheckpointState{Iteration: 1})
|
|
now = now.Add(10 * time.Second) // within throttle window
|
|
cp.Save(ctx, run.RunCheckpointState{Iteration: 2})
|
|
if got, _ := mem.Load(ctx, "r"); got.Iteration != 1 {
|
|
t.Errorf("throttled save should keep iteration 1, got %d", got.Iteration)
|
|
}
|
|
now = now.Add(time.Minute) // past throttle
|
|
cp.Save(ctx, run.RunCheckpointState{Iteration: 3})
|
|
if got, _ := mem.Load(ctx, "r"); got.Iteration != 3 {
|
|
t.Errorf("post-throttle save should land iteration 3, got %d", got.Iteration)
|
|
}
|
|
}
|
|
|
|
func TestNilStoreNoop(t *testing.T) {
|
|
cp := New(nil, RunCheckpointMeta{RunID: "r"}, 0, nil)
|
|
if err := cp.Save(context.Background(), run.RunCheckpointState{}); err != nil {
|
|
t.Errorf("nil-store Save should be a no-op, got %v", err)
|
|
}
|
|
if err := cp.Complete(context.Background()); err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|