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.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 TestRoomUsecaseIsLiveRequiresEnabledOnlinePublisher(t *testing.T) { catalog := &catalogRepoStub{rooms: []*Room{ {ID: 1, Enabled: true}, {ID: 2, Enabled: true}, {ID: 3, Enabled: false}, }} streams := NewStreamUsecase(&testRepo{streams: map[string]*Stream{ "room-1": {StreamID: "room-1", PublisherOnline: true}, "room-3": {StreamID: "room-3", PublisherOnline: true}, }}) rooms := NewRoomUsecase(catalog, streams) for roomID, want := range map[uint]bool{1: true, 2: false, 3: 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) } } } func TestRoomUsecaseGetPublicRequiresLiveRoomAndEnabledCategory(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}, {ID: 3, CategoryID: 1, Enabled: false}, {ID: 4, CategoryID: 2, 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) item, err := rooms.GetPublic(context.Background(), 1) if err != nil || item.ID != 1 || item.Status != RoomStatusLive { t.Fatalf("GetPublic(1) item=%+v err=%v", item, err) } for _, roomID := range []uint{2, 3, 4} { if _, err = rooms.GetPublic(context.Background(), roomID); !errors.Is(err, ErrRoomNotLive) { t.Errorf("GetPublic(%d) error=%v, want %v", roomID, err, ErrRoomNotLive) } if _, err = rooms.Get(context.Background(), roomID); err != nil { t.Errorf("admin Get(%d) error=%v", roomID, err) } } }