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  }