package reporting import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/gochat/gochat/internal/channel" "github.com/gochat/gochat/internal/model" ) func TestReportingEventListener_OnConversationCreated_Cov3(t *testing.T) { db := newTestDB(t) l := NewReportingEventListener(db) assigneeID := uint(5) conv := &model.Conversation{ Base: model.Base{ID: 1, CreatedAt: time.Now()}, AccountID: 1, InboxID: 2, AssigneeID: &assigneeID, } event := channel.NewChannelEvent(channel.EventConversationCreated, channel.ChannelAPI, 1, 2) event.Data["conversation"] = conv err := l.OnEvent(context.Background(), event) require.NoError(t, err) // Verify reporting event was created var events []ReportingEvent db.Find(&events) require.Len(t, events, 1) assert.Equal(t, "conversation_created", events[0].Name) assert.Equal(t, uint(1), events[0].AccountID) assert.NotNil(t, events[0].UserID) assert.Equal(t, assigneeID, *events[0].UserID) } func TestReportingEventListener_OnConversationCreated_NoAssignee_Cov3(t *testing.T) { db := newTestDB(t) l := NewReportingEventListener(db) conv := &model.Conversation{ Base: model.Base{ID: 1, CreatedAt: time.Now()}, AccountID: 1, InboxID: 2, } event := channel.NewChannelEvent(channel.EventConversationCreated, channel.ChannelAPI, 1, 2) event.Data["conversation"] = conv err := l.OnEvent(context.Background(), event) require.NoError(t, err) var events []ReportingEvent db.Find(&events) require.Len(t, events, 1) assert.Nil(t, events[0].UserID) } func TestReportingEventListener_OnConversationCreated_ExtractError_Cov3(t *testing.T) { db := newTestDB(t) l := NewReportingEventListener(db) // Event without conversation data — should return error event := channel.NewChannelEvent(channel.EventConversationCreated, channel.ChannelAPI, 1, 2) event.Data = map[string]interface{}{} err := l.OnEvent(context.Background(), event) assert.Error(t, err) } func TestReportingEventListener_OnConversationResolved_Cov3(t *testing.T) { db := newTestDB(t) l := NewReportingEventListener(db) assigneeID := uint(5) createdAt := time.Now().Add(-1 * time.Hour) conv := &model.Conversation{ Base: model.Base{ID: 1, CreatedAt: createdAt}, AccountID: 1, InboxID: 2, AssigneeID: &assigneeID, } event := channel.NewChannelEvent(channel.EventConversationResolved, channel.ChannelAPI, 1, 2) event.Data["conversation"] = conv err := l.OnEvent(context.Background(), event) require.NoError(t, err) var events []ReportingEvent db.Find(&events) require.Len(t, events, 2) // First event is resolution_time assert.Equal(t, "resolution_time", events[0].Name) assert.Greater(t, events[0].Value, 0.0) // Second is resolutions_count assert.Equal(t, "resolutions_count", events[1].Name) assert.Equal(t, 1.0, events[1].Value) } func TestReportingEventListener_OnConversationResolved_ExtractError_Cov3(t *testing.T) { db := newTestDB(t) l := NewReportingEventListener(db) event := channel.NewChannelEvent(channel.EventConversationResolved, channel.ChannelAPI, 1, 2) event.Data = map[string]interface{}{} err := l.OnEvent(context.Background(), event) assert.Error(t, err) } func TestReportingEventListener_OnConversationResolved_DBError_Cov3(t *testing.T) { // Close the db to trigger a DB error db := newTestDB(t) sqlDB, _ := db.DB() sqlDB.Close() l := NewReportingEventListener(db) conv := &model.Conversation{ Base: model.Base{ID: 1, CreatedAt: time.Now()}, AccountID: 1, InboxID: 2, } event := channel.NewChannelEvent(channel.EventConversationResolved, channel.ChannelAPI, 1, 2) event.Data["conversation"] = conv err := l.OnEvent(context.Background(), event) assert.Error(t, err) } func TestReportingEventListener_OnConversationAssigned_Cov3(t *testing.T) { db := newTestDB(t) l := NewReportingEventListener(db) assigneeID := uint(5) conv := &model.Conversation{ Base: model.Base{ID: 1, CreatedAt: time.Now()}, AccountID: 1, InboxID: 2, AssigneeID: &assigneeID, } event := channel.NewChannelEvent(channel.EventConversationAssigned, channel.ChannelAPI, 1, 2) event.Data["conversation"] = conv err := l.OnEvent(context.Background(), event) require.NoError(t, err) var events []ReportingEvent db.Find(&events) require.Len(t, events, 1) assert.Equal(t, "first_response", events[0].Name) } func TestReportingEventListener_OnConversationAssigned_ExtractError_Cov3(t *testing.T) { db := newTestDB(t) l := NewReportingEventListener(db) event := channel.NewChannelEvent(channel.EventConversationAssigned, channel.ChannelAPI, 1, 2) event.Data = map[string]interface{}{} err := l.OnEvent(context.Background(), event) assert.Error(t, err) } func TestReportingEventListener_OnMessageCreated_Outgoing_Cov3(t *testing.T) { db := newTestDB(t) l := NewReportingEventListener(db) senderID := uint(5) msg := &model.Message{ Base: model.Base{ID: 1, CreatedAt: time.Now()}, AccountID: 1, InboxID: 2, ConversationID: 3, MessageType: "outgoing", SenderType: "agent", SenderID: &senderID, } event := channel.NewChannelEvent(channel.EventMessageCreated, channel.ChannelAPI, 1, 2) event.Data["message"] = msg err := l.OnEvent(context.Background(), event) require.NoError(t, err) var events []ReportingEvent db.Find(&events) require.Len(t, events, 1) assert.Equal(t, "reply_time", events[0].Name) assert.Equal(t, senderID, *events[0].UserID) } func TestReportingEventListener_OnMessageCreated_Incoming_Cov3(t *testing.T) { db := newTestDB(t) l := NewReportingEventListener(db) msg := &model.Message{ Base: model.Base{ID: 1, CreatedAt: time.Now()}, AccountID: 1, InboxID: 2, ConversationID: 3, MessageType: "incoming", SenderType: "contact", } event := channel.NewChannelEvent(channel.EventMessageCreated, channel.ChannelAPI, 1, 2) event.Data["message"] = msg err := l.OnEvent(context.Background(), event) require.NoError(t, err) // Incoming messages are not tracked var events []ReportingEvent db.Find(&events) assert.Empty(t, events) } func TestReportingEventListener_OnMessageCreated_NoMessage_Cov3(t *testing.T) { db := newTestDB(t) l := NewReportingEventListener(db) event := channel.NewChannelEvent(channel.EventMessageCreated, channel.ChannelAPI, 1, 2) event.Data = map[string]interface{}{} err := l.OnEvent(context.Background(), event) require.NoError(t, err) // returns nil when no message } func TestReportingEventListener_OnMessageCreated_DBError_Cov3(t *testing.T) { db := newTestDB(t) sqlDB, _ := db.DB() sqlDB.Close() l := NewReportingEventListener(db) senderID := uint(5) msg := &model.Message{ Base: model.Base{ID: 1, CreatedAt: time.Now()}, AccountID: 1, InboxID: 2, ConversationID: 3, MessageType: "outgoing", SenderType: "agent", SenderID: &senderID, } event := channel.NewChannelEvent(channel.EventMessageCreated, channel.ChannelAPI, 1, 2) event.Data["message"] = msg err := l.OnEvent(context.Background(), event) assert.Error(t, err) } func TestReportingEventListener_OnEvent_Unknown_Cov3(t *testing.T) { db := newTestDB(t) l := NewReportingEventListener(db) event := channel.NewChannelEvent("unknown.event", channel.ChannelAPI, 1, 2) err := l.OnEvent(context.Background(), event) require.NoError(t, err) } func TestReportingEventListener_Name_Cov3(t *testing.T) { l := NewReportingEventListener(nil) assert.Equal(t, "reporting_event_listener", l.Name()) } func TestReportingService_CreateEvent_Cov3(t *testing.T) { db := newTestDB(t) svc := NewReportingService(db) now := time.Now() event := &ReportingEvent{ AccountID: 1, Name: "test_metric", Value: 42.0, EventStartTime: now, EventEndTime: now, } err := svc.CreateEvent(context.Background(), event) require.NoError(t, err) assert.NotZero(t, event.ID) } func TestReportingService_GetEvent_Cov3(t *testing.T) { db := newTestDB(t) svc := NewReportingService(db) now := time.Now() event := &ReportingEvent{ AccountID: 1, Name: "test_metric", Value: 42.0, EventStartTime: now, EventEndTime: now, } require.NoError(t, svc.CreateEvent(context.Background(), event)) got, err := svc.GetEvent(context.Background(), event.ID) require.NoError(t, err) assert.Equal(t, "test_metric", got.Name) } func TestReportingService_GetEvent_NotFound_Cov3(t *testing.T) { db := newTestDB(t) svc := NewReportingService(db) _, err := svc.GetEvent(context.Background(), 999) assert.Error(t, err) } func TestReportingService_ListEventsByAccount_Cov3(t *testing.T) { db := newTestDB(t) svc := NewReportingService(db) now := time.Now() event1 := &ReportingEvent{AccountID: 1, Name: "m1", Value: 1, EventStartTime: now, EventEndTime: now} event2 := &ReportingEvent{AccountID: 1, Name: "m2", Value: 2, EventStartTime: now, EventEndTime: now} event3 := &ReportingEvent{AccountID: 2, Name: "m3", Value: 3, EventStartTime: now, EventEndTime: now} require.NoError(t, svc.CreateEvent(context.Background(), event1)) require.NoError(t, svc.CreateEvent(context.Background(), event2)) require.NoError(t, svc.CreateEvent(context.Background(), event3)) events, err := svc.ListEventsByAccount(context.Background(), 1, now.Add(-1*time.Hour), now.Add(1*time.Hour)) require.NoError(t, err) assert.Len(t, events, 2) } func TestReportingService_ListEventsByAccountAndName_Cov3(t *testing.T) { db := newTestDB(t) svc := NewReportingService(db) now := time.Now() event1 := &ReportingEvent{AccountID: 1, Name: "reply_time", Value: 1, EventStartTime: now, EventEndTime: now} event2 := &ReportingEvent{AccountID: 1, Name: "resolution_time", Value: 2, EventStartTime: now, EventEndTime: now} require.NoError(t, svc.CreateEvent(context.Background(), event1)) require.NoError(t, svc.CreateEvent(context.Background(), event2)) events, err := svc.ListEventsByAccountAndName(context.Background(), 1, "reply_time", now.Add(-1*time.Hour), now.Add(1*time.Hour)) require.NoError(t, err) assert.Len(t, events, 1) assert.Equal(t, "reply_time", events[0].Name) } func TestReportingService_CreateRollup_Cov3(t *testing.T) { db := newTestDB(t) svc := NewReportingService(db) rollup := &ReportingEventsRollup{ AccountID: 1, Date: time.Now().Truncate(24 * time.Hour), DimensionType: DimensionAccount, DimensionID: 1, Metric: MetricResolutionsCount, Count: 5, SumValue: 100.0, } err := svc.CreateRollup(context.Background(), rollup) require.NoError(t, err) } func TestReportingService_GetRollups_Cov3(t *testing.T) { db := newTestDB(t) svc := NewReportingService(db) now := time.Now().Truncate(24 * time.Hour) rollup := &ReportingEventsRollup{ AccountID: 1, Date: now, DimensionType: DimensionAccount, DimensionID: 1, Metric: MetricResolutionsCount, Count: 5, } require.NoError(t, svc.CreateRollup(context.Background(), rollup)) rollups, err := svc.GetRollups(context.Background(), 1, DimensionAccount, 1, now.Add(-1*time.Hour), now.Add(1*time.Hour)) require.NoError(t, err) assert.Len(t, rollups, 1) } func TestReportingService_GetRollupsByMetric_Cov3(t *testing.T) { db := newTestDB(t) svc := NewReportingService(db) now := time.Now().Truncate(24 * time.Hour) rollup := &ReportingEventsRollup{ AccountID: 1, Date: now, DimensionType: DimensionAccount, DimensionID: 1, Metric: MetricReplyTime, Count: 3, } require.NoError(t, svc.CreateRollup(context.Background(), rollup)) rollups, err := svc.GetRollupsByMetric(context.Background(), 1, DimensionAccount, 1, MetricReplyTime, now.Add(-1*time.Hour), now.Add(1*time.Hour)) require.NoError(t, err) assert.Len(t, rollups, 1) } func TestReportingService_DeleteRollupsByDate_Cov3(t *testing.T) { db := newTestDB(t) svc := NewReportingService(db) now := time.Now().Truncate(24 * time.Hour) rollup := &ReportingEventsRollup{ AccountID: 1, Date: now, DimensionType: DimensionAccount, DimensionID: 1, Metric: MetricReplyTime, } require.NoError(t, svc.CreateRollup(context.Background(), rollup)) err := svc.DeleteRollupsByDate(context.Background(), 1, now.Add(-1*time.Hour), now.Add(1*time.Hour)) require.NoError(t, err) rollups, _ := svc.GetRollups(context.Background(), 1, DimensionAccount, 1, now.Add(-1*time.Hour), now.Add(1*time.Hour)) assert.Empty(t, rollups) } // --- Metric registry tests --- func TestGetMetricDefinition_Cov3(t *testing.T) { def, ok := GetMetricDefinition("avg_first_response_time") assert.True(t, ok) assert.Equal(t, "first_response", def.RawEventName) } func TestGetMetricDefinition_NotFound_Cov3(t *testing.T) { _, ok := GetMetricDefinition("nonexistent") assert.False(t, ok) } func TestAllMetricKeys_Cov3(t *testing.T) { keys := AllMetricKeys() assert.NotEmpty(t, keys) assert.Contains(t, keys, "avg_first_response_time") assert.Contains(t, keys, "resolutions_count") }