Files
Agent-System-Backend/internal/cmdbus/pending_store_test.go
T
weichen 4030449f4b Report honest receipts when handlers err after side effects
Why: F10 - any handler error became a no-side-effect rejected receipt
and an audit-store failure dropped the generated operation ID, losing
the reconciliation handle and inviting unsafe retries.
Evidence: cmdbus tests pass (verify-u7b.json); post-execution-unknown,
audit-failure-keeps-ID and confirmation-idempotency cases added.
Impact: unknown receipts now carry the trusted operation ID via the
U7A constructor; rejected stays ID-free; no auto-replay anywhere.
Refs: feature/agent-stack-audit-remediation-b1 U7B
2026-10-21 16:55:01 +00:00

68 lines
2.5 KiB
Go

package cmdbus
import (
"context"
"errors"
"path/filepath"
"strings"
"testing"
agentcommand "agent-command"
"agent-system-backend/internal/store"
)
func TestPendingConfirmRestoredAfterRestart(t *testing.T) {
st, err := store.OpenSQLite(filepath.Join(t.TempDir(), "c.db"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { st.Close() })
cards := &memCards{}
b := newTestBus(&memAudit{}, cards)
b.SetPendingStore(st)
resp := b.Dispatch(context.Background(), Request{
ID: "demo.risky", Params: map[string]any{"target": "prod"}, Entrance: "button", ConversationID: "cnv_1", Owner: "acme",
})
if resp.Card == nil {
t.Fatal("expected confirm card")
}
// 模拟重启:丢掉进程内 map,只留存储。
b.pending = map[string]pending{}
again := b.Confirm("acme", resp.Card.ID, "confirm")
if again.Receipt.State != agentcommand.StateCommitted {
t.Fatalf("restored confirm: %+v", again.Receipt)
}
}
// failingPendingStore 是"确认卡已创建、挂起态写不进去"的替身:受理已经发生
// (卡在收件箱里),只是结果不可知。
type failingPendingStore struct{ err error }
func (p *failingPendingStore) SavePendingConfirm(string, string, string) error { return p.err }
func (p *failingPendingStore) TakePendingConfirm(string) (string, string, bool, error) {
return "", "", false, p.err
}
// TestConfirmPendingWriteFailureKeepsCardOperationID:挂起态落盘失败不得吞掉操作
// 标识——卡 ID 就是本次受理的句柄,unknown 必须带出去(否则受理事实无凭证)。
func TestConfirmPendingWriteFailureKeepsCardOperationID(t *testing.T) {
audit, cards := &memAudit{}, &memCards{}
b := newTestBus(audit, cards)
b.SetPendingStore(&failingPendingStore{err: errors.New("pending store down")})
resp := b.Dispatch(context.Background(), Request{
ID: "demo.risky", Params: map[string]any{"target": "prod"}, ConversationID: "cnv_1", Owner: "alice",
})
if resp.Receipt.State != agentcommand.StateUnknown || !strings.Contains(resp.Receipt.Reason, "confirm pending") {
t.Fatalf("pending write failure: %+v", resp.Receipt)
}
if resp.Receipt.OperationID != "card_x1" || resp.Receipt.Validate() != nil {
t.Fatalf("pending write failure dropped the card operation handle: %+v", resp.Receipt)
}
if len(cards.created) != 1 || len(b.pending) != 0 {
t.Fatalf("card created=%d pending=%d", len(cards.created), len(b.pending))
}
if len(audit.logs) != 1 || audit.logs[0].State != agentcommand.StateUnknown || audit.logs[0].OperationID != "card_x1" {
t.Fatalf("audit envelope: %+v", audit.logs)
}
}