package bilibili import ( "context" "net/http" "net/http/httptest" "strings" "testing" "everypublish/server/internal/credentials" "everypublish/server/internal/platform" ) func TestQRLoginAndCredentialSave(t *testing.T) { var poll int srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if strings.HasSuffix(r.URL.Path, "/generate") { _, _ = w.Write([]byte(`{"code":0,"data":{"url":"https://qr.test/x","qrcode_key":"k"}}`)) return } poll++ switch poll { case 1: _, _ = w.Write([]byte(`{"code":86101}`)) case 2: _, _ = w.Write([]byte(`{"code":86090}`)) default: _, _ = w.Write([]byte(`{"code":0,"data":{"url":"https://passport.test/c?SESSDATA=s%2Bv&bili_jct=j%2Fv&DedeUserID=7"}}`)) } })) defer srv.Close() dir := t.TempDir() store, err := credentials.Open(dir) if err != nil { t.Fatal(err) } a := New(store) a.PassportBase = srv.URL s, err := a.BeginLogin(context.Background()) if err != nil || s.Token != "k" { t.Fatalf("begin: %#v %v", s, err) } p, err := a.PollLogin(context.Background(), s.Token) if err != nil || p.State != platform.LoginPending { t.Fatalf("pending: %#v %v", p, err) } p, err = a.PollLogin(context.Background(), s.Token) if err != nil || p.State != platform.LoginScanned { t.Fatalf("scanned: %#v %v", p, err) } p, err = a.PollLogin(context.Background(), s.Token) if err != nil || p.Cookies != "SESSDATA=s+v; bili_jct=j/v; DedeUserID=7" { t.Fatalf("confirmed: %#v %v", p, err) } if err := a.SaveCredentials(1, 2, p.Cookies); err != nil { t.Fatal(err) } got, err := store.Load(1, 2) if err != nil || string(got) != p.Cookies { t.Fatalf("stored=%q err=%v", got, err) } } func TestPublishUPOSUsesETagAndEncodedCSRF(t *testing.T) { dir := t.TempDir() store, err := credentials.Open(dir) if err != nil { t.Fatal(err) } if err = store.Save(1, 2, []byte("SESSDATA=s; bili_jct=a&b; DedeUserID=7")); err != nil { t.Fatal(err) } var sawPart, sawMerge, sawSubmit bool srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") switch r.URL.Path { case "/material": _, _ = w.Write([]byte("video")) case "/preupload": _, _ = w.Write([]byte(`{"OK":1,"endpoint":"//` + r.Host + `","auth":"auth","biz_id":1,"upos_uri":"upos://folder/v.mp4","chunk_size":2}`)) case "/folder/v.mp4": if r.Method == http.MethodPost && r.URL.Query().Has("uploads") { _, _ = w.Write([]byte(`{"upload_id":"u1"}`)) } else if r.Method == http.MethodPut { sawPart = true w.Header().Set("ETag", `"real-etag"`) w.WriteHeader(http.StatusOK) } else if r.Method == http.MethodPost { sawMerge = true _, _ = w.Write([]byte(`{"OK":1}`)) } case "/x/vu/web/add": if r.URL.Query().Get("csrf") != "a&b" { t.Errorf("csrf not decoded: %q", r.URL.RawQuery) } sawSubmit = true _, _ = w.Write([]byte(`{"code":0,"data":{"bvid":"BV1"}}`)) } })) defer srv.Close() a := New(store) a.MemberBase = srv.URL a.UpOSScheme = "http" material := srv.URL + "/material" result, err := a.Publish(context.Background(), platform.PublishInput{WorkspaceID: 1, AccountID: 2, Title: "title", Content: "body", MaterialURLs: []string{material}}) if err != nil { t.Fatal(err) } if result.URL != "https://www.bilibili.com/video/BV1" || !sawPart || !sawMerge || !sawSubmit { t.Fatalf("result=%#v part=%v merge=%v submit=%v", result, sawPart, sawMerge, sawSubmit) } } func TestSaveCredentialsRejectsIncompleteCookie(t *testing.T) { store, err := credentials.Open(t.TempDir()) if err != nil { t.Fatal(err) } if err := New(store).SaveCredentials(1, 1, "SESSDATA=only"); err == nil || platform.CategoryOf(err) != platform.Validation { t.Fatalf("expected validation error for incomplete cookie, got %v", err) } } func TestCheckCredentialsUsesEncryptedCookieAndNavEndpoint(t *testing.T) { store, err := credentials.Open(t.TempDir()) if err != nil { t.Fatal(err) } if err = store.Save(1, 2, []byte("SESSDATA=s; bili_jct=j; DedeUserID=7")); err != nil { t.Fatal(err) } srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/x/web-interface/nav" || r.Header.Get("Cookie") != "SESSDATA=s; bili_jct=j; DedeUserID=7" { t.Fatalf("unexpected nav request: %s cookie=%q", r.URL.Path, r.Header.Get("Cookie")) } _, _ = w.Write([]byte(`{"code":0,"data":{"isLogin":true}}`)) })) defer srv.Close() a := New(store) a.MemberBase = srv.URL if err := a.CheckCredentials(context.Background(), 1, 2); err != nil { t.Fatal(err) } }