[Ignore] /* Some unknown failure reason at the moment, will need to investigate */ public void UpdateLocalAsyncWithFailedLFSFetch() { // Setup var control = "test"; var repo = Substitute.For <ILocalRepository>(); var t = new GitContext(m_config, m_local, m_remote, m_git, m_lfs); // The first time the git fetch is called it will pretend success m_git.FetchAsync(repo).Returns(x => Task.Run(() => control)); // but the subsequent call to LFS fetch will fail m_lfs.FetchAsync(repo).Returns(x => Task.Run(() => { throw new Exception("test"); })); // Which should cause the git clone action m_git.CloneAsync(repo).Returns(x => Task.Run(() => control)); // Followed by a LFS fetch again //m_lfs.FetchAsync(repo).Returns(x => Task.Run(() => control)); m_lfs.FetchAsync(repo).Returns(x => Task.Run(() => control)); // Act var output = t.UpdateLocalAsync(repo).Result; // Assert Assert.AreEqual(control, output); m_git.Received(1).FetchAsync(repo).Wait(); m_git.Received(1).CloneAsync(repo).Wait(); m_lfs.Received(2).FetchAsync(repo).Wait(); } /* End of Function - UpdateLocalAsyncWithFailedLFSFetch */
public void UpdateLocalAsync() { // Setup var control = "test"; var repo = Substitute.For <ILocalRepository>(); var t = new GitContext(m_config, m_local, m_remote, m_git, m_lfs); m_git.FetchAsync(repo).Returns(x => Task.Run(() => control)); m_lfs.FetchAsync(repo).Returns(x => Task.Run(() => "foobar")); // Act var output = t.UpdateLocalAsync(repo).Result; // Assert Assert.AreEqual(control, output); m_git.Received(1).FetchAsync(repo).Wait(); m_lfs.Received(1).FetchAsync(repo).Wait(); } /* End of Function - UpdateLocalAsync */
public void UpdateLocalAsyncWithFailedGitFetch() { // Setup var control = "test"; var repo = Substitute.For <ILocalRepository>(); var t = new GitContext(m_config, m_local, m_remote, m_git, m_lfs); // The very first call to git fetch will throw m_git.FetchAsync(repo).Returns(x => Task.Run(() => { throw new Exception("test"); })); // Which should cause a clone to happen instead m_git.CloneAsync(repo).Returns(x => Task.Run(() => control)); // Act var output = t.UpdateLocalAsync(repo).Result; // Assert Assert.AreEqual(control, output); m_git.Received(1).FetchAsync(repo).Wait(); m_git.Received(1).CloneAsync(repo).Wait(); m_lfs.Received(1).FetchAsync(repo).Wait(); } /* End of Function - UpdateLocalAsyncWithFailedGitFetch */
} /* End of Function - ReaderWriterLockFilterAsyncAttribute */ /************************ Methods ****************************************/ /*----------------------- OnResourceExecutionAsync ----------------------*/ /// <summary> /// Wraps the execution with a lock, if out of date will get a writer lock /// otherwise will continue through as a reader /// </summary> /// <param name="context"></param> /// <param name="next"></param> /// <remarks> /// This method does not use the async keyword, due to `await` to possibly /// change the thread we are executing on, causing problems with the locks. /// </remarks> public Task OnResourceExecutionAsync( ResourceExecutingContext context, ResourceExecutionDelegate next) { if (null == context) { throw new ArgumentNullException( nameof(context), "A valid context must be provided"); } if (null == next) { throw new ArgumentNullException( nameof(next), "A valid execution delegate must be provided"); } return(Task.Factory.StartNew(() => { Logger.LogTrace("Enter main execution task"); // Grab the timeout value from configuration var timeout = GetWaitTimeSpan(); Logger.LogInformation($"Got a timeout from configuration: {timeout}"); Logger.LogInformation($" In milliseconds: {timeout.TotalMilliseconds}"); // Get a lock object associated with the resource key var lockObj = Manager.GetFor(GetResourceKey(context)); string auth = null; // Will look in the headers for an "Authentication" record var headers = context.HttpContext.Request.Headers; // If so, then we will get the value to use (assuming the first is // good enough, since there should just be one) if (headers.ContainsKey("Authorization")) { auth = headers["Authorization"].First(); } var repo = GitContext.RemoteFactory.Build(context.RouteData.Values["destinationServer"].ToString(), context.RouteData.Values["repositoryOwner"].ToString(), context.RouteData.Values["repository"].ToString(), auth); var local = GitContext.LocalFactory.Build(repo, Configuration); Logger.LogInformation("Grabbing reader lock"); lockObj.AcquireReaderLock(timeout); if (!IsRepositoryUpToDate(local)) { Logger.LogInformation("Repository out of date, asking for writer lock"); //lockObj.AcquireWriterLock(timeout); lockObj.UpgradeToWriterLock(timeout); } // end of if - out-of-date try { // Check to see if we are out of date, if we are then // upgrade to writer, to update our local values if (lockObj.IsWriterLockHeld) { // If still out of date, then we will update the local one, // because we are the instance stuck with the work if (!IsRepositoryUpToDate(local)) { Logger.LogInformation("We are responsible for updating the local repository"); GitContext.UpdateLocalAsync(local).Wait(); Logger.LogInformation("Local repository updated!"); } lockObj.DowngradeFromWriterLock(); } // end of if - repository is up-to-date Logger.LogInformation("Calling through to the next step in the pipeline"); // Allow the rest of the pipeline to continue next().Wait(); Logger.LogInformation("Next action has finished, continue to release locks"); } // end of try - to execute the job finally { // If we were holding the writer lock, release it // first if (lockObj.IsWriterLockHeld) { Logger.LogInformation("Releasing the writer lock"); lockObj.ReleaseWriterLock(); Logger.LogInformation("Writer lock released"); } // end of if - writer lock is held else { Logger.LogInformation("Releasing the reader lock"); lockObj.ReleaseReaderLock(); } // end of else - reader lock } // end of finally lockObj = null; Logger.LogTrace("Exit main execution task"); })); // end of task } /* End of Function - OnResourceExecutionAsync */