package analytics_test import ( "context" "strings" "testing" "tercul/internal/app/analytics" "tercul/internal/data/sql" "tercul/internal/domain" "tercul/internal/jobs/linguistics" "tercul/internal/platform/config" "tercul/internal/testutil" "github.com/stretchr/testify/suite" ) // AnalyticsServiceTestSuite is a test suite for the analytics service. // It embeds the IntegrationTestSuite to get access to the database, app, etc. type AnalyticsServiceTestSuite struct { testutil.IntegrationTestSuite service analytics.Service } // SetupSuite sets up the test suite with a real database and a real analytics service. func (s *AnalyticsServiceTestSuite) SetupSuite() { // Call the parent suite's setup s.IntegrationTestSuite.SetupSuite(nil) // Create a real analytics service with the test database cfg, err := config.LoadConfig() s.Require().NoError(err) analyticsRepo := sql.NewAnalyticsRepository(s.DB, cfg) analysisRepo := linguistics.NewGORMAnalysisRepository(s.DB) translationRepo := sql.NewTranslationRepository(s.DB, cfg) workRepo := sql.NewWorkRepository(s.DB, cfg) sentimentProvider, err := linguistics.NewGoVADERSentimentProvider() s.Require().NoError(err) // Create the service to be tested s.service = analytics.NewService(analyticsRepo, analysisRepo, translationRepo, workRepo, sentimentProvider) } // SetupTest cleans the database before each test. func (s *AnalyticsServiceTestSuite) SetupTest() { s.IntegrationTestSuite.SetupTest() s.DB.Exec("DELETE FROM trendings") } func (s *AnalyticsServiceTestSuite) TestIncrementWorkViews() { s.Run("should increment the view count for a work", func() { // Arrange work := s.CreateTestWork("Test Work", "en", "Test content") // Act err := s.service.IncrementWorkViews(context.Background(), work.ID) s.Require().NoError(err) // Assert stats, err := s.service.GetOrCreateWorkStats(context.Background(), work.ID) s.Require().NoError(err) s.Equal(int64(1), stats.Views) }) } func (s *AnalyticsServiceTestSuite) TestIncrementWorkLikes() { s.Run("should increment the like count for a work", func() { // Arrange work := s.CreateTestWork("Test Work", "en", "Test content") // Act err := s.service.IncrementWorkLikes(context.Background(), work.ID) s.Require().NoError(err) // Assert stats, err := s.service.GetOrCreateWorkStats(context.Background(), work.ID) s.Require().NoError(err) s.Equal(int64(1), stats.Likes) }) } func (s *AnalyticsServiceTestSuite) TestIncrementWorkComments() { s.Run("should increment the comment count for a work", func() { // Arrange work := s.CreateTestWork("Test Work", "en", "Test content") // Act err := s.service.IncrementWorkComments(context.Background(), work.ID) s.Require().NoError(err) // Assert stats, err := s.service.GetOrCreateWorkStats(context.Background(), work.ID) s.Require().NoError(err) s.Equal(int64(1), stats.Comments) }) } func (s *AnalyticsServiceTestSuite) TestIncrementWorkBookmarks() { s.Run("should increment the bookmark count for a work", func() { // Arrange work := s.CreateTestWork("Test Work", "en", "Test content") // Act err := s.service.IncrementWorkBookmarks(context.Background(), work.ID) s.Require().NoError(err) // Assert stats, err := s.service.GetOrCreateWorkStats(context.Background(), work.ID) s.Require().NoError(err) s.Equal(int64(1), stats.Bookmarks) }) } func (s *AnalyticsServiceTestSuite) TestIncrementWorkShares() { s.Run("should increment the share count for a work", func() { // Arrange work := s.CreateTestWork("Test Work", "en", "Test content") // Act err := s.service.IncrementWorkShares(context.Background(), work.ID) s.Require().NoError(err) // Assert stats, err := s.service.GetOrCreateWorkStats(context.Background(), work.ID) s.Require().NoError(err) s.Equal(int64(1), stats.Shares) }) } func (s *AnalyticsServiceTestSuite) TestIncrementWorkTranslationCount() { s.Run("should increment the translation count for a work", func() { // Arrange work := s.CreateTestWork("Test Work", "en", "Test content") // Act err := s.service.IncrementWorkTranslationCount(context.Background(), work.ID) s.Require().NoError(err) // Assert stats, err := s.service.GetOrCreateWorkStats(context.Background(), work.ID) s.Require().NoError(err) s.Equal(int64(1), stats.TranslationCount) }) } func (s *AnalyticsServiceTestSuite) TestUpdateWorkReadingTime() { s.Run("should update the reading time for a work", func() { // Arrange work := s.CreateTestWork("Test Work", "en", "Test content") s.DB.Create(&domain.ReadabilityScore{WorkID: work.ID}) s.DB.Create(&domain.LanguageAnalysis{WorkID: work.ID, Analysis: domain.JSONB{}}) textMetadata := &domain.TextMetadata{ WorkID: work.ID, WordCount: 1000, } s.DB.Create(textMetadata) // Act err := s.service.UpdateWorkReadingTime(context.Background(), work.ID) s.Require().NoError(err) // Assert stats, err := s.service.GetOrCreateWorkStats(context.Background(), work.ID) s.Require().NoError(err) s.Equal(5, stats.ReadingTime) // 1000 words / 200 wpm = 5 minutes }) } func (s *AnalyticsServiceTestSuite) TestUpdateTranslationReadingTime() { s.Run("should update the reading time for a translation", func() { // Arrange work := s.CreateTestWork("Test Work", "en", "Test content") translation := s.CreateTestTranslation(work.ID, "es", strings.Repeat("Contenido de prueba con quinientas palabras. ", 100)) // Act err := s.service.UpdateTranslationReadingTime(context.Background(), translation.ID) s.Require().NoError(err) // Assert stats, err := s.service.GetOrCreateTranslationStats(context.Background(), translation.ID) s.Require().NoError(err) s.Equal(3, stats.ReadingTime) // 500 words / 200 wpm = 2.5 -> 3 minutes }) } func (s *AnalyticsServiceTestSuite) TestUpdateWorkComplexity() { s.Run("should update the complexity for a work", func() { // Arrange work := s.CreateTestWork("Test Work", "en", "Test content") s.DB.Create(&domain.TextMetadata{WorkID: work.ID}) s.DB.Create(&domain.LanguageAnalysis{WorkID: work.ID, Analysis: domain.JSONB{}}) readabilityScore := &domain.ReadabilityScore{ WorkID: work.ID, Score: 12.34, } s.DB.Create(readabilityScore) // Act err := s.service.UpdateWorkComplexity(context.Background(), work.ID) s.Require().NoError(err) // Assert stats, err := s.service.GetOrCreateWorkStats(context.Background(), work.ID) s.Require().NoError(err) s.Equal(12.34, stats.Complexity) }) } func (s *AnalyticsServiceTestSuite) TestUpdateWorkSentiment() { s.Run("should update the sentiment for a work", func() { // Arrange work := s.CreateTestWork("Test Work", "en", "Test content") s.DB.Create(&domain.TextMetadata{WorkID: work.ID}) s.DB.Create(&domain.ReadabilityScore{WorkID: work.ID}) languageAnalysis := &domain.LanguageAnalysis{ WorkID: work.ID, Analysis: domain.JSONB{ "sentiment": 0.5678, }, } s.DB.Create(languageAnalysis) // Act err := s.service.UpdateWorkSentiment(context.Background(), work.ID) s.Require().NoError(err) // Assert stats, err := s.service.GetOrCreateWorkStats(context.Background(), work.ID) s.Require().NoError(err) s.Equal(0.5678, stats.Sentiment) }) } func (s *AnalyticsServiceTestSuite) TestUpdateTranslationSentiment() { s.Run("should update the sentiment for a translation", func() { // Arrange work := s.CreateTestWork("Test Work", "en", "Test content") translation := s.CreateTestTranslation(work.ID, "en", "This is a wonderfully positive and uplifting sentence.") // Act err := s.service.UpdateTranslationSentiment(context.Background(), translation.ID) s.Require().NoError(err) // Assert stats, err := s.service.GetOrCreateTranslationStats(context.Background(), translation.ID) s.Require().NoError(err) s.True(stats.Sentiment > 0.5) }) } func (s *AnalyticsServiceTestSuite) TestUpdateTrending() { s.Run("should update the trending works", func() { // Arrange work1 := s.CreateTestWork("Work 1", "en", "content") work2 := s.CreateTestWork("Work 2", "en", "content") s.DB.Create(&domain.WorkStats{WorkID: work1.ID, Views: 100, Likes: 10, Comments: 1}) s.DB.Create(&domain.WorkStats{WorkID: work2.ID, Views: 10, Likes: 100, Comments: 10}) // Act err := s.service.UpdateTrending(context.Background()) s.Require().NoError(err) // Assert var trendingWorks []*domain.Trending s.DB.Order("rank asc").Find(&trendingWorks) s.Require().Len(trendingWorks, 2) s.Equal(work2.ID, trendingWorks[0].EntityID) s.Equal(work1.ID, trendingWorks[1].EntityID) }) } // TestAnalyticsService runs the full test suite. func TestAnalyticsService(t *testing.T) { suite.Run(t, new(AnalyticsServiceTestSuite)) }