Phase 1 - Backend tests: - Add PlaybackAnalytics to AutoMigrate in setupTestTrackHandler - Create migration 081_create_playback_analytics.sql for production - PlaybackAnalyticsService: return ErrTrackNotFound for missing track - RecordPlay handler: return 404 when track not found - CreateShare: use RespondSuccess, fix services.ErrTrackNotFound/ErrForbidden - GetTrackLikes, UnlikeTrack: use RespondSuccess for consistent response - GetUserLikedTracks test: fix route /users/:id/likes and params - GetSharedTrack_InvalidToken: set share service in test Phase 4 - Job queue transcoding: - Add EnqueueTranscodingJob to JobEnqueuer interface - Add TypeTranscoding and processTranscodingJob (stub) in JobWorker - MockJobEnqueuer: implement EnqueueTranscodingJob Phase 5 - Gamification cleanup: - Move api_manager.go to internal/api/archive/ - Add archive/README.md documenting archived modules - Update TODOS_AUDIT.md and FEATURE_STATUS.md
260 lines
6.6 KiB
Go
260 lines
6.6 KiB
Go
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) EnqueueTranscodingJob(trackID uuid.UUID, inputPath, outputDir string) {}
|
|
|
|
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)
|
|
}
|