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

     1  package state
     2  
     3  import (
     4  	"sync"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  )
    10  
    11  const (
    12  	repo1Key = "repo1"
    13  	repo2Key = "repo2"
    14  	repo3Key = "repo3"
    15  	repo4Key = "repo4"
    16  )
    17  
    18  func TestFilesDiffRange(t *testing.T) {
    19  	stateManager, cleanUp := InitStateTest(t)
    20  	defer cleanUp()
    21  
    22  	transferStartTime := time.Now()
    23  	// Repo should be marked as not transferred. This also adds repo to state.
    24  	assertRepoTransferred(t, stateManager, false)
    25  	setAndAssertRepoFullyTransfer(t, stateManager, transferStartTime)
    26  
    27  	// Set diff start and assert handling range begins in transfer start time and ends in new diff start time.
    28  	_ = addAndAssertNewDiffPhase(t, stateManager, 1, transferStartTime)
    29  	// Set diff start again, as if previous was interrupted. Handling range start should be the same. Handling range end should be new diff start time.
    30  	diffStart := addAndAssertNewDiffPhase(t, stateManager, 2, transferStartTime)
    31  	// Set diff completed.
    32  	setAndAssertFilesDiffCompleted(t, stateManager, 2)
    33  	// Next diff handling range should begin on last completed diff start time.
    34  	_ = addAndAssertNewDiffPhase(t, stateManager, 3, diffStart)
    35  }
    36  
    37  func assertRepoTransferred(t *testing.T, stateManager *TransferStateManager, expected bool) {
    38  	transferred, err := stateManager.IsRepoTransferred()
    39  	assert.NoError(t, err)
    40  	assert.Equal(t, expected, transferred)
    41  }
    42  
    43  func setAndAssertRepoFullyTransfer(t *testing.T, stateManager *TransferStateManager, startTime time.Time) {
    44  	err := stateManager.SetRepoFullTransferStarted(startTime)
    45  	assert.NoError(t, err)
    46  	assertRepoTransferred(t, stateManager, false)
    47  
    48  	time.Sleep(time.Second)
    49  	err = stateManager.SetRepoFullTransferCompleted()
    50  	assert.NoError(t, err)
    51  	assertRepoTransferred(t, stateManager, true)
    52  
    53  	repo := stateManager.CurrentRepo
    54  	assert.Equal(t, ConvertTimeToRFC3339(startTime), repo.FullTransfer.Started)
    55  	assert.NotEmpty(t, repo.FullTransfer.Ended)
    56  	assert.NotEqual(t, repo.FullTransfer.Ended, repo.FullTransfer.Started)
    57  }
    58  
    59  func addAndAssertNewDiffPhase(t *testing.T, stateManager *TransferStateManager, expectedDiffs int, handlingExpectedTime time.Time) (diffStart time.Time) {
    60  	diffStart = time.Now()
    61  	err := stateManager.AddNewDiffToState(diffStart)
    62  	assert.NoError(t, err)
    63  	repo := stateManager.CurrentRepo
    64  	assert.Equal(t, expectedDiffs, len(repo.Diffs))
    65  	assert.Equal(t, ConvertTimeToRFC3339(diffStart), repo.Diffs[expectedDiffs-1].FilesDiffRunTime.Started)
    66  
    67  	handlingStart, handlingEnd, err := stateManager.GetDiffHandlingRange()
    68  	assert.NoError(t, err)
    69  	// Truncating the expected time because milliseconds are lost in conversions.
    70  	assert.True(t, handlingExpectedTime.Truncate(time.Second).Equal(handlingStart))
    71  	assert.True(t, diffStart.Truncate(time.Second).Equal(handlingEnd))
    72  	return
    73  }
    74  
    75  func setAndAssertFilesDiffCompleted(t *testing.T, stateManager *TransferStateManager, diffNum int) {
    76  	err := stateManager.SetFilesDiffHandlingCompleted()
    77  	assert.NoError(t, err)
    78  	assert.True(t, stateManager.CurrentRepo.Diffs[diffNum-1].Completed)
    79  }
    80  
    81  func TestResetRepoState(t *testing.T) {
    82  	stateManager, cleanUp := InitStateTest(t)
    83  	defer cleanUp()
    84  
    85  	// Reset a repository state on an empty state
    86  	err := stateManager.SetRepoState(repo1Key, 0, 0, false, true)
    87  	assert.NoError(t, err)
    88  	// Set repository fully transferred. It will fail the test if the repository is not in the state.
    89  	setAndAssertRepoFullyTransfer(t, stateManager, time.Now())
    90  
    91  	// Create another repository state
    92  	err = stateManager.SetRepoState(repo2Key, 0, 0, false, true)
    93  	assert.NoError(t, err)
    94  	setAndAssertRepoFullyTransfer(t, stateManager, time.Now())
    95  
    96  	// Reset repo1 only
    97  	err = stateManager.SetRepoState(repo1Key, 0, 0, false, true)
    98  	assert.NoError(t, err)
    99  	assertRepoTransferred(t, stateManager, false)
   100  }
   101  
   102  func TestReposTransferredSizeBytes(t *testing.T) {
   103  	stateManager, cleanUp := InitStateTest(t)
   104  	defer cleanUp()
   105  
   106  	// Inc repos transferred sizes.
   107  	assert.NoError(t, stateManager.SetRepoState(repo1Key, 0, 0, false, true))
   108  	assert.NoError(t, stateManager.IncTransferredSizeAndFilesPhase1(1, 10))
   109  	assert.NoError(t, stateManager.IncTransferredSizeAndFilesPhase1(5, 11))
   110  	assertCurrentRepoTransferredFiles(t, stateManager, 6)
   111  	assert.NoError(t, stateManager.SetRepoState(repo2Key, 0, 0, false, true))
   112  	assert.NoError(t, stateManager.IncTransferredSizeAndFilesPhase1(3, 200))
   113  	assertCurrentRepoTransferredFiles(t, stateManager, 3)
   114  
   115  	// Get repos transferred sizes, one at a time.
   116  	assertReposTransferredSize(t, stateManager, 21, repo1Key)
   117  	assertReposTransferredSize(t, stateManager, 200, repo2Key)
   118  	assertReposTransferredSize(t, stateManager, 0, repo3Key)
   119  
   120  	// Get a combination of all repos. Pass repo2 twice to verify its size is not duplicated.
   121  	assertReposTransferredSize(t, stateManager, 221, repo1Key, repo2Key, repo2Key, repo3Key)
   122  
   123  	// No repos.
   124  	assertReposTransferredSize(t, stateManager, 0)
   125  
   126  	// Assert the sum bytes of repo1 + repo2 in the run-status.
   127  	transferredSizeBytes, err := stateManager.GetTransferredSizeBytes()
   128  	assert.NoError(t, err)
   129  	assert.Equal(t, int64(221), transferredSizeBytes)
   130  }
   131  
   132  func TestReposOverallBiFiles(t *testing.T) {
   133  	stateManager, cleanUp := InitStateTest(t)
   134  	defer cleanUp()
   135  
   136  	// Inc repos transferred sizes and files.
   137  	assert.NoError(t, stateManager.SetRepoState(repo1Key, 0, 0, true, true))
   138  	assert.NoError(t, stateManager.IncTransferredSizeAndFilesPhase1(2, 9))
   139  	assert.NoError(t, stateManager.SetRepoState(repo2Key, 0, 0, true, true))
   140  	assert.NoError(t, stateManager.IncTransferredSizeAndFilesPhase1(1, 10))
   141  	assert.NoError(t, stateManager.IncTransferredSizeAndFilesPhase1(5, 11))
   142  
   143  	// Assert the number of transferred bi files in the state.
   144  	assert.Equal(t, repo2Key, stateManager.CurrentRepo.Name)
   145  	assert.Equal(t, repo2Key, stateManager.CurrentRepoKey)
   146  	assert.True(t, stateManager.BuildInfoRepo)
   147  	assert.Equal(t, int64(8), stateManager.OverallBiFiles.TransferredUnits)
   148  }
   149  
   150  func TestChangeDelayedFilesCountBy(t *testing.T) {
   151  	stateManager, cleanUp := InitStateTest(t)
   152  	defer cleanUp()
   153  
   154  	// Increase delayed files count
   155  	assert.NoError(t, stateManager.ChangeDelayedFilesCountBy(2, true))
   156  	assert.NoError(t, stateManager.ChangeDelayedFilesCountBy(4, true))
   157  	assert.Equal(t, uint64(6), stateManager.DelayedFiles)
   158  
   159  	// Decrease delayed files count
   160  	assert.NoError(t, stateManager.ChangeDelayedFilesCountBy(3, false))
   161  	assert.Equal(t, uint64(3), stateManager.DelayedFiles)
   162  }
   163  
   164  func TestVisitedFolders(t *testing.T) {
   165  	stateManager, cleanUp := InitStateTest(t)
   166  	defer cleanUp()
   167  
   168  	// Increase visited folders count
   169  	assert.NoError(t, stateManager.IncVisitedFolders())
   170  	assert.NoError(t, stateManager.IncVisitedFolders())
   171  	assert.Equal(t, uint64(2), stateManager.VisitedFolders)
   172  
   173  	// Set repository state and ensure the visited folders became 0
   174  	assert.NoError(t, stateManager.SetRepoState(repo1Key, 0, 0, true, true))
   175  	assert.Zero(t, stateManager.VisitedFolders)
   176  }
   177  
   178  func TestChangeTransferFailureCountBy(t *testing.T) {
   179  	stateManager, cleanUp := InitStateTest(t)
   180  	defer cleanUp()
   181  
   182  	// Increase failures count
   183  	assert.NoError(t, stateManager.ChangeTransferFailureCountBy(2, true))
   184  	assert.NoError(t, stateManager.ChangeTransferFailureCountBy(4, true))
   185  	assert.Equal(t, uint64(6), stateManager.TransferFailures)
   186  
   187  	// Decrease failures count
   188  	assert.NoError(t, stateManager.ChangeTransferFailureCountBy(3, false))
   189  	assert.Equal(t, uint64(3), stateManager.TransferFailures)
   190  }
   191  
   192  func assertReposTransferredSize(t *testing.T, stateManager *TransferStateManager, expectedSize int64, repoKeys ...string) {
   193  	totalTransferredSize, err := stateManager.GetReposTransferredSizeBytes(repoKeys...)
   194  	assert.NoError(t, err)
   195  	assert.Equal(t, expectedSize, totalTransferredSize)
   196  }
   197  
   198  func assertCurrentRepoTransferredFiles(t *testing.T, stateManager *TransferStateManager, expectedFiles int64) {
   199  	assert.Equal(t, expectedFiles, stateManager.CurrentRepo.Phase1Info.TransferredUnits)
   200  }
   201  
   202  func TestIncRepositoriesTransferred(t *testing.T) {
   203  	stateManager, cleanUp := InitStateTest(t)
   204  	defer cleanUp()
   205  
   206  	assert.Zero(t, stateManager.TotalRepositories.TransferredUnits)
   207  	assert.NoError(t, stateManager.IncRepositoriesTransferred())
   208  	assert.Equal(t, int64(1), stateManager.TotalRepositories.TransferredUnits)
   209  }
   210  
   211  func TestSetRepoPhase(t *testing.T) {
   212  	stateManager, cleanUp := InitStateTest(t)
   213  	defer cleanUp()
   214  
   215  	assert.Zero(t, stateManager.CurrentRepoPhase)
   216  	assert.NoError(t, stateManager.SetRepoPhase(1))
   217  	assert.Equal(t, 1, stateManager.CurrentRepoPhase)
   218  }
   219  
   220  func TestSetAndGetWorkingThreads(t *testing.T) {
   221  	stateManager, cleanUp := InitStateTest(t)
   222  	defer cleanUp()
   223  
   224  	assert.Zero(t, stateManager.WorkingThreads)
   225  	assert.NoError(t, stateManager.SetWorkingThreads(1))
   226  	assert.Equal(t, 1, stateManager.WorkingThreads)
   227  	workingThreads, err := stateManager.GetWorkingThreads()
   228  	assert.NoError(t, err)
   229  	assert.Equal(t, 1, workingThreads)
   230  }
   231  
   232  func TestTryLockStateManager(t *testing.T) {
   233  	stateManager, cleanUp := InitStateTest(t)
   234  	defer cleanUp()
   235  
   236  	assert.NoError(t, stateManager.tryLockStateManager())
   237  	assert.ErrorIs(t, new(AlreadyLockedError), stateManager.tryLockStateManager())
   238  }
   239  
   240  func TestRunning(t *testing.T) {
   241  	stateManager, cleanUp := InitStateTest(t)
   242  	defer cleanUp()
   243  
   244  	// Assert no running=false
   245  	running, err := stateManager.Running()
   246  	assert.NoError(t, err)
   247  	assert.False(t, running)
   248  
   249  	// Lock to simulate transfer
   250  	assert.NoError(t, stateManager.TryLockTransferStateManager())
   251  
   252  	// Assert running=true
   253  	running, err = stateManager.Running()
   254  	assert.NoError(t, err)
   255  	assert.True(t, running)
   256  }
   257  
   258  func TestInitStartTimestamp(t *testing.T) {
   259  	stateManager, cleanUp := InitStateTest(t)
   260  	defer cleanUp()
   261  
   262  	// Init start timestamp and expect timestamp zero
   263  	running, err := stateManager.InitStartTimestamp()
   264  	assert.NoError(t, err)
   265  	assert.False(t, running)
   266  	assert.True(t, stateManager.startTimestamp.IsZero())
   267  
   268  	// Lock to simulate transfer
   269  	assert.NoError(t, stateManager.TryLockTransferStateManager())
   270  
   271  	// Init start timestamp and expect timestamp non-zero
   272  	running, err = stateManager.InitStartTimestamp()
   273  	assert.NoError(t, err)
   274  	assert.True(t, running)
   275  	assert.False(t, stateManager.startTimestamp.IsZero())
   276  }
   277  
   278  var getRunningTimeStringCases = []struct {
   279  	startTimestamp time.Time
   280  	expectedString string
   281  }{
   282  	{time.Now(), "Less than a minute"},
   283  	{time.Now().Add(-time.Second), "Less than a minute"},
   284  	{time.Now().Add(-time.Minute), "1 minute"},
   285  	{time.Now().Add(-time.Hour), "1 hour"},
   286  	{time.Now().Add(-time.Hour).Add(time.Minute), "59 minutes"},
   287  	{time.Now().Add(-time.Hour).Add(time.Minute).Add(10 * time.Second), "58 minutes"},
   288  }
   289  
   290  func TestGetRunningTimeString(t *testing.T) {
   291  	stateManager, cleanUp := InitStateTest(t)
   292  	defer cleanUp()
   293  
   294  	runningTime := stateManager.GetRunningTimeString()
   295  	assert.Empty(t, runningTime)
   296  
   297  	// Lock and init start timestamp to simulate transfer
   298  	assert.NoError(t, stateManager.TryLockTransferStateManager())
   299  	running, err := stateManager.InitStartTimestamp()
   300  	assert.NoError(t, err)
   301  	assert.True(t, running)
   302  
   303  	// Run test cases
   304  	for _, testCase := range getRunningTimeStringCases {
   305  		t.Run(testCase.startTimestamp.String(), func(t *testing.T) {
   306  			// Set start timestamp
   307  			stateManager.startTimestamp = testCase.startTimestamp
   308  
   309  			// Assert running time string
   310  			assert.Equal(t, testCase.expectedString, stateManager.GetRunningTimeString())
   311  		})
   312  	}
   313  }
   314  
   315  func TestStateConcurrency(t *testing.T) {
   316  	stateManager, cleanUp := InitStateTest(t)
   317  	defer cleanUp()
   318  
   319  	// Concurrently increment variables in the state
   320  	var wg sync.WaitGroup
   321  	for i := 0; i < 1000; i++ {
   322  		wg.Add(1)
   323  		go func() {
   324  			defer wg.Done()
   325  			assert.NoError(t, stateManager.IncTransferredSizeAndFilesPhase1(1, 1))
   326  			assert.NoError(t, stateManager.IncTransferredSizeAndFilesPhase2(1, 1))
   327  			assert.NoError(t, stateManager.IncTransferredSizeAndFilesPhase3(1, 1))
   328  			assert.NoError(t, stateManager.IncVisitedFolders())
   329  			assert.NoError(t, stateManager.ChangeDelayedFilesCountBy(1, true))
   330  			assert.NoError(t, stateManager.ChangeTransferFailureCountBy(1, true))
   331  		}()
   332  	}
   333  	wg.Wait()
   334  
   335  	// Assert 1000 in all values
   336  	assert.Equal(t, 1000, int(stateManager.CurrentRepo.Phase1Info.TransferredSizeBytes))
   337  	assert.Equal(t, 1000, int(stateManager.CurrentRepo.Phase1Info.TransferredUnits))
   338  	assert.Equal(t, 1000, int(stateManager.CurrentRepo.Phase2Info.TransferredSizeBytes))
   339  	assert.Equal(t, 1000, int(stateManager.CurrentRepo.Phase2Info.TransferredUnits))
   340  	assert.Equal(t, 1000, int(stateManager.CurrentRepo.Phase3Info.TransferredSizeBytes))
   341  	assert.Equal(t, 1000, int(stateManager.CurrentRepo.Phase3Info.TransferredUnits))
   342  	assert.Equal(t, 1000, int(stateManager.OverallTransfer.TransferredSizeBytes))
   343  	assert.Equal(t, 1000, int(stateManager.VisitedFolders))
   344  	assert.Equal(t, 1000, int(stateManager.DelayedFiles))
   345  	assert.Equal(t, 1000, int(stateManager.TransferFailures))
   346  
   347  	// Concurrently decrement delayed artifacts and transfer failures
   348  	for i := 0; i < 500; i++ {
   349  		wg.Add(1)
   350  		go func() {
   351  			defer wg.Done()
   352  			assert.NoError(t, stateManager.ChangeDelayedFilesCountBy(1, false))
   353  			assert.NoError(t, stateManager.ChangeTransferFailureCountBy(1, false))
   354  		}()
   355  	}
   356  	wg.Wait()
   357  
   358  	// Assert 500 in delayed artifacts and transfer failures
   359  	assert.Equal(t, 500, int(stateManager.DelayedFiles))
   360  	assert.Equal(t, 500, int(stateManager.TransferFailures))
   361  }