github.com/qiniu/go-sdk/v7@v7.27.0/storage/resume_uploader_test.go (about) 1 //go:build integration 2 // +build integration 3 4 package storage 5 6 import ( 7 "bytes" 8 "context" 9 "crypto/md5" 10 "encoding/hex" 11 "encoding/json" 12 "fmt" 13 "io" 14 "math/rand" 15 "net/http" 16 "os" 17 "path/filepath" 18 "strings" 19 "sync/atomic" 20 "testing" 21 "time" 22 ) 23 24 func TestResumeUploadPutFile(t *testing.T) { 25 var putRet PutRet 26 ctx := context.TODO() 27 putPolicy := PutPolicy{ 28 Scope: testBucket, 29 DeleteAfterDays: 7, 30 } 31 upToken := putPolicy.UploadToken(mac) 32 r := rand.New(rand.NewSource(time.Now().UnixNano())) 33 testKey := fmt.Sprintf("testRPutFileKey_%d", r.Int()) 34 35 // prepare file for test uploading 36 testLocalFile, err := os.CreateTemp("", "TestResumeUploadPutFile") 37 if err != nil { 38 t.Fatalf("os.CreateTemp file failed, err: %v", err) 39 } 40 defer os.Remove(testLocalFile.Name()) 41 42 err = resumeUploader.PutFile(ctx, &putRet, upToken, testKey, testLocalFile.Name(), nil) 43 if err != nil { 44 t.Fatalf("ResumeUploader#PutFile() error, %s", err) 45 } 46 t.Logf("Key: %s, Hash:%s", putRet.Key, putRet.Hash) 47 } 48 49 func TestResumeUploadWithInvalidUpHost(t *testing.T) { 50 var putRet PutRet 51 ctx := context.TODO() 52 putPolicy := PutPolicy{ 53 Scope: testBucket, 54 DeleteAfterDays: 7, 55 } 56 upToken := putPolicy.UploadToken(mac) 57 r := rand.New(rand.NewSource(time.Now().UnixNano())) 58 testKey := fmt.Sprintf("testRPutFileKey_%d", r.Int()) 59 60 // prepare file for test uploading 61 testLocalFile, err := os.CreateTemp("", "TestResumeUploadPutFile") 62 if err != nil { 63 t.Fatalf("os.CreateTemp file failed, err: %v", err) 64 } 65 defer os.Remove(testLocalFile.Name()) 66 67 err = resumeUploader.PutFile(ctx, &putRet, upToken, testKey, testLocalFile.Name(), &RputExtra{ 68 UpHost: "mock.upload.io", 69 }) 70 if err == nil { 71 t.Fatalf("TestFormUploaderWithInvalidUpHost should error, %s", err) 72 } 73 if !strings.Contains(err.Error(), "lookup mock.upload.io: no such host") { 74 t.Fatalf("TestFormUploaderWithInvalidUpHost should use mock host, %s", err) 75 } 76 } 77 78 func TestPutWithoutSize(t *testing.T) { 79 var putRet PutRet 80 putPolicy := PutPolicy{ 81 Scope: testBucket, 82 DeleteAfterDays: 7, 83 } 84 upToken := putPolicy.UploadToken(mac) 85 86 r := rand.New(rand.NewSource(time.Now().UnixNano())) 87 sizes := []int64{ 88 64, 89 1 << blockBits, 90 (1 << blockBits) - 1, 91 (1 << blockBits) + 1, 92 (1 << (blockBits + 4)) + 1, 93 } 94 chunkSizes := []int{0, 1 << 20} 95 96 for _, chunkSize := range chunkSizes { 97 for _, size := range sizes { 98 md5Sumer := md5.New() 99 rd := io.TeeReader(&io.LimitedReader{R: r, N: size}, md5Sumer) 100 testKey := fmt.Sprintf("testRPutFileKey_%d", r.Int()) 101 err := resumeUploader.PutWithoutSize(context.Background(), &putRet, upToken, testKey, rd, &RputExtra{ 102 ChunkSize: chunkSize, 103 Notify: func(blkIdx int, blkSize int, ret *BlkputRet) { 104 t.Logf("Notify: blkIdx: %d, blkSize: %d, ret: %#v", blkIdx, blkSize, ret) 105 }, 106 NotifyErr: func(blkIdx int, blkSize int, err error) { 107 t.Logf("NotifyErr: blkIdx: %d, blkSize: %d, err: %s", blkIdx, blkSize, err) 108 }, 109 }) 110 if err != nil { 111 t.Fatalf("PutWithoutSize() error, %s", err) 112 } 113 md5Value := hex.EncodeToString(md5Sumer.Sum(nil)) 114 validateMD5(t, testKey, md5Value, size) 115 t.Logf("Size: %d, Chunk: %d, Key: %s, Hash:%s", size, chunkSize, putRet.Key, putRet.Hash) 116 } 117 } 118 } 119 120 func TestPutWithSize(t *testing.T) { 121 var putRet PutRet 122 putPolicy := PutPolicy{ 123 Scope: testBucket, 124 DeleteAfterDays: 7, 125 } 126 upToken := putPolicy.UploadToken(mac) 127 128 r := rand.New(rand.NewSource(time.Now().UnixNano())) 129 sizes := []int64{ 130 64, 131 1 << blockBits, 132 (1 << blockBits) - 1, 133 (1 << blockBits) + 1, 134 (1 << (blockBits + 4)) + 1, 135 } 136 chunkSizes := []int{0, 1 << 20} 137 138 hostSelector := 0 139 for _, chunkSize := range chunkSizes { 140 hostSelector += 1 141 for _, size := range sizes { 142 hostSelector += 1 143 upHost := "" 144 if hostSelector%3 == 1 { 145 upHost = testUpHost 146 } else { 147 upHost = "https://" + testUpHost 148 } 149 150 data := make([]byte, size) 151 if _, err := io.ReadFull(r, data); err != nil { 152 t.Fatal(err) 153 } 154 testKey := fmt.Sprintf("testRPutFileKey_%d", r.Int()) 155 err := resumeUploader.Put(context.Background(), &putRet, upToken, testKey, bytes.NewReader(data), size, &RputExtra{ 156 ChunkSize: chunkSize, 157 UpHost: upHost, 158 Notify: func(blkIdx int, blkSize int, ret *BlkputRet) { 159 t.Logf("Notify: blkIdx: %d, blkSize: %d, ret: %#v", blkIdx, blkSize, ret) 160 }, 161 NotifyErr: func(blkIdx int, blkSize int, err error) { 162 t.Logf("NotifyErr: blkIdx: %d, blkSize: %d, err: %s", blkIdx, blkSize, err) 163 }, 164 }) 165 if err != nil { 166 t.Fatalf("Put() error, %s", err) 167 } 168 md5ByteArray := md5.Sum(data) 169 md5Value := hex.EncodeToString(md5ByteArray[:]) 170 validateMD5(t, testKey, md5Value, size) 171 t.Logf("Size: %d, Chunk: %d, Key: %s, Hash:%s", size, chunkSize, putRet.Key, putRet.Hash) 172 } 173 } 174 } 175 176 func TestPutWithoutSizeAndKey(t *testing.T) { 177 var putRet PutRet 178 putPolicy := PutPolicy{ 179 Scope: testBucket, 180 DeleteAfterDays: 7, 181 } 182 upToken := putPolicy.UploadToken(mac) 183 184 r := rand.New(rand.NewSource(time.Now().UnixNano())) 185 sizes := []int64{ 186 64, 187 1 << blockBits, 188 (1 << blockBits) - 1, 189 (1 << blockBits) + 1, 190 (1 << (blockBits + 4)) + 1, 191 } 192 chunkSizes := []int{0, 1 << 20} 193 194 for _, chunkSize := range chunkSizes { 195 for _, size := range sizes { 196 md5Sumer := md5.New() 197 rd := io.TeeReader(&io.LimitedReader{R: r, N: size}, md5Sumer) 198 err := resumeUploader.PutWithoutKeyAndSize(context.Background(), &putRet, upToken, rd, &RputExtra{ 199 ChunkSize: chunkSize, 200 Notify: func(blkIdx int, blkSize int, ret *BlkputRet) { 201 t.Logf("Notify: blkIdx: %d, blkSize: %d, ret: %#v", blkIdx, blkSize, ret) 202 }, 203 NotifyErr: func(blkIdx int, blkSize int, err error) { 204 t.Logf("NotifyErr: blkIdx: %d, blkSize: %d, err: %s", blkIdx, blkSize, err) 205 }, 206 }) 207 if err != nil { 208 t.Fatalf("PutWithoutSize() error, %s", err) 209 } 210 testKey := putRet.Key 211 md5Value := hex.EncodeToString(md5Sumer.Sum(nil)) 212 validateMD5(t, testKey, md5Value, size) 213 t.Logf("Size: %d, Chunk: %d, Key: %s, Hash:%s", size, chunkSize, putRet.Key, putRet.Hash) 214 } 215 } 216 } 217 218 func TestPutWithRecovery(t *testing.T) { 219 var putRet PutRet 220 putPolicy := PutPolicy{ 221 Scope: testBucket, 222 DeleteAfterDays: 7, 223 } 224 upToken := putPolicy.UploadToken(mac) 225 226 r := rand.New(rand.NewSource(time.Now().UnixNano())) 227 testKey := fmt.Sprintf("testRPutFileKey_%d", r.Int()) 228 dirName, err := os.MkdirTemp("", "") 229 if err != nil { 230 t.Fatal(err) 231 } 232 defer os.RemoveAll(dirName) 233 recorder, err := NewFileRecorder(dirName) 234 if err != nil { 235 t.Fatal(err) 236 } 237 238 fileName := filepath.Join(dirName, "originalFile") 239 testFile, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) 240 if err != nil { 241 t.Fatal(err) 242 } 243 defer testFile.Close() 244 245 size := int64(4 * (1 << blockBits)) 246 io.CopyN(testFile, r, size) 247 248 for i := 0; i < 4; i++ { 249 ctx, cancelFunc := context.WithCancel(context.Background()) 250 counter := uint32(0) 251 err = resumeUploader.PutFile(ctx, &putRet, upToken, testKey, fileName, &RputExtra{ 252 Recorder: recorder, 253 ChunkSize: (1 << blockBits) / 2, 254 Notify: func(blkIdx int, blkSize int, ret *BlkputRet) { 255 t.Logf("[%d] Notify: blkIdx: %d, blkSize: %d, ret: %#v", i, blkIdx, blkSize, ret) 256 if atomic.AddUint32(&counter, 1) >= 2 { 257 cancelFunc() 258 } 259 }, 260 NotifyErr: func(blkIdx int, blkSize int, err error) { 261 t.Logf("[%d] NotifyErr: blkIdx: %d, blkSize: %d, err: %s", i, blkIdx, blkSize, err) 262 }, 263 }) 264 if err == nil { 265 return 266 } 267 } 268 t.Fatal(err) 269 } 270 271 func TestPutWithBackup(t *testing.T) { 272 var putRet PutRet 273 putPolicy := PutPolicy{ 274 Scope: testBucket, 275 DeleteAfterDays: 7, 276 } 277 upToken := putPolicy.UploadToken(mac) 278 279 r := rand.New(rand.NewSource(time.Now().UnixNano())) 280 sizes := []int64{ 281 64, 282 1 << blockBits, 283 (1 << blockBits) - 1, 284 (1 << blockBits) + 1, 285 (1 << (blockBits + 4)) + 1, 286 } 287 chunkSizes := []int{0, 1 << 20} 288 289 region, err := GetRegion(mac.AccessKey, testBucket) 290 if err != nil { 291 t.Fatal("get region error:", err) 292 } 293 294 customizedHost := []string{"mock.qiniu.com"} 295 customizedHost = append(customizedHost, region.SrcUpHosts...) 296 region.SrcUpHosts = customizedHost 297 cfg := Config{} 298 cfg.UseCdnDomains = false 299 cfg.Region = region 300 uploader := NewResumeUploader(&cfg) 301 302 hostSelector := 0 303 for _, chunkSize := range chunkSizes { 304 hostSelector += 1 305 for _, size := range sizes { 306 hostSelector += 1 307 data := make([]byte, size) 308 if _, err := io.ReadFull(r, data); err != nil { 309 t.Fatal(err) 310 } 311 testKey := fmt.Sprintf("testRPutFileKey_%d", r.Int()) 312 err := uploader.Put(context.Background(), &putRet, upToken, testKey, bytes.NewReader(data), size, &RputExtra{ 313 ChunkSize: chunkSize, 314 Notify: func(blkIdx int, blkSize int, ret *BlkputRet) { 315 t.Logf("Notify: blkIdx: %d, blkSize: %d, ret: %#v", blkIdx, blkSize, ret) 316 }, 317 NotifyErr: func(blkIdx int, blkSize int, err error) { 318 t.Logf("NotifyErr: blkIdx: %d, blkSize: %d, err: %s", blkIdx, blkSize, err) 319 }, 320 }) 321 if err != nil { 322 t.Fatalf("Put() error, %s", err) 323 } 324 md5ByteArray := md5.Sum(data) 325 md5Value := hex.EncodeToString(md5ByteArray[:]) 326 validateMD5(t, testKey, md5Value, size) 327 t.Logf("Size: %d, Chunk: %d, Key: %s, Hash:%s", size, chunkSize, putRet.Key, putRet.Hash) 328 } 329 } 330 } 331 332 func validateMD5(t *testing.T, key, md5Expected string, sizeExpected int64) { 333 var ( 334 body struct { 335 Hash string `json:"hash"` 336 Fsize int64 `json:"fsize"` 337 } 338 httpClient http.Client 339 ) 340 341 response, err := httpClient.Get("http://" + testBucketDomain + "/" + key + "?qhash/md5") 342 if err != nil { 343 t.Fatal(err) 344 } 345 if response.StatusCode != http.StatusOK { 346 t.Fatalf("MD5 Get Status code is not ok, expected: %d, actual: %d, req_id: %s", http.StatusOK, response.StatusCode, response.Header.Get("X-Reqid")) 347 } 348 if err = json.NewDecoder(response.Body).Decode(&body); err != nil { 349 t.Fatal(err) 350 } 351 if body.Hash != md5Expected { 352 t.Fatalf("MD5 Dismatch, expected: %s, actual: %s", md5Expected, body.Hash) 353 } 354 if body.Fsize != sizeExpected { 355 t.Fatalf("File Size Dismatch, expected: %d, actual: %d", sizeExpected, body.Fsize) 356 } 357 } 358 359 func TestRetryChecker(t *testing.T) { 360 var putRet PutRet 361 putPolicy := PutPolicy{ 362 Scope: testBucket, 363 DeleteAfterDays: 7, 364 } 365 366 upToken := putPolicy.UploadToken(mac) 367 r := rand.New(rand.NewSource(time.Now().UnixNano())) 368 testKey := fmt.Sprintf("testRPutFileKey_%d", r.Int()) 369 370 mycfg := Config{} 371 wrongZone := Region{ 372 SrcUpHosts: []string{ 373 "nocname-up.qiniup.com", 374 "nocname-up-nb.qiniup.com", 375 "nocname-up-xs.qiniup.com", 376 }, 377 CdnUpHosts: []string{ 378 "nocname-upload.qiniup.com", 379 "nocname-upload-nb.qiniup.com", 380 "nocname-upload-xs.qiniup.com", 381 }, 382 RsHost: "rs.qbox.me", 383 RsfHost: "rsf.qbox.me", 384 ApiHost: "api.qiniu.com", 385 IovipHost: "iovip.qbox.me", 386 } 387 mycfg.Zone = &wrongZone 388 mycfg.UseCdnDomains = true 389 myResumeUploader := NewResumeUploaderEx(&mycfg, &clt) 390 391 rd := strings.NewReader("hello world") 392 // host unkown, so go to retry, 393 // any way, no : panic: runtime error: invalid memory address or nil pointer dereference 394 err := myResumeUploader.PutWithoutSize(context.Background(), &putRet, upToken, testKey, rd, nil) 395 if err != nil { 396 t.Logf("TestRetryChecker() error, %s", err) 397 } else { 398 t.Fatalf("TestRetryChecker() should failed, %s", putRet) 399 } 400 }