feat: add manual interval creation and inline entry editing
- Service: CreateInterval() validates same-day, end>start, day not closed
- Service: Update() now rejects edits on closed-day entries (ErrDayAlreadyClosed)
- Handler: POST /api/entries creates a completed interval with explicit times
- API client: entries.createInterval(startMs, endMs, note)
- Utils: parseTimeInput / toTimeInput helpers for HH:MM <-> unix ms
- Today page: '+ Add interval' form (time pickers + optional note)
- Today page: pencil button on each entry opens inline edit row (start/end/note)
- Tests: TestCreateInterval, TestCreateIntervalEndBeforeStart,
TestCreateIntervalCrossesMidnight, TestUpdateRejectsClosedDay
This commit is contained in:
@@ -2,6 +2,7 @@ package service_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -134,3 +135,98 @@ func TestListEntries(t *testing.T) {
|
||||
t.Fatalf("expected 3 entries, got %d", len(entries))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateInterval(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
svc := newTestServices(t)
|
||||
|
||||
now := time.Now().UTC()
|
||||
startMs := now.Add(-2 * time.Hour).UnixMilli()
|
||||
endMs := now.Add(-1 * time.Hour).UnixMilli()
|
||||
|
||||
entry, err := svc.CreateInterval(ctx, service.CreateIntervalInput{
|
||||
StartTime: startMs,
|
||||
EndTime: endMs,
|
||||
Note: "manual entry",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateInterval: %v", err)
|
||||
}
|
||||
if entry.EndTime == nil {
|
||||
t.Fatal("expected EndTime to be set")
|
||||
}
|
||||
if *entry.EndTime != endMs {
|
||||
t.Errorf("expected EndTime %d, got %d", endMs, *entry.EndTime)
|
||||
}
|
||||
if entry.Note != "manual entry" {
|
||||
t.Errorf("expected note 'manual entry', got %q", entry.Note)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateIntervalEndBeforeStart(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
svc := newTestServices(t)
|
||||
|
||||
now := time.Now().UTC().UnixMilli()
|
||||
_, err := svc.CreateInterval(ctx, service.CreateIntervalInput{
|
||||
StartTime: now,
|
||||
EndTime: now - 1000,
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("expected error for end_time before start_time")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateIntervalCrossesMidnight(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
svc := newTestServices(t)
|
||||
|
||||
yesterday := time.Now().UTC().Add(-24 * time.Hour)
|
||||
startMs := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day(), 23, 0, 0, 0, time.UTC).UnixMilli()
|
||||
endMs := time.Now().UTC().Add(time.Hour).UnixMilli()
|
||||
|
||||
_, err := svc.CreateInterval(ctx, service.CreateIntervalInput{
|
||||
StartTime: startMs,
|
||||
EndTime: endMs,
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("expected ErrCrossesMidnight")
|
||||
}
|
||||
if !errors.Is(err, service.ErrCrossesMidnight) {
|
||||
t.Fatalf("expected ErrCrossesMidnight, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateRejectsClosedDay(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
db, err := store.Open(":memory:")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
entryStore := store.NewEntryStore(db)
|
||||
closedDayStore := store.NewClosedDayStore(db)
|
||||
settingsStore := store.NewSettingsStore(db)
|
||||
tz, _ := time.LoadLocation("UTC")
|
||||
svc := service.NewEntryService(entryStore, closedDayStore, settingsStore, tz)
|
||||
daySvc := service.NewDayService(entryStore, closedDayStore, settingsStore, tz)
|
||||
|
||||
entry, _ := svc.Start(ctx, "")
|
||||
svc.Stop(ctx)
|
||||
|
||||
today := time.Now().UTC().Format("2006-01-02")
|
||||
if _, err := daySvc.CloseDay(ctx, today); err != nil {
|
||||
t.Fatalf("CloseDay: %v", err)
|
||||
}
|
||||
|
||||
note := "should fail"
|
||||
_, err = svc.Update(ctx, entry.ID, service.UpdateEntryInput{Note: ¬e})
|
||||
if err == nil {
|
||||
t.Fatal("expected error updating entry in closed day")
|
||||
}
|
||||
if !errors.Is(err, service.ErrDayAlreadyClosed) {
|
||||
t.Fatalf("expected ErrDayAlreadyClosed, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user