mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-29 19:36:36 +08:00
279 lines
10 KiB
Go
279 lines
10 KiB
Go
package service
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/ShukeBta/MediaStationGo/internal/config"
|
|
"github.com/ShukeBta/MediaStationGo/internal/model"
|
|
)
|
|
|
|
func TestMgoBotUserManagementAndAuditCommands(t *testing.T) {
|
|
ctx := t.Context()
|
|
repos, bot := newBotTestService(t)
|
|
if err := repos.User.Create(ctx, &model.User{Username: "root", PasswordHash: "x", Role: "admin", IsActive: true}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
channel := &model.NotifyChannel{Name: "Telegram", Type: "telegram", Enabled: true, Config: `{"admin_user_ids":"9401"}`}
|
|
msg := &TelegramMessage{From: TelegramUser{ID: 9401, Username: "admin"}, Chat: TelegramChat{ID: 9401, Type: "private"}}
|
|
|
|
reply, err := bot.executeCommand(ctx, channel, msg, "/ucr viewer secret-pass 30")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(reply.Text, "已创建用户") {
|
|
t.Fatalf("expected user creation, got %q", reply.Text)
|
|
}
|
|
viewer, err := repos.User.FindByUsername(ctx, "viewer")
|
|
if err != nil || viewer == nil {
|
|
t.Fatalf("viewer should exist: %v", err)
|
|
}
|
|
if err := repos.UserDevice.Create(ctx, &model.UserDevice{
|
|
UserID: viewer.ID,
|
|
DeviceID: "dev-abc",
|
|
DeviceName: "Windows PC",
|
|
Client: "Infuse",
|
|
LastIP: "1.2.3.4",
|
|
FirstSeenAt: time.Now(),
|
|
LastSeenAt: time.Now(),
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
for _, tc := range []struct {
|
|
command string
|
|
want string
|
|
}{
|
|
{"/uinfo viewer", "用户信息"},
|
|
{"/userip viewer", "1.2.3.4"},
|
|
{"/auditip 1.2.3", "viewer"},
|
|
{"/auditdevice Windows", "viewer"},
|
|
{"/auditclient Infuse", "viewer"},
|
|
{"/udeviceid dev-abc", "viewer"},
|
|
} {
|
|
t.Run(tc.command, func(t *testing.T) {
|
|
reply, err := bot.executeCommand(ctx, channel, msg, tc.command)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(reply.Text, tc.want) {
|
|
t.Fatalf("%s expected %q in %q", tc.command, tc.want, reply.Text)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMgoBotBatchAndPermissionCommands(t *testing.T) {
|
|
ctx := t.Context()
|
|
repos, bot := newBotTestService(t)
|
|
channel := &model.NotifyChannel{Name: "Telegram", Type: "telegram", Enabled: true, Config: `{"admin_user_ids":"9501"}`}
|
|
msg := &TelegramMessage{From: TelegramUser{ID: 9501, Username: "admin"}, Chat: TelegramChat{ID: 9501, Type: "private"}}
|
|
users := []*model.User{
|
|
{Username: "root", PasswordHash: "x", Role: "admin", IsActive: true},
|
|
{Username: "viewer1", PasswordHash: "x", Role: "user", IsActive: true},
|
|
{Username: "viewer2", PasswordHash: "x", Role: "user", IsActive: true},
|
|
{Username: "viewer3", PasswordHash: "x", Role: "user", IsActive: true},
|
|
}
|
|
for _, user := range users {
|
|
if err := repos.User.Create(ctx, user); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if user.Role != "admin" && user.Username != "viewer3" {
|
|
if err := repos.Permission.Create(ctx, DefaultPermissions(user.ID)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
reply, err := bot.executeCommand(ctx, channel, msg, "/renewall 7 confirm")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(reply.Text, "批量续期完成") {
|
|
t.Fatalf("expected renewall success, got %q", reply.Text)
|
|
}
|
|
renewed, _ := repos.User.FindByUsername(ctx, "viewer1")
|
|
if renewed.ExpiredAt == nil {
|
|
t.Fatal("renewall should set expiry for normal users")
|
|
}
|
|
|
|
if reply, err = bot.executeCommand(ctx, channel, msg, "/embylibs_blockall"); err != nil || !strings.Contains(reply.Text, "关闭媒体播放权限") {
|
|
t.Fatalf("expected blockall success, reply=%q err=%v", reply.Text, err)
|
|
}
|
|
perm, _ := repos.Permission.FindByUserID(ctx, users[1].ID)
|
|
if perm == nil || perm.CanPlayMedia {
|
|
t.Fatal("embylibs_blockall should disable media playback for normal users")
|
|
}
|
|
perm, _ = repos.Permission.FindByUserID(ctx, users[3].ID)
|
|
if perm == nil || perm.CanPlayMedia {
|
|
t.Fatal("embylibs_blockall should create disabled media playback permissions when missing")
|
|
}
|
|
if reply, err = bot.executeCommand(ctx, channel, msg, "/embylibs_unblockall"); err != nil || !strings.Contains(reply.Text, "开启媒体播放权限") {
|
|
t.Fatalf("expected unblockall success, reply=%q err=%v", reply.Text, err)
|
|
}
|
|
perm, _ = repos.Permission.FindByUserID(ctx, users[1].ID)
|
|
if perm == nil || !perm.CanPlayMedia {
|
|
t.Fatal("embylibs_unblockall should enable media playback for normal users")
|
|
}
|
|
|
|
if reply, err = bot.executeCommand(ctx, channel, msg, "/banall confirm"); err != nil || !strings.Contains(reply.Text, "已禁用普通用户") {
|
|
t.Fatalf("expected banall success, reply=%q err=%v", reply.Text, err)
|
|
}
|
|
banned, _ := repos.User.FindByUsername(ctx, "viewer2")
|
|
if banned.IsActive {
|
|
t.Fatal("banall should disable normal users")
|
|
}
|
|
if reply, err = bot.executeCommand(ctx, channel, msg, "/unbanall confirm"); err != nil || !strings.Contains(reply.Text, "已解禁普通用户") {
|
|
t.Fatalf("expected unbanall success, reply=%q err=%v", reply.Text, err)
|
|
}
|
|
unbanned, _ := repos.User.FindByUsername(ctx, "viewer2")
|
|
if !unbanned.IsActive {
|
|
t.Fatal("unbanall should re-enable normal users")
|
|
}
|
|
}
|
|
|
|
func TestMgoBotSyncExpiryAndBotAdminCommands(t *testing.T) {
|
|
ctx := t.Context()
|
|
repos, bot := newBotTestService(t)
|
|
channel := &model.NotifyChannel{Name: "Telegram", Type: "telegram", Enabled: true, Config: `{"admin_user_ids":"9601"}`}
|
|
if err := repos.NotifyChannel.Create(ctx, channel); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
msg := &TelegramMessage{From: TelegramUser{ID: 9601, Username: "admin"}, Chat: TelegramChat{ID: 9601, Type: "private"}}
|
|
past := time.Now().Add(-24 * time.Hour)
|
|
if err := repos.User.Create(ctx, &model.User{Username: "expired", PasswordHash: "x", Role: "user", IsActive: true, ExpiredAt: &past}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
reply, err := bot.executeCommand(ctx, channel, msg, "/syncunbound")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(reply.Text, "expired") {
|
|
t.Fatalf("syncunbound should list unbound users, got %q", reply.Text)
|
|
}
|
|
reply, err = bot.executeCommand(ctx, channel, msg, "/check_ex")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(reply.Text, "expired") {
|
|
t.Fatalf("check_ex should list expired users, got %q", reply.Text)
|
|
}
|
|
reply, err = bot.executeCommand(ctx, channel, msg, "/proadmin 9602")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(reply.Text, "已添加") {
|
|
t.Fatalf("proadmin should update channel config, got %q", reply.Text)
|
|
}
|
|
updated, _ := repos.NotifyChannel.FindByID(ctx, channel.ID)
|
|
cfg := bot.telegramChannelConfig(updated)
|
|
if !strings.Contains(cfg["admin_user_ids"], "9602") {
|
|
t.Fatalf("expected admin ids to include 9602, got %#v", cfg)
|
|
}
|
|
}
|
|
|
|
func TestMgoBotProtectedUsersAndBackupCommands(t *testing.T) {
|
|
ctx := t.Context()
|
|
repos, bot := newBotTestService(t)
|
|
cfg := &config.Config{}
|
|
cfg.App.DataDir = t.TempDir()
|
|
bot.SetBackupService(NewBackupService(cfg, zap.NewNop(), repos.DB))
|
|
|
|
channel := &model.NotifyChannel{Name: "Telegram", Type: "telegram", Enabled: true, Config: `{"admin_user_ids":"9701"}`}
|
|
msg := &TelegramMessage{From: TelegramUser{ID: 9701, Username: "admin"}, Chat: TelegramChat{ID: 9701, Type: "private"}}
|
|
users := []*model.User{
|
|
{Username: "root", PasswordHash: "x", Role: "admin", IsActive: true},
|
|
{Username: "safe", PasswordHash: "x", Role: "user", IsActive: true},
|
|
{Username: "normal", PasswordHash: "x", Role: "user", IsActive: true},
|
|
}
|
|
for _, user := range users {
|
|
if err := repos.User.Create(ctx, user); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
reply, err := bot.executeCommand(ctx, channel, msg, "/prouser safe")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(reply.Text, "已加入保护名单") {
|
|
t.Fatalf("expected protect success, got %q", reply.Text)
|
|
}
|
|
if reason := bot.protectReason(ctx, users[1].ID); !strings.Contains(reason, "保护名单") {
|
|
t.Fatalf("protected user should have protect reason, got %q", reason)
|
|
}
|
|
reply, err = bot.executeCommand(ctx, channel, msg, "/banall confirm")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(reply.Text, "已禁用普通用户") {
|
|
t.Fatalf("expected banall success, got %q", reply.Text)
|
|
}
|
|
protected, _ := repos.User.FindByUsername(ctx, "safe")
|
|
normal, _ := repos.User.FindByUsername(ctx, "normal")
|
|
if !protected.IsActive {
|
|
t.Fatal("protected user should not be disabled by banall")
|
|
}
|
|
if normal.IsActive {
|
|
t.Fatal("normal user should be disabled by banall")
|
|
}
|
|
reply, err = bot.executeCommand(ctx, channel, msg, "/revuser safe")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(reply.Text, "已移出保护名单") {
|
|
t.Fatalf("expected unprotect success, got %q", reply.Text)
|
|
}
|
|
|
|
reply, err = bot.executeCommand(ctx, channel, msg, "/backup_db")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(reply.Text, "数据库备份完成") {
|
|
t.Fatalf("backup_db should create backup, got %q", reply.Text)
|
|
}
|
|
reply, err = bot.executeCommand(ctx, channel, msg, "/restore_from_db list")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(reply.Text, "mediastation_") {
|
|
t.Fatalf("restore list should show backup, got %q", reply.Text)
|
|
}
|
|
}
|
|
|
|
func TestMgoBotAliasesAndSyncGroupGuards(t *testing.T) {
|
|
ctx := t.Context()
|
|
repos, bot := newBotTestService(t)
|
|
if err := repos.User.Create(ctx, &model.User{Username: "root", PasswordHash: "x", Role: "admin", IsActive: true}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
channel := &model.NotifyChannel{Name: "Telegram", Type: "telegram", Enabled: true, Config: `{"admin_user_ids":"9801"}`}
|
|
msg := &TelegramMessage{From: TelegramUser{ID: 9801, Username: "admin"}, Chat: TelegramChat{ID: 9801, Type: "private"}}
|
|
|
|
reply, err := bot.executeCommand(ctx, channel, msg, "/kk")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(reply.Text, "用户管理") {
|
|
t.Fatalf("/kk should map to user management, got %q", reply.Text)
|
|
}
|
|
reply, err = bot.executeCommand(ctx, channel, msg, "/syncgroupm")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(reply.Text, "未配置可校验成员") {
|
|
t.Fatalf("syncgroupm should explain missing group config, got %q", reply.Text)
|
|
}
|
|
reply, err = bot.executeCommand(ctx, channel, msg, "/kick_not_emby")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(reply.Text, "无法枚举全部群成员") {
|
|
t.Fatalf("kick_not_emby should explain Telegram limitation, got %q", reply.Text)
|
|
}
|
|
}
|