- Add LogClosedDayDelete and LogClosedWeekDelete to SyncStore - Inject syncStore into EntryService; log Start, Stop, StopByID, Update, CreateInterval, Delete, AutoStopStalledEntries - Inject syncStore into DayService; log CloseDay, MarkDay, ReopenDay, and the recomputeWeek closed-week upsert - Inject syncStore into SettingsService; log Upsert, UpdateSettings, DeleteSettings - Add LogClosedWeek/LogClosedWeekDelete calls in WeekService.CloseWeek and ReopenWeek - Update main.go and all service test helpers for new constructor signatures - All Go tests and 19 Vitest tests pass
165 lines
4.2 KiB
Go
165 lines
4.2 KiB
Go
package service_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/wotra/wotra/internal/domain"
|
|
"github.com/wotra/wotra/internal/service"
|
|
"github.com/wotra/wotra/internal/store"
|
|
)
|
|
|
|
func newTestDayServices(t *testing.T) (*service.EntryService, *service.DayService, *service.SettingsService) {
|
|
t.Helper()
|
|
db, err := store.Open(":memory:")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { db.Close() })
|
|
|
|
entryStore := store.NewEntryStore(db)
|
|
closedDayStore := store.NewClosedDayStore(db)
|
|
closedWeekStore := store.NewClosedWeekStore(db)
|
|
settingsStore := store.NewSettingsStore(db)
|
|
syncStore := store.NewSyncStore(db)
|
|
tz, _ := time.LoadLocation("UTC")
|
|
|
|
entrySvc := service.NewEntryService(entryStore, closedDayStore, settingsStore, syncStore, tz)
|
|
daySvc := service.NewDayService(entryStore, closedDayStore, closedWeekStore, settingsStore, syncStore, tz)
|
|
settingsSvc := service.NewSettingsService(settingsStore, syncStore)
|
|
return entrySvc, daySvc, settingsSvc
|
|
}
|
|
|
|
func TestCloseDayBasic(t *testing.T) {
|
|
ctx := context.Background()
|
|
entrySvc, daySvc, _ := newTestDayServices(t)
|
|
|
|
// Start and stop an entry
|
|
_, err := entrySvc.Start(ctx, "work")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = entrySvc.Stop(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
today := time.Now().UTC().Format("2006-01-02")
|
|
cd, err := daySvc.CloseDay(ctx, today)
|
|
if err != nil {
|
|
t.Fatalf("CloseDay: %v", err)
|
|
}
|
|
if cd.Kind != domain.DayKindWork {
|
|
t.Errorf("expected kind=work, got %s", cd.Kind)
|
|
}
|
|
if cd.WorkedMs < 0 {
|
|
t.Error("expected non-negative worked_ms")
|
|
}
|
|
}
|
|
|
|
func TestCloseDayWithRunningEntryFails(t *testing.T) {
|
|
ctx := context.Background()
|
|
entrySvc, daySvc, _ := newTestDayServices(t)
|
|
|
|
_, err := entrySvc.Start(ctx, "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
today := time.Now().UTC().Format("2006-01-02")
|
|
_, err = daySvc.CloseDay(ctx, today)
|
|
if err == nil {
|
|
t.Fatal("expected error closing day with running entry")
|
|
}
|
|
if err != service.ErrRunningEntryOnDay {
|
|
t.Fatalf("expected ErrRunningEntryOnDay, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCloseDayTwiceFails(t *testing.T) {
|
|
ctx := context.Background()
|
|
entrySvc, daySvc, _ := newTestDayServices(t)
|
|
|
|
entrySvc.Start(ctx, "")
|
|
entrySvc.Stop(ctx)
|
|
today := time.Now().UTC().Format("2006-01-02")
|
|
daySvc.CloseDay(ctx, today)
|
|
|
|
_, err := daySvc.CloseDay(ctx, today)
|
|
if err != service.ErrDayAlreadyClosed {
|
|
t.Fatalf("expected ErrDayAlreadyClosed, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestMarkDayHoliday(t *testing.T) {
|
|
ctx := context.Background()
|
|
_, daySvc, _ := newTestDayServices(t)
|
|
|
|
today := time.Now().UTC().Format("2006-01-02")
|
|
cd, err := daySvc.MarkDay(ctx, today, domain.DayKindHoliday)
|
|
if err != nil {
|
|
t.Fatalf("MarkDay: %v", err)
|
|
}
|
|
if cd.Kind != domain.DayKindHoliday {
|
|
t.Errorf("expected kind=holiday, got %s", cd.Kind)
|
|
}
|
|
// Monday-Friday = 40h/5 = 8h = 28800000ms expected
|
|
if today == time.Now().UTC().Format("2006-01-02") {
|
|
wd := int(time.Now().UTC().Weekday())
|
|
// workdays Mon-Fri (mask=31): weekdays 1-5
|
|
if wd >= 1 && wd <= 5 {
|
|
if cd.WorkedMs != 8*3600*1000 {
|
|
t.Errorf("expected 8h worked_ms for holiday on workday, got %d", cd.WorkedMs)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestReopenDay(t *testing.T) {
|
|
ctx := context.Background()
|
|
entrySvc, daySvc, _ := newTestDayServices(t)
|
|
|
|
entrySvc.Start(ctx, "")
|
|
entrySvc.Stop(ctx)
|
|
today := time.Now().UTC().Format("2006-01-02")
|
|
daySvc.CloseDay(ctx, today)
|
|
|
|
if err := daySvc.ReopenDay(ctx, today); err != nil {
|
|
t.Fatalf("ReopenDay: %v", err)
|
|
}
|
|
|
|
// Should be closeable again
|
|
_, err := daySvc.CloseDay(ctx, today)
|
|
if err != nil {
|
|
t.Fatalf("CloseDay after reopen: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSettingsUpsertAndHistory(t *testing.T) {
|
|
ctx := context.Background()
|
|
_, _, settingsSvc := newTestDayServices(t)
|
|
|
|
set, err := settingsSvc.Upsert(ctx, service.UpsertSettingsInput{
|
|
EffectiveFrom: "2024-01-01",
|
|
HoursPerWeek: 38.5,
|
|
WorkdaysMask: 31,
|
|
Timezone: "Europe/Berlin",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Upsert: %v", err)
|
|
}
|
|
if set.HoursPerWeek != 38.5 {
|
|
t.Errorf("expected 38.5 h/week, got %f", set.HoursPerWeek)
|
|
}
|
|
|
|
history, err := settingsSvc.History(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Seeded default + our new one
|
|
if len(history) < 2 {
|
|
t.Fatalf("expected >=2 history entries, got %d", len(history))
|
|
}
|
|
}
|