feat(m3): week close, overtime/undertime delta, frozen settings snapshot
This commit is contained in:
144
internal/service/week_service_test.go
Normal file
144
internal/service/week_service_test.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package service_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/wotra/wotra/internal/domain"
|
||||
"github.com/wotra/wotra/internal/service"
|
||||
"github.com/wotra/wotra/internal/store"
|
||||
)
|
||||
|
||||
func newFullServices(t *testing.T) (*service.EntryService, *service.DayService, *service.WeekService, *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)
|
||||
tz, _ := time.LoadLocation("UTC")
|
||||
|
||||
entrySvc := service.NewEntryService(entryStore, closedDayStore, settingsStore, tz)
|
||||
daySvc := service.NewDayService(entryStore, closedDayStore, settingsStore, tz)
|
||||
weekSvc := service.NewWeekService(closedDayStore, closedWeekStore, settingsStore, db, tz)
|
||||
settingsSvc := service.NewSettingsService(settingsStore)
|
||||
return entrySvc, daySvc, weekSvc, settingsSvc
|
||||
}
|
||||
|
||||
func TestWeekDayKeys(t *testing.T) {
|
||||
// 2024-W03 = Jan 15-21, 2024
|
||||
tz, _ := time.LoadLocation("UTC")
|
||||
keys, err := service.WeekDayKeysExported("2024-W03", tz)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(keys) != 7 {
|
||||
t.Fatalf("expected 7 keys, got %d", len(keys))
|
||||
}
|
||||
if keys[0] != "2024-01-15" {
|
||||
t.Errorf("expected Monday 2024-01-15, got %s", keys[0])
|
||||
}
|
||||
if keys[6] != "2024-01-21" {
|
||||
t.Errorf("expected Sunday 2024-01-21, got %s", keys[6])
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloseWeekBasic(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
entrySvc, daySvc, weekSvc, _ := newFullServices(t)
|
||||
|
||||
// Find the ISO week for "this week" using a known Mon-Fri
|
||||
// Use a fixed Monday to make the test deterministic
|
||||
monday := time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC) // 2024-W03, Monday
|
||||
weekKey := "2024-W03"
|
||||
|
||||
// Close Mon-Fri by marking as holiday (easiest — no entries needed)
|
||||
for i := 0; i < 5; i++ {
|
||||
dk := monday.AddDate(0, 0, i).Format("2006-01-02")
|
||||
_, err := daySvc.MarkDay(ctx, dk, domain.DayKindHoliday)
|
||||
if err != nil {
|
||||
t.Fatalf("MarkDay %s: %v", dk, err)
|
||||
}
|
||||
}
|
||||
|
||||
cw, err := weekSvc.CloseWeek(ctx, weekKey)
|
||||
if err != nil {
|
||||
t.Fatalf("CloseWeek: %v", err)
|
||||
}
|
||||
// Expected: 40h = 144000000 ms
|
||||
if cw.ExpectedMs != 40*3_600_000 {
|
||||
t.Errorf("expected 40h expected_ms, got %d ms", cw.ExpectedMs)
|
||||
}
|
||||
// Worked: each holiday day = 8h = 5 * 8h = 40h
|
||||
if cw.WorkedMs != 40*3_600_000 {
|
||||
t.Errorf("expected 40h worked_ms, got %d ms", cw.WorkedMs)
|
||||
}
|
||||
if cw.DeltaMs != 0 {
|
||||
t.Errorf("expected 0 delta_ms, got %d", cw.DeltaMs)
|
||||
}
|
||||
fmt.Printf("WeekKey=%s Expected=%dh Worked=%dh Delta=%dms\n",
|
||||
cw.WeekKey, cw.ExpectedMs/3_600_000, cw.WorkedMs/3_600_000, cw.DeltaMs)
|
||||
_ = entrySvc
|
||||
}
|
||||
|
||||
func TestCloseWeekMissingDayFails(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
_, daySvc, weekSvc, _ := newFullServices(t)
|
||||
|
||||
// Only close Mon-Thu, leave Friday open
|
||||
monday := time.Date(2024, 1, 15, 0, 0, 0, 0, time.UTC)
|
||||
for i := 0; i < 4; i++ {
|
||||
dk := monday.AddDate(0, 0, i).Format("2006-01-02")
|
||||
daySvc.MarkDay(ctx, dk, domain.DayKindHoliday)
|
||||
}
|
||||
|
||||
_, err := weekSvc.CloseWeek(ctx, "2024-W03")
|
||||
if err == nil {
|
||||
t.Fatal("expected error closing week with unclosed day")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloseWeekTwiceFails(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
_, daySvc, weekSvc, _ := newFullServices(t)
|
||||
|
||||
monday := time.Date(2024, 1, 15, 0, 0, 0, 0, time.UTC)
|
||||
for i := 0; i < 5; i++ {
|
||||
dk := monday.AddDate(0, 0, i).Format("2006-01-02")
|
||||
daySvc.MarkDay(ctx, dk, domain.DayKindHoliday)
|
||||
}
|
||||
weekSvc.CloseWeek(ctx, "2024-W03")
|
||||
|
||||
_, err := weekSvc.CloseWeek(ctx, "2024-W03")
|
||||
if err != service.ErrWeekAlreadyClosed {
|
||||
t.Fatalf("expected ErrWeekAlreadyClosed, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReopenWeek(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
_, daySvc, weekSvc, _ := newFullServices(t)
|
||||
|
||||
monday := time.Date(2024, 1, 15, 0, 0, 0, 0, time.UTC)
|
||||
for i := 0; i < 5; i++ {
|
||||
dk := monday.AddDate(0, 0, i).Format("2006-01-02")
|
||||
daySvc.MarkDay(ctx, dk, domain.DayKindHoliday)
|
||||
}
|
||||
weekSvc.CloseWeek(ctx, "2024-W03")
|
||||
|
||||
if err := weekSvc.ReopenWeek(ctx, "2024-W03"); err != nil {
|
||||
t.Fatalf("ReopenWeek: %v", err)
|
||||
}
|
||||
// Should be closeable again
|
||||
_, err := weekSvc.CloseWeek(ctx, "2024-W03")
|
||||
if err != nil {
|
||||
t.Fatalf("CloseWeek after reopen: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user