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 }