From 2e2b2187b957e9af78797a67ec5c6874615fae02 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Sun, 8 Feb 2026 21:35:45 -1000 Subject: Initial project: task model, executor, API server, CLI, storage, reporter Claudomator automation toolkit for Claude Code with: - Task model with YAML parsing, validation, state machine (49 tests, 0 races) - SQLite storage for tasks and executions - Executor pool with bounded concurrency, timeout, cancellation - REST API + WebSocket for mobile PWA integration - Webhook/multi-notifier system - CLI: init, run, serve, list, status commands - Console, JSON, HTML reporters with cost tracking Co-Authored-By: Claude Opus 4.6 --- internal/task/task_test.go | 80 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 internal/task/task_test.go (limited to 'internal/task/task_test.go') diff --git a/internal/task/task_test.go b/internal/task/task_test.go new file mode 100644 index 0000000..96f5f6f --- /dev/null +++ b/internal/task/task_test.go @@ -0,0 +1,80 @@ +package task + +import ( + "testing" + "time" +) + +func TestValidTransition_AllowedTransitions(t *testing.T) { + tests := []struct { + name string + from State + to State + }{ + {"pending to queued", StatePending, StateQueued}, + {"pending to cancelled", StatePending, StateCancelled}, + {"queued to running", StateQueued, StateRunning}, + {"queued to cancelled", StateQueued, StateCancelled}, + {"running to completed", StateRunning, StateCompleted}, + {"running to failed", StateRunning, StateFailed}, + {"running to timed out", StateRunning, StateTimedOut}, + {"running to cancelled", StateRunning, StateCancelled}, + {"running to budget exceeded", StateRunning, StateBudgetExceeded}, + {"failed to queued (retry)", StateFailed, StateQueued}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if !ValidTransition(tt.from, tt.to) { + t.Errorf("expected transition %s -> %s to be valid", tt.from, tt.to) + } + }) + } +} + +func TestValidTransition_DisallowedTransitions(t *testing.T) { + tests := []struct { + name string + from State + to State + }{ + {"pending to running", StatePending, StateRunning}, + {"pending to completed", StatePending, StateCompleted}, + {"queued to completed", StateQueued, StateCompleted}, + {"completed to running", StateCompleted, StateRunning}, + {"completed to queued", StateCompleted, StateQueued}, + {"failed to completed", StateFailed, StateCompleted}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if ValidTransition(tt.from, tt.to) { + t.Errorf("expected transition %s -> %s to be invalid", tt.from, tt.to) + } + }) + } +} + +func TestDuration_UnmarshalYAML(t *testing.T) { + var d Duration + unmarshal := func(v interface{}) error { + ptr := v.(*string) + *ptr = "30m" + return nil + } + if err := d.UnmarshalYAML(unmarshal); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if d.Duration != 30*time.Minute { + t.Errorf("expected 30m, got %v", d.Duration) + } +} + +func TestDuration_MarshalYAML(t *testing.T) { + d := Duration{Duration: 15 * time.Minute} + v, err := d.MarshalYAML() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if v != "15m0s" { + t.Errorf("expected '15m0s', got %v", v) + } +} -- cgit v1.2.3