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:     &region.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:     &region.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  }