Add ticket time tracking with reports and entry management.
Support start/stop timer, manual durations, grouped reports, and per-ticket edit/delete of time entries with undo. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
package ticket_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"task/internal/ticket"
|
||||
)
|
||||
|
||||
func TestJSONTimeStoreRoundTrip(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
store := ticket.NewJSONTimeStore(dir)
|
||||
|
||||
created, err := store.Create(ticket.TimeEntry{
|
||||
TicketID: "t1",
|
||||
ProjectID: "inbox",
|
||||
Start: time.Now().UTC().Add(-time.Hour),
|
||||
End: time.Now().UTC(),
|
||||
Duration: time.Hour,
|
||||
Source: ticket.SourceManual,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if created.ID == "" {
|
||||
t.Fatal("expected id")
|
||||
}
|
||||
|
||||
list, err := store.List()
|
||||
if err != nil || len(list) != 1 {
|
||||
t.Fatalf("list: %v len=%d", err, len(list))
|
||||
}
|
||||
sum, err := store.SumByTicket("t1")
|
||||
if err != nil || sum != time.Hour {
|
||||
t.Fatalf("sum: %v %v", err, sum)
|
||||
}
|
||||
|
||||
timer := &ticket.ActiveTimer{TicketID: "t1", ProjectID: "inbox", StartedAt: time.Now().UTC()}
|
||||
if err := store.SaveActiveTimer(timer); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
loaded, err := store.LoadActiveTimer()
|
||||
if err != nil || loaded == nil || loaded.TicketID != "t1" {
|
||||
t.Fatalf("load timer: %v %+v", err, loaded)
|
||||
}
|
||||
if err := store.SaveActiveTimer(nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if loaded, err = store.LoadActiveTimer(); err != nil || loaded != nil {
|
||||
t.Fatalf("cleared timer: %v %+v", err, loaded)
|
||||
}
|
||||
|
||||
updated := created
|
||||
updated.Duration = 90 * time.Minute
|
||||
if err := store.Update(updated); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
list, err = store.ListByTicket("t1")
|
||||
if err != nil || len(list) != 1 || list[0].Duration != 90*time.Minute {
|
||||
t.Fatalf("after update: %v %+v", err, list)
|
||||
}
|
||||
|
||||
if err := store.Delete(created.ID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
list, err = store.List()
|
||||
if err != nil || len(list) != 0 {
|
||||
t.Fatalf("after delete: %v len=%d path=%s", err, len(list), filepath.Join(dir, "time_entries.json"))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user