agent/pkg/agentic/plan_retention_test.go
Virgil 09aa19afde feat(agentic): archive stale completed plans
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-02 02:24:40 +00:00

218 lines
5.7 KiB
Go

// SPDX-License-Identifier: EUPL-1.2
package agentic
import (
"context"
"testing"
"time"
core "dappco.re/go/core"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPlanRetention_PlanCleanup_Good_DeletesExpiredArchivedPlans(t *testing.T) {
dir := t.TempDir()
t.Setenv("CORE_WORKSPACE", dir)
s := newTestPrep(t)
oldPlan := &Plan{
ID: "old-plan-abc123",
Title: "Old Plan",
Status: "archived",
Objective: "Delete me",
ArchivedAt: time.Now().AddDate(0, 0, -100),
}
recentPlan := &Plan{
ID: "recent-plan-abc123",
Title: "Recent Plan",
Status: "archived",
Objective: "Keep me",
ArchivedAt: time.Now().AddDate(0, 0, -10),
}
activePlan := &Plan{
ID: "active-plan-abc123",
Title: "Active Plan",
Status: "ready",
Objective: "Keep me too",
}
_, err := writePlan(PlansRoot(), oldPlan)
require.NoError(t, err)
_, err = writePlan(PlansRoot(), recentPlan)
require.NoError(t, err)
_, err = writePlan(PlansRoot(), activePlan)
require.NoError(t, err)
result := s.planCleanup(core.NewOptions(core.Option{Key: "days", Value: 90}))
require.True(t, result.OK)
output, ok := result.Value.(PlanCleanupOutput)
require.True(t, ok)
assert.True(t, output.Success)
assert.Equal(t, 1, output.Deleted)
assert.Equal(t, 1, output.Matched)
assert.False(t, fs.Exists(core.JoinPath(PlansRoot(), "old-plan-abc123.json")))
assert.True(t, fs.Exists(core.JoinPath(PlansRoot(), "recent-plan-abc123.json")))
assert.True(t, fs.Exists(core.JoinPath(PlansRoot(), "active-plan-abc123.json")))
}
func TestPlanRetention_PlanCleanup_Good_ArchivesExpiredCompletedPlans(t *testing.T) {
dir := t.TempDir()
t.Setenv("CORE_WORKSPACE", dir)
s := newTestPrep(t)
plan := &Plan{
ID: "completed-plan-abc123",
Title: "Completed Plan",
Status: "approved",
Objective: "Archive me",
UpdatedAt: time.Now().AddDate(0, 0, -100),
}
_, err := writePlan(PlansRoot(), plan)
require.NoError(t, err)
result := s.planCleanup(core.NewOptions(core.Option{Key: "days", Value: 90}))
require.True(t, result.OK)
output, ok := result.Value.(PlanCleanupOutput)
require.True(t, ok)
assert.True(t, output.Success)
assert.Equal(t, 1, output.Archived)
assert.Equal(t, 0, output.Deleted)
assert.Equal(t, 1, output.Matched)
updated, err := readPlan(PlansRoot(), plan.ID)
require.NoError(t, err)
assert.Equal(t, "archived", updated.Status)
assert.False(t, updated.ArchivedAt.IsZero())
assert.True(t, fs.Exists(core.JoinPath(PlansRoot(), "completed-plan-abc123.json")))
}
func TestPlanRetention_PlanCleanup_Bad_DryRunKeepsFiles(t *testing.T) {
dir := t.TempDir()
t.Setenv("CORE_WORKSPACE", dir)
s := newTestPrep(t)
plan := &Plan{
ID: "dry-run-plan-abc123",
Title: "Dry Run Plan",
Status: "archived",
Objective: "Keep me for now",
ArchivedAt: time.Now().AddDate(0, 0, -100),
}
_, err := writePlan(PlansRoot(), plan)
require.NoError(t, err)
result := s.planCleanup(core.NewOptions(
core.Option{Key: "days", Value: 90},
core.Option{Key: "dry-run", Value: true},
))
require.True(t, result.OK)
output, ok := result.Value.(PlanCleanupOutput)
require.True(t, ok)
assert.True(t, output.Success)
assert.True(t, output.DryRun)
assert.Equal(t, 1, output.Matched)
assert.Equal(t, 0, output.Deleted)
assert.True(t, fs.Exists(core.JoinPath(PlansRoot(), "dry-run-plan-abc123.json")))
}
func TestPlanRetention_PlanCleanup_Ugly_DisabledCleanupKeepsFiles(t *testing.T) {
dir := t.TempDir()
t.Setenv("CORE_WORKSPACE", dir)
s := newTestPrep(t)
plan := &Plan{
ID: "disabled-plan-abc123",
Title: "Disabled Plan",
Status: "archived",
Objective: "Should remain",
ArchivedAt: time.Now().AddDate(0, 0, -100),
}
_, err := writePlan(PlansRoot(), plan)
require.NoError(t, err)
result := s.planCleanup(core.NewOptions(core.Option{Key: "days", Value: 0}))
require.True(t, result.OK)
output, ok := result.Value.(PlanCleanupOutput)
require.True(t, ok)
assert.True(t, output.Success)
assert.True(t, output.Disabled)
assert.Equal(t, 0, output.Deleted)
assert.True(t, fs.Exists(core.JoinPath(PlansRoot(), "disabled-plan-abc123.json")))
}
func TestPlanRetention_PlanArchivedAt_Good_FallsBackToFileModifiedTime(t *testing.T) {
dir := t.TempDir()
t.Setenv("CORE_WORKSPACE", dir)
path := core.JoinPath(PlansRoot(), "fallback-plan-abc123.json")
require.True(t, fs.Write(path, `{"id":"fallback-plan-abc123","title":"Fallback","status":"archived","objective":"Fallback"}`).OK)
stat := fs.Stat(path)
require.True(t, stat.OK)
plan := &Plan{
ID: "fallback-plan-abc123",
Title: "Fallback",
Status: "archived",
Objective: "Fallback",
}
archivedAt := planArchivedAt(path, plan)
assert.False(t, archivedAt.IsZero())
_, ok := stat.Value.(interface{ ModTime() time.Time })
assert.True(t, ok)
}
func TestPlanRetention_RunPlanCleanupLoop_Good_DeletesExpiredPlans(t *testing.T) {
dir := t.TempDir()
t.Setenv("CORE_WORKSPACE", dir)
s := newTestPrep(t)
plan := &Plan{
ID: "scheduled-plan-abc123",
Title: "Scheduled Plan",
Status: "archived",
Objective: "Remove me on the next retention pass",
ArchivedAt: time.Now().AddDate(0, 0, -100),
}
_, err := writePlan(PlansRoot(), plan)
require.NoError(t, err)
ctx, cancel := context.WithCancel(context.Background())
done := make(chan struct{})
go func() {
s.runPlanCleanupLoop(ctx, time.Millisecond)
close(done)
}()
require.Eventually(t, func() bool {
return !fs.Exists(core.JoinPath(PlansRoot(), "scheduled-plan-abc123.json"))
}, time.Second, 5*time.Millisecond)
cancel()
require.Eventually(t, func() bool {
select {
case <-done:
return true
default:
return false
}
}, time.Second, 5*time.Millisecond)
}