tercul-backend/internal/jobs/linguistics/text_analyzer_test.go
Damir Mukimov d50722dad5
Some checks failed
Test / Integration Tests (push) Successful in 4s
Build / Build Binary (push) Failing after 2m9s
Docker Build / Build Docker Image (push) Failing after 2m32s
Test / Unit Tests (push) Failing after 3m12s
Lint / Go Lint (push) Failing after 1m0s
Refactor ID handling to use UUIDs across the application
- Updated database models and repositories to replace uint IDs with UUIDs.
- Modified test fixtures to generate and use UUIDs for authors, translations, users, and works.
- Adjusted mock implementations to align with the new UUID structure.
- Ensured all relevant functions and methods are updated to handle UUIDs correctly.
- Added necessary imports for UUID handling in various files.
2025-12-27 00:33:34 +01:00

163 lines
5.2 KiB
Go

package linguistics
import (
"context"
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Mocks for provider interfaces
type mockLangDetector struct {
lang string
err error
}
func (m mockLangDetector) DetectLanguage(text string) (string, error) { return m.lang, m.err }
type mockSentimentProvider struct {
score float64
err error
}
func (m mockSentimentProvider) Score(text string, language string) (float64, error) {
return m.score, m.err
}
type mockKeywordProvider struct {
kws []Keyword
err error
}
func (m mockKeywordProvider) Extract(text string, language string) ([]Keyword, error) {
return m.kws, m.err
}
func TestAnalyzeText_Empty(t *testing.T) {
a := NewBasicTextAnalyzer()
res, err := a.AnalyzeText(context.Background(), "", "")
require.NoError(t, err)
assert.NotNil(t, res)
assert.Equal(t, 0, res.WordCount)
assert.Equal(t, 0, res.SentenceCount)
assert.Equal(t, 0.0, res.Sentiment)
assert.Len(t, res.Keywords, 0)
}
func TestAnalyzeText_ProvidersAndLangDetection(t *testing.T) {
// Arrange
a := NewBasicTextAnalyzer().
WithLanguageDetector(mockLangDetector{lang: "en", err: nil}).
WithSentimentProvider(mockSentimentProvider{score: 0.75}).
WithKeywordProvider(mockKeywordProvider{kws: []Keyword{{Text: "golang", Relevance: 0.42}}})
text := "Go is great. Go makes concurrency easier."
// Act
res, err := a.AnalyzeText(context.Background(), text, "")
// Assert
require.NoError(t, err)
require.NotNil(t, res)
assert.InDelta(t, 0.75, res.Sentiment, 1e-9)
require.Len(t, res.Keywords, 1)
assert.Equal(t, "golang", res.Keywords[0].Text)
assert.InDelta(t, 0.42, res.Keywords[0].Relevance, 1e-9)
// Basic stats make sense
assert.Greater(t, res.WordCount, 0)
assert.Greater(t, res.SentenceCount, 0)
// Readability is clamped to [0,100]
assert.GreaterOrEqual(t, res.ReadabilityScore, 0.0)
assert.LessOrEqual(t, res.ReadabilityScore, 100.0)
assert.Equal(t, "Simplified Flesch-Kincaid", res.ReadabilityMethod)
}
func TestAnalyzeText_FallbackOnProviderError(t *testing.T) {
// Arrange providers that fail so analyzer uses internal fallbacks
a := NewBasicTextAnalyzer().
WithSentimentProvider(mockSentimentProvider{err: errors.New("boom")}).
WithKeywordProvider(mockKeywordProvider{err: errors.New("boom")})
text := "I love good code but hate terrible bugs."
// Act
res, err := a.AnalyzeText(context.Background(), text, "en")
// Assert
require.NoError(t, err)
require.NotNil(t, res)
// Fallback sentiment should be between -1 and 1; with mixed words it should be non-zero
assert.GreaterOrEqual(t, res.Sentiment, -1.0)
assert.LessOrEqual(t, res.Sentiment, 1.0)
// Keywords should come from fallback extractor and be non-empty for this text
assert.NotEmpty(t, res.Keywords)
}
func TestAnalyzeTextConcurrently_AggregatesWithProviders(t *testing.T) {
// Providers return consistent values regardless of input
kw := []Keyword{{Text: "constant", Relevance: 0.3}}
a := NewBasicTextAnalyzer().
WithLanguageDetector(mockLangDetector{lang: "en", err: nil}).
WithSentimentProvider(mockSentimentProvider{score: 0.5}).
WithKeywordProvider(mockKeywordProvider{kws: kw})
text := "One sentence. Another sentence! And a question? Final one."
// Act
_, err1 := a.AnalyzeText(context.Background(), text, "")
conc, err2 := a.AnalyzeTextConcurrently(context.Background(), text, "", 3)
// Assert
require.NoError(t, err1)
require.NoError(t, err2)
// Basic stats: should be sane
assert.Greater(t, conc.WordCount, 0)
assert.GreaterOrEqual(t, conc.SentenceCount, 0)
assert.GreaterOrEqual(t, conc.ParagraphCount, 1)
assert.GreaterOrEqual(t, conc.AvgWordLength, 0.0)
assert.GreaterOrEqual(t, conc.AvgSentenceLength, 0.0)
// Readability is clamped to [0,100]
assert.GreaterOrEqual(t, conc.ReadabilityScore, 0.0)
assert.LessOrEqual(t, conc.ReadabilityScore, 100.0)
assert.Equal(t, "Simplified Flesch-Kincaid", conc.ReadabilityMethod)
// Provider-driven outputs should align
assert.InDelta(t, 0.5, conc.Sentiment, 1e-9)
require.Len(t, conc.Keywords, 1)
assert.Equal(t, "constant", conc.Keywords[0].Text)
assert.InDelta(t, 0.3, conc.Keywords[0].Relevance, 1e-9)
}
func TestAnalyzeTextConcurrently_ContextCanceled(t *testing.T) {
a := NewBasicTextAnalyzer().
WithLanguageDetector(mockLangDetector{lang: "en", err: nil}).
WithSentimentProvider(mockSentimentProvider{score: 0.9}).
WithKeywordProvider(mockKeywordProvider{kws: []Keyword{{Text: "x", Relevance: 0.1}}})
text := "This should not be processed. Another sentence. And one more."
ctx, cancel := context.WithCancel(context.Background())
cancel() // cancel immediately before processing
conc, err := a.AnalyzeTextConcurrently(ctx, text, "", 4)
require.NoError(t, err)
require.NotNil(t, conc)
// With immediate cancellation, goroutines should early-return and no values should be sent
assert.Equal(t, 0, conc.WordCount)
assert.Equal(t, 0, conc.SentenceCount)
assert.Equal(t, 0, conc.ParagraphCount)
assert.Equal(t, 0.0, conc.AvgWordLength)
assert.Equal(t, 0.0, conc.AvgSentenceLength)
// Readability is clamped [0,100]; with zero stats it becomes 100
assert.GreaterOrEqual(t, conc.ReadabilityScore, 0.0)
assert.LessOrEqual(t, conc.ReadabilityScore, 100.0)
assert.Empty(t, conc.Keywords)
assert.Equal(t, 0.0, conc.Sentiment)
}