Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions cmd/add/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@ var (
cmdLong = `Add a time entry`
cmdExample = `
# Add 2 hours to a project a project with issue ID PNX-123
tl add PNX-123 2h "Worked on feature X"`
date time.Time
tl add PNX-123 2h "Worked on feature X"

# Add 2 hours and indicate 1 hour was saved by AI
tl add PNX-123 2h "Worked on feature X" --ai-time-saved 1h
tl add PNX-123 2h "Worked on feature X" --aits 1h
tl add PNX-123 2h "Worked on feature X" -a 1h`
date time.Time
aiTimeSavedStr string
)

func NewCommand(r func() db.TimeEntriesInterface, s func() service.SyncInterface, i func() db.IssueStorageInterface) *cobra.Command {
Expand Down Expand Up @@ -84,6 +90,13 @@ func NewCommand(r func() db.TimeEntriesInterface, s func() service.SyncInterface
if len(args) > 2 {
entry.Description = args[2]
}
if aiTimeSavedStr != "" {
aiDur, err := time.ParseDuration(aiTimeSavedStr)
if err != nil {
return fmt.Errorf("invalid AI time saved duration: %s", aiTimeSavedStr)
}
entry.AISavedDuration = aiDur
}
entry.CreatedAt = date

err = r().CreateTimeEntry(entry)
Expand All @@ -101,6 +114,9 @@ func NewCommand(r func() db.TimeEntriesInterface, s func() service.SyncInterface
time.DateOnly,
}
cmd.Flags().TimeVarP(&date, "date", "d", time.Now(), timeFormats, "Date for the entry.")
cmd.Flags().StringVarP(&aiTimeSavedStr, "ai-time-saved", "a", "", "Duration of time saved by AI (e.g. 1h, 30m)")
cmd.Flags().StringVar(&aiTimeSavedStr, "aits", "", "Duration of time saved by AI (shorthand for --ai-time-saved)")
_ = cmd.Flags().MarkHidden("aits")

return cmd
}
73 changes: 73 additions & 0 deletions cmd/add/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package add
import (
"bytes"
"testing"
"time"

"github.com/stretchr/testify/assert"

Expand Down Expand Up @@ -61,3 +62,75 @@ func TestAdd_NoDescription(t *testing.T) {
output := buf.String()
assert.Contains(t, output, "Added time entry: ID=42")
}

func TestAdd_WithAITimeSaved(t *testing.T) {
mock := &dbmocks.MockRepository{}
cmd := NewCommand(
func() db.TimeEntriesInterface { return mock },
func() service.SyncInterface { return &servicemocks.MockSync{} },
func() db.IssueStorageInterface { return mock },
)

var buf bytes.Buffer
cmd.SetOut(&buf)
cmd.SetArgs([]string{"PNX-123", "2h", "Worked on feature X", "--ai-time-saved", "1h"})

err := cmd.Execute()
assert.NoError(t, err)
output := buf.String()
assert.Contains(t, output, "Added time entry: ID=42")
assert.Len(t, mock.Entries, 1)
assert.Equal(t, time.Hour, mock.Entries[0].AISavedDuration)
}

func TestAdd_WithAITimeSaved_ShortFlag(t *testing.T) {
mock := &dbmocks.MockRepository{}
cmd := NewCommand(
func() db.TimeEntriesInterface { return mock },
func() service.SyncInterface { return &servicemocks.MockSync{} },
func() db.IssueStorageInterface { return mock },
)

var buf bytes.Buffer
cmd.SetOut(&buf)
cmd.SetArgs([]string{"PNX-123", "2h", "Worked on feature X", "-a", "30m"})

err := cmd.Execute()
assert.NoError(t, err)
assert.Len(t, mock.Entries, 1)
assert.Equal(t, 30*time.Minute, mock.Entries[0].AISavedDuration)
}

func TestAdd_WithAITimeSaved_AitsAlias(t *testing.T) {
mock := &dbmocks.MockRepository{}
cmd := NewCommand(
func() db.TimeEntriesInterface { return mock },
func() service.SyncInterface { return &servicemocks.MockSync{} },
func() db.IssueStorageInterface { return mock },
)

var buf bytes.Buffer
cmd.SetOut(&buf)
cmd.SetArgs([]string{"PNX-123", "2h", "Worked on feature X", "--aits", "45m"})

err := cmd.Execute()
assert.NoError(t, err)
assert.Len(t, mock.Entries, 1)
assert.Equal(t, 45*time.Minute, mock.Entries[0].AISavedDuration)
}

func TestAdd_InvalidAITimeSaved_ReturnsError(t *testing.T) {
cmd := NewCommand(
func() db.TimeEntriesInterface { return &dbmocks.MockRepository{} },
func() service.SyncInterface { return &servicemocks.MockSync{} },
func() db.IssueStorageInterface { return &dbmocks.MockRepository{} },
)

var buf bytes.Buffer
cmd.SetOut(&buf)
cmd.SetArgs([]string{"PNX-123", "2h", "desc", "--ai-time-saved", "notaduration"})

err := cmd.Execute()
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid AI time saved duration")
}
Loading
Loading