package linguistics import ( "context" "tercul/internal/domain" "testing" "github.com/stretchr/testify/require" ) type fakeAnalysisRepo struct { work *domain.Work readability *domain.ReadabilityScore languageAnalysis *domain.LanguageAnalysis } func (f *fakeAnalysisRepo) StoreAnalysisResults(ctx context.Context, workID uint, result *AnalysisResult) error { return nil } func (f *fakeAnalysisRepo) GetWorkContent(ctx context.Context, workID uint, language string) (string, error) { return "", nil } func (f *fakeAnalysisRepo) StoreWorkAnalysis(ctx context.Context, workID uint, textMetadata *domain.TextMetadata, readabilityScore *domain.ReadabilityScore, languageAnalysis *domain.LanguageAnalysis) error { return nil } func (f *fakeAnalysisRepo) GetWorkByID(ctx context.Context, workID uint) (*domain.Work, error) { return f.work, nil } func (f *fakeAnalysisRepo) GetAnalysisData(ctx context.Context, workID uint) (*domain.TextMetadata, *domain.ReadabilityScore, *domain.LanguageAnalysis, error) { return nil, f.readability, f.languageAnalysis, nil } type fakeStatsRepo struct { workStats *domain.WorkStats translationStats map[uint]*domain.TranslationStats } func (f *fakeStatsRepo) GetOrCreateWorkStats(ctx context.Context, workID uint) (*domain.WorkStats, error) { return f.workStats, nil } func (f *fakeStatsRepo) GetOrCreateTranslationStats(ctx context.Context, translationID uint) (*domain.TranslationStats, error) { if f.translationStats == nil { return nil, nil } return f.translationStats[translationID], nil } type fakeTranslationLister struct { items []domain.Translation } func (f *fakeTranslationLister) ListByWorkID(ctx context.Context, workID uint) ([]domain.Translation, error) { return f.items, nil } func TestGetWorkAnalytics_UsesStatsAndPopularTranslations(t *testing.T) { ctx := context.Background() work := &domain.Work{} work.ID = 1 work.Title = "W" work.Language = "en" analysisRepo := &fakeAnalysisRepo{ work: work, readability: &domain.ReadabilityScore{Score: 12.34}, languageAnalysis: &domain.LanguageAnalysis{Analysis: domain.JSONB{ "sentiment": 0.5, "keywords": []interface{}{ map[string]interface{}{"text": "alpha"}, map[string]interface{}{"text": "beta"}, }, }}, } statsRepo := &fakeStatsRepo{ workStats: &domain.WorkStats{Views: 10, Likes: 2, Comments: 3, Bookmarks: 4, TranslationCount: 5}, translationStats: map[uint]*domain.TranslationStats{ 100: {Views: 7, Likes: 1, TranslationID: 100}, 101: {Views: 7, Likes: 2, TranslationID: 101}, }, } translations := &fakeTranslationLister{items: []domain.Translation{ {BaseModel: domain.BaseModel{ID: 100}, Language: "fr"}, {BaseModel: domain.BaseModel{ID: 101}, Language: "es"}, }} svc := NewWorkAnalysisService(nil, nil, analysisRepo, WorkAnalyticsDeps{ StatsRepo: statsRepo, TranslationList: translations, }, 1, false) got, err := svc.GetWorkAnalytics(ctx, 1) require.NoError(t, err) require.Equal(t, int64(10), got.ViewCount) require.Equal(t, int64(2), got.LikeCount) require.Equal(t, int64(3), got.CommentCount) require.Equal(t, int64(4), got.BookmarkCount) require.Equal(t, int64(5), got.TranslationCount) require.InDelta(t, 12.34, got.ReadabilityScore, 0.0001) require.InDelta(t, 0.5, got.SentimentScore, 0.0001) require.Equal(t, []string{"alpha", "beta"}, got.TopKeywords) require.Len(t, got.PopularTranslations, 2) // tie on views (7), break by likes desc require.Equal(t, uint(101), got.PopularTranslations[0].TranslationID) require.Equal(t, uint(100), got.PopularTranslations[1].TranslationID) }