package services import ( "context" "testing" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "go.uber.org/zap" ) // MockJobEnqueuer is a mock implementation of JobEnqueuer interface type MockJobEnqueuer struct { mock.Mock } func (m *MockJobEnqueuer) EnqueueEmailJob(to, subject, body string) { m.Called(to, subject, body) } func (m *MockJobEnqueuer) EnqueueEmailJobWithTemplate(to, subject, templateName string, templateData map[string]interface{}) { m.Called(to, subject, templateName, templateData) } func (m *MockJobEnqueuer) EnqueueThumbnailJob(inputPath, outputPath string, width, height int) { m.Called(inputPath, outputPath, width, height) } func (m *MockJobEnqueuer) EnqueueAnalyticsJob(eventName string, userID *uuid.UUID, payload map[string]interface{}) { m.Called(eventName, userID, payload) } func setupTestJobService(t *testing.T) (*JobService, *MockJobEnqueuer) { logger := zap.NewNop() service := NewJobService(logger) mockEnqueuer := new(MockJobEnqueuer) service.SetJobEnqueuer(mockEnqueuer) return service, mockEnqueuer } func TestJobService_NewJobService(t *testing.T) { logger := zap.NewNop() service := NewJobService(logger) assert.NotNil(t, service) assert.NotNil(t, service.logger) assert.Nil(t, service.jobEnqueuer) // Should be nil initially } func TestJobService_SetJobEnqueuer(t *testing.T) { logger := zap.NewNop() service := NewJobService(logger) mockEnqueuer := new(MockJobEnqueuer) service.SetJobEnqueuer(mockEnqueuer) assert.Equal(t, mockEnqueuer, service.jobEnqueuer) } func TestJobService_EnqueueEmail_Success(t *testing.T) { service, mockEnqueuer := setupTestJobService(t) ctx := context.Background() payload := &EmailPayload{ To: "test@example.com", Subject: "Test Subject", Body: "Test Body", } mockEnqueuer.On("EnqueueEmailJob", payload.To, payload.Subject, payload.Body).Return() err := service.EnqueueEmail(ctx, payload) assert.NoError(t, err) mockEnqueuer.AssertExpectations(t) } func TestJobService_EnqueueEmail_NoEnqueuer(t *testing.T) { logger := zap.NewNop() service := NewJobService(logger) // Don't set enqueuer ctx := context.Background() payload := &EmailPayload{ To: "test@example.com", Subject: "Test Subject", Body: "Test Body", } // Should not error, just log warning err := service.EnqueueEmail(ctx, payload) assert.NoError(t, err) } func TestJobService_EnqueueEmail_EmptyPayload(t *testing.T) { service, mockEnqueuer := setupTestJobService(t) ctx := context.Background() payload := &EmailPayload{ To: "", Subject: "", Body: "", } mockEnqueuer.On("EnqueueEmailJob", "", "", "").Return() err := service.EnqueueEmail(ctx, payload) assert.NoError(t, err) mockEnqueuer.AssertExpectations(t) } func TestJobService_EnqueueThumbnail_Success(t *testing.T) { service, mockEnqueuer := setupTestJobService(t) ctx := context.Background() payload := &ThumbnailPayload{ TrackID: 123, FileID: "file123", FilePath: "/path/to/file.jpg", } mockEnqueuer.On("EnqueueThumbnailJob", "/path/to/file.jpg", "/path/to/file.jpg.thumb", 300, 300).Return() err := service.EnqueueThumbnail(ctx, payload) assert.NoError(t, err) mockEnqueuer.AssertExpectations(t) } func TestJobService_EnqueueThumbnail_NoEnqueuer(t *testing.T) { logger := zap.NewNop() service := NewJobService(logger) // Don't set enqueuer ctx := context.Background() payload := &ThumbnailPayload{ TrackID: 123, FileID: "file123", FilePath: "/path/to/file.jpg", } // Should not error, just log warning err := service.EnqueueThumbnail(ctx, payload) assert.NoError(t, err) } func TestJobService_EnqueueThumbnail_EmptyFilePath(t *testing.T) { service, mockEnqueuer := setupTestJobService(t) ctx := context.Background() payload := &ThumbnailPayload{ TrackID: 123, FileID: "file123", FilePath: "", // Empty file path } // Should not call enqueuer if FilePath is empty err := service.EnqueueThumbnail(ctx, payload) assert.NoError(t, err) mockEnqueuer.AssertNotCalled(t, "EnqueueThumbnailJob") } func TestJobService_JobTypes_Constants(t *testing.T) { assert.Equal(t, "email:send", TypeEmailSend) assert.Equal(t, "thumbnail:generate", TypeThumbnailGenerate) assert.Equal(t, "analytics:process", TypeAnalyticsProcess) assert.Equal(t, "webhook:delivery", TypeWebhookDelivery) } func TestJobService_EmailPayload_Fields(t *testing.T) { payload := &EmailPayload{ To: "test@example.com", Subject: "Test Subject", Body: "Test Body", } assert.Equal(t, "test@example.com", payload.To) assert.Equal(t, "Test Subject", payload.Subject) assert.Equal(t, "Test Body", payload.Body) } func TestJobService_ThumbnailPayload_Fields(t *testing.T) { payload := &ThumbnailPayload{ TrackID: 123, FileID: "file123", FilePath: "/path/to/file.jpg", } assert.Equal(t, uint(123), payload.TrackID) assert.Equal(t, "file123", payload.FileID) assert.Equal(t, "/path/to/file.jpg", payload.FilePath) } func TestJobService_EnqueueEmail_MultipleCalls(t *testing.T) { service, mockEnqueuer := setupTestJobService(t) ctx := context.Background() payload1 := &EmailPayload{ To: "user1@example.com", Subject: "Subject 1", Body: "Body 1", } payload2 := &EmailPayload{ To: "user2@example.com", Subject: "Subject 2", Body: "Body 2", } mockEnqueuer.On("EnqueueEmailJob", payload1.To, payload1.Subject, payload1.Body).Return() mockEnqueuer.On("EnqueueEmailJob", payload2.To, payload2.Subject, payload2.Body).Return() err1 := service.EnqueueEmail(ctx, payload1) err2 := service.EnqueueEmail(ctx, payload2) assert.NoError(t, err1) assert.NoError(t, err2) mockEnqueuer.AssertExpectations(t) } func TestJobService_EnqueueThumbnail_DifferentPaths(t *testing.T) { service, mockEnqueuer := setupTestJobService(t) ctx := context.Background() testCases := []struct { name string filePath string expectedOutput string }{ {"JPG", "/path/to/image.jpg", "/path/to/image.jpg.thumb"}, {"PNG", "/path/to/image.png", "/path/to/image.png.thumb"}, {"Nested path", "/very/deep/path/to/image.jpg", "/very/deep/path/to/image.jpg.thumb"}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { payload := &ThumbnailPayload{ TrackID: 123, FileID: "file123", FilePath: tc.filePath, } mockEnqueuer.On("EnqueueThumbnailJob", tc.filePath, tc.expectedOutput, 300, 300).Return() err := service.EnqueueThumbnail(ctx, payload) assert.NoError(t, err) }) } mockEnqueuer.AssertExpectations(t) }