mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-11 07:46:37 +08:00
fix: cap CPU and avoid heavy startup work
This commit is contained in:
@@ -5,15 +5,18 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/glebarez/sqlite"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/ShukeBta/MediaStationGo/internal/config"
|
||||
"github.com/ShukeBta/MediaStationGo/internal/database"
|
||||
"github.com/ShukeBta/MediaStationGo/internal/model"
|
||||
"github.com/ShukeBta/MediaStationGo/internal/repository"
|
||||
)
|
||||
@@ -227,6 +230,67 @@ func TestLoginKeepsOnlyConfiguredActiveRefreshTokens(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoginRetriesTransientSQLiteBusy(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
cfg := &config.Config{}
|
||||
cfg.App.DataDir = t.TempDir()
|
||||
cfg.Database.DBPath = filepath.Join(cfg.App.DataDir, "busy-login.db")
|
||||
cfg.Database.WALMode = true
|
||||
cfg.Database.BusyTimeout = 20
|
||||
cfg.Database.MaxOpenConns = 4
|
||||
cfg.Database.MaxIdleConns = 2
|
||||
cfg.Secrets.JWTSecret = "test-secret"
|
||||
log := zap.NewNop()
|
||||
db, err := database.Open(cfg, log)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() { _ = sqlDB.Close() }()
|
||||
if err := database.AutoMigrate(db); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
repos := repository.New(db)
|
||||
permissions := NewPermissionService(log, repos)
|
||||
auth := NewAuthService(cfg, log, repos, NewTokenService(cfg, log, repos), permissions)
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte("password"), bcrypt.MinCost)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := repos.User.Create(ctx, &model.User{
|
||||
Username: "viewer",
|
||||
PasswordHash: string(hash),
|
||||
Role: "user",
|
||||
Tier: "free",
|
||||
IsActive: true,
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tx := repos.DB.Begin()
|
||||
if err := tx.Exec("UPDATE users SET updated_at = updated_at WHERE username = ?", "viewer").Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
release := make(chan struct{})
|
||||
go func() {
|
||||
timer := time.NewTimer(250 * time.Millisecond)
|
||||
defer timer.Stop()
|
||||
select {
|
||||
case <-release:
|
||||
case <-timer.C:
|
||||
}
|
||||
_ = tx.Rollback().Error
|
||||
}()
|
||||
defer close(release)
|
||||
|
||||
if _, err := auth.Login(ctx, "viewer", "password"); err != nil {
|
||||
t.Fatalf("login should survive a transient sqlite write lock: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultPermissionsAreViewerOnly(t *testing.T) {
|
||||
perms := DefaultPermissions("user-1")
|
||||
if !perms.CanViewDashboard || !perms.CanPlayMedia || !perms.CanExternalPlayer {
|
||||
|
||||
@@ -1049,7 +1049,6 @@ func (d *DownloadService) completedTorrentSource(torrent QBitTorrent) string {
|
||||
for _, candidate := range []string{
|
||||
torrent.ContentPath,
|
||||
filepath.Join(torrent.SavePath, torrent.Name),
|
||||
torrent.SavePath,
|
||||
} {
|
||||
clean := strings.TrimSpace(candidate)
|
||||
if clean == "" || clean == "." {
|
||||
|
||||
@@ -87,6 +87,26 @@ func TestDownloadCompleteAutoOrganizesContentPath(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompletedTorrentSourceDoesNotFallbackToSavePath(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
savePath := filepath.Join(root, "downloads", "日番")
|
||||
if err := os.MkdirAll(savePath, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
svc := NewDownloadService(zap.NewNop(), newOrganizerTestRepo(t), NewHub(zap.NewNop()), nil)
|
||||
|
||||
got := svc.completedTorrentSource(QBitTorrent{
|
||||
Hash: "done123",
|
||||
Name: "Missing.Payload.S01",
|
||||
SavePath: savePath,
|
||||
ContentPath: filepath.Join(savePath, "Missing.Payload.S01", "Missing.Payload.S01E01.mkv"),
|
||||
})
|
||||
|
||||
if got != "" {
|
||||
t.Fatalf("completedTorrentSource fell back to whole save_path %q; want empty", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDownloadPollBaselinesAlreadyCompletedTorrents(t *testing.T) {
|
||||
repos := newOrganizerTestRepo(t)
|
||||
svc := NewDownloadService(zap.NewNop(), repos, NewHub(zap.NewNop()), nil)
|
||||
|
||||
@@ -49,6 +49,16 @@ func ApplyRuntimeSetting(cfg *config.Config, key, value string) {
|
||||
}
|
||||
cfg.App.FFprobeMaxConcurrent = n
|
||||
}
|
||||
case "app.max_cpu_threads", "runtime.max_cpu_threads":
|
||||
if n, err := strconv.Atoi(value); err == nil {
|
||||
if n < 1 {
|
||||
n = 1
|
||||
}
|
||||
if n > 8 {
|
||||
n = 8
|
||||
}
|
||||
cfg.App.MaxCPUThreads = n
|
||||
}
|
||||
case "transcode.enabled", "transcoder.enabled":
|
||||
cfg.Transcoder.Enabled = parseBoolSetting(value, true)
|
||||
case "transcode.hw_enabled", "transcoder.hardware_accel":
|
||||
|
||||
@@ -30,4 +30,14 @@ func TestApplyRuntimeSettingTranscodeSwitches(t *testing.T) {
|
||||
if cfg.Transcoder.MaxConcurrent != 1 {
|
||||
t.Fatalf("max concurrent = %d, want 1", cfg.Transcoder.MaxConcurrent)
|
||||
}
|
||||
|
||||
ApplyRuntimeSetting(cfg, "app.max_cpu_threads", "99")
|
||||
if cfg.App.MaxCPUThreads != 8 {
|
||||
t.Fatalf("max cpu threads = %d, want clamp 8", cfg.App.MaxCPUThreads)
|
||||
}
|
||||
|
||||
ApplyRuntimeSetting(cfg, "app.max_cpu_threads", "0")
|
||||
if cfg.App.MaxCPUThreads != 1 {
|
||||
t.Fatalf("max cpu threads = %d, want clamp 1", cfg.App.MaxCPUThreads)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user