212 lines
6.2 KiB
Go
212 lines
6.2 KiB
Go
package baidu
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"strconv"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"gitea.dddbg.com/youbin/bdrclone/internal/config"
|
|
)
|
|
|
|
func TestRemotePathWithRoot(t *testing.T) {
|
|
client := New(testConfig("token"))
|
|
for input, want := range map[string]string{
|
|
"/": "/apps/bdrclone", "docs/a.txt": "/apps/bdrclone/docs/a.txt", "/../a": "/apps/bdrclone/a",
|
|
} {
|
|
if got := client.RemotePath(input); got != want {
|
|
t.Errorf("RemotePath(%q) = %q, want %q", input, got, want)
|
|
}
|
|
}
|
|
if got := client.APIPath("/apps/bdrclone/docs/a.txt"); got != "/docs/a.txt" {
|
|
t.Fatalf("APIPath = %q", got)
|
|
}
|
|
}
|
|
|
|
func TestExpiredTokenRefreshesAndPersists(t *testing.T) {
|
|
var saved Token
|
|
var listCalls int
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.URL.Path {
|
|
case "/oauth/2.0/token":
|
|
if r.URL.Query().Get("refresh_token") != "refresh-old" {
|
|
t.Errorf("unexpected refresh token: %q", r.URL.Query().Get("refresh_token"))
|
|
}
|
|
fmt.Fprint(w, `{"access_token":"access-new","refresh_token":"refresh-new","expires_in":3600}`)
|
|
case "/rest/2.0/xpan/file":
|
|
listCalls++
|
|
if r.URL.Query().Get("access_token") != "access-new" {
|
|
t.Errorf("unexpected access token: %q", r.URL.Query().Get("access_token"))
|
|
}
|
|
if got := r.Header.Get("User-Agent"); got != "pan.baidu.com" {
|
|
t.Errorf("User-Agent = %q", got)
|
|
}
|
|
fmt.Fprint(w, `{"errno":0,"list":[]}`)
|
|
default:
|
|
http.NotFound(w, r)
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
cfg := testConfig("expired")
|
|
cfg.RefreshToken = "refresh-old"
|
|
cfg.ExpiresAt = time.Now().Add(-time.Hour)
|
|
client := New(cfg, WithEndpoints(server.URL, server.URL, server.URL), WithTokenSaver(func(token Token, _ time.Time) error {
|
|
saved = token
|
|
return nil
|
|
}))
|
|
if _, err := client.List(context.Background(), "/"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if saved.AccessToken != "access-new" || saved.RefreshToken != "refresh-new" {
|
|
t.Fatalf("saved token = %+v", saved)
|
|
}
|
|
if listCalls != 1 {
|
|
t.Fatalf("list calls = %d", listCalls)
|
|
}
|
|
}
|
|
|
|
func TestOpenUsesOfficialUserAgentAndRange(t *testing.T) {
|
|
var server *httptest.Server
|
|
metadataCalls := 0
|
|
server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.URL.Path {
|
|
case "/rest/2.0/xpan/multimedia":
|
|
metadataCalls++
|
|
fmt.Fprintf(w, `{"errno":0,"list":[{"fs_id":42,"dlink":%q}]}`, server.URL+"/download?x=1")
|
|
case "/download":
|
|
if got := r.Header.Get("User-Agent"); got != "pan.baidu.com" {
|
|
t.Errorf("User-Agent = %q", got)
|
|
}
|
|
if got := r.Header.Get("Range"); got != "bytes=2-5" {
|
|
t.Errorf("Range = %q", got)
|
|
}
|
|
if got := r.URL.Query().Get("access_token"); got != "token" {
|
|
t.Errorf("access_token = %q", got)
|
|
}
|
|
w.WriteHeader(http.StatusPartialContent)
|
|
fmt.Fprint(w, "2345")
|
|
default:
|
|
http.NotFound(w, r)
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
client := New(testConfig("token"), WithEndpoints(server.URL, server.URL, server.URL))
|
|
body, err := client.Open(context.Background(), File{FSID: 42, Path: "/a"}, 2, 4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer body.Close()
|
|
b, err := io.ReadAll(body)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if string(b) != "2345" {
|
|
t.Fatalf("body = %q", b)
|
|
}
|
|
body, err = client.Open(context.Background(), File{FSID: 42, Path: "/a"}, 2, 4)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
body.Close()
|
|
if metadataCalls != 1 {
|
|
t.Fatalf("download metadata calls = %d, want cached URL to be reused", metadataCalls)
|
|
}
|
|
}
|
|
|
|
func TestUploadMultipartFlow(t *testing.T) {
|
|
content := append(bytes.Repeat([]byte("a"), int(defaultPartSize)), []byte("tail")...)
|
|
file, err := os.CreateTemp(t.TempDir(), "upload-*")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := file.Write(content); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var mu sync.Mutex
|
|
parts := map[int][]byte{}
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.URL.Path {
|
|
case "/rest/2.0/xpan/file":
|
|
if err := r.ParseForm(); err != nil {
|
|
t.Error(err)
|
|
}
|
|
switch r.URL.Query().Get("method") {
|
|
case "precreate":
|
|
if r.Form.Get("path") != "/apps/bdrclone/remote.bin" {
|
|
t.Errorf("precreate path = %q", r.Form.Get("path"))
|
|
}
|
|
var blocks []string
|
|
if err := json.Unmarshal([]byte(r.Form.Get("block_list")), &blocks); err != nil || len(blocks) != 2 {
|
|
t.Errorf("block list = %q, err=%v", r.Form.Get("block_list"), err)
|
|
}
|
|
fmt.Fprint(w, `{"errno":0,"return_type":1,"uploadid":"upload-1","block_list":[0,1]}`)
|
|
case "create":
|
|
if r.Form.Get("uploadid") != "upload-1" {
|
|
t.Errorf("create uploadid = %q", r.Form.Get("uploadid"))
|
|
}
|
|
fmt.Fprint(w, `{"errno":0,"fs_id":99,"path":"/apps/bdrclone/remote.bin","server_filename":"remote.bin","size":4194308}`)
|
|
default:
|
|
http.Error(w, "unexpected method", http.StatusBadRequest)
|
|
}
|
|
case "/rest/2.0/pcs/superfile2":
|
|
part, _ := strconv.Atoi(r.URL.Query().Get("partseq"))
|
|
if r.URL.Query().Get("uploadid") != "upload-1" {
|
|
t.Errorf("part uploadid = %q", r.URL.Query().Get("uploadid"))
|
|
}
|
|
if err := r.ParseMultipartForm(defaultPartSize + 1024); err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
partFile, _, err := r.FormFile("file")
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
b, err := io.ReadAll(partFile)
|
|
partFile.Close()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
mu.Lock()
|
|
parts[part] = b
|
|
mu.Unlock()
|
|
fmt.Fprint(w, `{"md5":"ok"}`)
|
|
default:
|
|
http.NotFound(w, r)
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
cfg := testConfig("token")
|
|
cfg.PartSize = defaultPartSize
|
|
cfg.UploadParts = 2
|
|
client := New(cfg, WithEndpoints(server.URL, server.URL, server.URL))
|
|
entry, err := client.Upload(context.Background(), file, int64(len(content)), "/remote.bin", time.Unix(1_700_000_000, 0), nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if entry.FSID != 99 {
|
|
t.Fatalf("entry = %+v", entry)
|
|
}
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
if !bytes.Equal(parts[0], content[:defaultPartSize]) || !bytes.Equal(parts[1], content[defaultPartSize:]) {
|
|
t.Fatalf("uploaded parts do not match input: sizes %d, %d", len(parts[0]), len(parts[1]))
|
|
}
|
|
}
|
|
|
|
func testConfig(token string) *config.Config {
|
|
return &config.Config{
|
|
ClientID: "client", ClientSecret: "secret", AccessToken: token,
|
|
Root: "/apps/bdrclone", UserAgent: "pan.baidu.com", UploadParts: 1,
|
|
}
|
|
}
|