返回 DeepSeek-Reasonix
subagent_store_outcome_test.go
根目录 / internal / agent / subagent_store_outcome_test.go
1 package agent
2
3 import (
4 "strings"
5 "testing"
6
7 "reasonix/internal/provider"
8 )
9
10 func TestSubagentStorePartialOutcomeCanBeReadAndResumed(t *testing.T) {
11 store := NewSubagentStore(t.TempDir())
12 spec := testSubagentSpec(t, "explore")
13 run, err := store.PrepareFresh(spec)
14 if err != nil {
15 t.Fatalf("PrepareFresh: %v", err)
16 }
17 run.Session.Add(provider.Message{Role: provider.RoleUser, Content: "inspect"})
18 run.Session.Add(provider.Message{Role: provider.RoleAssistant, Content: "partial finding"})
19 if err := store.SaveOutcome(run, SubagentOutcome{Ref: run.Ref, Status: SubagentOutcomePartial, FinalAnswer: "partial finding", ErrorCode: "completion_uncertain", Retryable: true}); err != nil {
20 t.Fatalf("SaveOutcome: %v", err)
21 }
22 run.Release()
23 answer, status, err := store.ReadFinalAnswer(run.Ref, spec.ParentSession, spec.WorkspaceRoot)
24 if err != nil || answer != "partial finding" || status != SubagentStatus(SubagentOutcomePartial) {
25 t.Fatalf("ReadFinalAnswer = %q/%q/%v, want partial result", answer, status, err)
26 }
27 meta, err := store.LoadMeta(run.Ref)
28 if err != nil || meta.Status != SubagentFailed || meta.Outcome != string(SubagentOutcomePartial) || !meta.Retryable {
29 t.Fatalf("partial metadata = %+v/%v", meta, err)
30 }
31 continued, err := store.PrepareContinue(run.Ref, spec)
32 if err != nil {
33 t.Fatalf("PrepareContinue partial: %v", err)
34 }
35 meta, err = store.LoadMeta(run.Ref)
36 if err != nil || meta.Status != SubagentRunning || meta.Outcome != "" {
37 t.Fatalf("resumed metadata = %+v/%v, want running without stale outcome", meta, err)
38 }
39 continued.Release()
40 }
41
42 func TestSubagentStoreDuplicateTerminalOutcomeIsIdempotent(t *testing.T) {
43 store := NewSubagentStore(t.TempDir())
44 spec := testSubagentSpec(t, "explore")
45 run, err := store.PrepareFresh(spec)
46 if err != nil {
47 t.Fatalf("PrepareFresh: %v", err)
48 }
49 defer run.Release()
50 answer := "same terminal answer"
51 run.Session.Add(provider.Message{Role: provider.RoleAssistant, Content: answer})
52 outcome := SubagentOutcome{Ref: run.Ref, Status: SubagentOutcomePartial, ErrorCode: "completion_uncertain", Retryable: true}
53 if err := store.SaveOutcome(run, outcome); err != nil {
54 t.Fatalf("first SaveOutcome: %v", err)
55 }
56 if err := store.SaveOutcome(run, outcome); err != nil {
57 t.Fatalf("duplicate SaveOutcome: %v", err)
58 }
59 if err := store.SaveOutcome(run, SubagentOutcome{Ref: run.Ref, Status: SubagentOutcomeFailed, ErrorCode: "provider_error"}); err == nil {
60 t.Fatal("different terminal outcome overwrote a persisted partial outcome")
61 }
62 meta, err := store.LoadMeta(run.Ref)
63 if err != nil || meta.Outcome != string(SubagentOutcomePartial) || meta.Status != SubagentFailed || !meta.Retryable {
64 t.Fatalf("duplicate terminal metadata = %+v/%v", meta, err)
65 }
66 }
67
68 func TestSubagentStoreFailedOutcomeRetainsReadableLastAnswer(t *testing.T) {
69 store := NewSubagentStore(t.TempDir())
70 spec := testSubagentSpec(t, "explore")
71 run, err := store.PrepareFresh(spec)
72 if err != nil {
73 t.Fatalf("PrepareFresh: %v", err)
74 }
75 run.Session.Add(provider.Message{Role: provider.RoleAssistant, Content: "last useful answer"})
76 if err := store.SaveOutcome(run, SubagentOutcome{Ref: run.Ref, Status: SubagentOutcomeFailed, ErrorCode: "provider_error"}); err != nil {
77 t.Fatalf("SaveOutcome: %v", err)
78 }
79 run.Release()
80 answer, status, err := store.ReadFinalAnswer(run.Ref, spec.ParentSession, spec.WorkspaceRoot)
81 if err != nil || answer != "last useful answer" || status != SubagentStatus(SubagentOutcomeFailed) {
82 t.Fatalf("ReadFinalAnswer = %q/%q/%v, want retained failed answer", answer, status, err)
83 }
84 if _, err := store.PrepareContinue(run.Ref, spec); err == nil || !strings.Contains(err.Error(), "failed and cannot be continued") {
85 t.Fatalf("non-retryable failed continuation error = %v", err)
86 }
87 }
88
88 lines GO