Files
Agent-System/internal/app/intentflow/authorize_test.go
weichenandCursor 08aa56b16c feat(intentflow): re-authorize steps before confirm and execute
Confirmation stays a command receipt; FakeActionOwner now fail-closes on revoke and resource version drift.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-16 01:01:39 +00:00

127 lines
4.1 KiB
Go

package intentflow_test
import (
"context"
"errors"
"testing"
"chat-agent-system/internal/app/intentflow"
)
func TestConfirmDeniedDoesNotMutatePlan(t *testing.T) {
p, _, owner := testPlans()
ctx := context.Background()
created, err := p.CreatePlan(ctx, draft("digest-a"))
if err != nil {
t.Fatal(err)
}
owner.Denied = true
_, err = p.DecidePlan(ctx, intentflow.DecisionCommand{
Principal: principal(), PlanID: created.PlanID, ExpectedRevision: created.Revision,
DecisionNonce: "n-deny", DecisionDigest: "d-deny", Kind: intentflow.DecisionConfirm,
})
if !errors.Is(err, intentflow.ErrPlanActionForbidden) {
t.Fatalf("want forbidden, got %v", err)
}
got, err := p.GetPlan(ctx, principal(), created.PlanID)
if err != nil {
t.Fatal(err)
}
if got.Revision != created.Revision || got.Status != intentflow.PlanAwaitingConfirmation {
t.Fatalf("confirm must be side-effect free on deny: %+v", got)
}
}
func TestResourceVersionConflictOnConfirm(t *testing.T) {
p, _, owner := testPlans()
ctx := context.Background()
cmd := draft("digest-ver")
cmd.Steps[1].ExpectedResourceVersion = "v1"
created, err := p.CreatePlan(ctx, cmd)
if err != nil {
t.Fatal(err)
}
owner.RequiredVersion = map[string]string{"task.run": "v2"}
_, err = p.DecidePlan(ctx, intentflow.DecisionCommand{
Principal: principal(), PlanID: created.PlanID, ExpectedRevision: created.Revision,
DecisionNonce: "n-ver", DecisionDigest: "d-ver", Kind: intentflow.DecisionConfirm,
})
if !errors.Is(err, intentflow.ErrPlanActionVersionConflict) {
t.Fatalf("want version conflict, got %v", err)
}
}
func TestRevokeBetweenConfirmAndExecute(t *testing.T) {
p, _, owner := testPlans()
ctx := context.Background()
created, err := p.CreatePlan(ctx, draft("digest-rev"))
if err != nil {
t.Fatal(err)
}
confirmed, err := p.DecidePlan(ctx, intentflow.DecisionCommand{
Principal: principal(), PlanID: created.PlanID, ExpectedRevision: created.Revision,
DecisionNonce: "n-ok", DecisionDigest: "d-ok", Kind: intentflow.DecisionConfirm,
})
if err != nil {
t.Fatal(err)
}
owner.Denied = true
stepID := confirmed.Steps[0].StepID
claimed, err := p.ClaimStep(ctx, "w1", confirmed.PlanID, stepID)
if err != nil {
t.Fatal(err)
}
if _, err := p.ExecuteClaimed(ctx, claimed); !errors.Is(err, intentflow.ErrPlanActionForbidden) {
t.Fatalf("revoked execute must fail closed, got %v", err)
}
}
func TestEditBumpsRevisionAndInvalidatesOldCard(t *testing.T) {
p, _, _ := testPlans()
ctx := context.Background()
created, err := p.CreatePlan(ctx, draft("digest-edit"))
if err != nil {
t.Fatal(err)
}
edited, err := p.DecidePlan(ctx, intentflow.DecisionCommand{
Principal: principal(), PlanID: created.PlanID, ExpectedRevision: created.Revision,
DecisionNonce: "n-edit", DecisionDigest: "d-edit", Kind: intentflow.DecisionEdit,
})
if err != nil {
t.Fatal(err)
}
if edited.Revision <= created.Revision {
t.Fatalf("edit must bump revision, got %d", edited.Revision)
}
if edited.Status != intentflow.PlanAwaitingConfirmation {
t.Fatalf("edit stays awaiting, got %s", edited.Status)
}
_, err = p.DecidePlan(ctx, intentflow.DecisionCommand{
Principal: principal(), PlanID: created.PlanID, ExpectedRevision: created.Revision,
DecisionNonce: "n-old", DecisionDigest: "d-old", Kind: intentflow.DecisionConfirm,
})
if !errors.Is(err, intentflow.ErrPlanStaleRevision) {
t.Fatalf("old card must be stale, got %v", err)
}
}
func TestIllegalPartialConfirmMissingDependency(t *testing.T) {
p, _, _ := testPlans()
ctx := context.Background()
cmd := draft("digest-dep")
cmd.Steps[1].DependsOn = []string{"stp_missing"}
created, err := p.CreatePlan(ctx, cmd)
if err != nil {
t.Fatal(err)
}
writeID := created.Steps[1].StepID
_, err = p.DecidePlan(ctx, intentflow.DecisionCommand{
Principal: principal(), PlanID: created.PlanID, ExpectedRevision: created.Revision,
DecisionNonce: "n-part", DecisionDigest: "d-part", Kind: intentflow.DecisionConfirm,
StepIDs: []string{writeID},
})
if !errors.Is(err, intentflow.ErrPlanDependencyInvalid) {
t.Fatalf("partial without closure must be invalid, got %v", err)
}
}