Align the SQLite run store test with honest step failures
Why: the U3B semantics change removed the failed-with-patches promotion; TestEngineRunsOnSQLiteStore still encoded the old behavior and failed under the fixed engine. Evidence: repair receipt success; sqlite store tests pass (verify-u3br2.json). Impact: test-only change. Refs: feature/agent-stack-audit-remediation-b1 U3B repair
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -127,7 +128,7 @@ func TestEngineRunsOnSQLiteStore(t *testing.T) {
|
||||
}
|
||||
}
|
||||
backends := squad.NewBackendResolver()
|
||||
backends.RegisterInstance("script", stubExecutor{result: &squad.ExecResult{Success: false, Logs: "ok", Patches: map[string][]byte{"output.md": []byte(goodReportLite), "notes.md": []byte(goodReportLite), "fields.json": []byte("{}")}}})
|
||||
backends.RegisterInstance("script", stubExecutor{result: &squad.ExecResult{Success: true, Logs: "ok", Patches: map[string][]byte{"output.md": []byte(goodReportLite), "notes.md": []byte(goodReportLite), "fields.json": []byte("{}")}}})
|
||||
blueprints := squad.NewBlueprintRegistry()
|
||||
if err := blueprints.Register(squad.Blueprint{
|
||||
Key: "demo", Title: "入库演示", Match: []string{"入库演示"},
|
||||
@@ -185,4 +186,78 @@ func TestEngineRunsOnSQLiteStore(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestEngineFailedResultWithCompletePatchesStaysFailedOnSQLiteStore:F05/U3B 的
|
||||
// 不变式在 SQLite-store 集成层同样成立——executor 报失败就是失败,patches
|
||||
// (交付物)齐全也不得把运行翻成 completed:文件只是诊断信息,不是权威结果。
|
||||
func TestEngineFailedResultWithCompletePatchesStaysFailedOnSQLiteStore(t *testing.T) {
|
||||
runStore, err := OpenSQLiteStore(filepath.Join(t.TempDir(), "runs.db"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer runStore.Close()
|
||||
dir := t.TempDir()
|
||||
reg := squad.NewRoleRegistry()
|
||||
if err := reg.Register(squad.RoleSpec{Role: "researcher", Backend: "script"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
backends := squad.NewBackendResolver()
|
||||
// 失败结果但携带全套交付物:旧语义会把它提升为成功,U3B 起必须保持失败。
|
||||
backends.RegisterInstance("script", stubExecutor{result: &squad.ExecResult{
|
||||
Success: false, Logs: "stub-refused",
|
||||
Patches: map[string][]byte{
|
||||
"output.md": []byte(goodReportLite),
|
||||
"notes.md": []byte(goodReportLite),
|
||||
"fields.json": []byte("{}"),
|
||||
},
|
||||
}})
|
||||
blueprints := squad.NewBlueprintRegistry()
|
||||
if err := blueprints.Register(squad.Blueprint{
|
||||
Key: "faildemo", Title: "失败演示", Match: []string{"失败演示"},
|
||||
Steps: []squad.StepSpec{{ID: "s1", Role: "researcher", Title: "调研", Targets: []string{"output.md"}}},
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
eng, err := squad.NewEngine(squad.Options{
|
||||
DataDir: dir, Registry: reg, Blueprints: blueprints,
|
||||
Planner: squad.NewRulePlanner(),
|
||||
Backends: backends, Sink: squad.NopSink{},
|
||||
Store: runStore,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res, err := eng.Run(context.Background(), squad.Goal{Text: "做一个失败演示", HitlMode: squad.HitlAuto}, squad.RunOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("run: %v", err)
|
||||
}
|
||||
if res.Status == squad.RunCompleted {
|
||||
t.Fatalf("failed executor result must not complete (patches are diagnostic only)")
|
||||
}
|
||||
if res.Status != squad.RunFailed {
|
||||
t.Fatalf("status = %s, want failed", res.Status)
|
||||
}
|
||||
if res.Plan == nil || res.Plan.Steps[0].Status != "failed" {
|
||||
t.Fatalf("s1 should be failed, plan = %+v", res.Plan)
|
||||
}
|
||||
if !strings.Contains(res.Error, "stub-refused") {
|
||||
t.Fatalf("failure reason not preserved: %q", res.Error)
|
||||
}
|
||||
// 同一断言穿过 Store 端口:SQLite 里落盘的也是失败,而非 completed。
|
||||
st, err := runStore.LoadRun(res.WorkDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if st.Plan.Workflow.Status != "failed" {
|
||||
t.Fatalf("persisted workflow status = %s, want failed", st.Plan.Workflow.Status)
|
||||
}
|
||||
if got := st.Plan.Workflow.Steps[0]; got.Status != "failed" || !strings.Contains(got.Error, "stub-refused") {
|
||||
t.Fatalf("persisted step = %+v", got)
|
||||
}
|
||||
// patches 确已物化(文件齐全),依然不翻转状态:权威是执行结果,不是文件。
|
||||
sub := filepath.FromSlash(st.Plan.Workflow.Steps[0].SubWorkspace)
|
||||
if _, err := os.Stat(filepath.Join(res.WorkDir, sub, "output.md")); err != nil {
|
||||
t.Fatalf("diagnostic patch should still be materialized: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
const goodReportLite = "# 测试交付物\n\n## 概述\n\n这是由脚本后端生成的合格交付物,长度超过五十个字符以满足最小质检要求。\n"
|
||||
|
||||
Reference in New Issue
Block a user