297 lines
7.2 KiB
Go
297 lines
7.2 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
)
|
|
|
|
// MockModerationService tests the AI moderation functionality
|
|
func TestModerationService_AnalyzeContent(t *testing.T) {
|
|
// Test with mock service (no API keys for testing)
|
|
pool := &pgxpool.Pool{} // Mock pool
|
|
service := NewModerationService(pool, "", "")
|
|
|
|
ctx := context.Background()
|
|
|
|
tests := []struct {
|
|
name string
|
|
content string
|
|
mediaURLs []string
|
|
wantReason string
|
|
wantHate float64
|
|
wantGreed float64
|
|
wantDelusion float64
|
|
}{
|
|
{
|
|
name: "Clean content",
|
|
content: "Hello world, how are you today?",
|
|
mediaURLs: []string{},
|
|
wantReason: "",
|
|
wantHate: 0.0,
|
|
wantGreed: 0.0,
|
|
wantDelusion: 0.0,
|
|
},
|
|
{
|
|
name: "Hate content",
|
|
content: "I hate everyone and want to attack them",
|
|
mediaURLs: []string{},
|
|
wantReason: "hate",
|
|
wantHate: 0.0, // Will be 0 without OpenAI API
|
|
wantGreed: 0.0,
|
|
wantDelusion: 0.0,
|
|
},
|
|
{
|
|
name: "Greed content",
|
|
content: "Get rich quick with crypto investment guaranteed returns",
|
|
mediaURLs: []string{},
|
|
wantReason: "greed",
|
|
wantHate: 0.0,
|
|
wantGreed: 0.7, // Keyword-based detection
|
|
wantDelusion: 0.0,
|
|
},
|
|
{
|
|
name: "Delusion content",
|
|
content: "Fake news conspiracy theories about truth",
|
|
mediaURLs: []string{},
|
|
wantReason: "delusion",
|
|
wantHate: 0.0,
|
|
wantGreed: 0.0,
|
|
wantDelusion: 0.0, // Will be 0 without OpenAI API
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
score, reason, err := service.AnalyzeContent(ctx, tt.content, tt.mediaURLs)
|
|
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tt.wantReason, reason)
|
|
assert.Equal(t, tt.wantHate, score.Hate)
|
|
assert.Equal(t, tt.wantGreed, score.Greed)
|
|
assert.Equal(t, tt.wantDelusion, score.Delusion)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestModerationService_KeywordDetection(t *testing.T) {
|
|
pool := &pgxpool.Pool{} // Mock pool
|
|
service := NewModerationService(pool, "", "")
|
|
|
|
ctx := context.Background()
|
|
|
|
// Test keyword-based greed detection
|
|
score, reason, err := service.AnalyzeContent(ctx, "Buy now get rich quick crypto scam", []string{})
|
|
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "greed", reason)
|
|
assert.Greater(t, score.Greed, 0.5)
|
|
}
|
|
|
|
func TestModerationService_ImageURLDetection(t *testing.T) {
|
|
// Test the isImageURL helper function
|
|
tests := []struct {
|
|
url string
|
|
expected bool
|
|
}{
|
|
{"https://example.com/image.jpg", true},
|
|
{"https://example.com/image.jpeg", true},
|
|
{"https://example.com/image.png", true},
|
|
{"https://example.com/image.gif", true},
|
|
{"https://example.com/image.webp", true},
|
|
{"https://example.com/video.mp4", false},
|
|
{"https://example.com/document.pdf", false},
|
|
{"https://example.com/", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.url, func(t *testing.T) {
|
|
result := isImageURL(tt.url)
|
|
assert.Equal(t, tt.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestModerationService_VisionScoreConversion(t *testing.T) {
|
|
pool := &pgxpool.Pool{} // Mock pool
|
|
service := NewModerationService(pool, "", "")
|
|
|
|
tests := []struct {
|
|
name string
|
|
safeSearch GoogleVisionSafeSearch
|
|
expectedHate float64
|
|
expectedDelusion float64
|
|
}{
|
|
{
|
|
name: "Clean image",
|
|
safeSearch: GoogleVisionSafeSearch{
|
|
Adult: "UNLIKELY",
|
|
Violence: "UNLIKELY",
|
|
Racy: "UNLIKELY",
|
|
},
|
|
expectedHate: 0.3,
|
|
expectedDelusion: 0.3,
|
|
},
|
|
{
|
|
name: "Violent image",
|
|
safeSearch: GoogleVisionSafeSearch{
|
|
Adult: "UNLIKELY",
|
|
Violence: "VERY_LIKELY",
|
|
Racy: "UNLIKELY",
|
|
},
|
|
expectedHate: 0.9,
|
|
expectedDelusion: 0.3,
|
|
},
|
|
{
|
|
name: "Adult content",
|
|
safeSearch: GoogleVisionSafeSearch{
|
|
Adult: "VERY_LIKELY",
|
|
Violence: "UNLIKELY",
|
|
Racy: "UNLIKELY",
|
|
},
|
|
expectedHate: 0.9,
|
|
expectedDelusion: 0.3,
|
|
},
|
|
{
|
|
name: "Racy content",
|
|
safeSearch: GoogleVisionSafeSearch{
|
|
Adult: "UNLIKELY",
|
|
Violence: "UNLIKELY",
|
|
Racy: "VERY_LIKELY",
|
|
},
|
|
expectedHate: 0.3,
|
|
expectedDelusion: 0.9,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
score := service.convertVisionScore(tt.safeSearch)
|
|
assert.Equal(t, tt.expectedHate, score.Hate)
|
|
assert.Equal(t, tt.expectedDelusion, score.Delusion)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestThreePoisonsScore_Max(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
values []float64
|
|
expected float64
|
|
}{
|
|
{
|
|
name: "Single value",
|
|
values: []float64{0.5},
|
|
expected: 0.5,
|
|
},
|
|
{
|
|
name: "Multiple values",
|
|
values: []float64{0.1, 0.7, 0.3},
|
|
expected: 0.7,
|
|
},
|
|
{
|
|
name: "All zeros",
|
|
values: []float64{0.0, 0.0, 0.0},
|
|
expected: 0.0,
|
|
},
|
|
{
|
|
name: "Empty slice",
|
|
values: []float64{},
|
|
expected: 0.0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := max(tt.values...)
|
|
assert.Equal(t, tt.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
// Integration test example (requires actual database and API keys)
|
|
func TestModerationService_Integration(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("Skipping integration test in short mode")
|
|
}
|
|
|
|
// This test requires:
|
|
// 1. A real database connection
|
|
// 2. OpenAI and Google Vision API keys
|
|
// 3. Proper test environment setup
|
|
|
|
t.Skip("Integration test requires database and API keys setup")
|
|
|
|
// Example structure for integration test:
|
|
/*
|
|
ctx := context.Background()
|
|
|
|
// Setup test database
|
|
pool := setupTestDB(t)
|
|
defer cleanupTestDB(t, pool)
|
|
|
|
// Setup service with real API keys
|
|
service := NewModerationService(pool, "test-openai-key", "test-google-key")
|
|
|
|
// Test actual content analysis
|
|
score, reason, err := service.AnalyzeContent(ctx, "Test content", []string{})
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, score)
|
|
|
|
// Test database operations
|
|
postID := uuid.New()
|
|
err = service.FlagPost(ctx, postID, score, reason)
|
|
assert.NoError(t, err)
|
|
|
|
// Verify flag was created
|
|
flags, err := service.GetPendingFlags(ctx, 10, 0)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, flags, 1)
|
|
*/
|
|
}
|
|
|
|
// Benchmark tests
|
|
func BenchmarkModerationService_AnalyzeContent(b *testing.B) {
|
|
pool := &pgxpool.Pool{} // Mock pool
|
|
service := NewModerationService(pool, "", "")
|
|
ctx := context.Background()
|
|
content := "This is a test post with some content to analyze"
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, _, _ = service.AnalyzeContent(ctx, content, []string{})
|
|
}
|
|
}
|
|
|
|
func BenchmarkModerationService_KeywordDetection(b *testing.B) {
|
|
pool := &pgxpool.Pool{} // Mock pool
|
|
service := NewModerationService(pool, "", "")
|
|
ctx := context.Background()
|
|
content := "Buy crypto get rich quick investment scam"
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, _, _ = service.AnalyzeContent(ctx, content, []string{})
|
|
}
|
|
}
|
|
|
|
// Helper function to setup test database (for integration tests)
|
|
func setupTestDB(t *testing.T) *pgxpool.Pool {
|
|
// This would setup a test database connection
|
|
// Implementation depends on your test environment
|
|
t.Helper()
|
|
return nil
|
|
}
|
|
|
|
// Helper function to cleanup test database (for integration tests)
|
|
func cleanupTestDB(t *testing.T, pool *pgxpool.Pool) {
|
|
// This would cleanup the test database
|
|
// Implementation depends on your test environment
|
|
t.Helper()
|
|
}
|