github.com/snowflakedb/gosnowflake@v1.9.0/gcs_storage_client_test.go (about)

     1  // Copyright (c) 2023 Snowflake Computing Inc. All rights reserved.
     2  
     3  package gosnowflake
     4  
     5  import (
     6  	"bytes"
     7  	"encoding/json"
     8  	"errors"
     9  	"net/http"
    10  	"net/url"
    11  	"os"
    12  	"path"
    13  	"testing"
    14  )
    15  
    16  type tcFileURL struct {
    17  	location string
    18  	fname    string
    19  	bucket   string
    20  	filepath string
    21  }
    22  
    23  func TestExtractBucketAndPath(t *testing.T) {
    24  	gcsUtil := new(snowflakeGcsClient)
    25  	testcases := []tcBucketPath{
    26  		{"sfc-eng-regression/test_sub_dir/", "sfc-eng-regression", "test_sub_dir/"},
    27  		{"sfc-eng-regression/dir/test_stg/test_sub_dir/", "sfc-eng-regression", "dir/test_stg/test_sub_dir/"},
    28  		{"sfc-eng-regression/", "sfc-eng-regression", ""},
    29  		{"sfc-eng-regression//", "sfc-eng-regression", "/"},
    30  		{"sfc-eng-regression///", "sfc-eng-regression", "//"},
    31  	}
    32  	for _, test := range testcases {
    33  		t.Run(test.in, func(t *testing.T) {
    34  			gcsLoc := gcsUtil.extractBucketNameAndPath(test.in)
    35  			if gcsLoc.bucketName != test.bucket {
    36  				t.Errorf("failed. in: %v, expected: %v, got: %v", test.in, test.bucket, gcsLoc.bucketName)
    37  			}
    38  			if gcsLoc.path != test.path {
    39  				t.Errorf("failed. in: %v, expected: %v, got: %v", test.in, test.path, gcsLoc.path)
    40  			}
    41  		})
    42  	}
    43  }
    44  
    45  func TestIsTokenExpiredWith401(t *testing.T) {
    46  	gcsUtil := new(snowflakeGcsClient)
    47  	dd := &execResponseData{}
    48  	execResp := &execResponse{
    49  		Data:    *dd,
    50  		Message: "token expired",
    51  		Code:    "401",
    52  		Success: true,
    53  	}
    54  	ba, err := json.Marshal(execResp)
    55  	if err != nil {
    56  		panic(err)
    57  	}
    58  	resp := &http.Response{StatusCode: http.StatusUnauthorized, Body: &fakeResponseBody{body: ba}}
    59  	if !gcsUtil.isTokenExpired(resp) {
    60  		t.Fatalf("expected true for token expired")
    61  	}
    62  }
    63  
    64  func TestIsTokenExpiredWith404(t *testing.T) {
    65  	gcsUtil := new(snowflakeGcsClient)
    66  	dd := &execResponseData{}
    67  	execResp := &execResponse{
    68  		Data:    *dd,
    69  		Message: "file not found",
    70  		Code:    "404",
    71  		Success: true,
    72  	}
    73  	ba, err := json.Marshal(execResp)
    74  	if err != nil {
    75  		panic(err)
    76  	}
    77  	resp := &http.Response{StatusCode: http.StatusNotFound, Body: &fakeResponseBody{body: ba}}
    78  	if gcsUtil.isTokenExpired(resp) {
    79  		t.Fatalf("should be false")
    80  	}
    81  	resp = &http.Response{
    82  		StatusCode: http.StatusOK,
    83  		Body:       &fakeResponseBody{body: []byte{0x12, 0x34}}}
    84  
    85  	if gcsUtil.isTokenExpired(resp) {
    86  		t.Fatalf("should be false")
    87  	}
    88  	resp = &http.Response{
    89  		StatusCode: http.StatusUnauthorized,
    90  		Body:       &fakeResponseBody{body: []byte{0x12, 0x34}}}
    91  
    92  	if !gcsUtil.isTokenExpired(resp) {
    93  		t.Fatalf("should be true")
    94  	}
    95  }
    96  
    97  func TestGenerateFileURL(t *testing.T) {
    98  	gcsUtil := new(snowflakeGcsClient)
    99  	testcases := []tcFileURL{
   100  		{"sfc-eng-regression/test_sub_dir/", "file1", "sfc-eng-regression", "test_sub_dir/file1"},
   101  		{"sfc-eng-regression/dir/test_stg/test_sub_dir/", "file2", "sfc-eng-regression", "dir/test_stg/test_sub_dir/file2"},
   102  		{"sfc-eng-regression/", "file3", "sfc-eng-regression", "file3"},
   103  		{"sfc-eng-regression//", "file4", "sfc-eng-regression", "/file4"},
   104  		{"sfc-eng-regression///", "file5", "sfc-eng-regression", "//file5"},
   105  	}
   106  	for _, test := range testcases {
   107  		t.Run(test.location, func(t *testing.T) {
   108  			gcsURL, err := gcsUtil.generateFileURL(test.location, test.fname)
   109  			if err != nil {
   110  				t.Error(err)
   111  			}
   112  			expectedURL, err := url.Parse("https://storage.googleapis.com/" + test.bucket + "/" + url.QueryEscape(test.filepath))
   113  			if err != nil {
   114  				t.Error(err)
   115  			}
   116  			if gcsURL.String() != expectedURL.String() {
   117  				t.Fatalf("failed. expected: %v but got: %v", expectedURL.String(), gcsURL.String())
   118  			}
   119  		})
   120  	}
   121  }
   122  
   123  type clientMock struct {
   124  	DoFunc func(req *http.Request) (*http.Response, error)
   125  }
   126  
   127  func (c *clientMock) Do(req *http.Request) (*http.Response, error) {
   128  	return c.DoFunc(req)
   129  }
   130  
   131  func TestUploadFileWithGcsUploadFailedError(t *testing.T) {
   132  	info := execResponseStageInfo{
   133  		Location:     "gcs-blob/storage/users/456/",
   134  		LocationType: "GCS",
   135  	}
   136  	initialParallel := int64(100)
   137  	dir, err := os.Getwd()
   138  	if err != nil {
   139  		t.Error(err)
   140  	}
   141  
   142  	gcsCli, err := new(snowflakeGcsClient).createClient(&info, false)
   143  	if err != nil {
   144  		t.Error(err)
   145  	}
   146  	uploadMeta := fileMetadata{
   147  		name:               "data1.txt.gz",
   148  		stageLocationType:  "GCS",
   149  		noSleepingTime:     true,
   150  		parallel:           initialParallel,
   151  		client:             gcsCli,
   152  		sha256Digest:       "123456789abcdef",
   153  		stageInfo:          &info,
   154  		dstFileName:        "data1.txt.gz",
   155  		srcFileName:        path.Join(dir, "/test_data/put_get_1.txt"),
   156  		overwrite:          true,
   157  		dstCompressionType: compressionTypes["GZIP"],
   158  		options: &SnowflakeFileTransferOptions{
   159  			MultiPartThreshold: dataSizeThreshold,
   160  		},
   161  		mockGcsClient: &clientMock{
   162  			DoFunc: func(req *http.Request) (*http.Response, error) {
   163  				return nil, errors.New("unexpected error uploading file")
   164  			},
   165  		},
   166  	}
   167  
   168  	uploadMeta.realSrcFileName = uploadMeta.srcFileName
   169  	fi, err := os.Stat(uploadMeta.srcFileName)
   170  	if err != nil {
   171  		t.Error(err)
   172  	}
   173  	uploadMeta.uploadSize = fi.Size()
   174  
   175  	err = new(remoteStorageUtil).uploadOneFile(&uploadMeta)
   176  	if err == nil {
   177  		t.Fatal("should have failed")
   178  	}
   179  }
   180  
   181  func TestUploadFileWithGcsUploadFailedWithRetry(t *testing.T) {
   182  	info := execResponseStageInfo{
   183  		Location:     "gcs-blob/storage/users/456/",
   184  		LocationType: "GCS",
   185  	}
   186  	encMat := snowflakeFileEncryption{
   187  		QueryStageMasterKey: "abCdEFO0upIT36dAxGsa0w==",
   188  		QueryID:             "01abc874-0406-1bf0-0000-53b10668e056",
   189  		SMKID:               92019681909886,
   190  	}
   191  	initialParallel := int64(100)
   192  	dir, err := os.Getwd()
   193  	if err != nil {
   194  		t.Error(err)
   195  	}
   196  
   197  	gcsCli, err := new(snowflakeGcsClient).createClient(&info, false)
   198  	if err != nil {
   199  		t.Error(err)
   200  	}
   201  	uploadMeta := fileMetadata{
   202  		name:               "data1.txt.gz",
   203  		stageLocationType:  "GCS",
   204  		noSleepingTime:     true,
   205  		parallel:           initialParallel,
   206  		client:             gcsCli,
   207  		sha256Digest:       "123456789abcdef",
   208  		stageInfo:          &info,
   209  		dstFileName:        "data1.txt.gz",
   210  		srcFileName:        path.Join(dir, "/test_data/put_get_1.txt"),
   211  		overwrite:          true,
   212  		dstCompressionType: compressionTypes["GZIP"],
   213  		encryptionMaterial: &encMat,
   214  		options: &SnowflakeFileTransferOptions{
   215  			MultiPartThreshold: dataSizeThreshold,
   216  		},
   217  		mockGcsClient: &clientMock{
   218  			DoFunc: func(req *http.Request) (*http.Response, error) {
   219  				return &http.Response{
   220  					Status:     "403 Forbidden",
   221  					StatusCode: 403,
   222  				}, nil
   223  			},
   224  		},
   225  	}
   226  
   227  	uploadMeta.realSrcFileName = uploadMeta.srcFileName
   228  	fi, err := os.Stat(uploadMeta.srcFileName)
   229  	if err != nil {
   230  		t.Error(err)
   231  	}
   232  	uploadMeta.uploadSize = fi.Size()
   233  
   234  	err = new(remoteStorageUtil).uploadOneFile(&uploadMeta)
   235  	if err == nil {
   236  		t.Error("should have raised an error")
   237  	}
   238  
   239  	if uploadMeta.resStatus != needRetry {
   240  		t.Fatalf("expected %v result status, got: %v",
   241  			needRetry, uploadMeta.resStatus)
   242  	}
   243  }
   244  
   245  func TestUploadFileWithGcsUploadFailedWithTokenExpired(t *testing.T) {
   246  	info := execResponseStageInfo{
   247  		Location:     "gcs-blob/storage/users/456/",
   248  		LocationType: "GCS",
   249  		Creds: execResponseCredentials{
   250  			GcsAccessToken: "test-token-124456577",
   251  		},
   252  	}
   253  	initialParallel := int64(100)
   254  	dir, err := os.Getwd()
   255  	if err != nil {
   256  		t.Error(err)
   257  	}
   258  
   259  	gcsCli, err := new(snowflakeGcsClient).createClient(&info, false)
   260  	if err != nil {
   261  		t.Error(err)
   262  	}
   263  	uploadMeta := fileMetadata{
   264  		name:              "data1.txt.gz",
   265  		stageLocationType: "GCS",
   266  		noSleepingTime:    true,
   267  		parallel:          initialParallel,
   268  		client:            gcsCli,
   269  		sha256Digest:      "123456789abcdef",
   270  		stageInfo:         &info,
   271  		dstFileName:       "data1.txt.gz",
   272  		srcFileName:       path.Join(dir, "/test_data/put_get_1.txt"),
   273  		overwrite:         true,
   274  		options: &SnowflakeFileTransferOptions{
   275  			MultiPartThreshold: dataSizeThreshold,
   276  		},
   277  		mockGcsClient: &clientMock{
   278  			DoFunc: func(req *http.Request) (*http.Response, error) {
   279  				return &http.Response{
   280  					Status:     "401 Unauthorized",
   281  					StatusCode: 401,
   282  				}, nil
   283  			},
   284  		},
   285  	}
   286  
   287  	uploadMeta.realSrcFileName = uploadMeta.srcFileName
   288  	fi, err := os.Stat(uploadMeta.srcFileName)
   289  	if err != nil {
   290  		t.Error(err)
   291  	}
   292  	uploadMeta.uploadSize = fi.Size()
   293  
   294  	err = new(remoteStorageUtil).uploadOneFile(&uploadMeta)
   295  	if err != nil {
   296  		t.Error(err)
   297  	}
   298  
   299  	if uploadMeta.resStatus != renewToken {
   300  		t.Fatalf("expected %v result status, got: %v",
   301  			renewToken, uploadMeta.resStatus)
   302  	}
   303  }
   304  
   305  func TestDownloadOneFileFromGcsFailed(t *testing.T) {
   306  	info := execResponseStageInfo{
   307  		Location:     "gcs/teststage/users/34/",
   308  		LocationType: "GCS",
   309  	}
   310  	dir, err := os.Getwd()
   311  	if err != nil {
   312  		t.Error(err)
   313  	}
   314  
   315  	gcsCli, err := new(snowflakeGcsClient).createClient(&info, false)
   316  	if err != nil {
   317  		t.Error(err)
   318  	}
   319  
   320  	downloadMeta := fileMetadata{
   321  		name:              "data1.txt.gz",
   322  		stageLocationType: "GCS",
   323  		noSleepingTime:    true,
   324  		client:            gcsCli,
   325  		stageInfo:         &info,
   326  		dstFileName:       "data1.txt.gz",
   327  		overwrite:         true,
   328  		srcFileName:       "data1.txt.gz",
   329  		localLocation:     dir,
   330  		options: &SnowflakeFileTransferOptions{
   331  			MultiPartThreshold: dataSizeThreshold,
   332  		},
   333  		mockGcsClient: &clientMock{
   334  			DoFunc: func(req *http.Request) (*http.Response, error) {
   335  				return nil, errors.New("unexpected error downloading file")
   336  			},
   337  		},
   338  		resStatus: downloaded, // bypass file header request
   339  	}
   340  	err = new(remoteStorageUtil).downloadOneFile(&downloadMeta)
   341  	if err == nil {
   342  		t.Error("should have raised an error")
   343  	}
   344  }
   345  
   346  func TestDownloadOneFileFromGcsFailedWithRetry(t *testing.T) {
   347  	info := execResponseStageInfo{
   348  		Location:     "gcs/teststage/users/34/",
   349  		LocationType: "GCS",
   350  	}
   351  	dir, err := os.Getwd()
   352  	if err != nil {
   353  		t.Error(err)
   354  	}
   355  
   356  	gcsCli, err := new(snowflakeGcsClient).createClient(&info, false)
   357  	if err != nil {
   358  		t.Error(err)
   359  	}
   360  
   361  	downloadMeta := fileMetadata{
   362  		name:              "data1.txt.gz",
   363  		stageLocationType: "GCS",
   364  		noSleepingTime:    true,
   365  		client:            gcsCli,
   366  		stageInfo:         &info,
   367  		dstFileName:       "data1.txt.gz",
   368  		overwrite:         true,
   369  		srcFileName:       "data1.txt.gz",
   370  		localLocation:     dir,
   371  		options: &SnowflakeFileTransferOptions{
   372  			MultiPartThreshold: dataSizeThreshold,
   373  		},
   374  		mockGcsClient: &clientMock{
   375  			DoFunc: func(req *http.Request) (*http.Response, error) {
   376  				return &http.Response{
   377  					Status:     "403 Forbidden",
   378  					StatusCode: 403,
   379  				}, nil
   380  			},
   381  		},
   382  		resStatus: downloaded, // bypass file header request
   383  	}
   384  	err = new(remoteStorageUtil).downloadOneFile(&downloadMeta)
   385  	if err == nil {
   386  		t.Error("should have raised an error")
   387  	}
   388  
   389  	if downloadMeta.resStatus != needRetry {
   390  		t.Fatalf("expected %v result status, got: %v",
   391  			needRetry, downloadMeta.resStatus)
   392  	}
   393  }
   394  
   395  func TestDownloadOneFileFromGcsFailedWithTokenExpired(t *testing.T) {
   396  	info := execResponseStageInfo{
   397  		Location:     "gcs/teststage/users/34/",
   398  		LocationType: "GCS",
   399  		Creds: execResponseCredentials{
   400  			GcsAccessToken: "test-token-124456577",
   401  		},
   402  	}
   403  	dir, err := os.Getwd()
   404  	if err != nil {
   405  		t.Error(err)
   406  	}
   407  
   408  	gcsCli, err := new(snowflakeGcsClient).createClient(&info, false)
   409  	if err != nil {
   410  		t.Error(err)
   411  	}
   412  
   413  	downloadMeta := fileMetadata{
   414  		name:              "data1.txt.gz",
   415  		stageLocationType: "GCS",
   416  		noSleepingTime:    true,
   417  		client:            gcsCli,
   418  		stageInfo:         &info,
   419  		dstFileName:       "data1.txt.gz",
   420  		overwrite:         true,
   421  		srcFileName:       "data1.txt.gz",
   422  		localLocation:     dir,
   423  		options: &SnowflakeFileTransferOptions{
   424  			MultiPartThreshold: dataSizeThreshold,
   425  		},
   426  		mockGcsClient: &clientMock{
   427  			DoFunc: func(req *http.Request) (*http.Response, error) {
   428  				return &http.Response{
   429  					Status:     "401 Unauthorized",
   430  					StatusCode: 401,
   431  				}, nil
   432  			},
   433  		},
   434  		resStatus: downloaded, // bypass file header request
   435  	}
   436  	err = new(remoteStorageUtil).downloadOneFile(&downloadMeta)
   437  	if err == nil {
   438  		t.Error("should have raised an error")
   439  	}
   440  
   441  	if downloadMeta.resStatus != renewToken {
   442  		t.Fatalf("expected %v result status, got: %v",
   443  			renewToken, downloadMeta.resStatus)
   444  	}
   445  }
   446  
   447  func TestDownloadOneFileFromGcsFailedWithFileNotFound(t *testing.T) {
   448  	info := execResponseStageInfo{
   449  		Location:     "gcs/teststage/users/34/",
   450  		LocationType: "GCS",
   451  		Creds: execResponseCredentials{
   452  			GcsAccessToken: "test-token-124456577",
   453  		},
   454  	}
   455  	dir, err := os.Getwd()
   456  	if err != nil {
   457  		t.Error(err)
   458  	}
   459  
   460  	gcsCli, err := new(snowflakeGcsClient).createClient(&info, false)
   461  	if err != nil {
   462  		t.Error(err)
   463  	}
   464  
   465  	downloadMeta := fileMetadata{
   466  		name:              "data1.txt.gz",
   467  		stageLocationType: "GCS",
   468  		noSleepingTime:    true,
   469  		client:            gcsCli,
   470  		stageInfo:         &info,
   471  		dstFileName:       "data1.txt.gz",
   472  		overwrite:         true,
   473  		srcFileName:       "data1.txt.gz",
   474  		localLocation:     dir,
   475  		options: &SnowflakeFileTransferOptions{
   476  			MultiPartThreshold: dataSizeThreshold,
   477  		},
   478  		mockGcsClient: &clientMock{
   479  			DoFunc: func(req *http.Request) (*http.Response, error) {
   480  				return &http.Response{
   481  					Status:     "404 Not Found",
   482  					StatusCode: 404,
   483  				}, nil
   484  			},
   485  		},
   486  		resStatus: downloaded, // bypass file header request
   487  	}
   488  	err = new(remoteStorageUtil).downloadOneFile(&downloadMeta)
   489  	if err == nil {
   490  		t.Error("should have raised an error")
   491  	}
   492  
   493  	if downloadMeta.resStatus != notFoundFile {
   494  		t.Fatalf("expected %v result status, got: %v",
   495  			notFoundFile, downloadMeta.resStatus)
   496  	}
   497  }
   498  
   499  func TestGetHeaderTokenExpiredError(t *testing.T) {
   500  	info := execResponseStageInfo{
   501  		Location:     "gcs/teststage/users/34/",
   502  		LocationType: "GCS",
   503  		Creds: execResponseCredentials{
   504  			GcsAccessToken: "test-token-124456577",
   505  		},
   506  	}
   507  	meta := fileMetadata{
   508  		client:    info.Creds.GcsAccessToken,
   509  		stageInfo: &info,
   510  		mockGcsClient: &clientMock{
   511  			DoFunc: func(req *http.Request) (*http.Response, error) {
   512  				return &http.Response{
   513  					Status:     "401 Unauthorized",
   514  					StatusCode: 401,
   515  				}, nil
   516  			},
   517  		},
   518  	}
   519  	if header, err := new(snowflakeGcsClient).getFileHeader(&meta, "file.txt"); header != nil || err == nil {
   520  		t.Fatalf("expected null header, got: %v", header)
   521  	}
   522  	if meta.resStatus != renewToken {
   523  		t.Fatalf("expected %v result status, got: %v",
   524  			renewToken, meta.resStatus)
   525  	}
   526  }
   527  
   528  func TestGetHeaderFileNotFound(t *testing.T) {
   529  	info := execResponseStageInfo{
   530  		Location:     "gcs/teststage/users/34/",
   531  		LocationType: "GCS",
   532  		Creds: execResponseCredentials{
   533  			GcsAccessToken: "test-token-124456577",
   534  		},
   535  	}
   536  	meta := fileMetadata{
   537  		client:    info.Creds.GcsAccessToken,
   538  		stageInfo: &info,
   539  		mockGcsClient: &clientMock{
   540  			DoFunc: func(req *http.Request) (*http.Response, error) {
   541  				return &http.Response{
   542  					Status:     "404 Not Found",
   543  					StatusCode: 404,
   544  				}, nil
   545  			},
   546  		},
   547  	}
   548  	if header, err := new(snowflakeGcsClient).getFileHeader(&meta, "file.txt"); header != nil || err == nil {
   549  		t.Fatalf("expected null header, got: %v", header)
   550  	}
   551  	if meta.resStatus != notFoundFile {
   552  		t.Fatalf("expected %v result status, got: %v",
   553  			notFoundFile, meta.resStatus)
   554  	}
   555  }
   556  
   557  func TestGetHeaderPresignedUrlReturns404(t *testing.T) {
   558  	info := execResponseStageInfo{
   559  		Location:     "gcs/teststage/users/34/",
   560  		LocationType: "GCS",
   561  		Creds: execResponseCredentials{
   562  			GcsAccessToken: "test-token-124456577",
   563  		},
   564  	}
   565  	presignedURL, err := url.Parse("https://google-cloud.test.com")
   566  	if err != nil {
   567  		t.Error(err)
   568  	}
   569  	meta := fileMetadata{
   570  		client:       info.Creds.GcsAccessToken,
   571  		stageInfo:    &info,
   572  		presignedURL: presignedURL,
   573  	}
   574  	header, err := new(snowflakeGcsClient).getFileHeader(&meta, "file.txt")
   575  	if header != nil {
   576  		t.Fatalf("expected null header, got: %v", header)
   577  	}
   578  	if err != nil {
   579  		t.Error(err)
   580  	}
   581  	if meta.resStatus != notFoundFile {
   582  		t.Fatalf("expected %v result status, got: %v",
   583  			notFoundFile, meta.resStatus)
   584  	}
   585  }
   586  
   587  func TestGetHeaderReturnsError(t *testing.T) {
   588  	info := execResponseStageInfo{
   589  		Location:     "gcs/teststage/users/34/",
   590  		LocationType: "GCS",
   591  		Creds: execResponseCredentials{
   592  			GcsAccessToken: "test-token-124456577",
   593  		},
   594  	}
   595  	meta := fileMetadata{
   596  		client:    info.Creds.GcsAccessToken,
   597  		stageInfo: &info,
   598  		mockGcsClient: &clientMock{
   599  			DoFunc: func(req *http.Request) (*http.Response, error) {
   600  				return nil, errors.New("unexpected exception getting file header")
   601  			},
   602  		},
   603  	}
   604  	if header, err := new(snowflakeGcsClient).getFileHeader(&meta, "file.txt"); header != nil || err == nil {
   605  		t.Fatalf("expected null header, got: %v", header)
   606  	}
   607  }
   608  
   609  func TestGetHeaderBadRequest(t *testing.T) {
   610  	info := execResponseStageInfo{
   611  		Location:     "gcs/teststage/users/34/",
   612  		LocationType: "GCS",
   613  		Creds: execResponseCredentials{
   614  			GcsAccessToken: "test-token-124456577",
   615  		},
   616  	}
   617  	meta := fileMetadata{
   618  		client:    info.Creds.GcsAccessToken,
   619  		stageInfo: &info,
   620  		mockGcsClient: &clientMock{
   621  			DoFunc: func(req *http.Request) (*http.Response, error) {
   622  				return &http.Response{
   623  					Status:     "400 Bad Request",
   624  					StatusCode: 400,
   625  				}, nil
   626  			},
   627  		},
   628  	}
   629  	if header, err := new(snowflakeGcsClient).getFileHeader(&meta, "file.txt"); header != nil || err == nil {
   630  		t.Fatalf("expected null header, got: %v", header)
   631  	}
   632  
   633  	if meta.resStatus != errStatus {
   634  		t.Fatalf("expected %v result status, got: %v",
   635  			errStatus, meta.resStatus)
   636  	}
   637  }
   638  
   639  func TestGetHeaderRetryableError(t *testing.T) {
   640  	info := execResponseStageInfo{
   641  		Location:     "gcs/teststage/users/34/",
   642  		LocationType: "GCS",
   643  		Creds: execResponseCredentials{
   644  			GcsAccessToken: "test-token-124456577",
   645  		},
   646  	}
   647  	meta := fileMetadata{
   648  		client:    info.Creds.GcsAccessToken,
   649  		stageInfo: &info,
   650  		mockGcsClient: &clientMock{
   651  			DoFunc: func(req *http.Request) (*http.Response, error) {
   652  				return &http.Response{
   653  					Status:     "403 Forbidden",
   654  					StatusCode: 403,
   655  				}, nil
   656  			},
   657  		},
   658  	}
   659  	if header, err := new(snowflakeGcsClient).getFileHeader(&meta, "file.txt"); header != nil || err == nil {
   660  		t.Fatalf("expected null header, got: %v", header)
   661  	}
   662  	if meta.resStatus != needRetry {
   663  		t.Fatalf("expected %v result status, got: %v",
   664  			needRetry, meta.resStatus)
   665  	}
   666  }
   667  
   668  func TestUploadStreamFailed(t *testing.T) {
   669  	info := execResponseStageInfo{
   670  		Location:     "gcs-blob/storage/users/456/",
   671  		LocationType: "GCS",
   672  	}
   673  	initialParallel := int64(100)
   674  	src := []byte{65, 66, 67}
   675  
   676  	gcsCli, err := new(snowflakeGcsClient).createClient(&info, false)
   677  	if err != nil {
   678  		t.Error(err)
   679  	}
   680  
   681  	uploadMeta := fileMetadata{
   682  		name:              "data1.txt.gz",
   683  		stageLocationType: "GCS",
   684  		noSleepingTime:    true,
   685  		parallel:          initialParallel,
   686  		client:            gcsCli,
   687  		sha256Digest:      "123456789abcdef",
   688  		stageInfo:         &info,
   689  		dstFileName:       "data1.txt.gz",
   690  		srcStream:         bytes.NewBuffer(src),
   691  		overwrite:         true,
   692  		options: &SnowflakeFileTransferOptions{
   693  			MultiPartThreshold: dataSizeThreshold,
   694  		},
   695  		mockGcsClient: &clientMock{
   696  			DoFunc: func(req *http.Request) (*http.Response, error) {
   697  				return nil, errors.New("unexpected error uploading file")
   698  			},
   699  		},
   700  	}
   701  
   702  	uploadMeta.realSrcStream = uploadMeta.srcStream
   703  
   704  	err = new(remoteStorageUtil).uploadOneFile(&uploadMeta)
   705  	if err == nil {
   706  		t.Fatal("should have failed")
   707  	}
   708  }
   709  
   710  func TestUploadFileWithBadRequest(t *testing.T) {
   711  	info := execResponseStageInfo{
   712  		Location:     "gcs-blob/storage/users/456/",
   713  		LocationType: "GCS",
   714  	}
   715  	initialParallel := int64(100)
   716  	dir, err := os.Getwd()
   717  	if err != nil {
   718  		t.Error(err)
   719  	}
   720  
   721  	gcsCli, err := new(snowflakeGcsClient).createClient(&info, false)
   722  	if err != nil {
   723  		t.Error(err)
   724  	}
   725  	uploadMeta := fileMetadata{
   726  		name:              "data1.txt.gz",
   727  		stageLocationType: "GCS",
   728  		noSleepingTime:    true,
   729  		parallel:          initialParallel,
   730  		client:            gcsCli,
   731  		sha256Digest:      "123456789abcdef",
   732  		stageInfo:         &info,
   733  		dstFileName:       "data1.txt.gz",
   734  		srcFileName:       path.Join(dir, "/test_data/put_get_1.txt"),
   735  		overwrite:         true,
   736  		lastError:         nil,
   737  		options: &SnowflakeFileTransferOptions{
   738  			MultiPartThreshold: dataSizeThreshold,
   739  		},
   740  		mockGcsClient: &clientMock{
   741  			DoFunc: func(req *http.Request) (*http.Response, error) {
   742  				return &http.Response{
   743  					StatusCode: 400,
   744  				}, nil
   745  			},
   746  		},
   747  	}
   748  
   749  	uploadMeta.realSrcFileName = uploadMeta.srcFileName
   750  	fi, err := os.Stat(uploadMeta.srcFileName)
   751  	if err != nil {
   752  		t.Error(err)
   753  	}
   754  	uploadMeta.uploadSize = fi.Size()
   755  
   756  	err = new(remoteStorageUtil).uploadOneFile(&uploadMeta)
   757  	if err != nil {
   758  		t.Error(err)
   759  	}
   760  
   761  	if uploadMeta.resStatus != renewPresignedURL {
   762  		t.Fatalf("expected %v result status, got: %v",
   763  			renewPresignedURL, uploadMeta.resStatus)
   764  	}
   765  }
   766  
   767  func TestGetFileHeaderEncryptionData(t *testing.T) {
   768  	mockEncDataResp := "{\"EncryptionMode\":\"FullBlob\",\"WrappedContentKey\": {\"KeyId\":\"symmKey1\",\"EncryptedKey\":\"testencryptedkey12345678910==\",\"Algorithm\":\"AES_CBC_256\"},\"EncryptionAgent\": {\"Protocol\":\"1.0\",\"EncryptionAlgorithm\":\"AES_CBC_256\"},\"ContentEncryptionIV\":\"testIVkey12345678910==\",\"KeyWrappingMetadata\":{\"EncryptionLibrary\":\"Java 5.3.0\"}}"
   769  	mockMatDesc := "{\"queryid\":\"01abc874-0406-1bf0-0000-53b10668e056\",\"smkid\":\"92019681909886\",\"key\":\"128\"}"
   770  	info := execResponseStageInfo{
   771  		Location:     "gcs/teststage/users/34/",
   772  		LocationType: "GCS",
   773  		Creds: execResponseCredentials{
   774  			GcsAccessToken: "test-token-124456577",
   775  		},
   776  	}
   777  	meta := fileMetadata{
   778  		client:    info.Creds.GcsAccessToken,
   779  		stageInfo: &info,
   780  		mockGcsClient: &clientMock{
   781  			DoFunc: func(req *http.Request) (*http.Response, error) {
   782  				return &http.Response{
   783  					Status:     "200 OK",
   784  					StatusCode: 200,
   785  					Header: http.Header{
   786  						"X-Goog-Meta-Encryptiondata": []string{mockEncDataResp},
   787  						"Content-Length":             []string{"4256"},
   788  						"X-Goog-Meta-Sfc-Digest":     []string{"123456789abcdef"},
   789  						"X-Goog-Meta-Matdesc":        []string{mockMatDesc},
   790  					},
   791  				}, nil
   792  			},
   793  		},
   794  	}
   795  	header, err := new(snowflakeGcsClient).getFileHeader(&meta, "file.txt")
   796  	if err != nil {
   797  		t.Fatal(err)
   798  	}
   799  	expectedFileHeader := &fileHeader{
   800  		digest:        "123456789abcdef",
   801  		contentLength: 4256,
   802  		encryptionMetadata: &encryptMetadata{
   803  			key:     "testencryptedkey12345678910==",
   804  			iv:      "testIVkey12345678910==",
   805  			matdesc: mockMatDesc,
   806  		},
   807  	}
   808  	if header.contentLength != expectedFileHeader.contentLength || header.digest != expectedFileHeader.digest || header.encryptionMetadata.iv != expectedFileHeader.encryptionMetadata.iv || header.encryptionMetadata.key != expectedFileHeader.encryptionMetadata.key || header.encryptionMetadata.matdesc != expectedFileHeader.encryptionMetadata.matdesc {
   809  		t.Fatalf("unexpected file header. expected: %v, got: %v", expectedFileHeader, header)
   810  	}
   811  }
   812  
   813  func TestGetFileHeaderEncryptionDataInterfaceConversionError(t *testing.T) {
   814  	mockEncDataResp := "{\"EncryptionMode\":\"FullBlob\",\"WrappedContentKey\": {\"KeyId\":\"symmKey1\",\"EncryptedKey\":\"testencryptedkey12345678910==\",\"Algorithm\":\"AES_CBC_256\"},\"EncryptionAgent\": {\"Protocol\":\"1.0\",\"EncryptionAlgorithm\":\"AES_CBC_256\"},\"ContentEncryptionIV\":\"testIVkey12345678910==\",\"KeyWrappingMetadata\":{\"EncryptionLibrary\":\"Java 5.3.0\"}}"
   815  	mockMatDesc := "{\"queryid\":\"01abc874-0406-1bf0-0000-53b10668e056\",\"smkid\":\"92019681909886\",\"key\":\"128\"}"
   816  	info := execResponseStageInfo{
   817  		Location:     "gcs/teststage/users/34/",
   818  		LocationType: "GCS",
   819  		Creds: execResponseCredentials{
   820  			GcsAccessToken: "test-token-124456577",
   821  		},
   822  	}
   823  	meta := fileMetadata{
   824  		client:    1,
   825  		stageInfo: &info,
   826  		mockGcsClient: &clientMock{
   827  			DoFunc: func(req *http.Request) (*http.Response, error) {
   828  				return &http.Response{
   829  					Status:     "200 OK",
   830  					StatusCode: 200,
   831  					Header: http.Header{
   832  						"X-Goog-Meta-Encryptiondata": []string{mockEncDataResp},
   833  						"Content-Length":             []string{"4256"},
   834  						"X-Goog-Meta-Sfc-Digest":     []string{"123456789abcdef"},
   835  						"X-Goog-Meta-Matdesc":        []string{mockMatDesc},
   836  					},
   837  				}, nil
   838  			},
   839  		},
   840  	}
   841  	_, err := new(snowflakeGcsClient).getFileHeader(&meta, "file.txt")
   842  	if err == nil {
   843  		t.Error("should have raised an error")
   844  	}
   845  }
   846  
   847  func TestUploadFileToGcsNoStatus(t *testing.T) {
   848  	info := execResponseStageInfo{
   849  		Location:     "gcs-blob/storage/users/456/",
   850  		LocationType: "GCS",
   851  	}
   852  	encMat := snowflakeFileEncryption{
   853  		QueryStageMasterKey: "abCdEFO0upIT36dAxGsa0w==",
   854  		QueryID:             "01abc874-0406-1bf0-0000-53b10668e056",
   855  		SMKID:               92019681909886,
   856  	}
   857  	initialParallel := int64(100)
   858  	dir, err := os.Getwd()
   859  	if err != nil {
   860  		t.Error(err)
   861  	}
   862  
   863  	gcsCli, err := new(snowflakeGcsClient).createClient(&info, false)
   864  	if err != nil {
   865  		t.Error(err)
   866  	}
   867  	uploadMeta := fileMetadata{
   868  		name:               "data1.txt.gz",
   869  		stageLocationType:  "GCS",
   870  		noSleepingTime:     true,
   871  		parallel:           initialParallel,
   872  		client:             gcsCli,
   873  		sha256Digest:       "123456789abcdef",
   874  		stageInfo:          &info,
   875  		dstFileName:        "data1.txt.gz",
   876  		srcFileName:        path.Join(dir, "/test_data/put_get_1.txt"),
   877  		overwrite:          true,
   878  		dstCompressionType: compressionTypes["GZIP"],
   879  		encryptionMaterial: &encMat,
   880  		options: &SnowflakeFileTransferOptions{
   881  			MultiPartThreshold: dataSizeThreshold,
   882  		},
   883  		mockGcsClient: &clientMock{
   884  			DoFunc: func(req *http.Request) (*http.Response, error) {
   885  				return &http.Response{
   886  					Status:     "401 Unauthorized",
   887  					StatusCode: 401,
   888  				}, nil
   889  			},
   890  		},
   891  	}
   892  
   893  	uploadMeta.realSrcFileName = uploadMeta.srcFileName
   894  	fi, err := os.Stat(uploadMeta.srcFileName)
   895  	if err != nil {
   896  		t.Error(err)
   897  	}
   898  	uploadMeta.uploadSize = fi.Size()
   899  
   900  	err = new(remoteStorageUtil).uploadOneFile(&uploadMeta)
   901  	if err == nil {
   902  		t.Error("should have raised an error")
   903  	}
   904  }
   905  
   906  func TestDownloadFileFromGcsError(t *testing.T) {
   907  	info := execResponseStageInfo{
   908  		Location:     "gcs/teststage/users/34/",
   909  		LocationType: "GCS",
   910  	}
   911  	dir, err := os.Getwd()
   912  	if err != nil {
   913  		t.Error(err)
   914  	}
   915  
   916  	gcsCli, err := new(snowflakeGcsClient).createClient(&info, false)
   917  	if err != nil {
   918  		t.Error(err)
   919  	}
   920  
   921  	downloadMeta := fileMetadata{
   922  		name:              "data1.txt.gz",
   923  		stageLocationType: "GCS",
   924  		noSleepingTime:    true,
   925  		client:            gcsCli,
   926  		stageInfo:         &info,
   927  		dstFileName:       "data1.txt.gz",
   928  		overwrite:         true,
   929  		srcFileName:       "data1.txt.gz",
   930  		localLocation:     dir,
   931  		options: &SnowflakeFileTransferOptions{
   932  			MultiPartThreshold: dataSizeThreshold,
   933  		},
   934  		mockGcsClient: &clientMock{
   935  			DoFunc: func(req *http.Request) (*http.Response, error) {
   936  				return &http.Response{
   937  					Status:     "403 Unauthorized",
   938  					StatusCode: 401,
   939  				}, nil
   940  			},
   941  		},
   942  		resStatus: downloaded, // bypass file header request
   943  	}
   944  	err = new(remoteStorageUtil).downloadOneFile(&downloadMeta)
   945  	if err == nil {
   946  		t.Error("should have raised an error")
   947  	}
   948  }
   949  
   950  func TestDownloadFileWithBadRequest(t *testing.T) {
   951  	info := execResponseStageInfo{
   952  		Location:     "gcs/teststage/users/34/",
   953  		LocationType: "GCS",
   954  	}
   955  	dir, err := os.Getwd()
   956  	if err != nil {
   957  		t.Error(err)
   958  	}
   959  
   960  	gcsCli, err := new(snowflakeGcsClient).createClient(&info, false)
   961  	if err != nil {
   962  		t.Error(err)
   963  	}
   964  
   965  	downloadMeta := fileMetadata{
   966  		name:              "data1.txt.gz",
   967  		stageLocationType: "GCS",
   968  		noSleepingTime:    true,
   969  		client:            gcsCli,
   970  		stageInfo:         &info,
   971  		dstFileName:       "data1.txt.gz",
   972  		overwrite:         true,
   973  		srcFileName:       "data1.txt.gz",
   974  		localLocation:     dir,
   975  		options: &SnowflakeFileTransferOptions{
   976  			MultiPartThreshold: dataSizeThreshold,
   977  		},
   978  		mockGcsClient: &clientMock{
   979  			DoFunc: func(req *http.Request) (*http.Response, error) {
   980  				return &http.Response{
   981  					Status:     "400 Bad Request",
   982  					StatusCode: 400,
   983  				}, nil
   984  			},
   985  		},
   986  		resStatus: downloaded, // bypass file header request
   987  	}
   988  	err = new(remoteStorageUtil).downloadOneFile(&downloadMeta)
   989  	if err == nil {
   990  		t.Error("should have raised an error")
   991  	}
   992  
   993  	if downloadMeta.resStatus != renewPresignedURL {
   994  		t.Fatalf("expected %v result status, got: %v",
   995  			renewPresignedURL, downloadMeta.resStatus)
   996  	}
   997  }
   998  
   999  func Test_snowflakeGcsClient_uploadFile(t *testing.T) {
  1000  	info := execResponseStageInfo{
  1001  		Location:     "gcs/teststage/users/34/",
  1002  		LocationType: "GCS",
  1003  		Creds: execResponseCredentials{
  1004  			GcsAccessToken: "test-token-124456577",
  1005  		},
  1006  	}
  1007  	meta := fileMetadata{
  1008  		client:    1,
  1009  		stageInfo: &info,
  1010  	}
  1011  	err := new(snowflakeGcsClient).uploadFile("somedata", &meta, nil, 1, 1)
  1012  	if err == nil {
  1013  		t.Error("should have raised an error")
  1014  	}
  1015  }
  1016  
  1017  func Test_snowflakeGcsClient_nativeDownloadFile(t *testing.T) {
  1018  	info := execResponseStageInfo{
  1019  		Location:     "gcs/teststage/users/34/",
  1020  		LocationType: "GCS",
  1021  		Creds: execResponseCredentials{
  1022  			GcsAccessToken: "test-token-124456577",
  1023  		},
  1024  	}
  1025  	meta := fileMetadata{
  1026  		client:    1,
  1027  		stageInfo: &info,
  1028  	}
  1029  	err := new(snowflakeGcsClient).nativeDownloadFile(&meta, "dummy data", 1)
  1030  	if err == nil {
  1031  		t.Error("should have raised an error")
  1032  	}
  1033  }