Files
2026-07-13 13:00:08 +08:00

189 lines
6.9 KiB
Go

package builtin
import (
"context"
"encoding/json"
"strings"
"testing"
"reasonix/internal/evidence"
)
func TestTodoWriteAcceptsLevels(t *testing.T) {
args := json.RawMessage(`{"todos":[` +
`{"content":"Phase","status":"in_progress","level":0},` +
`{"content":"sub","status":"pending","level":1}]}`)
if _, err := (todoWrite{}).Execute(context.Background(), args); err != nil {
t.Fatalf("levels 0/1 should be accepted: %v", err)
}
}
func TestTodoWriteRejectsBadLevel(t *testing.T) {
args := json.RawMessage(`{"todos":[{"content":"x","status":"pending","level":2}]}`)
_, err := (todoWrite{}).Execute(context.Background(), args)
if err == nil || !strings.Contains(err.Error(), "level") {
t.Fatalf("level 2 should be rejected with a level error, got %v", err)
}
}
func TestTodoWriteRejectsNewCompletedWithoutCompleteStepReceipt(t *testing.T) {
ledger := evidence.NewLedger()
ledger.Record(evidence.Receipt{
ToolName: "todo_write",
Success: true,
Todos: []evidence.TodoItem{{Content: "Add parser", Status: "in_progress"}},
})
ctx := evidence.WithLedger(context.Background(), ledger)
args := json.RawMessage(`{"todos":[{"content":"Add parser","status":"completed"}]}`)
_, err := (todoWrite{}).Execute(ctx, args)
if err == nil || !strings.Contains(err.Error(), "complete_step") {
t.Fatalf("new completion without complete_step should be rejected, got %v", err)
}
}
func TestTodoWriteAcceptsNewCompletedWithCompleteStepReceipt(t *testing.T) {
ledger := evidence.NewLedger()
ledger.Record(evidence.Receipt{
ToolName: "todo_write",
Success: true,
Todos: []evidence.TodoItem{{Content: "Add parser", Status: "in_progress"}},
})
ledger.Record(evidence.Receipt{ToolName: "complete_step", Success: true, Step: "Add parser"})
ctx := evidence.WithLedger(context.Background(), ledger)
args := json.RawMessage(`{"todos":[{"content":"Add parser","status":"completed"}]}`)
if _, err := (todoWrite{}).Execute(ctx, args); err != nil {
t.Fatalf("matching complete_step should authorize new completion: %v", err)
}
}
func TestTodoWriteAllowsInitialCompletedWithoutBaseline(t *testing.T) {
ctx := evidence.WithLedger(context.Background(), evidence.NewLedger())
args := json.RawMessage(`{"todos":[{"content":"Add parser","status":"completed"}]}`)
if _, err := (todoWrite{}).Execute(ctx, args); err != nil {
t.Fatalf("initial completed todo without baseline should preserve existing behavior: %v", err)
}
}
func TestTodoWriteRejectsFailedCompleteStepReceipt(t *testing.T) {
ledger := evidence.NewLedger()
ledger.Record(evidence.Receipt{
ToolName: "todo_write",
Success: true,
Todos: []evidence.TodoItem{{Content: "Add parser", Status: "in_progress"}},
})
ledger.Record(evidence.Receipt{ToolName: "complete_step", Success: false, Step: "Add parser"})
ctx := evidence.WithLedger(context.Background(), ledger)
args := json.RawMessage(`{"todos":[{"content":"Add parser","status":"completed"}]}`)
_, err := (todoWrite{}).Execute(ctx, args)
if err == nil || !strings.Contains(err.Error(), "complete_step") {
t.Fatalf("failed complete_step without proof-bearing recovery should not authorize completion, got %v", err)
}
}
func TestTodoWriteRejectsFailedCompleteStepWithoutProof(t *testing.T) {
ledger := evidence.NewLedger()
ledger.Record(evidence.Receipt{
ToolName: "todo_write",
Success: true,
Todos: []evidence.TodoItem{{Content: "Run project script", Status: "in_progress"}},
})
ledger.Record(evidence.Receipt{
ToolName: "bash",
Success: true,
Command: `python "script.py"`,
})
ledger.Record(evidence.ReceiptFromToolCall("complete_step", json.RawMessage(`{
"step":"Run project script",
"result":"script ran",
"evidence":[]
}`), false, true))
ctx := evidence.WithLedger(context.Background(), ledger)
args := json.RawMessage(`{"todos":[{"content":"Run project script","status":"completed"}]}`)
_, err := (todoWrite{}).Execute(ctx, args)
if err == nil || !strings.Contains(err.Error(), "complete_step") {
t.Fatalf("failed complete_step without proof should not authorize completion, got %v", err)
}
}
func TestTodoWriteRejectsFailedCompleteStepMissingResult(t *testing.T) {
ledger := evidence.NewLedger()
ledger.Record(evidence.Receipt{
ToolName: "todo_write",
Success: true,
Todos: []evidence.TodoItem{{Content: "Run project script", Status: "in_progress"}},
})
ledger.Record(evidence.Receipt{
ToolName: "bash",
Success: true,
Command: `python "script.py"`,
})
ledger.Record(evidence.ReceiptFromToolCall("complete_step", json.RawMessage(`{
"step":"Run project script",
"evidence":[{"kind":"manual","summary":"checked manually"}]
}`), false, true))
ctx := evidence.WithLedger(context.Background(), ledger)
args := json.RawMessage(`{"todos":[{"content":"Run project script","status":"completed"}]}`)
_, err := (todoWrite{}).Execute(ctx, args)
if err == nil || !strings.Contains(err.Error(), "complete_step") {
t.Fatalf("failed complete_step without result should not authorize completion, got %v", err)
}
}
func TestTodoWriteRecoversAfterFailedCompleteStepWithProgressReceipt(t *testing.T) {
ledger := evidence.NewLedger()
ledger.Record(evidence.Receipt{
ToolName: "todo_write",
Success: true,
Todos: []evidence.TodoItem{{Content: "Run project script", Status: "in_progress"}},
})
ledger.Record(evidence.Receipt{
ToolName: "bash",
Success: true,
Command: `python "script.py"`,
})
ledger.Record(evidence.ReceiptFromToolCall("complete_step", json.RawMessage(`{
"step":"Run project script",
"result":"script ran",
"evidence":[{"kind":"verification","summary":"script completed","command":"python script.py"}]
}`), false, true))
ctx := evidence.WithLedger(context.Background(), ledger)
args := json.RawMessage(`{"todos":[{"content":"Run project script","status":"completed"}]}`)
if _, err := (todoWrite{}).Execute(ctx, args); err != nil {
t.Fatalf("matching failed complete_step with progress receipt should recover todo completion: %v", err)
}
}
func TestTodoWriteRejectsRecoveryWhenProgressIsAfterFailedCompleteStep(t *testing.T) {
ledger := evidence.NewLedger()
ledger.Record(evidence.Receipt{
ToolName: "todo_write",
Success: true,
Todos: []evidence.TodoItem{{Content: "Run project script", Status: "in_progress"}},
})
ledger.Record(evidence.ReceiptFromToolCall("complete_step", json.RawMessage(`{
"step":"Run project script",
"result":"script ran",
"evidence":[{"kind":"verification","summary":"script completed","command":"python other.py"}]
}`), false, true))
ledger.Record(evidence.Receipt{
ToolName: "write_file",
Success: true,
Paths: []string{"docs/notes.md"},
Write: true,
})
ctx := evidence.WithLedger(context.Background(), ledger)
args := json.RawMessage(`{"todos":[{"content":"Run project script","status":"completed"}]}`)
_, err := (todoWrite{}).Execute(ctx, args)
if err == nil || !strings.Contains(err.Error(), "complete_step") {
t.Fatalf("progress after a failed complete_step should not authorize recovery, got %v", err)
}
}