diff --git a/veza-backend-api/internal/models/live_stream.go b/veza-backend-api/internal/models/live_stream.go index e4dbd6000..d4c7988bd 100644 --- a/veza-backend-api/internal/models/live_stream.go +++ b/veza-backend-api/internal/models/live_stream.go @@ -21,7 +21,7 @@ type LiveStream struct { StartedAt *time.Time `json:"startedAt" db:"started_at"` EndedAt *time.Time `json:"endedAt" db:"ended_at"` ViewerCount int `gorm:"default:0" json:"viewers" db:"viewer_count"` - Tags []string `gorm:"type:jsonb;default:'[]'" json:"tags" db:"tags"` + Tags []string `gorm:"serializer:json;default:'[]'" json:"tags" db:"tags"` ScheduledAt *time.Time `json:"scheduled_at,omitempty" db:"scheduled_at"` StreamURL string `gorm:"type:text;default:''" json:"stream_url,omitempty" db:"stream_url"` IsVOD bool `gorm:"default:false" json:"is_vod" db:"is_vod"` diff --git a/veza-backend-api/internal/services/live_stream_service_test.go b/veza-backend-api/internal/services/live_stream_service_test.go new file mode 100644 index 000000000..6906564aa --- /dev/null +++ b/veza-backend-api/internal/services/live_stream_service_test.go @@ -0,0 +1,142 @@ +package services + +import ( + "context" + "testing" + + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gorm.io/driver/sqlite" + "gorm.io/gorm" + + "veza-backend-api/internal/models" + "veza-backend-api/internal/repositories" +) + +func setupLiveStreamTest(t *testing.T) (*LiveStreamService, *gorm.DB) { + db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) + require.NoError(t, err) + require.NoError(t, db.AutoMigrate(&models.LiveStream{})) + repo := repositories.NewLiveStreamRepository(db) + svc := NewLiveStreamService(repo) + return svc, db +} + +func TestCreate_GeneratesStreamKey(t *testing.T) { + svc, _ := setupLiveStreamTest(t) + stream := &models.LiveStream{Title: "Test Stream"} + created, err := svc.Create(context.Background(), uuid.New(), stream) + require.NoError(t, err) + assert.NotEmpty(t, created.StreamKey) + _, parseErr := uuid.Parse(created.StreamKey) + assert.NoError(t, parseErr, "stream key should be valid UUID") +} + +func TestCreate_RequiresTitle(t *testing.T) { + svc, _ := setupLiveStreamTest(t) + _, err := svc.Create(context.Background(), uuid.New(), &models.LiveStream{}) + assert.Error(t, err) +} + +func TestListByUser_ReturnsOnlyUserStreams(t *testing.T) { + svc, _ := setupLiveStreamTest(t) + user1 := uuid.New() + user2 := uuid.New() + + _, err := svc.Create(context.Background(), user1, &models.LiveStream{Title: "User1 Stream 1"}) + require.NoError(t, err) + _, err = svc.Create(context.Background(), user1, &models.LiveStream{Title: "User1 Stream 2"}) + require.NoError(t, err) + _, err = svc.Create(context.Background(), user2, &models.LiveStream{Title: "User2 Stream 1"}) + require.NoError(t, err) + + streams, err := svc.ListByUser(context.Background(), user1) + require.NoError(t, err) + assert.Len(t, streams, 2) + for _, s := range streams { + assert.Equal(t, user1, s.UserID) + } +} + +func TestRegenerateStreamKey_Success(t *testing.T) { + svc, _ := setupLiveStreamTest(t) + userID := uuid.New() + created, err := svc.Create(context.Background(), userID, &models.LiveStream{Title: "Test Stream"}) + require.NoError(t, err) + oldKey := created.StreamKey + + newKey, err := svc.RegenerateStreamKey(context.Background(), created.ID, userID) + require.NoError(t, err) + assert.NotEqual(t, oldKey, newKey) + assert.NotEmpty(t, newKey) +} + +func TestRegenerateStreamKey_WrongUser(t *testing.T) { + svc, _ := setupLiveStreamTest(t) + user1 := uuid.New() + user2 := uuid.New() + created, err := svc.Create(context.Background(), user1, &models.LiveStream{Title: "Test Stream"}) + require.NoError(t, err) + + _, err = svc.RegenerateStreamKey(context.Background(), created.ID, user2) + assert.Error(t, err) +} + +func TestSetIsLive_SetsStartedAt(t *testing.T) { + svc, _ := setupLiveStreamTest(t) + userID := uuid.New() + created, err := svc.Create(context.Background(), userID, &models.LiveStream{Title: "Test Stream"}) + require.NoError(t, err) + + err = svc.SetIsLive(context.Background(), created.ID, true) + require.NoError(t, err) + + updated, err := svc.Get(context.Background(), created.ID) + require.NoError(t, err) + assert.True(t, updated.IsLive) + assert.NotNil(t, updated.StartedAt) + assert.Nil(t, updated.EndedAt) +} + +func TestSetIsLive_SetsEndedAt(t *testing.T) { + svc, _ := setupLiveStreamTest(t) + userID := uuid.New() + created, err := svc.Create(context.Background(), userID, &models.LiveStream{Title: "Test Stream"}) + require.NoError(t, err) + require.NoError(t, svc.SetIsLive(context.Background(), created.ID, true)) + + err = svc.SetIsLive(context.Background(), created.ID, false) + require.NoError(t, err) + + updated, err := svc.Get(context.Background(), created.ID) + require.NoError(t, err) + assert.False(t, updated.IsLive) + assert.NotNil(t, updated.EndedAt) +} + +func TestUpdateViewerCount_Clamps(t *testing.T) { + svc, _ := setupLiveStreamTest(t) + userID := uuid.New() + created, err := svc.Create(context.Background(), userID, &models.LiveStream{Title: "Test Stream"}) + require.NoError(t, err) + + err = svc.UpdateViewerCount(context.Background(), created.ID, -100) + require.NoError(t, err) + + updated, err := svc.Get(context.Background(), created.ID) + require.NoError(t, err) + assert.Equal(t, 0, updated.ViewerCount) +} + +func TestUpdate_OwnershipCheck(t *testing.T) { + svc, _ := setupLiveStreamTest(t) + user1 := uuid.New() + user2 := uuid.New() + created, err := svc.Create(context.Background(), user1, &models.LiveStream{Title: "Test Stream"}) + require.NoError(t, err) + + stream := &models.LiveStream{Title: "Hijacked"} + _, err = svc.Update(context.Background(), created.ID, user2, stream) + assert.Error(t, err) +}