package laosiji import ( "context" "fmt" "io" "net/http" "net/http/httptest" "net/url" "strings" "testing" ) type roundTripFunc func(*http.Request) (*http.Response, error) func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { return f(req) } func preserveConfig(t *testing.T) { t.Helper() old := Config{ AppID: Appid, APIKey: APIKey, APIUrl: APIUrl, ImageYuan: IMAGEYUAN, NoticeURL: NoticeURL, } t.Cleanup(func() { InitConfig(old) }) } func TestConfiguredRejectsMissingCredentials(t *testing.T) { preserveConfig(t) InitConfig(Config{}) if Configured() { t.Fatal("empty configuration must not be ready") } _, err := SystemDomains(context.Background()) if err == nil || !strings.Contains(err.Error(), "appId,apiKey,apiUrl") { t.Fatalf("expected a diagnostic configuration error, got %v", err) } } func TestMovieM3u8PathNormalization(t *testing.T) { tests := map[string]string{ "": "", "demo.m3u8": "laosiji/m3m/demo.m3u8", "m3m/demo.m3u8": "laosiji/m3m/demo.m3u8", "laosiji/m3m/demo.m3u8": "laosiji/m3m/demo.m3u8", "https://cdn.example.com/m3m/demo.m3u8?token=ignored": "laosiji/m3m/demo.m3u8", "https://cdn.example.com/rk130/hevc/demo/index.m3u8": "laosiji/rk130/hevc/demo/index.m3u8", "laosiji/rk130/hevc/demo/index.m3u8?token=also-ignored": "laosiji/rk130/hevc/demo/index.m3u8", } for input, want := range tests { if got := MovieM3u8SourcePath(input); got != want { t.Errorf("MovieM3u8SourcePath(%q) = %q, want %q", input, got, want) } } } func TestTranscodeTaskNoNeedIsExplicit(t *testing.T) { sourceURL := "https://app.example/api/app/vid/h5/light/m3u8/source.m3u8" result := transcodeTaskToResult(SystemDomainsResp{MovieSourceCDN: "https://cdn.example"}, transcodeTask{ Status: transcodeNoNeedStatus, FileURL: sourceURL, }, sourceURL, TranscodeFileID(sourceURL)) if !result.Done || !result.NoNeed { t.Fatalf("status=4 must be done and no-need: %+v", result) } if result.HevcURL != sourceURL { t.Fatalf("no-need diagnostic URL = %q, want %q", result.HevcURL, sourceURL) } } func TestTranscodeSuccessWithoutFileIsFailed(t *testing.T) { result := transcodeTaskToResult(SystemDomainsResp{}, transcodeTask{ Status: transcodeSuccessStatus, }, "https://app.example/source.m3u8", "file-id") if result.Done || !result.Failed { t.Fatalf("empty successful task must fail locally instead of staying pending: %+v", result) } if !strings.Contains(result.ErrorMsg, "without transcode_file") { t.Fatalf("missing diagnostic error: %+v", result) } } func TestTranscodeFailureRedactsCredentials(t *testing.T) { preserveConfig(t) const ( appID = "app-id-secret" apiKey = "api-key-secret" uploadKey = "upload key+secret" ) InitConfig(Config{ AppID: appID, APIKey: apiKey, APIUrl: "https://api.example", }) result := transcodeTaskToResult(SystemDomainsResp{UploadKey: uploadKey}, transcodeTask{ Status: transcodeFailedStatus, TranscodeError: fmt.Sprintf( "failure app=%s api=%s upload=%s encoded=%s", appID, apiKey, uploadKey, url.QueryEscape(uploadKey), ), }, "https://app.example/source.m3u8", "file-id") if !result.Failed { t.Fatalf("negative cloud status must be failed: %+v", result) } for _, secret := range []string{appID, apiKey, uploadKey, url.QueryEscape(uploadKey)} { if strings.Contains(result.ErrorMsg, secret) { t.Fatalf("transcode error leaked credential %q: %s", secret, result.ErrorMsg) } } if !strings.Contains(result.ErrorMsg, "[REDACTED]") { t.Fatalf("transcode error did not show redaction marker: %s", result.ErrorMsg) } } func TestUploadNetworkErrorRedactsKey(t *testing.T) { preserveConfig(t) InitConfig(Config{ AppID: "app-id-secret", APIKey: "1234567890abcdef", APIUrl: "https://api.example", }) uploadKey := "upload key+secret" oldClient := uploadHTTPClient uploadHTTPClient = &http.Client{Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) { return nil, fmt.Errorf("dial failed for %s", req.URL.String()) })} t.Cleanup(func() { uploadHTTPClient = oldClient }) _, err := uploadAPICall(context.Background(), "https://upload.example/query", map[string]string{ "key": uploadKey, }) if err == nil { t.Fatal("expected network error") } errText := err.Error() if strings.Contains(errText, uploadKey) || strings.Contains(errText, url.QueryEscape(uploadKey)) { t.Fatalf("network error leaked upload key: %s", errText) } if !strings.Contains(errText, "[REDACTED]") { t.Fatalf("network error did not show redaction marker: %s", errText) } } func TestMoviePostNon2xxIsDiagnosticAndRedacted(t *testing.T) { preserveConfig(t) const ( appID = "app-id-secret" apiKey = "1234567890abcdef" ) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusBadGateway) _, _ = fmt.Fprintf(w, "failure app=%s key=%s %s", appID, apiKey, strings.Repeat("x", 500)) })) defer server.Close() InitConfig(Config{AppID: appID, APIKey: apiKey, APIUrl: server.URL}) err := moviePost(context.Background(), server.URL, map[string]interface{}{}, &struct{}{}) if err == nil || !strings.Contains(err.Error(), "status:502") { t.Fatalf("expected HTTP status diagnostic, got %v", err) } if strings.Contains(err.Error(), appID) || strings.Contains(err.Error(), apiKey) { t.Fatalf("HTTP error leaked credentials: %s", err) } if !strings.Contains(err.Error(), "[REDACTED]") || !strings.HasSuffix(err.Error(), "...") { t.Fatalf("HTTP error was not safely redacted/truncated: %s", err) } } func TestQueryH264ToH265TaskUsesExplicitStableFileID(t *testing.T) { const fileID = "stable-file-id" oldClient := uploadHTTPClient uploadHTTPClient = &http.Client{Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) { if got := req.URL.Query().Get("file_id"); got != fileID { t.Fatalf("query file_id = %q, want %q", got, fileID) } return &http.Response{ StatusCode: http.StatusOK, Header: make(http.Header), Body: io.NopCloser(strings.NewReader( `{"status":"y","data":{"file_id":"stable-file-id","status":1}}`, )), Request: req, }, nil })} t.Cleanup(func() { uploadHTTPClient = oldClient }) result, found, err := QueryH264ToH265Task( context.Background(), fileID, "https://app.example/source.m3u8?hevc_exp=1&hevc_sig=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", SystemDomainsResp{UploadURL: "https://upload.example", UploadKey: "upload-key"}, ) if err != nil || !found { t.Fatalf("explicit task query failed: found=%v err=%v", found, err) } if result.FileID != fileID { t.Fatalf("result file ID = %q, want %q", result.FileID, fileID) } }