189 lines
6.9 KiB
Go
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)
|
|
}
|
|
}
|