mirror of
https://github.com/SamyRai/tercul-backend.git
synced 2025-12-27 00:31:35 +00:00
This commit implements a robust, production-ready analytics system using an event-driven architecture with Redis and `asynq`. Key changes: - Event-Driven Architecture: Instead of synchronous database updates, analytics events (e.g., views, likes, comments) are now published to a Redis queue. This improves API response times and decouples the analytics system from the main application flow. - Background Worker: A new worker process (`cmd/worker`) has been created to consume events from the queue and update the analytics counters in the database. - View Counting: Implemented the missing view counting feature for both works and translations. - New Analytics Query: Added a `popularTranslations` GraphQL query to demonstrate how to use the collected analytics data. - Testing: Added unit tests for the new event publisher and integration tests for the analytics worker. Known Issue: The integration tests for the analytics worker (`AnalyticsWorkerSuite`) and the GraphQL API (`GraphQLIntegrationSuite`) are currently failing due to the lack of a Redis service in the test environment. The tests are written and are expected to pass in an environment where Redis is available on `localhost:6379`, as configured in the CI pipeline.
55 lines
1.4 KiB
Go
55 lines
1.4 KiB
Go
package analytics_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"testing"
|
|
"tercul/internal/app/analytics"
|
|
"time"
|
|
|
|
"github.com/hibiken/asynq"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
type mockAsynqClient struct {
|
|
asynq.Client
|
|
enqueuedTasks []*asynq.Task
|
|
}
|
|
|
|
func (m *mockAsynqClient) EnqueueContext(ctx context.Context, task *asynq.Task, opts ...asynq.Option) (*asynq.TaskInfo, error) {
|
|
m.enqueuedTasks = append(m.enqueuedTasks, task)
|
|
return &asynq.TaskInfo{}, nil
|
|
}
|
|
|
|
func (m *mockAsynqClient) Close() error {
|
|
return nil
|
|
}
|
|
|
|
func TestAsynqEventPublisher_Publish(t *testing.T) {
|
|
mockClient := &mockAsynqClient{}
|
|
publisher := analytics.NewEventPublisher(mockClient)
|
|
|
|
workID := uint(123)
|
|
userID := uint(456)
|
|
event := analytics.AnalyticsEvent{
|
|
EventType: analytics.EventTypeWorkLiked,
|
|
WorkID: &workID,
|
|
UserID: &userID,
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
err := publisher.Publish(context.Background(), event)
|
|
assert.NoError(t, err)
|
|
|
|
assert.Len(t, mockClient.enqueuedTasks, 1)
|
|
task := mockClient.enqueuedTasks[0]
|
|
assert.Equal(t, string(analytics.EventTypeWorkLiked), task.Type())
|
|
|
|
var publishedEvent analytics.AnalyticsEvent
|
|
err = json.Unmarshal(task.Payload(), &publishedEvent)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, event.EventType, publishedEvent.EventType)
|
|
assert.Equal(t, *event.WorkID, *publishedEvent.WorkID)
|
|
assert.Equal(t, *event.UserID, *publishedEvent.UserID)
|
|
}
|