summaryrefslogtreecommitdiff
path: root/internal/task/task_test.go
blob: e6a17b82d46336c0f7d5860920814e7b469bdb7b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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},
		{"timed out to queued (retry)", StateTimedOut, StateQueued},
		{"running to blocked (question)", StateRunning, StateBlocked},
		{"blocked to queued (answer resume)", StateBlocked, StateQueued},
		{"blocked to ready (parent unblocked by subtasks)", StateBlocked, StateReady},
		{"blocked to cancelled (user cancels)", StateBlocked, StateCancelled},
		{"budget exceeded to queued (retry)", StateBudgetExceeded, StateQueued},
		{"cancelled to queued (restart)", StateCancelled, 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},
		{"timed out to completed", StateTimedOut, StateCompleted},
		{"ready to queued", StateReady, 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 invalid", tt.from, tt.to)
			}
		})
	}
}

func TestValidTransition_ReadyState(t *testing.T) {
	valid := []struct{ from, to State }{
		{StateRunning, StateReady},
		{StateReady, StateCompleted},
		{StateReady, StatePending},
	}
	for _, tt := range valid {
		if !ValidTransition(tt.from, tt.to) {
			t.Errorf("expected transition %s -> %s to be valid", 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)
	}
}

func TestTask_ProjectField(t *testing.T) {
	t.Run("struct assignment", func(t *testing.T) {
		task := Task{Project: "my-project"}
		if task.Project != "my-project" {
			t.Errorf("expected Project 'my-project', got %q", task.Project)
		}
	})

	t.Run("yaml parsing", func(t *testing.T) {
		yaml := `
name: "Test Task"
project: my-project
agent:
  instructions: "Do something"
`
		tasks, err := Parse([]byte(yaml))
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if len(tasks) != 1 {
			t.Fatalf("expected 1 task, got %d", len(tasks))
		}
		if tasks[0].Project != "my-project" {
			t.Errorf("expected Project 'my-project', got %q", tasks[0].Project)
		}
	})
}