package tools
import (
"strings"
"testing"
)
func TestTodoWriteRendersAndStores(t *testing.T) {
todos := NewTodos()
got, err := run(t, NewTodoWrite(todos), TodoWriteArgs{Todos: []Todo{
{Content: "Read the failing test", Status: TodoDone},
{Content: "Fix the parser", Status: TodoInProgress},
{Content: "Run the suite", Status: TodoPending},
}})
if err != nil {
t.Fatalf("todo_write: %v", err)
}
for _, want := range []string{"[x] Read the failing test", "[»] Fix the parser", "[ ] Run the suite", "1 of 3 done"} {
if !strings.Contains(got, want) {
t.Errorf("rendering lacks %q:\n%s", want, got)
}
}
if snap := todos.Snapshot(); len(snap) != 3 || snap[1].Status != TodoInProgress {
t.Errorf("the list was not stored for a front end to read: %+v", snap)
}
}
// The list is replaced wholesale, not merged: a model sending three items
// after four must end up with three.
func TestTodoWriteReplacesTheWholeList(t *testing.T) {
todos := NewTodos()
tool := NewTodoWrite(todos)
run(t, tool, TodoWriteArgs{Todos: []Todo{
{Content: "a", Status: TodoPending}, {Content: "b", Status: TodoPending},
{Content: "c", Status: TodoPending}, {Content: "d", Status: TodoPending},
}})
run(t, tool, TodoWriteArgs{Todos: []Todo{{Content: "only", Status: TodoInProgress}}})
if snap := todos.Snapshot(); len(snap) != 1 || snap[0].Content != "only" {
t.Errorf("list = %+v, want it replaced", snap)
}
}
// Snapshot must not hand out the slice the tool keeps writing to.
func TestTodoSnapshotIsACopy(t *testing.T) {
todos := NewTodos()
run(t, NewTodoWrite(todos), TodoWriteArgs{Todos: []Todo{{Content: "a", Status: TodoPending}}})
snap := todos.Snapshot()
snap[0].Content = "mutated"
if todos.Snapshot()[0].Content != "a" {
t.Error("Snapshot handed out the live slice")
}
}
func TestTodoWriteRejectsBadInput(t *testing.T) {
tool := NewTodoWrite(NewTodos())
if _, err := run(t, tool, TodoWriteArgs{}); err == nil {
t.Error("an empty list must be refused")
}
_, err := run(t, tool, TodoWriteArgs{Todos: []Todo{{Content: "x", Status: "doing"}}})
if err == nil || !strings.Contains(err.Error(), TodoInProgress) {
t.Errorf("err = %v, want it to list the statuses that exist", err)
}
if _, err := run(t, tool, TodoWriteArgs{Todos: []Todo{{Content: " ", Status: TodoPending}}}); err == nil {
t.Error("an item with no content must be refused")
}
}
// Two in_progress items are a nudge, not a failure: refusing would cost a
// whole turn to fix something that changes nothing about the work.
func TestTodoWriteNudgesRatherThanFailingOnTwoInProgress(t *testing.T) {
got, err := run(t, NewTodoWrite(NewTodos()), TodoWriteArgs{Todos: []Todo{
{Content: "a", Status: TodoInProgress}, {Content: "b", Status: TodoInProgress},
}})
if err != nil {
t.Fatalf("this should be accepted: %v", err)
}
if !strings.Contains(got, "2 items are in_progress") {
t.Errorf("no nudge about the second in_progress item:\n%s", got)
}
}
func TestTodoWriteNotesWhenNothingIsStarted(t *testing.T) {
got, _ := run(t, NewTodoWrite(NewTodos()), TodoWriteArgs{Todos: []Todo{
{Content: "a", Status: TodoPending}, {Content: "b", Status: TodoDone},
}})
if !strings.Contains(got, "Nothing is in_progress") {
t.Errorf("a stalled list should say so:\n%s", got)
}
}
// A finished list is not nagged at.
func TestTodoWriteSaysNothingWhenAllDone(t *testing.T) {
got, _ := run(t, NewTodoWrite(NewTodos()), TodoWriteArgs{Todos: []Todo{
{Content: "a", Status: TodoDone},
}})
if strings.Contains(got, "Nothing is in_progress") {
t.Errorf("a completed list needs no nudge:\n%s", got)
}
if !strings.Contains(got, "1 of 1 done") {
t.Errorf("missing the count:\n%s", got)
}
}
func TestTodoWriteIsNotRegisteredWithoutAStore(t *testing.T) {
if NewTodoWrite(nil) != nil {
t.Error("with nowhere to keep the list the tool must not exist")
}
}