269 lines
10 KiB
Go
269 lines
10 KiB
Go
package live
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
)
|
|
|
|
type catalogRepoStub struct {
|
|
categories []*Category
|
|
rooms []*Room
|
|
}
|
|
|
|
func (*catalogRepoStub) ListCategories(context.Context, CategoryFilter, ListOptions) ([]*Category, int64, error) {
|
|
return nil, 0, nil
|
|
}
|
|
func (r *catalogRepoStub) GetCategory(_ context.Context, id uint) (*Category, error) {
|
|
for _, category := range r.categories {
|
|
if category.ID == id {
|
|
copy := *category
|
|
return ©, nil
|
|
}
|
|
}
|
|
return nil, ErrCategoryNotFound
|
|
}
|
|
func (*catalogRepoStub) CreateCategory(context.Context, *Category) (*Category, error) {
|
|
return nil, nil
|
|
}
|
|
func (*catalogRepoStub) UpdateCategory(context.Context, *Category) (*Category, error) {
|
|
return nil, nil
|
|
}
|
|
func (*catalogRepoStub) DeleteCategory(context.Context, uint) error { return nil }
|
|
|
|
func (r *catalogRepoStub) ListRooms(_ context.Context, filter RoomFilter, options ListOptions) ([]*Room, int64, error) {
|
|
filtered := make([]*Room, 0, len(r.rooms))
|
|
for _, room := range r.rooms {
|
|
if filter.CategoryID > 0 && room.CategoryID != filter.CategoryID {
|
|
continue
|
|
}
|
|
if filter.Enabled != nil && room.Enabled != *filter.Enabled {
|
|
continue
|
|
}
|
|
if filter.Status == RoomStatusDisabled && room.Enabled {
|
|
continue
|
|
}
|
|
if (filter.Status == RoomStatusLive || filter.Status == RoomStatusOffline) && !room.Enabled {
|
|
continue
|
|
}
|
|
if filter.CategoryEnabled != nil {
|
|
category, err := r.GetCategory(context.Background(), room.CategoryID)
|
|
if err != nil || category.Enabled != *filter.CategoryEnabled {
|
|
continue
|
|
}
|
|
}
|
|
copy := *room
|
|
filtered = append(filtered, ©)
|
|
}
|
|
total := int64(len(filtered))
|
|
if options.Offset >= len(filtered) {
|
|
return []*Room{}, total, nil
|
|
}
|
|
end := options.Offset + options.Limit
|
|
if end > len(filtered) {
|
|
end = len(filtered)
|
|
}
|
|
return filtered[options.Offset:end], total, nil
|
|
}
|
|
|
|
func (r *catalogRepoStub) GetRoom(_ context.Context, id uint) (*Room, error) {
|
|
for _, room := range r.rooms {
|
|
if room.ID == id {
|
|
copy := *room
|
|
return ©, nil
|
|
}
|
|
}
|
|
return nil, ErrRoomNotFound
|
|
}
|
|
|
|
func (*catalogRepoStub) CreateRoom(context.Context, *Room) (*Room, error) { return nil, nil }
|
|
func (*catalogRepoStub) UpdateRoom(context.Context, *Room) (*Room, error) { return nil, nil }
|
|
func (*catalogRepoStub) DeleteRoom(context.Context, uint) error { return nil }
|
|
func (*catalogRepoStub) UpdateRoomEnabled(context.Context, uint, bool) error {
|
|
return nil
|
|
}
|
|
func (*catalogRepoStub) SaveRoomSort(context.Context, map[uint]int) error { return nil }
|
|
|
|
func TestRoomUsecaseFiltersSortsAndPaginatesLiveRooms(t *testing.T) {
|
|
enabled := true
|
|
catalog := &catalogRepoStub{rooms: []*Room{
|
|
{ID: 1, CategoryID: 1, Enabled: true},
|
|
{ID: 2, CategoryID: 1, Enabled: true},
|
|
{ID: 3, CategoryID: 1, Enabled: true},
|
|
{ID: 4, CategoryID: 2, Enabled: true},
|
|
}}
|
|
streams := NewStreamUsecase(&testRepo{streams: map[string]*Stream{
|
|
"room-1": {StreamID: "room-1", PublisherOnline: true, SubscriberCount: 100},
|
|
"room-2": {StreamID: "room-2", PublisherOnline: true, SubscriberCount: 5600},
|
|
"room-4": {StreamID: "room-4", PublisherOnline: true, SubscriberCount: 10000},
|
|
}})
|
|
rooms := NewRoomUsecase(catalog, streams)
|
|
|
|
items, total, err := rooms.List(context.Background(), RoomFilter{CategoryID: 1, Status: RoomStatusLive, Enabled: &enabled}, ListOptions{Offset: 1, Limit: 1, OrderBy: "viewer_count_desc"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if total != 2 || len(items) != 1 || items[0].ID != 1 {
|
|
t.Fatalf("live page total=%d items=%+v", total, items)
|
|
}
|
|
if items[0].ViewerCount != 100 || items[0].ViewerCountText != "100" || items[0].Status != RoomStatusLive {
|
|
t.Fatalf("runtime fields = %+v", items[0])
|
|
}
|
|
}
|
|
|
|
func TestRoomUsecaseIsLiveRequiresEnabledRoomCategoryAndOnlinePublisher(t *testing.T) {
|
|
catalog := &catalogRepoStub{
|
|
categories: []*Category{{ID: 1, Enabled: true}, {ID: 2, Enabled: false}},
|
|
rooms: []*Room{
|
|
{ID: 1, CategoryID: 1, Enabled: true},
|
|
{ID: 2, CategoryID: 1, Enabled: true, ReplayURL: "https://example.test/replay.mp4"},
|
|
{ID: 3, CategoryID: 1, Enabled: false},
|
|
{ID: 4, CategoryID: 2, Enabled: true},
|
|
{ID: 5, CategoryID: 3, Enabled: true},
|
|
},
|
|
}
|
|
streams := NewStreamUsecase(&testRepo{streams: map[string]*Stream{
|
|
"room-1": {StreamID: "room-1", PublisherOnline: true},
|
|
"room-3": {StreamID: "room-3", PublisherOnline: true},
|
|
"room-4": {StreamID: "room-4", PublisherOnline: true},
|
|
"room-5": {StreamID: "room-5", PublisherOnline: true},
|
|
}})
|
|
rooms := NewRoomUsecase(catalog, streams)
|
|
|
|
for roomID, want := range map[uint]bool{1: true, 2: false, 3: false, 4: false} {
|
|
got, err := rooms.IsLive(context.Background(), roomID)
|
|
if err != nil {
|
|
t.Fatalf("IsLive(%d) error = %v", roomID, err)
|
|
}
|
|
if got != want {
|
|
t.Errorf("IsLive(%d) = %v, want %v", roomID, got, want)
|
|
}
|
|
}
|
|
if live, err := rooms.IsLive(context.Background(), 5); live || !errors.Is(err, ErrCategoryNotFound) {
|
|
t.Fatalf("missing category IsLive(5) = %v, err=%v", live, err)
|
|
}
|
|
}
|
|
|
|
func TestRoomUsecaseGetPublicIncludesOfflineRoomsAndRequiresEnabledCategory(t *testing.T) {
|
|
catalog := &catalogRepoStub{
|
|
categories: []*Category{{ID: 1, Enabled: true}, {ID: 2, Enabled: false}},
|
|
rooms: []*Room{
|
|
{ID: 1, CategoryID: 1, Enabled: true},
|
|
{ID: 2, CategoryID: 1, Enabled: true, ReplayURL: "https://example.test/replay.mp4"},
|
|
{ID: 3, CategoryID: 1, Enabled: false},
|
|
{ID: 4, CategoryID: 2, Enabled: true},
|
|
{ID: 5, CategoryID: 1, Enabled: true},
|
|
{ID: 6, CategoryID: 3, Enabled: true},
|
|
},
|
|
}
|
|
streams := NewStreamUsecase(&testRepo{streams: map[string]*Stream{
|
|
"room-1": {StreamID: "room-1", PublisherOnline: true},
|
|
"room-3": {StreamID: "room-3", PublisherOnline: true},
|
|
"room-4": {StreamID: "room-4", PublisherOnline: true},
|
|
}})
|
|
rooms := NewRoomUsecase(catalog, streams)
|
|
|
|
for _, test := range []struct {
|
|
id uint
|
|
status string
|
|
replay string
|
|
}{
|
|
{id: 1, status: RoomStatusLive},
|
|
{id: 2, status: RoomStatusOffline, replay: "https://example.test/replay.mp4"},
|
|
{id: 5, status: RoomStatusOffline},
|
|
} {
|
|
item, err := rooms.GetPublic(context.Background(), test.id)
|
|
if err != nil || item.ID != test.id || item.Status != test.status || item.ReplayURL != test.replay {
|
|
t.Fatalf("GetPublic(%d) item=%+v err=%v", test.id, item, err)
|
|
}
|
|
}
|
|
for roomID, want := range map[uint]error{0: ErrRoomNotFound, 3: ErrRoomDisabled, 4: ErrCategoryDisabled, 6: ErrCategoryNotFound, 99: ErrRoomNotFound} {
|
|
if _, err := rooms.GetPublic(context.Background(), roomID); !errors.Is(err, want) {
|
|
t.Errorf("GetPublic(%d) error=%v, want %v", roomID, err, want)
|
|
}
|
|
}
|
|
for _, roomID := range []uint{3, 4, 6} {
|
|
if _, err := rooms.Get(context.Background(), roomID); err != nil {
|
|
t.Errorf("admin Get(%d) error=%v", roomID, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRoomUsecaseListsPublicRoomsByRuntimeBeforePagination(t *testing.T) {
|
|
catalog := &catalogRepoStub{
|
|
categories: []*Category{{ID: 1, Enabled: true}, {ID: 2, Enabled: false}, {ID: 3, Enabled: true}},
|
|
rooms: []*Room{
|
|
{ID: 1, CategoryID: 1, Enabled: true, ReplayURL: "https://example.test/replay.mp4"},
|
|
{ID: 2, CategoryID: 1, Enabled: true},
|
|
{ID: 3, CategoryID: 1, Enabled: true},
|
|
{ID: 4, CategoryID: 1, Enabled: false},
|
|
{ID: 5, CategoryID: 2, Enabled: true},
|
|
{ID: 6, CategoryID: 1, Enabled: true},
|
|
{ID: 7, CategoryID: 3, Enabled: true},
|
|
},
|
|
}
|
|
streams := NewStreamUsecase(&testRepo{streams: map[string]*Stream{
|
|
"room-2": {StreamID: "room-2", PublisherOnline: true, SubscriberCount: 100},
|
|
"room-3": {StreamID: "room-3", PublisherOnline: true, SubscriberCount: 200},
|
|
"room-4": {StreamID: "room-4", PublisherOnline: true, SubscriberCount: 10000},
|
|
"room-5": {StreamID: "room-5", PublisherOnline: true, SubscriberCount: 1000},
|
|
"room-7": {StreamID: "room-7", PublisherOnline: true, SubscriberCount: 50},
|
|
}})
|
|
rooms := NewRoomUsecase(catalog, streams)
|
|
for _, test := range []struct {
|
|
name string
|
|
status string
|
|
categoryID uint
|
|
order string
|
|
offset int
|
|
limit int
|
|
total int64
|
|
want []uint
|
|
}{
|
|
{name: "all default", limit: 10, total: 5, want: []uint{1, 2, 3, 6, 7}},
|
|
{name: "all popular", order: "viewer_count_desc", limit: 10, total: 5, want: []uint{3, 2, 7, 1, 6}},
|
|
{name: "all popular page", order: "viewer_count_desc", offset: 1, limit: 2, total: 5, want: []uint{2, 7}},
|
|
{name: "all ascending", order: "VIEWER_COUNT_ASC", limit: 10, total: 5, want: []uint{1, 6, 7, 2, 3}},
|
|
{name: "live only", status: RoomStatusLive, order: "viewer_count_desc", limit: 10, total: 3, want: []uint{3, 2, 7}},
|
|
{name: "offline replay and empty", status: RoomStatusOffline, order: "viewer_count_desc", limit: 10, total: 2, want: []uint{1, 6}},
|
|
{name: "category filter", categoryID: 1, order: "viewer_count_desc", limit: 10, total: 4, want: []uint{3, 2, 1, 6}},
|
|
{name: "disabled excluded", status: RoomStatusDisabled, order: "viewer_count_desc", limit: 10},
|
|
{name: "past final page", order: "viewer_count_desc", offset: 5, limit: 2, total: 5},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
enabled := true
|
|
items, total, err := rooms.List(context.Background(), RoomFilter{Status: test.status, CategoryID: test.categoryID, Enabled: &enabled, CategoryEnabled: &enabled}, ListOptions{Offset: test.offset, Limit: test.limit, OrderBy: test.order})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if total != test.total || len(items) != len(test.want) {
|
|
t.Fatalf("total=%d items=%+v, want total=%d ids=%v", total, items, test.total, test.want)
|
|
}
|
|
for index, id := range test.want {
|
|
if items[index].ID != id {
|
|
t.Fatalf("item[%d]=%d, want %d", index, items[index].ID, id)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRoomUsecasePopularSortIncludesRoomsBeyondStorageBatch(t *testing.T) {
|
|
catalog := &catalogRepoStub{categories: []*Category{{ID: 1, Enabled: true}}}
|
|
for id := uint(1); id <= 105; id++ {
|
|
catalog.rooms = append(catalog.rooms, &Room{ID: id, CategoryID: 1, Enabled: true})
|
|
}
|
|
streams := NewStreamUsecase(&testRepo{streams: map[string]*Stream{
|
|
"room-105": {StreamID: "room-105", PublisherOnline: true, SubscriberCount: 500},
|
|
}})
|
|
rooms := NewRoomUsecase(catalog, streams)
|
|
enabled := true
|
|
items, total, err := rooms.List(context.Background(), RoomFilter{Enabled: &enabled, CategoryEnabled: &enabled}, ListOptions{Limit: 2, OrderBy: "viewer_count_desc"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if total != 105 || len(items) != 2 || items[0].ID != 105 || items[1].ID != 1 {
|
|
t.Fatalf("popular rooms total=%d items=%+v", total, items)
|
|
}
|
|
}
|