package live import ( "context" "errors" "testing" bizlive "kra/internal/biz/live" "github.com/glebarez/sqlite" "gorm.io/gorm" ) type catalogTestProvider struct{ db *gorm.DB } func (p catalogTestProvider) DB() *gorm.DB { return p.db } func openCatalogTestDB(t *testing.T) *gorm.DB { t.Helper() db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared"), &gorm.Config{}) if err != nil { t.Fatal(err) } if err = db.AutoMigrate(&categoryPO{}, &roomPO{}); err != nil { t.Fatal(err) } if err = db.Exec(`CREATE TABLE sys_users ( id integer primary key, username text, nick_name text, header_img text, deleted_at datetime )`).Error; err != nil { t.Fatal(err) } return db } func TestCatalogRepoScansCategoryAndRoomFields(t *testing.T) { db := openCatalogTestDB(t) if err := db.Exec(`INSERT INTO sys_users(id, username, nick_name, header_img) VALUES (2, 'anchor', 'Anchor', 'avatar.png')`).Error; err != nil { t.Fatal(err) } category := categoryPO{Name: "Games", Icon: "game.png", Sort: 1, Enabled: true} if err := db.Create(&category).Error; err != nil { t.Fatal(err) } room := roomPO{Name: "Test Room", CategoryID: category.ID, CoverURL: "cover.png", Intro: "intro", Sort: 2, AnchorUserID: 2, ReplayURL: "https://example.test/replay.mp4", Enabled: true} if err := db.Create(&room).Error; err != nil { t.Fatal(err) } repo := NewCatalogRepo(catalogTestProvider{db: db}) categories, total, err := repo.ListCategories(context.Background(), bizlive.CategoryFilter{}, bizlive.ListOptions{Limit: 10}) if err != nil { t.Fatal(err) } if total != 1 || len(categories) != 1 || categories[0].ID != category.ID || categories[0].Name != category.Name || !categories[0].Enabled || categories[0].RoomCount != 1 { t.Fatalf("category scan result total=%d categories=%+v", total, categories) } rooms, total, err := repo.ListRooms(context.Background(), bizlive.RoomFilter{}, bizlive.ListOptions{Limit: 10}) if err != nil { t.Fatal(err) } if total != 1 || len(rooms) != 1 || rooms[0].ID != room.ID || rooms[0].Name != room.Name || rooms[0].CategoryName != category.Name || rooms[0].AnchorName != "Anchor" || !rooms[0].Enabled { t.Fatalf("room list scan result total=%d rooms=%+v", total, rooms) } got, err := repo.GetRoom(context.Background(), room.ID) if err != nil { t.Fatal(err) } if got.ID != room.ID || got.Name != room.Name || got.AnchorUserID != 2 || got.AnchorAvatar != "avatar.png" || got.ReplayURL != room.ReplayURL || !got.Enabled { t.Fatalf("room get scan result=%+v", got) } } func TestCatalogRepoFiltersRoomsByCategoryEnabled(t *testing.T) { db := openCatalogTestDB(t) if err := db.Exec(`INSERT INTO sys_users(id, username, nick_name, header_img) VALUES (2, 'anchor', 'Anchor', 'avatar.png')`).Error; err != nil { t.Fatal(err) } enabledCategory := categoryPO{Name: "Games", Enabled: true} disabledCategory := categoryPO{Name: "Hidden", Enabled: false} if err := db.Create(&enabledCategory).Error; err != nil { t.Fatal(err) } if err := db.Create(&disabledCategory).Error; err != nil { t.Fatal(err) } if err := db.Model(&disabledCategory).Update("enabled", false).Error; err != nil { t.Fatal(err) } for _, room := range []*roomPO{ {Name: "Visible", CategoryID: enabledCategory.ID, CoverURL: "visible.png", AnchorUserID: 2, Enabled: true}, {Name: "Hidden", CategoryID: disabledCategory.ID, CoverURL: "hidden.png", AnchorUserID: 2, Enabled: true}, } { if err := db.Create(room).Error; err != nil { t.Fatal(err) } } repo := NewCatalogRepo(catalogTestProvider{db: db}) enabled := true rooms, total, err := repo.ListRooms(context.Background(), bizlive.RoomFilter{CategoryEnabled: &enabled}, bizlive.ListOptions{Limit: 10}) if err != nil { t.Fatal(err) } if total != 1 || len(rooms) != 1 || rooms[0].Name != "Visible" { t.Fatalf("enabled-category rooms total=%d rooms=%+v", total, rooms) } } func TestCatalogRepoPublicVisibilityFiltersBeforePagination(t *testing.T) { db := openCatalogTestDB(t) if err := db.Exec(`INSERT INTO sys_users(id, username, nick_name, header_img) VALUES (2, 'anchor', 'Anchor', 'avatar.png')`).Error; err != nil { t.Fatal(err) } visible := categoryPO{Name: "Visible", Enabled: true} disabled := categoryPO{Name: "Disabled", Enabled: true} deleted := categoryPO{Name: "Deleted", Enabled: true} for _, category := range []*categoryPO{&visible, &disabled, &deleted} { if err := db.Create(category).Error; err != nil { t.Fatal(err) } } if err := db.Model(&disabled).Update("enabled", false).Error; err != nil { t.Fatal(err) } if err := db.Delete(&deleted).Error; err != nil { t.Fatal(err) } rooms := []*roomPO{ {Name: "Offline Room", CategoryID: visible.ID, Enabled: true}, {Name: "Replay Room", CategoryID: visible.ID, Enabled: true, ReplayURL: "https://example.test/replay.mp4"}, {Name: "Disabled Room", CategoryID: visible.ID, Enabled: true}, {Name: "Disabled Category", CategoryID: disabled.ID, Enabled: true}, {Name: "Deleted Category", CategoryID: deleted.ID, Enabled: true}, {Name: "Missing Category", CategoryID: 999, Enabled: true}, {Name: "Deleted Room", CategoryID: visible.ID, Enabled: true}, } for _, room := range rooms { room.AnchorUserID = 2 room.CoverURL = "cover.png" if err := db.Create(room).Error; err != nil { t.Fatal(err) } } if err := db.Model(rooms[2]).Update("enabled", false).Error; err != nil { t.Fatal(err) } if err := db.Delete(rooms[6]).Error; err != nil { t.Fatal(err) } repo := NewCatalogRepo(catalogTestProvider{db: db}) enabled := true filter := bizlive.RoomFilter{Enabled: &enabled, CategoryEnabled: &enabled} items, total, err := repo.ListRooms(context.Background(), filter, bizlive.ListOptions{Limit: 10, OrderBy: "id_asc"}) if err != nil { t.Fatal(err) } if total != 2 || len(items) != 2 || items[0].ID != rooms[0].ID || items[1].ID != rooms[1].ID { t.Fatalf("public candidates total=%d items=%+v", total, items) } if items[1].ReplayURL != rooms[1].ReplayURL { t.Fatalf("replay URL=%q, want %q", items[1].ReplayURL, rooms[1].ReplayURL) } items, total, err = repo.ListRooms(context.Background(), filter, bizlive.ListOptions{Offset: 1, Limit: 1, OrderBy: "id_asc"}) if err != nil { t.Fatal(err) } if total != 2 || len(items) != 1 || items[0].ID != rooms[1].ID { t.Fatalf("public page total=%d items=%+v", total, items) } filter.CategoryID = disabled.ID items, total, err = repo.ListRooms(context.Background(), filter, bizlive.ListOptions{Limit: 10}) if err != nil { t.Fatal(err) } if total != 0 || len(items) != 0 { t.Fatalf("disabled category returned rooms: total=%d items=%+v", total, items) } } func TestCatalogRepoCreatePreservesExplicitEnabledState(t *testing.T) { db := openCatalogTestDB(t) // Existing installations still have the old database-level default. for _, table := range []string{"live_categories", "live_rooms"} { if err := db.Table(table).AutoMigrate(&struct { Enabled bool `gorm:"default:true"` }{}); err != nil { t.Fatal(err) } } if err := db.Exec(`INSERT INTO sys_users(id, username, nick_name, header_img) VALUES (2, 'anchor', 'Anchor', 'avatar.png')`).Error; err != nil { t.Fatal(err) } repo := NewCatalogRepo(catalogTestProvider{db: db}) ctx := context.Background() for _, enabled := range []bool{false, true} { name := "Disabled" if enabled { name = "Enabled" } t.Run(name+" category", func(t *testing.T) { created, err := repo.CreateCategory(ctx, &bizlive.Category{Name: name, Enabled: enabled}) if err != nil { t.Fatal(err) } if created.Enabled != enabled { t.Fatalf("created category enabled=%v, want %v", created.Enabled, enabled) } reloaded, err := repo.GetCategory(ctx, created.ID) if err != nil || reloaded.Enabled != enabled { t.Fatalf("reloaded category=%+v error=%v, want enabled=%v", reloaded, err, enabled) } }) } enabled := true categories, total, err := repo.ListCategories(ctx, bizlive.CategoryFilter{Enabled: &enabled}, bizlive.ListOptions{Limit: 10}) if err != nil { t.Fatal(err) } if total != 1 || len(categories) != 1 || categories[0].Name != "Enabled" { t.Fatalf("public categories include a disabled new category: total=%d categories=%+v", total, categories) } categoryID := categories[0].ID streams := bizlive.NewStreamUsecase(NewStreamRepo()) rooms := bizlive.NewRoomUsecase(repo, streams) for _, enabled := range []bool{false, true} { name := "Disabled" if enabled { name = "Enabled" } t.Run(name+" room", func(t *testing.T) { created, err := repo.CreateRoom(ctx, &bizlive.Room{ Name: name, CategoryID: categoryID, AnchorUserID: 2, CoverURL: "cover.png", ReplayURL: "https://example.test/replay.mp4", Enabled: enabled, }) if err != nil { t.Fatal(err) } if created.Enabled != enabled { t.Fatalf("created room enabled=%v, want %v", created.Enabled, enabled) } reloaded, err := repo.GetRoom(ctx, created.ID) if err != nil || reloaded.Enabled != enabled { t.Fatalf("reloaded room=%+v error=%v, want enabled=%v", reloaded, err, enabled) } public, err := rooms.GetPublic(ctx, created.ID) if enabled { if err != nil || public.Status != bizlive.RoomStatusOffline { t.Fatalf("enabled offline public room=%+v error=%v", public, err) } } else if public != nil || !errors.Is(err, bizlive.ErrRoomDisabled) { t.Fatalf("disabled new room passed public/publisher authorization: room=%+v error=%v", public, err) } }) } public, total, err := repo.ListRooms(ctx, bizlive.RoomFilter{Enabled: &enabled, CategoryEnabled: &enabled}, bizlive.ListOptions{Limit: 10}) if err != nil { t.Fatal(err) } if total != 1 || len(public) != 1 || public[0].Name != "Enabled" { t.Fatalf("public list includes a disabled new room: total=%d rooms=%+v", total, public) } }