package services import ( "context" "testing" "time" "go.uber.org/zap" "gorm.io/driver/sqlite" "gorm.io/gorm" ) func TestNewAnalyticsAggregationService(t *testing.T) { db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) if err != nil { t.Fatalf("Failed to open database: %v", err) } logger := zap.NewNop() service := NewAnalyticsAggregationService(db, logger) if service == nil { t.Error("NewAnalyticsAggregationService() returned nil") } if service.db == nil { t.Error("NewAnalyticsAggregationService() returned service with nil db") } if service.logger == nil { t.Error("NewAnalyticsAggregationService() returned service with nil logger") } } func TestAnalyticsAggregationPeriod_Constants(t *testing.T) { tests := []struct { name string period AnalyticsAggregationPeriod expected string }{ { name: "hour period", period: AnalyticsPeriodHour, expected: "hour", }, { name: "day period", period: AnalyticsPeriodDay, expected: "day", }, { name: "week period", period: AnalyticsPeriodWeek, expected: "week", }, { name: "month period", period: AnalyticsPeriodMonth, expected: "month", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if string(tt.period) != tt.expected { t.Errorf("Period = %v, want %v", tt.period, tt.expected) } }) } } func TestEventAggregationParams_Validation(t *testing.T) { now := time.Now() params := EventAggregationParams{ StartDate: now.Add(-7 * 24 * time.Hour), EndDate: now, Period: AnalyticsPeriodDay, } // Test that invalid period would be caught in AggregateEvents invalidPeriod := AnalyticsAggregationPeriod("invalid") params.Period = invalidPeriod // This would fail validation in AggregateEvents if params.Period == AnalyticsPeriodDay || params.Period == AnalyticsPeriodWeek || params.Period == AnalyticsPeriodMonth || params.Period == AnalyticsPeriodHour { t.Error("Invalid period should not match valid periods") } } func TestMin(t *testing.T) { tests := []struct { name string a int b int expected int }{ { name: "a < b", a: 5, b: 10, expected: 5, }, { name: "a > b", a: 10, b: 5, expected: 5, }, { name: "a == b", a: 5, b: 5, expected: 5, }, { name: "negative values", a: -5, b: -10, expected: -10, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := min(tt.a, tt.b) if result != tt.expected { t.Errorf("min(%d, %d) = %d, want %d", tt.a, tt.b, result, tt.expected) } }) } } func TestGetEventCounts_EmptyParams(t *testing.T) { db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) if err != nil { t.Fatalf("Failed to open database: %v", err) } service := NewAnalyticsAggregationService(db, zap.NewNop()) ctx := context.Background() now := time.Now() counts, err := service.GetEventCounts(ctx, now.Add(-7*24*time.Hour), now, []string{}) if err != nil { // Expected to fail with empty database, but should not panic if counts != nil { t.Error("GetEventCounts() should return nil counts on error") } } } func TestGetTopEvents_EmptyParams(t *testing.T) { db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) if err != nil { t.Fatalf("Failed to open database: %v", err) } service := NewAnalyticsAggregationService(db, zap.NewNop()) ctx := context.Background() now := time.Now() results, err := service.GetTopEvents(ctx, now.Add(-7*24*time.Hour), now, 10) if err != nil { // Expected to fail with empty database, but should not panic if results != nil { t.Error("GetTopEvents() should return nil results on error") } } else { // If no error, results should be empty slice, not nil if results == nil { t.Error("GetTopEvents() should return empty slice, not nil") } } } // Note: Full integration tests would require: // 1. A real PostgreSQL database with analytics_events table // 2. Test data (analytics events with various event names, user IDs, payloads) // 3. Verification of aggregation queries // 4. Performance testing with large datasets // // Example integration test structure: // func TestAnalyticsAggregationService_AggregateEvents_Integration(t *testing.T) { // // Setup test database // db := setupTestDB(t) // defer cleanupTestDB(t, db) // // // Create test data // userID1 := uuid.New() // userID2 := uuid.New() // createTestAnalyticsEvents(t, db, []AnalyticsEvent{ // {EventName: "track_play", UserID: &userID1, Payload: `{"track_id": "123"}`}, // {EventName: "track_play", UserID: &userID2, Payload: `{"track_id": "123"}`}, // {EventName: "user_login", UserID: &userID1, Payload: `{"ip": "1.2.3.4"}`}, // }) // // service := NewAnalyticsAggregationService(db, zap.NewNop()) // // ctx := context.Background() // params := EventAggregationParams{ // StartDate: time.Now().Add(-7 * 24 * time.Hour), // EndDate: time.Now(), // Period: AnalyticsPeriodDay, // } // // summary, err := service.AggregateEvents(ctx, params) // if err != nil { // t.Fatalf("AggregateEvents() error = %v", err) // } // // if summary.TotalEvents == 0 { // t.Error("AggregateEvents() returned zero total events") // } // // if len(summary.Results) == 0 { // t.Error("AggregateEvents() returned no results") // } // }