github.com/qiniu/go-sdk/v7@v7.27.0/storagev2/uploader/schedulers_test.go (about) 1 //go:build unit 2 // +build unit 3 4 package uploader 5 6 import ( 7 "bytes" 8 "context" 9 "encoding/base64" 10 "encoding/json" 11 "hash/crc32" 12 "io" 13 "math/rand" 14 "net/http" 15 "net/http/httptest" 16 "os" 17 "strconv" 18 "strings" 19 "testing" 20 "time" 21 22 "github.com/gorilla/mux" 23 internal_io "github.com/qiniu/go-sdk/v7/internal/io" 24 "github.com/qiniu/go-sdk/v7/storagev2/apis" 25 "github.com/qiniu/go-sdk/v7/storagev2/credentials" 26 "github.com/qiniu/go-sdk/v7/storagev2/http_client" 27 "github.com/qiniu/go-sdk/v7/storagev2/region" 28 "github.com/qiniu/go-sdk/v7/storagev2/uploader/source" 29 ) 30 31 func TestMultiPartsUploaderScheduler(t *testing.T) { 32 tmpFile, err := os.CreateTemp("", "multi-parts-uploader-test-*") 33 if err != nil { 34 t.Fatal(err) 35 } 36 defer os.Remove(tmpFile.Name()) 37 defer tmpFile.Close() 38 39 r := rand.New(rand.NewSource(time.Now().UnixNano())) 40 if _, err = io.CopyN(tmpFile, r, 5*1024*1024); err != nil { 41 t.Fatal(err) 42 } 43 if _, err = tmpFile.Seek(0, io.SeekStart); err != nil { 44 t.Fatal(err) 45 } 46 47 var server *httptest.Server 48 serveMux := mux.NewRouter() 49 serveMux.HandleFunc("/mkblk/4194304", func(w http.ResponseWriter, r *http.Request) { 50 if !strings.HasPrefix(r.Header.Get("Authorization"), "UpToken testak:") { 51 t.Fatalf("unexpected authorization") 52 } 53 actualBody, err := internal_io.ReadAll(r.Body) 54 if err != nil { 55 t.Fatal(err) 56 } 57 expectedBody, err := internal_io.ReadAll(io.NewSectionReader(tmpFile, 0, 4*1024*1024)) 58 if err != nil { 59 t.Fatal(err) 60 } 61 if !bytes.Equal(actualBody, expectedBody) { 62 t.Fatalf("unexpected body") 63 } 64 jsonBody, err := json.Marshal(&apis.ResumableUploadV1MakeBlockResponse{ 65 Ctx: "testctx1", 66 Checksum: "testchecksum1", 67 Crc32: int64(crc32.ChecksumIEEE(actualBody)), 68 Host: server.URL, 69 ExpiredAt: time.Now().Add(1 * time.Hour).Unix(), 70 }) 71 if err != nil { 72 t.Fatal(err) 73 } 74 w.Header().Add("X-ReqId", "fakereqid") 75 w.Write(jsonBody) 76 }).Methods(http.MethodPost) 77 serveMux.HandleFunc("/mkblk/1048576", func(w http.ResponseWriter, r *http.Request) { 78 if !strings.HasPrefix(r.Header.Get("Authorization"), "UpToken testak:") { 79 t.Fatalf("unexpected authorization") 80 } 81 actualBody, err := internal_io.ReadAll(r.Body) 82 if err != nil { 83 t.Fatal(err) 84 } 85 expectedBody, err := internal_io.ReadAll(io.NewSectionReader(tmpFile, 4*1024*1024, 1*1024*1024)) 86 if err != nil { 87 t.Fatal(err) 88 } 89 if !bytes.Equal(actualBody, expectedBody) { 90 t.Fatalf("unexpected body") 91 } 92 jsonBody, err := json.Marshal(&apis.ResumableUploadV1MakeBlockResponse{ 93 Ctx: "testctx2", 94 Checksum: "testchecksum2", 95 Crc32: int64(crc32.ChecksumIEEE(actualBody)), 96 Host: server.URL, 97 ExpiredAt: time.Now().Add(1 * time.Hour).Unix(), 98 }) 99 if err != nil { 100 t.Fatal(err) 101 } 102 w.Header().Add("X-ReqId", "fakereqid") 103 w.Write(jsonBody) 104 }).Methods(http.MethodPost) 105 serveMux.PathPrefix("/mkfile/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 106 if !strings.HasPrefix(r.Header.Get("Authorization"), "UpToken testak:") { 107 t.Fatalf("unexpected authorization") 108 } 109 components := strings.Split(strings.TrimPrefix(r.URL.Path, "/mkfile/"), "/") 110 if components[0] != strconv.FormatInt(5*1024*1024, 10) { 111 t.Fatalf("unexpected fileSize") 112 } 113 components = components[1:] 114 for len(components) > 0 { 115 switch components[0] { 116 case "key": 117 value := components[1] 118 valueBytes, err := base64.URLEncoding.DecodeString(value) 119 if err != nil { 120 t.Fatal(err) 121 } 122 if string(valueBytes) != "testkey" { 123 t.Fatalf("unexpected key") 124 } 125 case "fname": 126 value := components[1] 127 valueBytes, err := base64.URLEncoding.DecodeString(value) 128 if err != nil { 129 t.Fatal(err) 130 } 131 if string(valueBytes) != "testfilename" { 132 t.Fatalf("unexpected fname") 133 } 134 case "mimeType": 135 value := components[1] 136 valueBytes, err := base64.URLEncoding.DecodeString(value) 137 if err != nil { 138 t.Fatal(err) 139 } 140 if string(valueBytes) != "application/json" { 141 t.Fatalf("unexpected mimeType") 142 } 143 case "x-qn-meta-a": 144 value := components[1] 145 valueBytes, err := base64.URLEncoding.DecodeString(value) 146 if err != nil { 147 t.Fatal(err) 148 } 149 if string(valueBytes) != "b" { 150 t.Fatalf("unexpected x-qn-meta-a") 151 } 152 case "x-qn-meta-c": 153 value := components[1] 154 valueBytes, err := base64.URLEncoding.DecodeString(value) 155 if err != nil { 156 t.Fatal(err) 157 } 158 if string(valueBytes) != "d" { 159 t.Fatalf("unexpected x-qn-meta-c") 160 } 161 case "x:a": 162 value := components[1] 163 valueBytes, err := base64.URLEncoding.DecodeString(value) 164 if err != nil { 165 t.Fatal(err) 166 } 167 if string(valueBytes) != "b" { 168 t.Fatalf("unexpected x:a") 169 } 170 case "x:c": 171 value := components[1] 172 valueBytes, err := base64.URLEncoding.DecodeString(value) 173 if err != nil { 174 t.Fatal(err) 175 } 176 if string(valueBytes) != "d" { 177 t.Fatalf("unexpected x:c") 178 } 179 default: 180 t.Fatalf("unexpected component key: %s", components[0]) 181 } 182 components = components[2:] 183 } 184 actualBody, err := internal_io.ReadAll(r.Body) 185 if err != nil { 186 t.Fatal(err) 187 } 188 if string(actualBody) != "testctx1,testctx2" { 189 t.Fatalf("unexpected body") 190 } 191 w.Header().Add("X-ReqId", "fakereqid") 192 w.Write([]byte(`{"ok":true}`)) 193 }).Methods(http.MethodPost) 194 server = httptest.NewServer(serveMux) 195 defer server.Close() 196 197 schedulers := []multiPartsUploaderScheduler{ 198 newSerialMultiPartsUploaderScheduler(NewMultiPartsUploaderV1(&MultiPartsUploaderOptions{ 199 Options: http_client.Options{ 200 Regions: ®ion.Region{Up: region.Endpoints{Preferred: []string{server.URL}}}, 201 Credentials: credentials.NewCredentials("testak", "testsk"), 202 }, 203 }), &serialMultiPartsUploaderSchedulerOptions{PartSize: 1 << 22}), 204 newConcurrentMultiPartsUploaderScheduler(NewMultiPartsUploaderV1(&MultiPartsUploaderOptions{ 205 Options: http_client.Options{ 206 Regions: ®ion.Region{Up: region.Endpoints{Preferred: []string{server.URL}}}, 207 Credentials: credentials.NewCredentials("testak", "testsk"), 208 }, 209 }), &concurrentMultiPartsUploaderSchedulerOptions{PartSize: 1 << 22, Concurrency: 2}), 210 } 211 key := "testkey" 212 for _, scheduler := range schedulers { 213 src, err := source.NewFileSource(tmpFile.Name()) 214 if err != nil { 215 t.Fatal(err) 216 } 217 initializedPart, err := scheduler.MultiPartsUploader().InitializeParts(context.Background(), src, &MultiPartsObjectOptions{ 218 ObjectOptions{ 219 BucketName: "testbucket", 220 ObjectName: &key, 221 FileName: "testfilename", 222 ContentType: "application/json", 223 Metadata: map[string]string{"a": "b", "c": "d"}, 224 CustomVars: map[string]string{"a": "b", "c": "d"}, 225 }, 226 4 * 1024 * 1024, 227 }) 228 if err != nil { 229 t.Fatal(err) 230 } 231 defer initializedPart.Close() 232 233 var lastUploaded [2]uint64 234 var uploadedPartSizes [2]uint64 235 uploadedParts, err := scheduler.UploadParts(context.Background(), initializedPart, src, &UploadPartsOptions{ 236 OnUploadingProgress: func(partNumber uint64, progress *UploadingPartProgress) { 237 if partNumber == 1 && progress.PartSize != 4*1024*1024 { 238 t.Fatalf("unexpected partSize") 239 } else if partNumber == 2 && progress.PartSize != 1024*1024 { 240 t.Fatalf("unexpected partSize") 241 } else if progress.Uploaded < lastUploaded[partNumber-1] || progress.Uploaded > progress.PartSize { 242 t.Fatalf("unexpected uploaded") 243 } 244 lastUploaded[partNumber-1] = progress.Uploaded 245 }, 246 OnPartUploaded: func(part UploadedPart) error { 247 if uploadedPartSizes[part.PartNumber()-1] > 0 { 248 t.Fatalf("unexpected OnPartUploaded call") 249 } else { 250 uploadedPartSizes[part.PartNumber()-1] = part.PartSize() 251 } 252 return nil 253 }, 254 }) 255 if err != nil { 256 t.Fatal(err) 257 } 258 var returnValue struct { 259 Ok bool `json:"ok"` 260 } 261 err = scheduler.MultiPartsUploader().CompleteParts(context.Background(), initializedPart, uploadedParts, &returnValue) 262 if err != nil { 263 t.Fatal(err) 264 } else if !returnValue.Ok { 265 t.Fatalf("unexpected response body") 266 } else if lastUploaded[0] != 4*1024*1024 || lastUploaded[1] != 1024*1024 { 267 t.Fatalf("unexpected OnUploadingProgress call") 268 } else if uploadedPartSizes[0] != 4*1024*1024 || uploadedPartSizes[1] != 1024*1024 { 269 t.Fatalf("unexpected OnPartUploaded call") 270 } 271 } 272 }