summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/ask/main_test.go4
-rw-r--r--integrationtests/ask_test.go66
-rw-r--r--internal/askcli/command_info_add.go26
-rw-r--r--internal/askcli/command_info_add_test.go79
-rw-r--r--internal/askcli/taskexport.go34
-rw-r--r--internal/askcli/taskexport_test.go39
6 files changed, 86 insertions, 162 deletions
diff --git a/cmd/ask/main_test.go b/cmd/ask/main_test.go
index f91afbd..db6b436 100644
--- a/cmd/ask/main_test.go
+++ b/cmd/ask/main_test.go
@@ -25,8 +25,8 @@ func TestMain_WiresDispatcher(t *testing.T) {
if code != 0 {
t.Fatalf("exitCode = %d, want 0", code)
}
- if len(gotArgs) < 2 || gotArgs[0] != "export" {
- t.Fatalf("args = %v, want [export, ...]", gotArgs)
+ if len(gotArgs) < 1 || gotArgs[len(gotArgs)-1] != "export" {
+ t.Fatalf("args = %v, want [..., export]", gotArgs)
}
}
diff --git a/integrationtests/ask_test.go b/integrationtests/ask_test.go
index e18aa10..42825dd 100644
--- a/integrationtests/ask_test.go
+++ b/integrationtests/ask_test.go
@@ -9,7 +9,6 @@ import (
"os/exec"
"path/filepath"
"regexp"
- "strconv"
"strings"
"testing"
"time"
@@ -114,59 +113,20 @@ func runTaskWithStdin(ctx context.Context, args []string, stdin string) (stdout,
return stdout, stderr, ee.ExitCode()
}
+// createTask creates a new task via ask add and returns its UUID.
+// ask add now outputs the UUID directly, so no follow-up lookup is needed.
func createTask(ctx context.Context, desc string) (string, error) {
- stdout, _, code := runAskWithStdin(ctx, []string{"add", "+integrationtest", desc}, "yes\n")
+ stdout, stderr, code := runAsk(ctx, []string{"add", "+integrationtest", desc})
if code != 0 {
- return "", fmt.Errorf("create task failed (code %d): %s", code, stdout.String())
+ return "", fmt.Errorf("create task failed (code %d): stdout=%s stderr=%s", code, stdout.String(), stderr.String())
}
-
- taskID := extractTaskID(stdout.String())
- if taskID == "" {
- return "", fmt.Errorf("could not extract task ID from output: %s", stdout.String())
- }
-
- time.Sleep(100 * time.Millisecond)
-
- infoOut, _, _ := runTask(ctx, []string{taskID, "info"})
- uuid := extractUUIDFromTaskInfo(infoOut.String())
+ uuid := strings.TrimSpace(stdout.String())
if uuid == "" {
- return "", fmt.Errorf("could not extract UUID from task info")
+ return "", fmt.Errorf("could not extract UUID from ask add output: %s", stdout.String())
}
return uuid, nil
}
-var taskUUIDRx = regexp.MustCompile(`UUID\s+(.+)`)
-
-func extractUUIDFromTaskInfo(output string) string {
- if m := taskUUIDRx.FindStringSubmatch(output); len(m) > 1 {
- return strings.TrimSpace(m[1])
- }
- return ""
-}
-
-func extractTaskID(output string) string {
- output = strings.TrimSpace(output)
- lines := strings.Split(output, "\n")
- for _, line := range lines {
- line = strings.TrimSpace(line)
- if strings.Contains(line, "Created task") {
- fields := strings.Fields(line)
- for i, f := range fields {
- if f == "task" && i+1 < len(fields) {
- return strings.TrimSuffix(fields[i+1], ".")
- }
- }
- }
- }
- for _, line := range lines {
- line = strings.TrimSpace(line)
- if _, err := strconv.Atoi(line); err == nil {
- return line
- }
- }
- return ""
-}
-
func deleteTask(ctx context.Context, uuid string) {
runTaskWithStdin(ctx, []string{"uuid:" + uuid, "delete"}, "yes\n")
}
@@ -201,12 +161,14 @@ type taskInfo struct {
Start string
}
-var uuidFieldRx = regexp.MustCompile(`UUID:\s+(.+)`)
-var descFieldRx = regexp.MustCompile(`Description:\s+(.+)`)
-var statusFieldRx = regexp.MustCompile(`Status:\s+(.+)`)
-var priorityFieldRx = regexp.MustCompile(`Priority:\s+(.+)`)
-var tagsFieldRx = regexp.MustCompile(`Tags:\s+(.+)`)
-var startFieldRx = regexp.MustCompile(`Started:\s+(.+)`)
+var (
+ uuidFieldRx = regexp.MustCompile(`UUID:\s+(.+)`)
+ descFieldRx = regexp.MustCompile(`Description:\s+(.+)`)
+ statusFieldRx = regexp.MustCompile(`Status:\s+(.+)`)
+ priorityFieldRx = regexp.MustCompile(`Priority:\s+(.+)`)
+ tagsFieldRx = regexp.MustCompile(`Tags:\s+(.+)`)
+ startFieldRx = regexp.MustCompile(`Started:\s+(.+)`)
+)
func parseTaskInfoText(output string, uuid string) taskInfo {
ti := taskInfo{UUID: uuid}
diff --git a/internal/askcli/command_info_add.go b/internal/askcli/command_info_add.go
index 477ad62..5b76b2b 100644
--- a/internal/askcli/command_info_add.go
+++ b/internal/askcli/command_info_add.go
@@ -38,22 +38,38 @@ func (d Dispatcher) handleAdd(ctx context.Context, args []string, stdout, stderr
}
modifiers, description := parseAddArgs(args[1:])
var outBuf bytes.Buffer
- taskArgs := []string{"add"}
+ // rc.verbose=new-uuid instructs taskwarrior to emit "Created task <uuid>."
+ // so we get the UUID directly from the add output without a follow-up export.
+ taskArgs := []string{"add", "rc.verbose=new-uuid"}
taskArgs = append(taskArgs, modifiers...)
taskArgs = append(taskArgs, description)
code, err := d.runner.Run(ctx, taskArgs, nil, &outBuf, stderr)
if code != 0 {
return code, err
}
- createdUUID := ExtractUUIDFromOutput(outBuf.String())
- if createdUUID == "" {
- io.WriteString(stderr, "error: could not extract UUID from task creation output\n")
+ uuid := extractUUIDFromAddOutput(outBuf.String())
+ if uuid == "" {
+ io.WriteString(stderr, "error: could not parse UUID from task creation output\n")
return 1, nil
}
- io.WriteString(stdout, createdUUID+"\n")
+ io.WriteString(stdout, uuid+"\n")
return 0, nil
}
+// extractUUIDFromAddOutput parses the UUID from taskwarrior's
+// "Created task <uuid>." output (produced when rc.verbose=new-uuid is set).
+func extractUUIDFromAddOutput(output string) string {
+ for _, line := range strings.Split(strings.TrimSpace(output), "\n") {
+ if strings.HasPrefix(line, "Created task ") {
+ parts := strings.Fields(line)
+ if len(parts) >= 3 {
+ return strings.TrimSuffix(parts[2], ".")
+ }
+ }
+ }
+ return ""
+}
+
func parseAddArgs(args []string) (modifiers []string, description string) {
for i, arg := range args {
if strings.HasPrefix(arg, "priority:") || strings.HasPrefix(arg, "+") || strings.HasPrefix(arg, "-") {
diff --git a/internal/askcli/command_info_add_test.go b/internal/askcli/command_info_add_test.go
index 47cd790..b809097 100644
--- a/internal/askcli/command_info_add_test.go
+++ b/internal/askcli/command_info_add_test.go
@@ -53,8 +53,9 @@ func TestHandleInfo_MissingUUID(t *testing.T) {
}
func TestHandleAdd_Success(t *testing.T) {
+ // With rc.verbose=new-uuid, task add outputs "Created task <uuid>." directly.
d := NewDispatcher(&spyRunner{runFn: func(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) (int, error) {
- io.WriteString(stdout, "Created task 123.\nUUID: abc-123-def")
+ io.WriteString(stdout, "Created task abc-123-def.")
return 0, nil
}})
var stdout, stderr bytes.Buffer
@@ -62,9 +63,8 @@ func TestHandleAdd_Success(t *testing.T) {
if code != 0 {
t.Fatalf("add code = %d, want 0", code)
}
- output := stdout.String()
- if !strings.Contains(output, "abc-123-def") {
- t.Fatalf("output missing UUID: %s", output)
+ if !strings.Contains(stdout.String(), "abc-123-def") {
+ t.Fatalf("output missing UUID: %s", stdout.String())
}
}
@@ -79,37 +79,44 @@ func TestHandleAdd_MissingDescription(t *testing.T) {
}
}
+func makeAddRunner(onAdd func(args []string, stdout io.Writer)) *spyRunner {
+ return &spyRunner{runFn: func(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) (int, error) {
+ onAdd(args, stdout)
+ return 0, nil
+ }}
+}
+
func TestHandleAdd_MultipleWords(t *testing.T) {
var capturedArgs []string
- d := NewDispatcher(&spyRunner{runFn: func(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) (int, error) {
+ d := NewDispatcher(makeAddRunner(func(args []string, stdout io.Writer) {
capturedArgs = args
- io.WriteString(stdout, "Created task 123.\nUUID: xyz-789")
- return 0, nil
- }})
+ io.WriteString(stdout, "Created task test-uuid.")
+ }))
var stdout, stderr bytes.Buffer
d.Dispatch(context.Background(), []string{"add", "Multi", "word", "description"}, nil, &stdout, &stderr)
- if len(capturedArgs) < 2 || capturedArgs[0] != "add" {
- t.Fatalf("capturedArgs = %v, want [add, Multi word description]", capturedArgs)
+ // args[0]="add", args[1]="rc.verbose=new-uuid", then description
+ if len(capturedArgs) < 3 || capturedArgs[0] != "add" || capturedArgs[1] != "rc.verbose=new-uuid" {
+ t.Fatalf("capturedArgs = %v, want [add, rc.verbose=new-uuid, ...]", capturedArgs)
}
}
func TestHandleAdd_WithPriority(t *testing.T) {
var capturedArgs []string
- d := NewDispatcher(&spyRunner{runFn: func(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) (int, error) {
+ d := NewDispatcher(makeAddRunner(func(args []string, stdout io.Writer) {
capturedArgs = args
- io.WriteString(stdout, "Created task 123.\nUUID: uuid-priority")
- return 0, nil
- }})
+ io.WriteString(stdout, "Created task test-uuid.")
+ }))
var stdout, stderr bytes.Buffer
code, _ := d.Dispatch(context.Background(), []string{"add", "priority:H", "Fix critical bug"}, nil, &stdout, &stderr)
if code != 0 {
t.Fatalf("add code = %d, want 0", code)
}
- if len(capturedArgs) < 3 {
- t.Fatalf("capturedArgs = %v, want at least [add, priority:H, Fix critical bug]", capturedArgs)
+ // args: [add, rc.verbose=new-uuid, priority:H, Fix critical bug]
+ if len(capturedArgs) < 4 {
+ t.Fatalf("capturedArgs = %v, want at least 4 elements", capturedArgs)
}
- if capturedArgs[1] != "priority:H" {
- t.Errorf("capturedArgs[1] = %s, want priority:H", capturedArgs[1])
+ if capturedArgs[2] != "priority:H" {
+ t.Errorf("capturedArgs[2] = %s, want priority:H", capturedArgs[2])
}
if capturedArgs[len(capturedArgs)-1] != "Fix critical bug" {
t.Errorf("last arg = %s, want 'Fix critical bug'", capturedArgs[len(capturedArgs)-1])
@@ -118,35 +125,47 @@ func TestHandleAdd_WithPriority(t *testing.T) {
func TestHandleAdd_WithTag(t *testing.T) {
var capturedArgs []string
- d := NewDispatcher(&spyRunner{runFn: func(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) (int, error) {
+ d := NewDispatcher(makeAddRunner(func(args []string, stdout io.Writer) {
capturedArgs = args
- io.WriteString(stdout, "Created task 123.\nUUID: uuid-tag")
- return 0, nil
- }})
+ io.WriteString(stdout, "Created task test-uuid.")
+ }))
var stdout, stderr bytes.Buffer
code, _ := d.Dispatch(context.Background(), []string{"add", "+cli", "New feature"}, nil, &stdout, &stderr)
if code != 0 {
t.Fatalf("add code = %d, want 0", code)
}
- if capturedArgs[1] != "+cli" {
- t.Errorf("capturedArgs[1] = %s, want +cli", capturedArgs[1])
+ // args: [add, rc.verbose=new-uuid, +cli, New feature]
+ if capturedArgs[2] != "+cli" {
+ t.Errorf("capturedArgs[2] = %s, want +cli", capturedArgs[2])
}
}
func TestHandleAdd_WithPriorityAndTag(t *testing.T) {
var capturedArgs []string
- d := NewDispatcher(&spyRunner{runFn: func(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) (int, error) {
+ d := NewDispatcher(makeAddRunner(func(args []string, stdout io.Writer) {
capturedArgs = args
- io.WriteString(stdout, "Created task 123.\nUUID: uuid-combined")
- return 0, nil
- }})
+ io.WriteString(stdout, "Created task test-uuid.")
+ }))
var stdout, stderr bytes.Buffer
code, _ := d.Dispatch(context.Background(), []string{"add", "priority:H", "+cli", "Complex task"}, nil, &stdout, &stderr)
if code != 0 {
t.Fatalf("add code = %d, want 0", code)
}
- if capturedArgs[1] != "priority:H" || capturedArgs[2] != "+cli" {
- t.Errorf("capturedArgs = %v, want [add, priority:H, +cli, Complex task]", capturedArgs)
+ // args: [add, rc.verbose=new-uuid, priority:H, +cli, Complex task]
+ if capturedArgs[2] != "priority:H" || capturedArgs[3] != "+cli" {
+ t.Errorf("capturedArgs = %v, want [add, rc.verbose=new-uuid, priority:H, +cli, Complex task]", capturedArgs)
+ }
+}
+
+func TestExtractUUIDFromAddOutput(t *testing.T) {
+ if uuid := extractUUIDFromAddOutput("Created task abc-123-def."); uuid != "abc-123-def" {
+ t.Fatalf("got %q, want abc-123-def", uuid)
+ }
+ if uuid := extractUUIDFromAddOutput("Created task abc-123-def.\nsome other line"); uuid != "abc-123-def" {
+ t.Fatalf("got %q, want abc-123-def", uuid)
+ }
+ if uuid := extractUUIDFromAddOutput("no match here"); uuid != "" {
+ t.Fatalf("got %q, want empty", uuid)
}
}
diff --git a/internal/askcli/taskexport.go b/internal/askcli/taskexport.go
index c18cd4e..9841821 100644
--- a/internal/askcli/taskexport.go
+++ b/internal/askcli/taskexport.go
@@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"io"
- "strings"
)
type TaskExport struct {
@@ -41,36 +40,3 @@ func MustParseTaskExport(data []byte) []TaskExport {
}
return tasks
}
-
-func ExtractUUIDFromOutput(output string) string {
- lines := strings.Split(strings.TrimSpace(output), "\n")
- for _, line := range lines {
- if strings.HasPrefix(line, "UUID:") {
- parts := strings.Fields(line)
- if len(parts) >= 2 {
- return parts[1]
- }
- }
- }
- for _, line := range lines {
- if strings.HasPrefix(line, "Created task ") {
- parts := strings.Fields(line)
- if len(parts) >= 3 {
- return strings.TrimSuffix(parts[2], ".")
- }
- }
- }
- fields := strings.Fields(output)
- for i, f := range fields {
- if f == "uuid" && i+1 < len(fields) {
- return fields[i+1]
- }
- if strings.HasPrefix(f, "Created task") {
- parts := strings.Split(f, " ")
- if len(parts) >= 2 {
- return strings.TrimSuffix(parts[1], ".")
- }
- }
- }
- return strings.TrimSpace(output)
-}
diff --git a/internal/askcli/taskexport_test.go b/internal/askcli/taskexport_test.go
index af468e2..e7779aa 100644
--- a/internal/askcli/taskexport_test.go
+++ b/internal/askcli/taskexport_test.go
@@ -51,38 +51,6 @@ func TestMustParseTaskExport_ValidJSON(t *testing.T) {
}
}
-func TestExtractUUIDFromOutput_CreatedTask(t *testing.T) {
- output := "Created task 123.\nUUID: abc-123-def"
- uuid := ExtractUUIDFromOutput(output)
- if uuid != "abc-123-def" {
- t.Fatalf("ExtractUUIDFromOutput = %q, want %q", uuid, "abc-123-def")
- }
-}
-
-func TestExtractUUIDFromOutput_CreatedTaskOnly(t *testing.T) {
- output := "Created task 123."
- uuid := ExtractUUIDFromOutput(output)
- if uuid != "123" {
- t.Fatalf("ExtractUUIDFromOutput = %q, want %q", uuid, "123")
- }
-}
-
-func TestExtractUUIDFromOutput_UUIDField(t *testing.T) {
- output := "Some text\nuuid abc-123-def\nmore text"
- uuid := ExtractUUIDFromOutput(output)
- if uuid != "abc-123-def" {
- t.Fatalf("ExtractUUIDFromOutput = %q, want %q", uuid, "abc-123-def")
- }
-}
-
-func TestExtractUUIDFromOutput_PlainText(t *testing.T) {
- output := "abc-456-xyz"
- uuid := ExtractUUIDFromOutput(output)
- if uuid != output {
- t.Fatalf("ExtractUUIDFromOutput = %q, want %q", uuid, output)
- }
-}
-
func TestTaskExport_JSONRoundTrip(t *testing.T) {
original := TaskExport{
UUID: "test-uuid",
@@ -133,13 +101,6 @@ func TestParseTaskExport_MultipleTasks(t *testing.T) {
}
}
-func TestExtractUUIDFromOutput_NilOutput(t *testing.T) {
- uuid := ExtractUUIDFromOutput("")
- if uuid != "" {
- t.Fatalf("ExtractUUIDFromOutput = %q, want empty string", uuid)
- }
-}
-
func TestParseTaskExport_ReadError(t *testing.T) {
_, err := ParseTaskExport(&errReader{})
if err == nil {