Initial commit

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 13:57:10 +08:00
co-authored by Claude Opus 5
commit 8679200f41
1897 changed files with 257900 additions and 0 deletions
+330
View File
@@ -0,0 +1,330 @@
package job
import (
"net/url"
"reflect"
"strings"
"testing"
"time"
"91porn-server/common/hevcpull"
"91porn-server/common/laosiji"
"91porn-server/models/v/vidmod"
"91porn-server/skd/skdg"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
func TestBuildH265AppTranscodeM3u8URL(t *testing.T) {
const pullSecret = "test-pull-secret-strong-32-bytes!!"
now := time.Date(2026, 7, 25, 12, 0, 0, 0, time.UTC)
expiresAt := now.Add(h265PullURLTTL)
tests := []struct {
name string
base string
source string
wantPath string
}{
{
name: "normalizes slashes",
base: " https://app.example.com/ ",
source: " /video/demo/index.m3u8 ",
wantPath: "/api/app/vid/transcode/m3u8/video/demo/index.m3u8",
},
{name: "empty base", source: "video.m3u8"},
{name: "empty source", base: "https://app.example.com"},
{name: "base without scheme", base: "app.example.com", source: "video.m3u8"},
{name: "plain http is forbidden", base: "http://app.example.com", source: "video.m3u8"},
{name: "unsupported scheme", base: "ftp://app.example.com", source: "video.m3u8"},
{name: "base with path", base: "https://app.example.com/prefix", source: "video.m3u8"},
{name: "base with query", base: "https://app.example.com?token=x", source: "video.m3u8"},
{name: "source traversal", base: "https://app.example.com", source: "video/../index.m3u8"},
{name: "absolute source", base: "https://app.example.com", source: "https://cdn.example/index.m3u8"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := buildH265AppTranscodeM3u8URL(tt.base, tt.source, pullSecret, expiresAt)
if tt.wantPath == "" {
if got != "" {
t.Fatalf("buildH265AppTranscodeM3u8URL() = %q, want empty", got)
}
return
}
parsed, err := url.Parse(got)
if err != nil {
t.Fatalf("parse signed URL: %v", err)
}
if parsed.Path != tt.wantPath {
t.Fatalf("signed URL path = %q, want %q", parsed.Path, tt.wantPath)
}
if err = hevcpull.VerifyURL(parsed, pullSecret, now); err != nil {
t.Fatalf("signed URL did not verify: %v", err)
}
})
}
}
func TestHevcAppTranscodeM3u8URLUsesStableClaimTime(t *testing.T) {
oldConf := skdg.Conf
t.Cleanup(func() {
skdg.Conf = oldConf
})
claimedAt := time.Date(2026, 7, 25, 12, 0, 0, 0, time.UTC)
skdg.Conf = nil
if got := hevcAppTranscodeM3u8URL("video.m3u8", claimedAt); got != "" {
t.Fatalf("nil config returned %q", got)
}
skdg.Conf = &skdg.GlobalConfig{}
skdg.Conf.Url.AppApiUrl = "https://app.example.com"
skdg.Conf.Hevc.PullSecret = "test-pull-secret-strong-32-bytes!!"
first := hevcAppTranscodeM3u8URL("video.m3u8", claimedAt)
second := hevcAppTranscodeM3u8URL("video.m3u8", claimedAt)
if first == "" || first != second {
t.Fatalf("signed URL must be stable for one claim: first=%q second=%q", first, second)
}
parsed, err := url.Parse(first)
if err != nil {
t.Fatalf("parse signed URL: %v", err)
}
if !strings.HasPrefix(parsed.Path, "/api/app/vid/transcode/m3u8/") {
t.Fatalf("unexpected signed URL path: %s", parsed.Path)
}
if err = hevcpull.VerifyURL(parsed, skdg.Conf.Hevc.PullSecret, claimedAt); err != nil {
t.Fatalf("signed URL did not verify: %v", err)
}
}
func TestH265CloudFileIDIsIndependentFromSignedPullURL(t *testing.T) {
video := &vidmod.VideoModel{
ID: primitive.NewObjectID(),
SourceURL: " /sp/movie/index.m3u8 ",
H265FailCount: 1,
H265PendingAt: time.Date(2026, 7, 25, 12, 0, 0, 0, time.UTC),
}
first := h265CloudFileID(video)
if first == "" {
t.Fatal("empty cloud file ID")
}
video.H265PendingAt = video.H265PendingAt.Add(time.Hour)
video.SourceURL = "/sp/movie/index.m3u8"
if got := h265CloudFileID(video); got != first {
t.Fatalf("equivalent source or claim time changed cloud file ID: %q != %q", got, first)
}
video.SourceURL = "/sp/other/index.m3u8"
if got := h265CloudFileID(video); got == first {
t.Fatal("different source reused cloud file ID")
}
video.SourceURL = "/sp/movie/index.m3u8"
video.H265FailCount++
if got := h265CloudFileID(video); got == first {
t.Fatal("new retry attempt reused cloud file ID")
}
}
func TestH265TranscodingJobEnabledRequiresEnvironmentSwitchAndConfig(t *testing.T) {
oldConf := skdg.Conf
oldLSJConfig := laosiji.Config{
AppID: laosiji.Appid,
APIKey: laosiji.APIKey,
APIUrl: laosiji.APIUrl,
ImageYuan: laosiji.IMAGEYUAN,
NoticeURL: laosiji.NoticeURL,
}
t.Cleanup(func() {
skdg.Conf = oldConf
laosiji.InitConfig(oldLSJConfig)
})
skdg.Conf = &skdg.GlobalConfig{}
skdg.Conf.Base.Env = "prod"
skdg.Conf.Url.AppApiUrl = "https://app.example.com"
laosiji.InitConfig(laosiji.Config{})
if h265TranscodingJobEnabled() {
t.Fatal("job enabled without laosiji credentials")
}
laosiji.InitConfig(laosiji.Config{
AppID: "test-app",
APIKey: "test-key",
APIUrl: "https://laosiji.example.com",
})
skdg.Conf.Base.Env = "test"
if h265TranscodingJobEnabled() {
t.Fatal("job enabled in test without explicit switch")
}
enabled := true
skdg.Conf.Hevc.EnableTranscode = &enabled
if h265TranscodingJobEnabled() {
t.Fatal("job enabled in test without hevc.pullSecret")
}
skdg.Conf.Hevc.PullSecret = "test-pull-secret-strong-32-bytes!!"
skdg.Conf.Url.AppApiUrl = ""
if h265TranscodingJobEnabled() {
t.Fatal("job enabled without appApiUrl")
}
skdg.Conf.Url.AppApiUrl = "https://app.example.com"
if !h265TranscodingJobEnabled() {
t.Fatal("job disabled in test with explicit switch and complete configuration")
}
skdg.Conf.Base.Env = "prod"
skdg.Conf.Hevc.EnableTranscode = nil
if !h265TranscodingJobEnabled() {
t.Fatal("job disabled with complete production configuration")
}
disabled := false
skdg.Conf.Hevc.EnableTranscode = &disabled
if h265TranscodingJobEnabled() {
t.Fatal("job enabled after explicit production kill switch")
}
}
func TestH265PlayableURL(t *testing.T) {
video := &vidmod.VideoModel{SourceURL: "sp/source/index.m3u8"}
if got := h265PlayableURL(video, laosiji.TranscodeResult{
Done: true,
NoNeed: true,
HevcURL: "https://app.example.com/api/app/vid/h5/light/m3u8/sp/source/index.m3u8",
}); got != video.SourceURL {
t.Fatalf("no-need result returned %q, want original source %q", got, video.SourceURL)
}
if got := h265PlayableURL(video, laosiji.TranscodeResult{
Done: true,
HevcURL: "https://cdn.example.com/m3m/transcoded/index.m3u8",
}); got != "laosiji/m3m/transcoded/index.m3u8" {
t.Fatalf("transcoded result returned %q", got)
}
if got := h265PlayableURL(nil, laosiji.TranscodeResult{Done: true, NoNeed: true}); got != "" {
t.Fatalf("nil video no-need result returned %q", got)
}
}
func TestH265PendingTimedOut(t *testing.T) {
now := time.Date(2026, 7, 25, 12, 0, 0, 0, time.UTC)
tests := []struct {
name string
video *vidmod.VideoModel
want bool
}{
{name: "nil video"},
{
name: "not pending",
video: &vidmod.VideoModel{
H265Status: vidmod.H265StatusQueued,
H265PendingAt: now.Add(-25 * time.Hour),
},
},
{
name: "missing claim time",
video: &vidmod.VideoModel{
H265Status: vidmod.H265StatusPending,
},
},
{
name: "still within timeout",
video: &vidmod.VideoModel{
H265Status: vidmod.H265StatusPending,
H265PendingAt: now.Add(-h265PendingTimeout + time.Second),
},
},
{
name: "at timeout",
video: &vidmod.VideoModel{
H265Status: vidmod.H265StatusPending,
H265PendingAt: now.Add(-h265PendingTimeout),
},
want: true,
},
{
name: "past timeout",
video: &vidmod.VideoModel{
H265Status: vidmod.H265StatusPending,
H265PendingAt: now.Add(-h265PendingTimeout - time.Second),
},
want: true,
},
{
name: "future timestamp",
video: &vidmod.VideoModel{
H265Status: vidmod.H265StatusPending,
H265PendingAt: now.Add(time.Second),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := h265PendingTimedOut(tt.video, now); got != tt.want {
t.Fatalf("h265PendingTimedOut() = %v, want %v", got, tt.want)
}
})
}
}
func TestHomeH265QueryMatchesAppHomepageSorts(t *testing.T) {
for _, sortType := range []int{1, 2, 3, 7, 9} {
filter := homeH265Filter("module-id", sortType)
if _, exists := filter["reviewAt"]; exists {
t.Fatalf("sort %d unexpectedly limits reviewAt: %#v", sortType, filter["reviewAt"])
}
}
tests := []struct {
sortType int
want bson.D
}{
{
sortType: 1,
want: bson.D{{Key: "reviewAt", Value: -1}},
},
{
sortType: 2,
want: bson.D{
{Key: "liaoBaTopSort", Value: -1},
{Key: "likeCount", Value: -1},
{Key: "reviewAt", Value: -1},
},
},
{
sortType: 3,
want: bson.D{
{Key: "playCount", Value: -1},
{Key: "reviewAt", Value: -1},
},
},
{
sortType: 7,
want: bson.D{
{Key: "collectCount", Value: -1},
{Key: "reviewAt", Value: -1},
},
},
{
sortType: 9,
want: bson.D{
{Key: "hot", Value: -1},
{Key: "reviewAt", Value: -1},
},
},
}
for _, tt := range tests {
got := homeH265Options(tt.sortType).Sort
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("sort %d = %#v, want %#v", tt.sortType, got, tt.want)
}
}
}