package cli import ( "net/http" "net/http/httptest" "strings" "testing" "time" ) func TestCreateTask_TimesOut(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { select { case <-r.Context().Done(): case <-time.After(5 * time.Second): // fallback so srv.Close() never deadlocks } })) defer srv.Close() orig := httpClient httpClient = &http.Client{Timeout: 50 * time.Millisecond} defer func() { httpClient = orig }() err := createTask(srv.URL, "test", "do something", "", "", "claude", "", 1.0, "15m", "normal", false) if err == nil { t.Fatal("expected timeout error, got nil") } if !strings.Contains(err.Error(), "POST /api/tasks") { t.Errorf("expected error mentioning POST /api/tasks, got: %v", err) } } func TestStartTask_EscapesTaskID(t *testing.T) { var capturedPath string srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { capturedPath = r.URL.RawPath if capturedPath == "" { capturedPath = r.URL.Path } w.WriteHeader(http.StatusOK) w.Write([]byte(`{}`)) })) defer srv.Close() err := startTask(srv.URL, "task/with/slashes", "") if err != nil { t.Fatalf("unexpected error: %v", err) } if strings.Contains(capturedPath, "task/with/slashes") { t.Errorf("task ID was not escaped; raw path contains unescaped slashes: %s", capturedPath) } if !strings.Contains(capturedPath, "task%2Fwith%2Fslashes") { t.Errorf("expected escaped path segment, got: %s", capturedPath) } } func TestCreateTask_MissingIDField_ReturnsError(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte(`{"name":"test"}`)) // no "id" field })) defer srv.Close() err := createTask(srv.URL, "test", "do something", "", "", "claude", "", 1.0, "15m", "normal", false) if err == nil { t.Fatal("expected error for missing id field, got nil") } if !strings.Contains(err.Error(), "without id") { t.Errorf("expected error mentioning missing id, got: %v", err) } } func TestCreateTask_NonJSONResponse_ReturnsError(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadGateway) w.Write([]byte(`502 Bad Gateway`)) })) defer srv.Close() err := createTask(srv.URL, "test", "do something", "", "", "claude", "", 1.0, "15m", "normal", false) if err == nil { t.Fatal("expected error for non-JSON response, got nil") } if !strings.Contains(err.Error(), "invalid JSON") { t.Errorf("expected error mentioning invalid JSON, got: %v", err) } } func TestStartTask_NonJSONResponse_ReturnsError(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadGateway) w.Write([]byte(`502 Bad Gateway`)) })) defer srv.Close() err := startTask(srv.URL, "task-abc", "") if err == nil { t.Fatal("expected error for non-JSON response, got nil") } if !strings.Contains(err.Error(), "invalid JSON") { t.Errorf("expected error mentioning invalid JSON, got: %v", err) } } func TestStartTask_TimesOut(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { select { case <-r.Context().Done(): case <-time.After(5 * time.Second): // fallback so srv.Close() never deadlocks } })) defer srv.Close() orig := httpClient httpClient = &http.Client{Timeout: 50 * time.Millisecond} defer func() { httpClient = orig }() err := startTask(srv.URL, "task-abc", "") if err == nil { t.Fatal("expected timeout error, got nil") } if !strings.Contains(err.Error(), "POST") { t.Errorf("expected error mentioning POST, got: %v", err) } }