mirror of
https://github.com/SamyRai/tercul-backend.git
synced 2025-12-27 00:31:35 +00:00
This commit introduces a new event-driven analytics system to track user interactions with works and translations. The system is designed to be scalable and production-ready. Key changes: - An asynchronous event-driven architecture using `asynq` for handling analytics. - A new background worker process (`cmd/worker`) to process analytics events from a Redis-backed queue. - GraphQL resolvers now publish `AnalyticsEvent`s to the queue instead of directly calling the analytics service. - New `popularTranslations` GraphQL query to leverage the new analytics data. - Integration tests now use `miniredis` to mock Redis, making them self-contained. - The `TODO.md` file has been updated to reflect the completed work.
86 lines
2.0 KiB
Go
86 lines
2.0 KiB
Go
package analytics_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"testing"
|
|
"tercul/internal/app/analytics"
|
|
analyticsjob "tercul/internal/jobs/analytics"
|
|
"tercul/internal/testutil"
|
|
"time"
|
|
|
|
"github.com/hibiken/asynq"
|
|
"github.com/stretchr/testify/suite"
|
|
)
|
|
|
|
type WorkerIntegrationTestSuite struct {
|
|
testutil.IntegrationTestSuite
|
|
}
|
|
|
|
func TestWorkerIntegrationTestSuite(t *testing.T) {
|
|
suite.Run(t, new(WorkerIntegrationTestSuite))
|
|
}
|
|
|
|
func (s *WorkerIntegrationTestSuite) SetupTest() {
|
|
s.IntegrationTestSuite.SetupSuite(nil)
|
|
s.IntegrationTestSuite.SetupTest()
|
|
}
|
|
|
|
func (s *WorkerIntegrationTestSuite) TestWorker_ProcessTask() {
|
|
// Create a new worker
|
|
worker := analyticsjob.NewWorker(s.App.AnalyticsService)
|
|
|
|
// Create a new asynq client
|
|
redisAddr := s.Config.RedisAddr
|
|
client := asynq.NewClient(asynq.RedisClientOpt{Addr: redisAddr})
|
|
defer client.Close()
|
|
|
|
// Create a new asynq server and register the handler
|
|
srv := asynq.NewServer(
|
|
asynq.RedisClientOpt{Addr: redisAddr},
|
|
asynq.Config{
|
|
Concurrency: 1,
|
|
Queues: map[string]int{
|
|
"analytics": 1,
|
|
},
|
|
},
|
|
)
|
|
mux := asynq.NewServeMux()
|
|
mux.HandleFunc("analytics:event", worker.ProcessTask)
|
|
|
|
// Enqueue a task
|
|
work := testutil.CreateWork(s.Ctx, s.DB, "Test Work", "Test Author")
|
|
event := analytics.AnalyticsEvent{
|
|
EventType: analytics.EventTypeWorkViewed,
|
|
WorkID: &work.ID,
|
|
}
|
|
payload, err := json.Marshal(event)
|
|
s.Require().NoError(err)
|
|
|
|
task := asynq.NewTask("analytics:event", payload)
|
|
_, err = client.Enqueue(task, asynq.Queue("analytics"))
|
|
s.Require().NoError(err)
|
|
|
|
// Process the task
|
|
go func() {
|
|
err := srv.Run(mux)
|
|
s.Require().NoError(err)
|
|
}()
|
|
defer srv.Stop()
|
|
|
|
// Verify
|
|
s.Eventually(func() bool {
|
|
popular, err := s.App.AnalyticsService.GetPopularWorks(context.Background(), 10)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
for _, p := range popular {
|
|
if p.WorkID == work.ID {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}, 5*time.Second, 100*time.Millisecond, "work should be in popular list")
|
|
}
|