Files
Agent-System-Backend/internal/server/human_decision_test.go
weichen 451aa49954 Alias the core package consistently
Why: An interrupted import cleanup removed the cmdbus core import while two human-decision files used an explicit alias.

Evidence: go test ./internal/cmdbus ./internal/server passes.

Impact: The backend worktree is buildable and consistently names the core package without behavior changes.

Refs: W4b human decision verifier
2026-10-15 06:01:32 +00:00

106 lines
3.8 KiB
Go

package server
import (
core "agent-system-core"
"context"
"net/http/httptest"
"testing"
"time"
agentcommand "agent-command"
"agent-system-backend/internal/cmdbus"
"agent-system-backend/internal/store"
"agent-system-backend/pkg/extension"
)
type nopHumanAudit struct{}
func (nopHumanAudit) AppendAudit(string, agentcommand.AuditEnvelope) error { return nil }
func TestResolveHumanDecisionBindsFollowUpAndRetry(t *testing.T) {
st, err := store.OpenSQLite(t.TempDir() + "/decision.db")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { st.Close() })
issued, err := st.IssueHumanDecision(store.HumanDecision{
PluginID: "rdm", Audience: extension.HumanDecisionAudienceStructure,
CommandID: "rdm.structure.decide", HumanActor: "alice", Owner: "alice",
ProjectID: "p1", ProposalID: "prop-1", ProposalRevision: 1,
PayloadHash: "hash-1", Question: "确认结构变更",
}, time.Now().UTC())
if err != nil {
t.Fatal(err)
}
var got *cmdbus.HumanGrant
bus := cmdbus.New(nopHumanAudit{}, nil, nil)
if err := bus.RegisterPluginCommand(agentcommand.CommandSpec{ID: "rdm.structure.decide", Title: "确认结构"},
func(_ context.Context, req cmdbus.Request) (any, error) {
got = req.HumanGrant
if req.Params["confirmed"] != nil {
t.Fatal("follow-up copied a client confirmation flag")
}
return map[string]any{"applied": true}, nil
}); err != nil {
t.Fatal(err)
}
s := &Server{st: st, Bus: bus}
rec := httptest.NewRecorder()
req := httptest.NewRequest("POST", "/api/hitl/"+issued.ID+"/resolve", nil)
req = req.WithContext(context.WithValue(req.Context(), ownerCtxKey{}, "alice"))
handled, err := s.resolveHumanDecision(rec, req, core.HITLResponse{CardID: core.ID(issued.ID), ChoiceID: "accept"})
if err != nil || !handled {
t.Fatalf("resolve handled=%v err=%v body=%s", handled, err, rec.Body.String())
}
if got == nil || got.Actor != "alice" || got.ProofID == "" || got.CardID != issued.ID {
t.Fatalf("follow-up grant = %+v", got)
}
grant, ok := s.sessionHumanGrant("alice", "rdm.structure.decide", map[string]any{
"projectId": "p1", "proposalId": "prop-1", "proposalRevision": float64(1),
"payloadHash": "hash-1", "decision": "accept", "confirmed": true,
})
if !ok || grant.ProofID != got.ProofID {
t.Fatalf("retry grant = %+v ok=%v", grant, ok)
}
if _, ok := s.sessionHumanGrant("bob", "rdm.structure.decide", map[string]any{
"projectId": "p1", "proposalId": "prop-1", "proposalRevision": float64(1),
"payloadHash": "hash-1", "decision": "accept",
}); ok {
t.Fatal("another actor received the grant")
}
}
func TestNaturalLanguageApprovalRequiresOnePendingCard(t *testing.T) {
st, err := store.OpenSQLite(t.TempDir() + "/decision.db")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { st.Close() })
now := time.Now().UTC()
first, err := st.IssueHumanDecision(store.HumanDecision{
PluginID: "rdm", Audience: extension.HumanDecisionAudienceStructure,
CommandID: "rdm.structure.decide", HumanActor: "alice", Owner: "alice",
ProjectID: "p1", ProposalID: "prop-1", ProposalRevision: 1,
PayloadHash: "hash-1", Question: "第一张",
}, now)
if err != nil {
t.Fatal(err)
}
if _, err := st.IssueHumanDecision(store.HumanDecision{
PluginID: "rdm", Audience: extension.HumanDecisionAudienceStructure,
CommandID: "rdm.structure.decide", HumanActor: "alice", Owner: "alice",
ProjectID: "p1", ProposalID: "prop-2", ProposalRevision: 1,
PayloadHash: "hash-2", Question: "第二张",
}, now); err != nil {
t.Fatal(err)
}
s := &Server{st: st}
rec := httptest.NewRecorder()
req := httptest.NewRequest("POST", "/api/hitl/"+first.ID+"/resolve", nil)
req = req.WithContext(context.WithValue(req.Context(), ownerCtxKey{}, "alice"))
handled, err := s.resolveHumanDecision(rec, req, core.HITLResponse{CardID: core.ID(first.ID), FreeText: "批准"})
if !handled || err == nil {
t.Fatal("ambiguous natural-language approval was accepted")
}
}