github.com/jfrog/jfrog-cli-core/v2@v2.51.0/artifactory/commands/transferfiles/transfer_test.go (about)

     1  package transferfiles
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"io"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"os"
    10  	"path/filepath"
    11  	"sync"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/gocarina/gocsv"
    16  	"github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/transferfiles/api"
    17  	"github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/transferfiles/state"
    18  	"github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/utils"
    19  	coreUtils "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
    20  	commonTests "github.com/jfrog/jfrog-cli-core/v2/common/tests"
    21  	coreConfig "github.com/jfrog/jfrog-cli-core/v2/utils/config"
    22  	"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
    23  	"github.com/jfrog/jfrog-cli-core/v2/utils/tests"
    24  	"github.com/jfrog/jfrog-client-go/artifactory"
    25  	"github.com/jfrog/jfrog-client-go/artifactory/services"
    26  	artifactoryUtils "github.com/jfrog/jfrog-client-go/artifactory/services/utils"
    27  	clientutils "github.com/jfrog/jfrog-client-go/utils"
    28  	"github.com/stretchr/testify/assert"
    29  )
    30  
    31  func TestHandleStopInitAndClose(t *testing.T) {
    32  	transferFilesCommand, err := NewTransferFilesCommand(nil, nil)
    33  	assert.NoError(t, err)
    34  	finishStopping, _ := transferFilesCommand.handleStop(nil)
    35  	finishStopping()
    36  }
    37  
    38  func TestCancelFunc(t *testing.T) {
    39  	transferFilesCommand, err := NewTransferFilesCommand(nil, nil)
    40  	assert.NoError(t, err)
    41  	assert.False(t, transferFilesCommand.shouldStop())
    42  
    43  	transferFilesCommand.cancelFunc()
    44  	assert.True(t, transferFilesCommand.shouldStop())
    45  }
    46  
    47  func TestSignalStop(t *testing.T) {
    48  	cleanUpJfrogHome, err := tests.SetJfrogHome()
    49  	assert.NoError(t, err)
    50  	defer cleanUpJfrogHome()
    51  
    52  	// Create transfer files command and mark the transfer as started
    53  	transferFilesCommand, err := NewTransferFilesCommand(nil, nil)
    54  	assert.NoError(t, err)
    55  	assert.NoError(t, transferFilesCommand.initTransferDir())
    56  	assert.NoError(t, transferFilesCommand.stateManager.TryLockTransferStateManager())
    57  
    58  	// Make sure that the '.jfrog/transfer/stop' doesn't exist
    59  	transferDir, err := coreutils.GetJfrogTransferDir()
    60  	assert.NoError(t, err)
    61  	assert.NoFileExists(t, filepath.Join(transferDir, StopFileName))
    62  
    63  	// Run signalStop and make sure that the '.jfrog/transfer/stop' exists
    64  	assert.NoError(t, transferFilesCommand.signalStop())
    65  	assert.FileExists(t, filepath.Join(transferDir, StopFileName))
    66  }
    67  
    68  func TestSignalStopError(t *testing.T) {
    69  	cleanUpJfrogHome, err := tests.SetJfrogHome()
    70  	assert.NoError(t, err)
    71  	defer cleanUpJfrogHome()
    72  
    73  	// Create transfer files command and mark the transfer as started
    74  	transferFilesCommand, err := NewTransferFilesCommand(nil, nil)
    75  	assert.NoError(t, err)
    76  
    77  	// Check "not active file transfer" error
    78  	assert.EqualError(t, transferFilesCommand.signalStop(), "There is no active file transfer process.")
    79  
    80  	// Mock start transfer
    81  	assert.NoError(t, transferFilesCommand.initTransferDir())
    82  	assert.NoError(t, transferFilesCommand.stateManager.TryLockTransferStateManager())
    83  
    84  	// Check "already in progress" error
    85  	assert.NoError(t, transferFilesCommand.signalStop())
    86  	assert.EqualError(t, transferFilesCommand.signalStop(), "Graceful stop is already in progress. Please wait...")
    87  }
    88  
    89  /* #nosec G101 -- Not credentials. */
    90  const (
    91  	firstUuidTokenForTest  = "347cd3e9-86b6-4bec-9be9-e053a485f327"
    92  	secondUuidTokenForTest = "af14706e-e0c1-4b7d-8791-6a18bd1fd339"
    93  	nodeIdForTest          = "nodea0gwihu76sk5g-artifactory-primary-0"
    94  )
    95  
    96  func TestValidateDataTransferPluginMinimumVersion(t *testing.T) {
    97  	t.Run("valid version", func(t *testing.T) { testValidateDataTransferPluginMinimumVersion(t, "9.9.9", false) })
    98  	t.Run("exact version", func(t *testing.T) {
    99  		testValidateDataTransferPluginMinimumVersion(t, dataTransferPluginMinVersion, false)
   100  	})
   101  	t.Run("invalid version", func(t *testing.T) { testValidateDataTransferPluginMinimumVersion(t, "1.0.0", true) })
   102  	t.Run("snapshot version", func(t *testing.T) { testValidateDataTransferPluginMinimumVersion(t, "1.0.x-SNAPSHOT", false) })
   103  }
   104  
   105  func testValidateDataTransferPluginMinimumVersion(t *testing.T, curVersion string, errorExpected bool) {
   106  	var pluginVersion string
   107  	testServer, serverDetails, _ := commonTests.CreateRtRestsMockServer(t, func(w http.ResponseWriter, r *http.Request) {
   108  		if r.RequestURI == "/"+utils.PluginsExecuteRestApi+"verifyCompatibility" {
   109  			content, err := json.Marshal(utils.VersionResponse{Version: pluginVersion})
   110  			assert.NoError(t, err)
   111  			_, err = w.Write(content)
   112  			assert.NoError(t, err)
   113  		}
   114  	})
   115  	defer testServer.Close()
   116  	srcPluginManager := initSrcUserPluginServiceManager(t, serverDetails)
   117  
   118  	pluginVersion = curVersion
   119  	err := getAndValidateDataTransferPlugin(srcPluginManager)
   120  	if errorExpected {
   121  		assert.EqualError(t, err, clientutils.ValidateMinimumVersion(clientutils.DataTransfer, curVersion, dataTransferPluginMinVersion).Error())
   122  		return
   123  	}
   124  	assert.NoError(t, err)
   125  }
   126  
   127  func TestVerifySourceTargetConnectivity(t *testing.T) {
   128  	testServer, serverDetails, _ := commonTests.CreateRtRestsMockServer(t, func(w http.ResponseWriter, r *http.Request) {
   129  		if r.RequestURI == "/"+utils.PluginsExecuteRestApi+"verifySourceTargetConnectivity" {
   130  			w.WriteHeader(http.StatusOK)
   131  		}
   132  	})
   133  	defer testServer.Close()
   134  	srcPluginManager := initSrcUserPluginServiceManager(t, serverDetails)
   135  	transferFilesCommand, err := NewTransferFilesCommand(serverDetails, serverDetails)
   136  	assert.NoError(t, err)
   137  	err = transferFilesCommand.verifySourceTargetConnectivity(srcPluginManager)
   138  	assert.NoError(t, err)
   139  }
   140  
   141  func TestVerifySourceTargetConnectivityError(t *testing.T) {
   142  	testServer, serverDetails, _ := commonTests.CreateRtRestsMockServer(t, func(w http.ResponseWriter, r *http.Request) {
   143  		if r.RequestURI == "/"+utils.PluginsExecuteRestApi+"verifySourceTargetConnectivity" {
   144  			w.WriteHeader(http.StatusBadRequest)
   145  			_, err := w.Write([]byte("No connection to target"))
   146  			assert.NoError(t, err)
   147  		}
   148  	})
   149  	defer testServer.Close()
   150  	srcPluginManager := initSrcUserPluginServiceManager(t, serverDetails)
   151  	transferFilesCommand, err := NewTransferFilesCommand(serverDetails, serverDetails)
   152  	assert.NoError(t, err)
   153  	err = transferFilesCommand.verifySourceTargetConnectivity(srcPluginManager)
   154  	assert.ErrorContains(t, err, "No connection to target")
   155  }
   156  
   157  func initSrcUserPluginServiceManager(t *testing.T, serverDetails *coreConfig.ServerDetails) *srcUserPluginService {
   158  	srcPluginManager, err := createSrcRtUserPluginServiceManager(context.Background(), serverDetails)
   159  	assert.NoError(t, err)
   160  	return srcPluginManager
   161  }
   162  
   163  func TestVerifyConfigImportPluginNotInstalled(t *testing.T) {
   164  	testServer, serverDetails, _ := commonTests.CreateRtRestsMockServer(t, func(w http.ResponseWriter, r *http.Request) {
   165  		if r.RequestURI == "/"+utils.PluginsExecuteRestApi+"dataTransferVersion" {
   166  			w.WriteHeader(http.StatusNotFound)
   167  			_, err := w.Write([]byte("Not found"))
   168  			assert.NoError(t, err)
   169  		}
   170  	})
   171  	defer testServer.Close()
   172  	srcPluginManager := initSrcUserPluginServiceManager(t, serverDetails)
   173  
   174  	_, err := srcPluginManager.version()
   175  	assert.ErrorContains(t, err, "Response from Artifactory: 404 Not Found.")
   176  }
   177  
   178  func TestUploadChunkAndPollUploads(t *testing.T) {
   179  	stateManager, cleanUp := state.InitStateTest(t)
   180  	defer cleanUp()
   181  
   182  	totalChunkStatusVisits := 0
   183  	totalUploadChunkVisits := 0
   184  	fileSample := api.FileRepresentation{
   185  		Repo: repo1Key,
   186  		Path: "rel-path",
   187  		Name: "name-demo",
   188  	}
   189  
   190  	testServer, serverDetails, _ := initPollUploadsTestMockServer(t, &totalChunkStatusVisits, &totalUploadChunkVisits, fileSample)
   191  	defer testServer.Close()
   192  	srcPluginManager := initSrcUserPluginServiceManager(t, serverDetails)
   193  
   194  	assert.NoError(t, stateManager.SetRepoState(repo1Key, 0, 0, false, true))
   195  	phaseBase := &phaseBase{context: context.Background(), stateManager: stateManager, srcUpService: srcPluginManager, repoKey: repo1Key}
   196  	uploadChunkAndPollTwice(t, phaseBase, fileSample)
   197  
   198  	// Assert that exactly 2 requests to chunk status were made
   199  	// First request - get one DONE chunk and one IN PROGRESS
   200  	// Second Request - get DONE for the other chunk
   201  	assert.Equal(t, 2, totalChunkStatusVisits)
   202  }
   203  
   204  // Sends chunk to upload, polls on chunk three times - once when it is still in progress, once after done received and once to notify back to the source.
   205  func uploadChunkAndPollTwice(t *testing.T, phaseBase *phaseBase, fileSample api.FileRepresentation) {
   206  	curChunkUploaderThreads = coreUtils.DefaultThreads
   207  	curChunkBuilderThreads = coreUtils.DefaultThreads
   208  	uploadChunksChan := make(chan UploadedChunk, 3)
   209  	doneChan := make(chan bool, 1)
   210  	var runWaitGroup sync.WaitGroup
   211  
   212  	pcWrapper := newProducerConsumerWrapper()
   213  	chunk := api.UploadChunk{}
   214  	chunk.AppendUploadCandidateIfNeeded(fileSample, false)
   215  	stopped := uploadChunkWhenPossible(&pcWrapper, phaseBase, chunk, uploadChunksChan, nil)
   216  	assert.False(t, stopped)
   217  	stopped = uploadChunkWhenPossible(&pcWrapper, phaseBase, chunk, uploadChunksChan, nil)
   218  	assert.False(t, stopped)
   219  	assert.Equal(t, 2, pcWrapper.totalProcessedUploadChunks)
   220  
   221  	runWaitGroup.Add(1)
   222  	go func() {
   223  		defer runWaitGroup.Done()
   224  		pollUploads(&pcWrapper, phaseBase, phaseBase.srcUpService, uploadChunksChan, doneChan, nil)
   225  	}()
   226  	// Let the whole process run for a few chunk status checks, then mark it as done.
   227  	time.Sleep(5 * waitTimeBetweenChunkStatusSeconds * time.Second)
   228  	doneChan <- true
   229  	// Wait for the go routine to return.
   230  	runWaitGroup.Wait()
   231  }
   232  
   233  func getUploadChunkMockResponse(t *testing.T, w http.ResponseWriter, totalUploadChunkVisits *int) {
   234  	w.WriteHeader(http.StatusAccepted)
   235  	var resp api.UploadChunkResponse
   236  	if *totalUploadChunkVisits == 1 {
   237  		resp = api.UploadChunkResponse{UuidTokenResponse: api.UuidTokenResponse{UuidToken: firstUuidTokenForTest}, NodeIdResponse: api.NodeIdResponse{NodeId: nodeIdForTest}}
   238  	} else {
   239  		resp = api.UploadChunkResponse{UuidTokenResponse: api.UuidTokenResponse{UuidToken: secondUuidTokenForTest}, NodeIdResponse: api.NodeIdResponse{NodeId: nodeIdForTest}}
   240  	}
   241  	writeMockResponse(t, w, resp)
   242  }
   243  
   244  func validateChunkStatusBody(t *testing.T, r *http.Request) {
   245  	// Read body
   246  	content, err := io.ReadAll(r.Body)
   247  	assert.NoError(t, err)
   248  	var actual api.UploadChunksStatusBody
   249  	assert.NoError(t, json.Unmarshal(content, &actual))
   250  
   251  	// Make sure all parameters as expected
   252  	if len(actual.ChunksToDelete) == 0 {
   253  		assert.Len(t, actual.AwaitingStatusChunks, 2)
   254  		assert.ElementsMatch(t, []api.ChunkId{firstUuidTokenForTest, secondUuidTokenForTest}, actual.AwaitingStatusChunks)
   255  	} else {
   256  		assert.Len(t, actual.ChunksToDelete, 1)
   257  		assert.Len(t, actual.AwaitingStatusChunks, 1)
   258  		assert.Equal(t, api.ChunkId(firstUuidTokenForTest), actual.ChunksToDelete[0])
   259  		assert.Equal(t, api.ChunkId(secondUuidTokenForTest), actual.AwaitingStatusChunks[0])
   260  	}
   261  
   262  }
   263  
   264  func getChunkStatusMockFirstResponse(t *testing.T, w http.ResponseWriter) {
   265  	resp := getSampleChunkStatus()
   266  	resp.ChunksStatus[0].Status = api.Done
   267  	writeMockResponse(t, w, resp)
   268  }
   269  
   270  func getChunkStatusMockSecondResponse(t *testing.T, w http.ResponseWriter, file api.FileRepresentation) {
   271  	resp := api.UploadChunksStatusResponse{
   272  		ChunksStatus: []api.ChunkStatus{
   273  			{
   274  				UuidTokenResponse: api.UuidTokenResponse{UuidToken: secondUuidTokenForTest},
   275  				Status:            api.Done,
   276  				Files:             []api.FileUploadStatusResponse{{FileRepresentation: file, Status: api.Success, StatusCode: http.StatusOK}},
   277  			},
   278  		},
   279  		DeletedChunks: []string{
   280  			firstUuidTokenForTest,
   281  		},
   282  		NodeIdResponse: api.NodeIdResponse{
   283  			NodeId: nodeIdForTest,
   284  		},
   285  	}
   286  
   287  	writeMockResponse(t, w, resp)
   288  }
   289  
   290  func writeMockResponse(t *testing.T, w http.ResponseWriter, resp interface{}) {
   291  	content, err := json.Marshal(resp)
   292  	assert.NoError(t, err)
   293  	_, err = w.Write(content)
   294  	assert.NoError(t, err)
   295  }
   296  
   297  func initPollUploadsTestMockServer(t *testing.T, totalChunkStatusVisits *int, totalUploadChunkVisits *int, file api.FileRepresentation) (*httptest.Server, *coreConfig.ServerDetails, artifactory.ArtifactoryServicesManager) {
   298  	return commonTests.CreateRtRestsMockServer(t, func(w http.ResponseWriter, r *http.Request) {
   299  		if r.RequestURI == "/"+utils.PluginsExecuteRestApi+"uploadChunk" {
   300  			*totalUploadChunkVisits++
   301  			getUploadChunkMockResponse(t, w, totalUploadChunkVisits)
   302  		} else if r.RequestURI == "/"+utils.PluginsExecuteRestApi+syncChunks {
   303  			*totalChunkStatusVisits++
   304  			validateChunkStatusBody(t, r)
   305  			// If already visited chunk status, return status done this time.
   306  			if *totalChunkStatusVisits == 1 {
   307  				getChunkStatusMockFirstResponse(t, w)
   308  			} else {
   309  				getChunkStatusMockSecondResponse(t, w, file)
   310  			}
   311  		}
   312  	})
   313  }
   314  
   315  func TestGetAllLocalRepositories(t *testing.T) {
   316  	// Prepare mock server
   317  	testServer, serverDetails, _ := commonTests.CreateRtRestsMockServer(t, func(w http.ResponseWriter, r *http.Request) {
   318  		switch r.RequestURI {
   319  		case "/api/storageinfo/calculate":
   320  			// Response for CalculateStorageInfo
   321  			w.WriteHeader(http.StatusAccepted)
   322  		case "/api/storageinfo":
   323  			// Response for GetStorageInfo
   324  			w.WriteHeader(http.StatusOK)
   325  			response := &artifactoryUtils.StorageInfo{RepositoriesSummaryList: []artifactoryUtils.RepositorySummary{
   326  				{RepoKey: "repo-1"}, {RepoKey: "repo-2"},
   327  				{RepoKey: "federated-repo-1"}, {RepoKey: "federated-repo-2"},
   328  				{RepoKey: "artifactory-build-info", PackageType: "BuildInfo"}, {RepoKey: "proj-build-info", PackageType: "BuildInfo"}},
   329  			}
   330  			bytes, err := json.Marshal(response)
   331  			assert.NoError(t, err)
   332  			_, err = w.Write(bytes)
   333  			assert.NoError(t, err)
   334  		case "/api/repositories?type=local&packageType=":
   335  			// Response for GetWithFilter
   336  			w.WriteHeader(http.StatusOK)
   337  			response := &[]services.RepositoryDetails{{Key: "repo-1"}, {Key: "repo-2"}}
   338  			bytes, err := json.Marshal(response)
   339  			assert.NoError(t, err)
   340  			_, err = w.Write(bytes)
   341  			assert.NoError(t, err)
   342  		case "/api/repositories?type=federated&packageType=":
   343  			// Response for GetWithFilter
   344  			w.WriteHeader(http.StatusOK)
   345  			// We add a build info repository to the response to cover cases whereby a federated build-info repository is returned
   346  			response := &[]services.RepositoryDetails{{Key: "federated-repo-1"}, {Key: "federated-repo-2"}, {Key: "proj-build-info"}}
   347  			bytes, err := json.Marshal(response)
   348  			assert.NoError(t, err)
   349  			_, err = w.Write(bytes)
   350  			assert.NoError(t, err)
   351  		}
   352  	})
   353  	defer testServer.Close()
   354  
   355  	// Get and assert regular local and build info repositories
   356  	transferFilesCommand, err := NewTransferFilesCommand(nil, nil)
   357  	assert.NoError(t, err)
   358  	storageInfoManager, err := coreUtils.NewStorageInfoManager(context.Background(), serverDetails)
   359  	assert.NoError(t, err)
   360  	localRepos, localBuildInfoRepo, err := transferFilesCommand.getAllLocalRepos(serverDetails, storageInfoManager)
   361  	assert.NoError(t, err)
   362  	assert.ElementsMatch(t, []string{"repo-1", "repo-2", "federated-repo-1", "federated-repo-2"}, localRepos)
   363  	assert.ElementsMatch(t, []string{"artifactory-build-info", "proj-build-info"}, localBuildInfoRepo)
   364  }
   365  
   366  func TestInitStorageInfoManagers(t *testing.T) {
   367  	sourceServerCalculated, targetServerCalculated := false, false
   368  	// Prepare source mock server
   369  	sourceTestServer, sourceServerDetails, _ := commonTests.CreateRtRestsMockServer(t, func(w http.ResponseWriter, r *http.Request) {
   370  		if r.RequestURI == "/api/storageinfo/calculate" {
   371  			w.WriteHeader(http.StatusAccepted)
   372  			sourceServerCalculated = true
   373  		}
   374  	})
   375  	defer sourceTestServer.Close()
   376  
   377  	// Prepare target mock server
   378  	targetTestServer, targetServerDetails, _ := commonTests.CreateRtRestsMockServer(t, func(w http.ResponseWriter, r *http.Request) {
   379  		if r.RequestURI == "/api/storageinfo/calculate" {
   380  			w.WriteHeader(http.StatusAccepted)
   381  			targetServerCalculated = true
   382  		}
   383  	})
   384  	defer targetTestServer.Close()
   385  
   386  	// Init and assert storage info managers
   387  	transferFilesCommand, err := NewTransferFilesCommand(sourceServerDetails, targetServerDetails)
   388  	assert.NoError(t, err)
   389  	err = transferFilesCommand.initStorageInfoManagers()
   390  	assert.NoError(t, err)
   391  	assert.True(t, sourceServerCalculated)
   392  	assert.True(t, targetServerCalculated)
   393  }
   394  
   395  func getSampleChunkStatus() api.UploadChunksStatusResponse {
   396  	return api.UploadChunksStatusResponse{
   397  		NodeIdResponse: api.NodeIdResponse{NodeId: nodeIdForTest},
   398  		ChunksStatus: []api.ChunkStatus{
   399  			{
   400  				UuidTokenResponse: api.UuidTokenResponse{UuidToken: firstUuidTokenForTest},
   401  				Status:            api.InProgress,
   402  				Files: []api.FileUploadStatusResponse{
   403  					{
   404  						FileRepresentation: api.FileRepresentation{
   405  							Repo: "my-repo-local-2",
   406  							Path: "rel-path-2",
   407  							Name: "name-demo-2",
   408  						},
   409  					},
   410  				},
   411  			},
   412  			{
   413  				UuidTokenResponse: api.UuidTokenResponse{UuidToken: secondUuidTokenForTest},
   414  				Status:            api.InProgress,
   415  				Files: []api.FileUploadStatusResponse{
   416  					{
   417  						FileRepresentation: api.FileRepresentation{
   418  							Repo: "my-repo-local-1",
   419  							Path: "rel-path-1",
   420  							Name: "name-demo-1",
   421  						},
   422  					},
   423  				},
   424  			},
   425  		},
   426  	}
   427  }
   428  
   429  func TestCheckChunkStatusSync(t *testing.T) {
   430  	chunkStatus := getSampleChunkStatus()
   431  	manager := ChunksLifeCycleManager{
   432  		nodeToChunksMap: map[api.NodeId]map[api.ChunkId]UploadedChunkData{},
   433  	}
   434  	manager.nodeToChunksMap[nodeIdForTest] = map[api.ChunkId]UploadedChunkData{}
   435  	manager.nodeToChunksMap[nodeIdForTest][firstUuidTokenForTest] = UploadedChunkData{}
   436  	manager.nodeToChunksMap[nodeIdForTest][secondUuidTokenForTest] = UploadedChunkData{}
   437  	pcWrapper := newProducerConsumerWrapper()
   438  	errChanMng := createErrorsChannelMng()
   439  	checkChunkStatusSync(&pcWrapper, &chunkStatus, &manager, &errChanMng)
   440  	assert.Len(t, manager.nodeToChunksMap[nodeIdForTest], 2)
   441  	chunkStatus.ChunksStatus = chunkStatus.ChunksStatus[:len(chunkStatus.ChunksStatus)-1]
   442  	checkChunkStatusSync(&pcWrapper, &chunkStatus, &manager, &errChanMng)
   443  	assert.Len(t, manager.nodeToChunksMap[nodeIdForTest], 1)
   444  	chunkStatus.ChunksStatus = chunkStatus.ChunksStatus[:len(chunkStatus.ChunksStatus)-1]
   445  	checkChunkStatusSync(&pcWrapper, &chunkStatus, &manager, &errChanMng)
   446  	assert.Len(t, manager.nodeToChunksMap[nodeIdForTest], 0)
   447  }
   448  
   449  func TestCreateErrorsSummaryFile(t *testing.T) {
   450  	cleanUpJfrogHome, err := tests.SetJfrogHome()
   451  	assert.NoError(t, err)
   452  	defer cleanUpJfrogHome()
   453  
   454  	testDataDir := filepath.Join("..", "testdata", "transfer_summary")
   455  	logFiles := []string{filepath.Join(testDataDir, "logs1.json"), filepath.Join(testDataDir, "logs2.json")}
   456  	allErrors, err := parseErrorsFromLogFiles(logFiles)
   457  	assert.NoError(t, err)
   458  	// Create Errors Summary Csv File from given JSON log files
   459  	createdCsvPath, err := utils.CreateCSVFile("transfer-files-logs", allErrors.Errors, time.Now())
   460  	assert.NoError(t, err)
   461  	assert.NotEmpty(t, createdCsvPath)
   462  	createdFile, err := os.Open(createdCsvPath)
   463  	assert.NoError(t, err)
   464  	defer func() {
   465  		assert.NoError(t, createdFile.Close())
   466  	}()
   467  	actualFileErrors := new([]api.FileUploadStatusResponse)
   468  	assert.NoError(t, gocsv.UnmarshalFile(createdFile, actualFileErrors))
   469  
   470  	// Create expected csv file
   471  	expectedFile, err := os.Open(filepath.Join(testDataDir, "logs.csv"))
   472  	assert.NoError(t, err)
   473  	defer func() {
   474  		assert.NoError(t, expectedFile.Close())
   475  	}()
   476  	expectedFileErrors := new([]api.FileUploadStatusResponse)
   477  	assert.NoError(t, gocsv.UnmarshalFile(expectedFile, expectedFileErrors))
   478  	assert.ElementsMatch(t, *expectedFileErrors, *actualFileErrors)
   479  }