Files
MeBox/internal/service/strm_queue_test.go
T
truewhile b0fe40142a Rebrand MMTL to MeBox (name, logo, Docker image) (#17)
* Rebrand MMTL to MeBox across codebase and assets

Rename the project display name, Go module path, environment variable
prefix (MEBOX_*), Docker image references, and UI branding from MMTL/mmtl
to MeBox/mebox. Replace logo assets with the new MeBox icon and keep
legacy SQLite migration support for existing mmtl.db deployments.

Co-authored-by: truewhile <truewhile@users.noreply.github.com>

* Fix logo icons: use cube-only crop without truncated text

Previous icon generation cropped too much of the source image, including
partial MeBox wordmark text that was cut off in square icon containers.
Regenerate logo-64/192/512, favicon, and SVG from cube-only region.

Co-authored-by: truewhile <truewhile@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: truewhile <truewhile@users.noreply.github.com>
2026-09-02 16:26:28 +08:00

167 lines
5.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package service
import (
"context"
"errors"
"testing"
"time"
"github.com/truewhile/MeBox/internal/model"
"github.com/truewhile/MeBox/internal/repository"
"go.uber.org/zap"
)
func TestIs115Blocked(t *testing.T) {
cases := []struct {
err error
want bool
}{
{errors.New("115 接口返回 HTTP 405:<!doctypehtml>...访问被阻断"), true},
{errors.New("115 接口返回 HTTP 405"), true},
{errors.New("115 接口错误(770004):访问频率过高"), true},
{errors.New("115 接口错误(406):达到访问上限"), true},
{errors.New("下载失败:http 403"), false},
{errors.New("解析下载地址失败:115 接口调用失败"), false},
{errors.New("database is locked (517)"), false},
{nil, false},
}
for _, c := range cases {
if got := is115Blocked(c.err); got != c.want {
t.Errorf("is115Blocked(%v) = %v, want %v", c.err, got, c.want)
}
}
}
func TestIsHTTPDownloadFailure(t *testing.T) {
cases := []struct {
err error
want bool
}{
{errors.New("http 403"), true},
{errors.New("http 404"), true},
{errors.New("http 410"), true},
{errors.New("http 500"), true},
{errors.New("Get \"https://x\": connection refused"), false},
{nil, false},
}
for _, c := range cases {
if got := isHTTPDownloadFailure(c.err); got != c.want {
t.Errorf("isHTTPDownloadFailure(%v) = %v, want %v", c.err, got, c.want)
}
}
}
func TestStrmUploadTasksClearAndRetry(t *testing.T) {
db := newServiceTestDB(t, &model.StrmUploadTask{})
repos := repository.New(db)
svc := NewStrmService(nil, zap.NewNop(), repos, nil)
ctx := context.Background()
tasks := []*model.StrmUploadTask{
{Base: model.Base{ID: "task-pending"}, Status: model.StrmTaskPending, FileName: "1.nfo"},
{Base: model.Base{ID: "task-running"}, Status: model.StrmTaskRunning, FileName: "2.nfo"},
{Base: model.Base{ID: "task-done"}, Status: model.StrmTaskDone, FileName: "3.nfo"},
{Base: model.Base{ID: "task-failed"}, Status: model.StrmTaskFailed, FileName: "4.nfo", Error: "some error", RetryCount: 3},
{Base: model.Base{ID: "task-canceled"}, Status: model.StrmTaskCanceled, FileName: "5.nfo"},
}
for _, task := range tasks {
if err := db.Create(task).Error; err != nil {
t.Fatalf("failed to insert task: %v", err)
}
}
// 1. RetryAllFailedUploadTasks
retried, err := svc.RetryAllFailedUploadTasks(ctx)
if err != nil {
t.Fatalf("RetryAllFailedUploadTasks failed: %v", err)
}
if retried != 1 {
t.Fatalf("expected 1 retried task, got %d", retried)
}
var failedTask model.StrmUploadTask
if err := db.First(&failedTask, "id = ?", "task-failed").Error; err != nil {
t.Fatalf("failed to get task-failed: %v", err)
}
if failedTask.Status != model.StrmTaskPending || failedTask.Error != "" || failedTask.RetryCount != 0 {
t.Fatalf("task-failed was not reset properly: %+v", failedTask)
}
// 再次改为 failed 以便测试 ClearFinished
db.Model(&model.StrmUploadTask{}).Where("id = ?", "task-failed").Updates(map[string]any{"status": model.StrmTaskFailed})
// 2. ClearFinishedUploadTasks 应删除 done, failed, canceled 三条历史记录
deleted, err := svc.ClearFinishedUploadTasks(ctx)
if err != nil {
t.Fatalf("ClearFinishedUploadTasks failed: %v", err)
}
if deleted != 3 {
t.Fatalf("expected 3 deleted tasks (done, failed, canceled), got %d", deleted)
}
// 验证剩余的任务只有 pending 和 running
var count int64
db.Model(&model.StrmUploadTask{}).Count(&count)
if count != 2 {
t.Fatalf("expected 2 remaining tasks, got %d", count)
}
}
func TestDownloadSemSeparateByProvider(t *testing.T) {
svc := NewStrmService(nil, zap.NewNop(), nil, nil)
svc.initDownloadSems(6)
if cap(svc.downloadSem115) != strm115DownloadSemCap {
t.Fatalf("115 sem cap = %d, want %d", cap(svc.downloadSem115), strm115DownloadSemCap)
}
if cap(svc.downloadSemDAV) != 6 {
t.Fatalf("dav sem cap = %d, want 6", cap(svc.downloadSemDAV))
}
ctx := context.Background()
for i := 0; i < strm115DownloadSemCap; i++ {
if !svc.acquireDownloadSlot(ctx, model.StrmProvider115) {
t.Fatalf("failed to acquire 115 slot %d", i)
}
}
// 115 槽位占满后,OpenList 仍应能独立获取槽位。
if !svc.acquireDownloadSlot(ctx, model.StrmProviderOpenList) {
t.Fatal("openlist slot should remain available while 115 slots are full")
}
for i := 0; i < 5; i++ {
if !svc.acquireDownloadSlot(ctx, model.StrmProviderOpenList) {
t.Fatalf("failed to acquire extra openlist slot %d", i)
}
}
timeoutCtx, cancel := context.WithTimeout(ctx, 50*time.Millisecond)
defer cancel()
if svc.acquireDownloadSlot(timeoutCtx, model.StrmProviderOpenList) {
t.Fatal("expected openlist slots to be exhausted after 6 acquisitions")
}
}
func TestRequeueDownloadTask(t *testing.T) {
db := newServiceTestDB(t, &model.StrmDownloadTask{})
repos := repository.New(db)
svc := NewStrmService(nil, zap.NewNop(), repos, nil)
now := time.Now()
task := &model.StrmDownloadTask{
Base: model.Base{ID: "dl-running"},
Status: model.StrmTaskRunning,
Provider: model.StrmProviderOpenList,
StartedAt: &now,
}
if err := db.Create(task).Error; err != nil {
t.Fatalf("insert task: %v", err)
}
svc.requeueDownloadTask(task)
var got model.StrmDownloadTask
if err := db.First(&got, "id = ?", task.ID).Error; err != nil {
t.Fatalf("load task: %v", err)
}
if got.Status != model.StrmTaskPending || got.StartedAt != nil {
t.Fatalf("task not requeued: %+v", got)
}
}