public async Task AddJobLog_GivenJobNotFound_ReturnsNotFoundResult() { //Arrange string jobId = "job-id-1"; JobLogUpdateModel jobLogUpdateModel = new JobLogUpdateModel(); IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetJobById(Arg.Is(jobId)) .Returns((Job)null); ILogger logger = CreateLogger(); JobManagementService jobManagementService = CreateJobManagementService(jobRepository: jobRepository, logger: logger); //Act IActionResult actionResult = await jobManagementService.AddJobLog(jobId, jobLogUpdateModel); //Assert actionResult .Should() .BeAssignableTo <NotFoundObjectResult>() .Which .Value .Should() .Be($"A job could not be found for job id: '{jobId}'"); logger .Received(1) .Error($"A job could not be found for job id: '{jobId}'"); }
public async Task ProcessJobCompletion_JobIsNotComplete_ThenNoActionTaken() { // Arrange IJobRepository jobRepository = CreateJobRepository(); JobManagementService jobManagementService = CreateJobManagementService(jobRepository); string jobId = "abc123"; JobNotification jobNotification = new JobNotification { JobId = jobId, RunningStatus = RunningStatus.InProgress }; string json = JsonConvert.SerializeObject(jobNotification); Message message = new Message(Encoding.UTF8.GetBytes(json)); message.UserProperties["jobId"] = jobId; // Act await jobManagementService.ProcessJobNotification(message); // Assert jobRepository .DidNotReceive() .GetJobById(Arg.Is(jobId)); }
public async Task SupersedeJob_WhenNotSameJobIdButSameParentJobId_ThenJobNotSuperseded() { // Arrange string jobId = "job-id-1"; string supersedeJobId = "job-id-2"; Job runningJob = new Job { Id = jobId, ParentJobId = "parent-1", }; Job replacementJob = new Job { Id = supersedeJobId, ParentJobId = "parent-1", }; IJobRepository jobRepository = CreateJobRepository(); JobManagementService jobManagementService = CreateJobManagementService(jobRepository); // Act await jobManagementService.SupersedeJob(runningJob, replacementJob); // Assert await jobRepository .DidNotReceive() .UpdateJob(Arg.Any <Job>()); }
public async Task ProcessJobCompletion_JobIdNotFound_ThenNoActionTakenAndErrorLogged() { // Arrange string jobId = "abc123"; ILogger logger = CreateLogger(); IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetJobById(Arg.Is(jobId)) .Returns((Job)null); JobManagementService jobManagementService = CreateJobManagementService(jobRepository, logger: logger); JobNotification jobNotification = new JobNotification { RunningStatus = RunningStatus.Completed }; string json = JsonConvert.SerializeObject(jobNotification); Message message = new Message(Encoding.UTF8.GetBytes(json)); message.UserProperties["jobId"] = jobId; // Act await jobManagementService.ProcessJobNotification(message); // Assert logger .Received(1) .Error(Arg.Is("Could not find job with id {JobId}"), Arg.Is(jobId)); }
public async Task SupersedeJob_WhenNotSameJobIdAndNotSameParentJobId_ThenJobSuperseded() { // Arrange string jobId = "job-id-1"; string supersedeJobId = "job-id-2"; Job runningJob = new Job { Id = jobId, ParentJobId = "parent-1", }; Job replacementJob = new Job { Id = supersedeJobId, ParentJobId = "parent-2", }; IJobRepository jobRepository = CreateJobRepository(); JobManagementService jobManagementService = CreateJobManagementService(jobRepository); // Act await jobManagementService.SupersedeJob(runningJob, replacementJob); // Assert await jobRepository .Received(1) .UpdateJob(Arg.Is <Job>(j => j.CompletionStatus == CompletionStatus.Superseded && j.Completed.HasValue && j.RunningStatus == RunningStatus.Completed && j.SupersededByJobId == supersedeJobId)); }
public async Task SupersedeJob_WhenDifferentJobId_ThenJobSuperseded() { // Arrange string jobId = "job-id-1"; string supersedeJobId = "job-id-2"; Job runningJob = new Job { Id = jobId, }; Job replacementJob = new Job { Id = supersedeJobId }; IJobRepository jobRepository = CreateJobRepository(); jobRepository .UpdateJob(Arg.Any <Job>()) .Returns(HttpStatusCode.OK); JobManagementService jobManagementService = CreateJobManagementService(jobRepository); // Act await jobManagementService.SupersedeJob(runningJob, replacementJob); // Assert await jobRepository .Received(1) .UpdateJob(Arg.Is <Job>(_ => _.CompletionStatus == CompletionStatus.Superseded && _.Completed.HasValue && _.OutcomeType == OutcomeType.Inconclusive && _.RunningStatus == RunningStatus.Completed && _.SupersededByJobId == supersedeJobId)); }
public async Task SupersedeJob_WhenJobSuperseded_ThenNotificationSent() { // Arrange string jobId = "job-id-1"; string supersedeJobId = "job-id-2"; Job runningJob = new Job { Id = jobId }; Job replacementJob = new Job { Id = supersedeJobId }; IJobRepository jobRepository = CreateJobRepository(); jobRepository .UpdateJob(Arg.Any <Job>()) .Returns(HttpStatusCode.OK); INotificationService notificationService = CreateNotificationsService(); JobManagementService jobManagementService = CreateJobManagementService(jobRepository, notificationService); // Act await jobManagementService.SupersedeJob(runningJob, replacementJob); // Assert await notificationService .Received(1) .SendNotification(Arg.Is <JobNotification>(n => n.JobId == jobId)); }
public async Task ProcessJobCompletion_JobHasParentTwoChildrenOnlyOneCompleted_ThenParentNotCompleted() { // Arrange string parentJobId = "parent123"; string jobId = "child123"; Job job = new Job { Id = jobId, ParentJobId = parentJobId, CompletionStatus = CompletionStatus.Succeeded, RunningStatus = RunningStatus.Completed }; Job job2 = new Job { Id = "child456", ParentJobId = parentJobId, RunningStatus = RunningStatus.InProgress }; Job parentJob = new Job { Id = parentJobId, RunningStatus = RunningStatus.InProgress }; ILogger logger = CreateLogger(); IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetJobById(Arg.Is(jobId)) .Returns(job); jobRepository .GetJobById(Arg.Is(parentJobId)) .Returns(parentJob); jobRepository .GetChildJobsForParent(Arg.Is(parentJobId)) .Returns(new List <Job> { job, job2 }); JobManagementService jobManagementService = CreateJobManagementService(jobRepository, logger: logger); JobSummary JobSummary = new JobSummary { JobId = jobId, RunningStatus = RunningStatus.Completed }; string json = JsonConvert.SerializeObject(JobSummary); Message message = new Message(Encoding.UTF8.GetBytes(json)); message.UserProperties["jobId"] = jobId; // Act await jobManagementService.Process(message); // Assert await jobRepository .DidNotReceive() .UpdateJob(Arg.Is <Job>(j => j.Id == parentJobId && j.RunningStatus == RunningStatus.Completed && j.Completed.HasValue)); logger .Received(1) .Information(Arg.Is("Completed Job {JobId} parent {ParentJobId} has in progress child jobs and cannot be completed"), Arg.Is(jobId), Arg.Is(parentJobId)); }
public async Task ProcessJobCompletion_JobHasParentTwoChildrenOnlyBothCompleted_ThenParentCompleted() { // Arrange string parentJobId = "parent123"; string jobId = "child123"; Job job = new Job { Id = jobId, ParentJobId = parentJobId, CompletionStatus = CompletionStatus.Succeeded, RunningStatus = RunningStatus.Completed }; Job job2 = new Job { Id = "child456", ParentJobId = parentJobId, CompletionStatus = CompletionStatus.Succeeded, RunningStatus = RunningStatus.Completed }; Job parentJob = new Job { Id = parentJobId, RunningStatus = RunningStatus.InProgress }; ILogger logger = CreateLogger(); IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetJobById(Arg.Is(jobId)) .Returns(job); jobRepository .GetJobById(Arg.Is(parentJobId)) .Returns(parentJob); jobRepository .GetChildJobsForParent(Arg.Is(parentJobId)) .Returns(new List <Job> { job, job2 }); JobManagementService jobManagementService = CreateJobManagementService(jobRepository, logger: logger); JobNotification jobNotification = new JobNotification { JobId = jobId, RunningStatus = RunningStatus.Completed }; string json = JsonConvert.SerializeObject(jobNotification); Message message = new Message(Encoding.UTF8.GetBytes(json)); message.UserProperties["jobId"] = jobId; // Act await jobManagementService.ProcessJobNotification(message); // Assert await jobRepository .Received(1) .UpdateJob(Arg.Is <Job>(j => j.Id == parentJobId && j.RunningStatus == RunningStatus.Completed && j.Completed.HasValue && j.Outcome == "All child jobs completed")); logger .Received(1) .Information(Arg.Is("Parent Job {ParentJobId} of Completed Job {JobId} has been completed because all child jobs are now complete"), Arg.Is(job.ParentJobId), Arg.Is(jobId)); }
public async Task ProcessJobCompletion_JobHasParentWithMultipleCompletedChildrenWithAllSucceeded_ThenParentCompletedStatusIsSucceeded() { // Arrange string parentJobId = "parent123"; string jobId = "child123"; Job job = new Job { Id = jobId, ParentJobId = parentJobId, CompletionStatus = CompletionStatus.Succeeded, RunningStatus = RunningStatus.Completed }; Job job2 = new Job { Id = "child456", ParentJobId = parentJobId, RunningStatus = RunningStatus.Completed, CompletionStatus = CompletionStatus.Succeeded }; Job job3 = new Job { Id = "child789", ParentJobId = parentJobId, RunningStatus = RunningStatus.Completed, CompletionStatus = CompletionStatus.Succeeded }; Job parentJob = new Job { Id = parentJobId, RunningStatus = RunningStatus.InProgress }; ILogger logger = CreateLogger(); IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetJobById(Arg.Is(jobId)) .Returns(job); jobRepository .GetJobById(Arg.Is(parentJobId)) .Returns(parentJob); jobRepository .GetChildJobsForParent(Arg.Is(parentJobId)) .Returns(new List <Job> { job, job2 }); JobManagementService jobManagementService = CreateJobManagementService(jobRepository, logger: logger); JobSummary JobSummary = new JobSummary { JobId = jobId, RunningStatus = RunningStatus.Completed }; string json = JsonConvert.SerializeObject(JobSummary); Message message = new Message(Encoding.UTF8.GetBytes(json)); message.UserProperties["jobId"] = jobId; // Act await jobManagementService.Process(message); // Assert await jobRepository .Received(1) .UpdateJob(Arg.Is <Job>(j => j.Id == parentJobId && j.CompletionStatus == CompletionStatus.Succeeded)); }
public async Task AddJobLog_GivenJobUpdated_EnsuresNewJobLogCreated() { //Arrange string jobId = "job-id-1"; JobLogUpdateModel jobLogUpdateModel = new JobLogUpdateModel { CompletedSuccessfully = false, Outcome = "outcome", ItemsFailed = 40, ItemsProcessed = 100, ItemsSucceeded = 60 }; Job job = new Job { Id = jobId, RunningStatus = RunningStatus.InProgress }; IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetJobById(Arg.Is(jobId)) .Returns(job); jobRepository .UpdateJob(Arg.Is(job)) .Returns(HttpStatusCode.OK); jobRepository .CreateJobLog(Arg.Any <JobLog>()) .Returns(HttpStatusCode.OK); ILogger logger = CreateLogger(); JobManagementService jobManagementService = CreateJobManagementService(jobRepository: jobRepository, logger: logger); //Act IActionResult actionResult = await jobManagementService.AddJobLog(jobId, jobLogUpdateModel); //Assert actionResult .Should() .BeAssignableTo <OkObjectResult>(); await jobRepository .Received(1) .CreateJobLog(Arg.Is <JobLog>(m => !string.IsNullOrWhiteSpace(m.Id) && m.JobId == jobId && m.ItemsProcessed == 100 && m.ItemsFailed == 40 && m.ItemsSucceeded == 60 && m.CompletedSuccessfully == false && m.Timestamp.Date == DateTimeOffset.Now.ToLocalTime().Date)); }
public async Task ProcessJobCompletion_JobHasParentThatIsCompleted_ThenNotificationSent() { // Arrange string parentJobId = "parent123"; string jobId = "child123"; Job job = new Job { Id = jobId, ParentJobId = parentJobId, CompletionStatus = CompletionStatus.Succeeded, RunningStatus = RunningStatus.Completed }; Job parentJob = new Job { Id = parentJobId, RunningStatus = RunningStatus.InProgress }; ILogger logger = CreateLogger(); IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetJobById(Arg.Is(jobId)) .Returns(job); jobRepository .GetJobById(Arg.Is(parentJobId)) .Returns(parentJob); jobRepository .GetChildJobsForParent(Arg.Is(parentJobId)) .Returns(new List <Job> { job }); INotificationService notificationService = CreateNotificationsService(); JobManagementService jobManagementService = CreateJobManagementService(jobRepository, logger: logger, notificationService: notificationService); JobNotification jobNotification = new JobNotification { JobId = jobId, RunningStatus = RunningStatus.Completed }; string json = JsonConvert.SerializeObject(jobNotification); Message message = new Message(Encoding.UTF8.GetBytes(json)); message.UserProperties["jobId"] = jobId; // Act await jobManagementService.ProcessJobNotification(message); // Assert await notificationService .Received(1) .SendNotification(Arg.Is <JobNotification>(n => n.JobId == parentJobId && n.RunningStatus == RunningStatus.Completed)); logger .Received(1) .Information(Arg.Is("Parent Job {ParentJobId} of Completed Job {JobId} has been completed because all child jobs are now complete"), Arg.Is(job.ParentJobId), Arg.Is(jobId)); }
public async Task CheckAndProcessTimedOutJobs_GivenNonCompletedJobsAndHasTimeOutButFailedToUpdate_LogsError() { //Arrange Job job = new Job { Id = "job-id-1", JobDefinitionId = "job-def-2", Created = DateTimeOffset.Now.AddHours(-13) }; IEnumerable <Job> nonCompletedJobs = new[] { job }; IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetNonCompletedJobs() .Returns(nonCompletedJobs); jobRepository .GetLatestJobBySpecificationIdAndDefinitionId(Arg.Any <string>(), Arg.Any <string>()) .Returns(job); jobRepository .UpdateJob(Arg.Any <Job>()) .Returns(HttpStatusCode.BadRequest); IEnumerable <JobDefinition> jobDefinitions = new[] { new JobDefinition { Id = "job-def-1", Timeout = TimeSpan.FromHours(12) }, new JobDefinition { Id = "job-def-2", Timeout = TimeSpan.FromHours(12) } }; IJobDefinitionsService jobDefinitionsService = CreateJobDefinitionsService(); jobDefinitionsService .GetAllJobDefinitions() .Returns(jobDefinitions); ILogger logger = CreateLogger(); JobManagementService jobManagementService = CreateJobManagementService(jobRepository, logger: logger, jobDefinitionsService: jobDefinitionsService); //Act await jobManagementService.CheckAndProcessTimedOutJobs(); //Assert logger .Received(1) .Error(Arg.Is("Failed to update timeout job, Id: 'job-id-1' with status code 400")); }
public async Task AddJobLog_GivenJobUpdatedButCreatingJobLogFails_ThrowsExceptionDoesNotSendNotification() { //Arrange string jobId = "job-id-1"; JobLogUpdateModel jobLogUpdateModel = new JobLogUpdateModel { CompletedSuccessfully = false, Outcome = "outcome", ItemsFailed = 40, ItemsProcessed = 100, ItemsSucceeded = 60 }; Job job = new Job { Id = jobId, RunningStatus = RunningStatus.InProgress }; IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetJobById(Arg.Is(jobId)) .Returns(job); jobRepository .UpdateJob(Arg.Is(job)) .Returns(HttpStatusCode.OK); jobRepository .CreateJobLog(Arg.Any <JobLog>()) .Returns(HttpStatusCode.BadRequest); INotificationService notificationService = CreateNotificationsService(); JobManagementService jobManagementService = CreateJobManagementService(jobRepository: jobRepository, notificationService: notificationService); //Act Func <Task> test = async() => await jobManagementService.AddJobLog(jobId, jobLogUpdateModel); //Assert test .Should() .ThrowExactly <Exception>() .Which.Message .Should() .Be($"Failed to create a job log for job id: '{jobId}'"); await notificationService .DidNotReceive() .SendNotification(Arg.Any <JobNotification>()); }
public async Task AddJobLog_GivenJobFoundAndSetToInProgressButFailedToUpdateJob_ReturnsInternalServerErrorResult() { //Arrange string jobId = "job-id-1"; JobLogUpdateModel jobLogUpdateModel = new JobLogUpdateModel(); Job job = new Job { Id = jobId, RunningStatus = RunningStatus.Queued }; IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetJobById(Arg.Is(jobId)) .Returns(job); jobRepository .GetLatestJobBySpecificationIdAndDefinitionId(Arg.Any <string>(), Arg.Any <string>()) .Returns(job); jobRepository .UpdateJob(Arg.Is(job)) .Returns(HttpStatusCode.BadRequest); ILogger logger = CreateLogger(); JobManagementService jobManagementService = CreateJobManagementService(jobRepository: jobRepository, logger: logger); //Act IActionResult actionResult = await jobManagementService.AddJobLog(jobId, jobLogUpdateModel); //Assert actionResult .Should() .BeAssignableTo <InternalServerErrorResult>() .Which .Value .Should() .Be($"Failed to update job id: '{jobId}' with status code '400'"); logger .Received(1) .Error($"Failed to update job id: '{jobId}' with status code '400'"); job .RunningStatus .Should() .Be(RunningStatus.InProgress); }
public async Task AddJobLog_GivenJobFoundAndSetToCompletedAndIsFailed_EnsuresJobIsUpdatedWithCorrectValues() { //Arrange string jobId = "job-id-1"; JobLogUpdateModel jobLogUpdateModel = new JobLogUpdateModel { CompletedSuccessfully = false, Outcome = "outcome", OutcomeType = NewRandomOutcomeType() }; Job job = new Job { Id = jobId, RunningStatus = RunningStatus.InProgress }; IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetJobById(Arg.Is(jobId)) .Returns(job); jobRepository .UpdateJob(Arg.Is(job)) .Returns(HttpStatusCode.OK); jobRepository .CreateJobLog(Arg.Any <JobLog>()) .Returns(HttpStatusCode.OK); ILogger logger = CreateLogger(); JobManagementService jobManagementService = CreateJobManagementService(jobRepository: jobRepository, logger: logger); //Act IActionResult actionResult = await jobManagementService.AddJobLog(jobId, jobLogUpdateModel); //Assert actionResult .Should() .BeAssignableTo <OkObjectResult>(); job.RunningStatus.Should().Be(RunningStatus.Completed); job.Completed.Should().NotBeNull(); job.CompletionStatus.Should().Be(CompletionStatus.Failed); job.Outcome.Should().Be("outcome"); job.OutcomeType.Should().Be(jobLogUpdateModel.OutcomeType); }
public void CheckAndProcessTimedOutJobs_GivenNonCompletedJobsFoundButNoDefinitionsReturned_LogsAndThrowsException() { //Arrange IEnumerable <Job> nonCompletedJobs = new[] { new Job { Id = "job-id-1" }, new Job { Id = "job-id-2" } }; IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetNonCompletedJobs() .Returns(nonCompletedJobs); IJobDefinitionsService jobDefinitionsService = CreateJobDefinitionsService(); jobDefinitionsService .GetAllJobDefinitions() .Returns(Enumerable.Empty <JobDefinition>()); ILogger logger = CreateLogger(); JobManagementService jobManagementService = CreateJobManagementService(jobRepository, logger: logger, jobDefinitionsService: jobDefinitionsService); //Act Func <Task> test = async() => await jobManagementService.CheckAndProcessTimedOutJobs(); //Assert test .Should() .ThrowExactly <Exception>() .Which .Message .Should() .Be("Failed to retrieve job definitions when processing timed out jobs"); logger .Received(1) .Error(Arg.Is("Failed to retrieve job definitions when processing timed out jobs")); logger .Received(1) .Information($"{nonCompletedJobs.Count()} non completed jobs to process"); }
public async Task CheckAndProcessTimedOutJobs_GivenNonCompletedJobsFoundButHasNotTimedOut_DoesNotUpdateJob() { //Arrange IEnumerable <Job> nonCompletedJobs = new[] { new Job { Id = "job-id-1", JobDefinitionId = "job-def-2", Created = DateTimeOffset.Now.AddHours(-6) }, }; IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetNonCompletedJobs() .Returns(nonCompletedJobs); IEnumerable <JobDefinition> jobDefinitions = new[] { new JobDefinition { Id = "job-def-1", Timeout = TimeSpan.FromHours(12) }, new JobDefinition { Id = "job-def-2", Timeout = TimeSpan.FromHours(12) } }; IJobDefinitionsService jobDefinitionsService = CreateJobDefinitionsService(); jobDefinitionsService .GetAllJobDefinitions() .Returns(jobDefinitions); ILogger logger = CreateLogger(); JobManagementService jobManagementService = CreateJobManagementService(jobRepository, logger: logger, jobDefinitionsService: jobDefinitionsService); //Act await jobManagementService.CheckAndProcessTimedOutJobs(); //Assert await jobRepository .DidNotReceive() .UpdateJob(Arg.Any <Job>()); }
public async Task SupersedeJob_WhenJobSuperseded_ThenNotificationSent() { // Arrange string jobId = "job-id-1"; string supersedeJobId = "job-id-2"; string outcome = "outcome-1"; List <Outcome> outcomes = new List <Outcome> { new Outcome { Description = "outcome-1" } }; Job runningJob = new Job { Id = jobId, Outcomes = outcomes, Outcome = outcome }; Job replacementJob = new Job { Id = supersedeJobId }; IJobRepository jobRepository = CreateJobRepository(); jobRepository .UpdateJob(Arg.Any <Job>()) .Returns(HttpStatusCode.OK); INotificationService notificationService = CreateNotificationsService(); JobManagementService jobManagementService = CreateJobManagementService(jobRepository, notificationService); // Act await jobManagementService.SupersedeJob(runningJob, replacementJob); // Assert await notificationService .Received(1) .SendNotification(Arg.Is <JobSummary>(n => n.JobId == jobId && n.Outcomes.SequenceEqual(outcomes) && n.Outcome == outcome)); }
public async Task CheckAndProcessTimedOutJobs_GivenNonCompletedJobsFoundButCouldNotFindJobDefinition_LogsError() { //Arrange IEnumerable <Job> nonCompletedJobs = new[] { new Job { Id = "job-id-1", JobDefinitionId = "job-def-3" }, }; IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetNonCompletedJobs() .Returns(nonCompletedJobs); IEnumerable <JobDefinition> jobDefinitions = new[] { new JobDefinition { Id = "job-def-1", Timeout = TimeSpan.FromHours(12) }, new JobDefinition { Id = "job-def-2", Timeout = TimeSpan.FromHours(12) } }; IJobDefinitionsService jobDefinitionsService = CreateJobDefinitionsService(); jobDefinitionsService .GetAllJobDefinitions() .Returns(jobDefinitions); ILogger logger = CreateLogger(); JobManagementService jobManagementService = CreateJobManagementService(jobRepository, logger: logger, jobDefinitionsService: jobDefinitionsService); //Act await jobManagementService.CheckAndProcessTimedOutJobs(); //Assert logger .Received(1) .Error($"Failed to find job definition : 'job-def-3' for job id: 'job-id-1'"); }
public void ProcesJobCompletion_MessageIsNull_ArgumentNullExceptionThrown() { // Arrange JobManagementService jobManagementService = CreateJobManagementService(); // Act Func <Task> action = async() => await jobManagementService.Process(null); // Assert action .Should() .Throw <ArgumentNullException>() .And .ParamName .Should() .Be("message"); }
public void ProcessJobCompletion_MessageBodyIsNull_ArgumentNullExceptionThrown() { // Arrange JobManagementService jobManagementService = CreateJobManagementService(); Message message = new Message(); // Act Func <Task> action = async() => await jobManagementService.ProcessJobNotification(message); // Assert action .Should() .Throw <ArgumentNullException>() .And .ParamName .Should() .Be("message payload"); }
public async Task AddJobLog_WhenNoOutcomeTypeIsSuppliedInTheViewModelItIsDeterminedByTheJobLogDetails(JobLogUpdateModel jobLogUpdateModel, OutcomeType?expectedOutcomeType) { //Arrange string jobId = "job-id-1"; Job job = new Job { Id = jobId, RunningStatus = RunningStatus.InProgress }; IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetJobById(Arg.Is(jobId)) .Returns(job); jobRepository .UpdateJob(Arg.Is(job)) .Returns(HttpStatusCode.OK); jobRepository .CreateJobLog(Arg.Any <JobLog>()) .Returns(HttpStatusCode.OK); ILogger logger = CreateLogger(); JobManagementService jobManagementService = CreateJobManagementService(jobRepository: jobRepository, logger: logger); //Act IActionResult actionResult = await jobManagementService.AddJobLog(jobId, jobLogUpdateModel); //Assert actionResult .Should() .BeAssignableTo <OkObjectResult>(); job.RunningStatus.Should().Be(RunningStatus.Completed); job.Completed.Should().NotBeNull(); job.OutcomeType.Should().Be(expectedOutcomeType); }
public async Task ProcessJobCompletion_JobHasNoParent_ThenNoActionTakenAndMessageLogged() { // Arrange string jobId = "abc123"; Job job = new Job { Id = jobId, ParentJobId = null }; ILogger logger = CreateLogger(); IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetJobById(Arg.Is(jobId)) .Returns(job); JobManagementService jobManagementService = CreateJobManagementService(jobRepository, logger: logger); JobSummary JobSummary = new JobSummary { JobId = jobId, RunningStatus = RunningStatus.Completed }; string json = JsonConvert.SerializeObject(JobSummary); Message message = new Message(Encoding.UTF8.GetBytes(json)); message.UserProperties["jobId"] = jobId; // Act await jobManagementService.Process(message); // Assert logger .Received(1) .Information(Arg.Is("Completed Job {JobId} has no parent"), Arg.Is(jobId)); await jobRepository .DidNotReceive() .GetChildJobsForParent(Arg.Any <string>()); }
public async Task CheckAndProcessTimedOutJobs_GivenZeroNonCompletedJobsToProcess_LogsAndReturns() { //Arrange IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetNonCompletedJobs() .Returns(Enumerable.Empty <Job>()); ILogger logger = CreateLogger(); JobManagementService jobManagementService = CreateJobManagementService(jobRepository, logger: logger); //Act await jobManagementService.CheckAndProcessTimedOutJobs(); //Assert logger .Received(1) .Information(Arg.Is("Zero non completed jobs to process, finished processing timed out jobs")); }
public async Task AddJobLog_GivenJobUpdated_EnsuresNewNotificationIsSent() { //Arrange string jobId = "job-id-1"; JobLogUpdateModel jobLogUpdateModel = new JobLogUpdateModel { CompletedSuccessfully = false, Outcome = "outcome", ItemsFailed = 40, ItemsProcessed = 100, ItemsSucceeded = 60 }; Job job = new Job { Id = jobId, RunningStatus = RunningStatus.InProgress, JobDefinitionId = "job-definition-id", InvokerUserDisplayName = "authorName", InvokerUserId = "authorId", ItemCount = 100, SpecificationId = "spec-id-1", Trigger = new Trigger { EntityId = "spec-id-1", EntityType = "Specification", Message = "allocating" } }; IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetJobById(Arg.Is(jobId)) .Returns(job); jobRepository .UpdateJob(Arg.Is(job)) .Returns(HttpStatusCode.OK); jobRepository .CreateJobLog(Arg.Any <JobLog>()) .Returns(HttpStatusCode.OK); ILogger logger = CreateLogger(); INotificationService notificationService = CreateNotificationsService(); JobManagementService jobManagementService = CreateJobManagementService(jobRepository: jobRepository, logger: logger, notificationService: notificationService); //Act IActionResult actionResult = await jobManagementService.AddJobLog(jobId, jobLogUpdateModel); //Assert actionResult .Should() .BeAssignableTo <OkObjectResult>(); await notificationService .Received(1) .SendNotification(Arg.Is <JobNotification>(m => m.JobId == jobId && m.JobType == "job-definition-id" && m.CompletionStatus == CompletionStatus.Failed && m.InvokerUserDisplayName == "authorName" && m.InvokerUserId == "authorId" && m.ItemCount == 100 && m.Outcome == "outcome" && m.OverallItemsFailed == 40 && m.OverallItemsProcessed == 100 && m.OverallItemsSucceeded == 60 && m.ParentJobId == null && m.SpecificationId == "spec-id-1" && m.StatusDateTime.Date == DateTimeOffset.Now.ToLocalTime().Date&& string.IsNullOrWhiteSpace(m.SupersededByJobId) && m.Trigger.EntityId == "spec-id-1" && m.Trigger.EntityType == "Specification" && m.Trigger.Message == "allocating" && m.RunningStatus == RunningStatus.Completed )); }
public async Task ProcessJobCompletion_JobHasParentTwoChildrenOnlyBothCompleted_ThenParentCompleted(CompletionStatus childOneCompletionStatus, OutcomeType expectedOutcomeType) { // Arrange string parentJobId = "parent123"; string jobId = "child123"; Outcome outcomeOne = NewOutcome(); Outcome outcomeTwo = NewOutcome(); Outcome outcomeThree = NewOutcome(); Outcome outcomeFour = NewOutcome(); Job job = new Job { Id = jobId, JobDefinitionId = NewRandomString(), ParentJobId = parentJobId, CompletionStatus = childOneCompletionStatus, RunningStatus = RunningStatus.Completed, OutcomeType = OutcomeType.Succeeded, Outcome = NewRandomString(), Outcomes = new List <Outcome> { outcomeOne, outcomeTwo, outcomeThree } }; Job job2 = new Job { Id = "child456", JobDefinitionId = NewRandomString(), ParentJobId = parentJobId, CompletionStatus = CompletionStatus.Succeeded, RunningStatus = RunningStatus.Completed, OutcomeType = OutcomeType.Succeeded, Outcome = NewRandomString(), Outcomes = new List <Outcome> { outcomeFour } }; Job parentJob = new Job { Id = parentJobId, RunningStatus = RunningStatus.InProgress }; ILogger logger = CreateLogger(); IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetJobById(Arg.Is(jobId)) .Returns(job); jobRepository .GetJobById(Arg.Is(parentJobId)) .Returns(parentJob); jobRepository .GetChildJobsForParent(Arg.Is(parentJobId)) .Returns(new List <Job> { job, job2 }); JobManagementService jobManagementService = CreateJobManagementService(jobRepository, logger: logger); JobSummary JobSummary = new JobSummary { JobId = jobId, RunningStatus = RunningStatus.Completed }; string json = JsonConvert.SerializeObject(JobSummary); Message message = new Message(Encoding.UTF8.GetBytes(json)); message.UserProperties["jobId"] = jobId; // Act await jobManagementService.Process(message); // Assert await jobRepository .Received(1) .UpdateJob(Arg.Is <Job>(_ => _.Id == parentJobId && _.RunningStatus == RunningStatus.Completed && _.OutcomeType == expectedOutcomeType && _.Completed.HasValue && _.Outcome == "All child jobs completed")); logger .Received(1) .Information(Arg.Is("Parent Job {ParentJobId} of Completed Job {JobId} has been completed because all child jobs are now complete"), Arg.Is(job.ParentJobId), Arg.Is(jobId)); Outcome expectedChildJobOneOutcome = NewOutcome(_ => _.WithJobDefinitionId(job.JobDefinitionId) .WithType(OutcomeType.Succeeded) .WithDescription(job.Outcome) .WithIsSuccessful(childOneCompletionStatus == CompletionStatus.Succeeded)); Outcome expectedChildJobTwoOutcome = NewOutcome(_ => _.WithJobDefinitionId(job2.JobDefinitionId) .WithType(OutcomeType.Succeeded) .WithDescription(job2.Outcome) .WithIsSuccessful(true)); //rolls up all outcomes to the parent job parentJob.Outcomes .Should() .BeEquivalentTo(new object[] { outcomeOne, outcomeTwo, outcomeThree, outcomeFour, expectedChildJobOneOutcome, expectedChildJobTwoOutcome }); }
public async Task ProcessJobCompletion_WhenMultipleChildJobsCompleted_EnsuresOnlyCompletesParentOnce() { // Arrange string parentJobId = "parent123"; string jobId1 = "child123"; string jobId2 = "child456"; Job job1 = new Job { Id = jobId1, ParentJobId = parentJobId, CompletionStatus = CompletionStatus.Succeeded, RunningStatus = RunningStatus.Completed }; Job job2 = new Job { Id = jobId2, ParentJobId = parentJobId, RunningStatus = RunningStatus.Completed }; Job parentJob = new Job { Id = parentJobId, RunningStatus = RunningStatus.InProgress }; ILogger logger = CreateLogger(); IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetJobById(jobId1) .Returns(job1); jobRepository .GetJobById(jobId2) .Returns(job2); jobRepository .GetJobById(Arg.Is(parentJobId)) .Returns(parentJob, new Job { Id = parentJobId, RunningStatus = RunningStatus.Completed }); jobRepository .GetChildJobsForParent(Arg.Is(parentJobId)) .Returns(new List <Job> { job1, job2 }); INotificationService notificationService = CreateNotificationsService(); JobManagementService jobManagementService = CreateJobManagementService(jobRepository, logger: logger, notificationService: notificationService); JobSummary JobSummary = new JobSummary { JobId = jobId1, RunningStatus = RunningStatus.Completed }; string json = JsonConvert.SerializeObject(JobSummary); Message message1 = new Message(Encoding.UTF8.GetBytes(json)); message1.UserProperties["jobId"] = jobId1; Message message2 = new Message(Encoding.UTF8.GetBytes(json)); message2.UserProperties["jobId"] = jobId2; // Act await jobManagementService.Process(message1); await jobManagementService.Process(message2); // Assert await jobRepository .Received(1) .UpdateJob(Arg.Is <Job>(j => j.Id == parentJobId && j.RunningStatus == RunningStatus.Completed && j.Completed.HasValue)); await notificationService .Received(1) .SendNotification(Arg.Is <JobSummary>(m => m.JobId == parentJobId)); }
public async Task AddJobLog_GivenJobUpdatedAndCompletedUnsuccesfully_EnsuresLatestCacheIsNotUpdated(CompletionStatus completionStatus) { //Arrange string jobId = "job-id-1"; DateTimeOffset lastUpdated = new RandomDateTime(); List <Outcome> outcomes = new List <Outcome> { new Outcome { Description = "outcome-1" } }; string specificationId = "spec-id-1"; string jobDefinitionId = "job-definition-id"; JobLogUpdateModel jobLogUpdateModel = new JobLogUpdateModel { CompletedSuccessfully = false, Outcome = "outcome", ItemsFailed = 40, ItemsProcessed = 100, ItemsSucceeded = 60 }; Job job = new Job { Id = jobId, RunningStatus = RunningStatus.InProgress, JobDefinitionId = jobDefinitionId, InvokerUserDisplayName = "authorName", InvokerUserId = "authorId", LastUpdated = lastUpdated, ItemCount = 100, SpecificationId = specificationId, Trigger = new Trigger { EntityId = "spec-id-1", EntityType = "Specification", Message = "allocating" }, Outcomes = outcomes }; Job completedJob = new Job { Id = jobId, RunningStatus = RunningStatus.Completed, JobDefinitionId = jobDefinitionId, InvokerUserDisplayName = "authorName", InvokerUserId = "authorId", LastUpdated = lastUpdated, ItemCount = 100, SpecificationId = specificationId, Trigger = new Trigger { EntityId = "spec-id-1", EntityType = "Specification", Message = "allocating" }, Outcomes = outcomes, CompletionStatus = completionStatus, }; IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetJobById(Arg.Is(jobId)) .Returns(job); jobRepository .UpdateJob(Arg.Is(job)) .Returns(HttpStatusCode.OK); jobRepository .CreateJobLog(Arg.Any <JobLog>()) .Returns(HttpStatusCode.OK); jobRepository .GetLatestJobBySpecificationIdAndDefinitionId(Arg.Is(specificationId), Arg.Is(jobDefinitionId)) .Returns(completedJob); ILogger logger = CreateLogger(); ICacheProvider cacheProvider = CreateCacheProvider(); JobManagementService jobManagementService = CreateJobManagementService( jobRepository: jobRepository, logger: logger, cacheProvider: cacheProvider); //Act IActionResult actionResult = await jobManagementService.AddJobLog(jobId, jobLogUpdateModel); //Assert actionResult .Should() .BeAssignableTo <OkObjectResult>(); string cacheKey = $"{CacheKeys.LatestJobs}{job.SpecificationId}:{job.JobDefinitionId}"; string latestSuccessfulJobCacheKey = $"{CacheKeys.LatestSuccessfulJobs}{job.SpecificationId}:{job.JobDefinitionId}"; await cacheProvider .Received(1) .SetAsync(cacheKey, Arg.Is <Job>(_ => _.Id == jobId)); await cacheProvider .Received(0) .SetAsync(latestSuccessfulJobCacheKey, Arg.Is <Job>(_ => _.Id == jobId)); }
public async Task ProcessJobCompletion_JobHasParentOnlyOneChild_ThenCacheUpdated() { // Arrange string specificationId = "specification123"; string parentJobId = "parent123"; string jobId = "child123"; Job job = new Job { Id = jobId, SpecificationId = specificationId, ParentJobId = parentJobId, CompletionStatus = CompletionStatus.Succeeded, RunningStatus = RunningStatus.Completed }; Job parentJob = new Job { Id = parentJobId, SpecificationId = specificationId, RunningStatus = RunningStatus.InProgress }; ILogger logger = CreateLogger(); IJobRepository jobRepository = CreateJobRepository(); jobRepository .GetJobById(Arg.Is(jobId)) .Returns(job); jobRepository .GetJobById(Arg.Is(parentJobId)) .Returns(parentJob); jobRepository .GetChildJobsForParent(Arg.Is(parentJobId)) .Returns(new List <Job> { job }); var moreRecentJob = new Job { Id = "newer-job-id", SpecificationId = specificationId }; jobRepository .GetLatestJobBySpecificationIdAndDefinitionId(Arg.Any <string>(), Arg.Any <string>()) .Returns(moreRecentJob); jobRepository .UpdateJob(Arg.Any <Job>()) .Returns(HttpStatusCode.OK); string cacheKey = $"{CacheKeys.LatestJobs}{job.SpecificationId}:{job.JobDefinitionId}"; ICacheProvider cacheProvider = CreateCacheProvider(); cacheProvider .SetAsync(cacheKey, Arg.Is <Job>(_ => _.Id == job.Id)) .Returns(Task.CompletedTask); JobManagementService jobManagementService = CreateJobManagementService( jobRepository, logger: logger, cacheProvider: cacheProvider); JobSummary JobSummary = new JobSummary { JobId = jobId, RunningStatus = RunningStatus.Completed }; string json = JsonConvert.SerializeObject(JobSummary); Message message = new Message(Encoding.UTF8.GetBytes(json)); message.UserProperties["jobId"] = jobId; // Act await jobManagementService.Process(message); // Assert await jobRepository .Received(1) .UpdateJob(Arg.Is <Job>(j => j.Id == parentJobId && j.RunningStatus == RunningStatus.Completed && j.Completed.HasValue && j.Outcome == "All child jobs completed")); await cacheProvider .Received(1) .SetAsync(cacheKey, Arg.Is <Job>(_ => _.Id == moreRecentJob.Id)); }