218 lines
5.4 KiB
Go
218 lines
5.4 KiB
Go
|
|
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")
|
||
|
|
// }
|
||
|
|
// }
|