//process 2 new job messages, and one cancel message public async void TestRunAsync() { using (var hc = new TestHostContext(this)) { //Arrange var runner = new Runner.Listener.Runner(); hc.SetSingleton <IConfigurationManager>(_configurationManager.Object); hc.SetSingleton <IJobNotification>(_jobNotification.Object); hc.SetSingleton <IMessageListener>(_messageListener.Object); hc.SetSingleton <IPromptManager>(_promptManager.Object); hc.SetSingleton <IRunnerServer>(_runnerServer.Object); hc.SetSingleton <IConfigurationStore>(_configStore.Object); runner.Initialize(hc); var settings = new RunnerSettings { PoolId = 43242 }; var message = new TaskAgentMessage() { Body = JsonUtility.ToString(CreateJobRequestMessage("job1")), MessageId = 4234, MessageType = JobRequestMessageTypes.PipelineAgentJobRequest }; var messages = new Queue <TaskAgentMessage>(); messages.Enqueue(message); var signalWorkerComplete = new SemaphoreSlim(0, 1); _configurationManager.Setup(x => x.LoadSettings()) .Returns(settings); _configurationManager.Setup(x => x.IsConfigured()) .Returns(true); _messageListener.Setup(x => x.CreateSessionAsync(It.IsAny <CancellationToken>())) .Returns(Task.FromResult <bool>(true)); _messageListener.Setup(x => x.GetNextMessageAsync(It.IsAny <CancellationToken>())) .Returns(async() => { if (0 == messages.Count) { signalWorkerComplete.Release(); await Task.Delay(2000, hc.RunnerShutdownToken); } return(messages.Dequeue()); }); _messageListener.Setup(x => x.DeleteSessionAsync()) .Returns(Task.CompletedTask); _messageListener.Setup(x => x.DeleteMessageAsync(It.IsAny <TaskAgentMessage>())) .Returns(Task.CompletedTask); _jobDispatcher.Setup(x => x.Run(It.IsAny <Pipelines.AgentJobRequestMessage>(), It.IsAny <bool>())) .Callback(() => { }); _jobNotification.Setup(x => x.StartClient(It.IsAny <String>())) .Callback(() => { }); hc.EnqueueInstance <IJobDispatcher>(_jobDispatcher.Object); _configStore.Setup(x => x.IsServiceConfigured()).Returns(false); //Act var command = new CommandSettings(hc, new string[] { "run" }); Task runnerTask = runner.ExecuteCommand(command); //Assert //wait for the runner to run one job if (!await signalWorkerComplete.WaitAsync(2000)) { Assert.True(false, $"{nameof(_messageListener.Object.GetNextMessageAsync)} was not invoked."); } else { //Act hc.ShutdownRunner(ShutdownReason.UserCancelled); //stop Runner //Assert Task[] taskToWait2 = { runnerTask, Task.Delay(2000) }; //wait for the runner to exit await Task.WhenAny(taskToWait2); Assert.True(runnerTask.IsCompleted, $"{nameof(runner.ExecuteCommand)} timed out."); Assert.True(!runnerTask.IsFaulted, runnerTask.Exception?.ToString()); Assert.True(runnerTask.IsCanceled); _jobDispatcher.Verify(x => x.Run(It.IsAny <Pipelines.AgentJobRequestMessage>(), It.IsAny <bool>()), Times.Once(), $"{nameof(_jobDispatcher.Object.Run)} was not invoked."); _messageListener.Verify(x => x.GetNextMessageAsync(It.IsAny <CancellationToken>()), Times.AtLeastOnce()); _messageListener.Verify(x => x.CreateSessionAsync(It.IsAny <CancellationToken>()), Times.Once()); _messageListener.Verify(x => x.DeleteSessionAsync(), Times.Once()); _messageListener.Verify(x => x.DeleteMessageAsync(It.IsAny <TaskAgentMessage>()), Times.AtLeastOnce()); // verify that we didn't try to delete local settings file (since we're not ephemeral) _configurationManager.Verify(x => x.DeleteLocalRunnerConfig(), Times.Never()); } } }