package api import ( "testing" "time" ) func TestCalculateTrend(t *testing.T) { tests := []struct { name string current int64 previous int64 wantDelta *float64 wantDirection string }{ { name: "zero previous, zero current - stable", current: 0, previous: 0, wantDelta: nil, wantDirection: "stable", }, { name: "zero previous, positive current - new", current: 100, previous: 0, wantDelta: nil, wantDirection: "new", }, { name: "100% increase - up", current: 200, previous: 100, wantDelta: ptr(100.0), wantDirection: "up", }, { name: "50% decrease - down", current: 50, previous: 100, wantDelta: ptr(-50.0), wantDirection: "down", }, { name: "0.3% increase - stable", current: 1003, previous: 1000, wantDelta: ptr(0.3), wantDirection: "stable", }, { name: "0.5% increase - stable (boundary)", current: 1005, previous: 1000, wantDelta: ptr(0.5), wantDirection: "stable", }, { name: "0.6% increase - up", current: 1006, previous: 1000, wantDelta: ptr(0.6), wantDirection: "up", }, { name: "0.6% decrease - down", current: 994, previous: 1000, wantDelta: ptr(-0.6), wantDirection: "down", }, { name: "no change - stable", current: 100, previous: 100, wantDelta: ptr(0.0), wantDirection: "stable", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := CalculateTrend(tt.current, tt.previous) if tt.wantDelta == nil { if got.Delta != nil { t.Errorf("expected nil delta, got %v", *got.Delta) } } else { if got.Delta == nil { t.Errorf("expected delta %v, got nil", *tt.wantDelta) } else if *got.Delta != *tt.wantDelta { t.Errorf("expected delta %v, got %v", *tt.wantDelta, *got.Delta) } } if got.Direction != tt.wantDirection { t.Errorf("expected direction %q, got %q", tt.wantDirection, got.Direction) } }) } } func TestCalculateTrendFloat(t *testing.T) { tests := []struct { name string current float64 previous float64 wantDelta *float64 wantDirection string }{ { name: "zero previous, zero current - stable", current: 0, previous: 0, wantDelta: nil, wantDirection: "stable", }, { name: "zero previous, positive current - new", current: 0.5, previous: 0, wantDelta: nil, wantDirection: "new", }, { name: "50% increase - up", current: 0.15, previous: 0.10, wantDelta: ptr(50.0), wantDirection: "up", }, { name: "33.3% decrease - down", current: 0.10, previous: 0.15, wantDelta: ptr(-33.3), wantDirection: "down", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := CalculateTrendFloat(tt.current, tt.previous) if tt.wantDelta == nil { if got.Delta != nil { t.Errorf("expected nil delta, got %v", *got.Delta) } } else { if got.Delta == nil { t.Errorf("expected delta %v, got nil", *tt.wantDelta) } else if *got.Delta != *tt.wantDelta { t.Errorf("expected delta %v, got %v", *tt.wantDelta, *got.Delta) } } if got.Direction != tt.wantDirection { t.Errorf("expected direction %q, got %q", tt.wantDirection, got.Direction) } }) } } func TestPreviousPeriodWindow(t *testing.T) { // These tests verify the structure of the period calculation // Not testing exact timestamps since they depend on current time tests := []struct { period string expectZero bool expectWindow bool }{ {"today", false, true}, {"last7d", false, true}, {"last30d", false, true}, {"week", false, true}, {"month", false, true}, {"all", true, false}, {"invalid", true, false}, } for _, tt := range tests { t.Run(tt.period, func(t *testing.T) { start, end := previousPeriodWindow(tt.period) if tt.expectZero { if !start.IsZero() || !end.IsZero() { t.Errorf("expected zero times for period %q", tt.period) } } else { if start.IsZero() || end.IsZero() { t.Errorf("expected non-zero times for period %q", tt.period) } if !start.Before(end) { t.Errorf("expected start < end for period %q", tt.period) } } }) } } func TestPreviousPeriodWindowToday(t *testing.T) { start, end := previousPeriodWindow("today") // Previous period for "today" should be "yesterday" now := time.Now().UTC() startOfToday := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC) yesterday := startOfToday.AddDate(0, 0, -1) if !start.Equal(yesterday) { t.Errorf("expected start to be yesterday (%v), got %v", yesterday, start) } if !end.Equal(startOfToday) { t.Errorf("expected end to be start of today (%v), got %v", startOfToday, end) } } func TestPreviousPeriodWindowLast7d(t *testing.T) { start, end := previousPeriodWindow("last7d") now := time.Now().UTC() expected14DaysAgo := now.AddDate(0, 0, -14) expected7DaysAgo := now.AddDate(0, 0, -7) // Allow 1 second tolerance for test execution time if start.Sub(expected14DaysAgo).Abs() > time.Second { t.Errorf("expected start ~14 days ago, got %v (expected %v)", start, expected14DaysAgo) } if end.Sub(expected7DaysAgo).Abs() > time.Second { t.Errorf("expected end ~7 days ago, got %v (expected %v)", end, expected7DaysAgo) } } func ptr(v float64) *float64 { return &v }