public async Task AddJobLog_Called_ReturnsJobLog()
        {
            string jobId = "5678";

            IJobsApiClient jobsApiClient             = Substitute.For <IJobsApiClient>();
            JobManagementResiliencePolicies policies = new JobManagementResiliencePolicies
            {
                JobsApiClient = Policy.NoOpAsync()
            };
            IMessengerService messengerService = Substitute.For <IMessengerService>();
            ILogger           logger           = Substitute.For <ILogger>();

            JobLog jobLog = new JobLog
            {
                JobId = jobId
            };
            ApiResponse <JobLog> jobLogApiResponse = new ApiResponse <JobLog>(HttpStatusCode.OK, jobLog);

            JobLogUpdateModel jobLogUpdateModel = new JobLogUpdateModel();

            jobsApiClient
            .AddJobLog(jobId, jobLogUpdateModel)
            .Returns(jobLogApiResponse);

            JobManagement jobManagement = new JobManagement(jobsApiClient, logger, policies, messengerService);

            //Act
            JobLog result = await jobManagement.AddJobLog(jobId, jobLogUpdateModel);

            Assert.AreEqual(result, jobLog);

            await jobsApiClient
            .Received(1)
            .AddJobLog(jobId, jobLogUpdateModel);
        }
        public async Task UpdateJobStatusInternal_ApiResponseFailure_Logs(ApiResponse <JobLog> jobLogApiResponse)
        {
            //Arrange
            IJobsApiClient jobsApiClient             = Substitute.For <IJobsApiClient>();
            JobManagementResiliencePolicies policies = new JobManagementResiliencePolicies
            {
                JobsApiClient = Policy.NoOpAsync()
            };
            IMessengerService messengerService = Substitute.For <IMessengerService>();
            ILogger           logger           = Substitute.For <ILogger>();

            jobsApiClient
            .AddJobLog(Arg.Any <string>(), Arg.Any <JobLogUpdateModel>())
            .Returns(jobLogApiResponse);

            JobLogUpdateModel updateModel = new JobLogUpdateModel();

            JobManagement jobManagement = new JobManagement(jobsApiClient, logger, policies, messengerService);

            string jobId = "3456";

            //Act
            await jobManagement.UpdateJobStatus(jobId, updateModel);

            //Assert
            await jobsApiClient
            .Received(1)
            .AddJobLog(jobId, updateModel);

            logger
            .Received(1)
            .Write(LogEventLevel.Error, $"Failed to add a job log for job id '{jobId}'");
        }
예제 #3
0
        public async Task RefreshPublishResults_GivenSingleSpecificationId_ShouldReturnNoContentResult()
        {
            // Arrange
            const string specificationId1 = "123";

            HttpRequest httpRequest = Substitute.For <HttpRequest>();

            ISpecificationsRepository mockSpecificationsRepository = Substitute.For <ISpecificationsRepository>();

            mockSpecificationsRepository.GetSpecificationById(Arg.Any <string>()).Returns(new Specification());

            SpecificationCalculationExecutionStatus expectedSpecificationStatusCall1 = new SpecificationCalculationExecutionStatus(specificationId1, 0, CalculationProgressStatus.NotStarted);

            ICacheProvider mockCacheProvider = Substitute.For <ICacheProvider>();

            IJobsApiClient jobsApiClient = CreateJobsApiClient();

            SpecificationsService specificationsService = CreateService(specificationsRepository: mockSpecificationsRepository,
                                                                        cacheProvider: mockCacheProvider, jobsApiClient: jobsApiClient);

            httpRequest.Query.Returns(new QueryCollection(new Dictionary <string, StringValues>()
            {
                { "specificationId", new StringValues($"{specificationId1}") }
            }));

            // Act
            IActionResult actionResultReturned = await specificationsService.RefreshPublishedResults(httpRequest);

            // Assert
            actionResultReturned.Should().BeOfType <NoContentResult>();
            await mockCacheProvider.Received().SetAsync($"{CalculationProgressPrependKey}{specificationId1}", expectedSpecificationStatusCall1, TimeSpan.FromHours(6), false);

            await jobsApiClient.Received(1).CreateJob(Arg.Is <JobCreateModel>(j => j.JobDefinitionId == JobConstants.DefinitionNames.PublishProviderResultsJob && j.SpecificationId == specificationId1 && j.Trigger.Message == $"Refreshing published provider results for specification"));
        }
        public async Task UpdateJobStatus_ApiResponseSuccess_Runs()
        {
            //Arrange
            IJobsApiClient jobsApiClient             = Substitute.For <IJobsApiClient>();
            JobManagementResiliencePolicies policies = new JobManagementResiliencePolicies
            {
                JobsApiClient = Policy.NoOpAsync()
            };
            IMessengerService messengerService = Substitute.For <IMessengerService>();
            ILogger           logger           = Substitute.For <ILogger>();

            ApiResponse <JobLog> jobLogApiResponse = new ApiResponse <JobLog>(HttpStatusCode.OK, new JobLog());

            jobsApiClient
            .AddJobLog(Arg.Any <string>(), Arg.Any <JobLogUpdateModel>())
            .Returns(jobLogApiResponse);

            JobLogUpdateModel updateModel = new JobLogUpdateModel();

            JobManagement jobManagement = new JobManagement(jobsApiClient, logger, policies, messengerService);

            string jobId = "3456";

            //Act
            await jobManagement.UpdateJobStatus(jobId, updateModel);

            //Assert
            await jobsApiClient
            .Received(1)
            .AddJobLog(jobId, updateModel);

            logger
            .Received(0)
            .Write(Arg.Any <LogEventLevel>(), Arg.Any <string>());
        }
예제 #5
0
        public async Task SelectSpecificationForFunding_GivenValidSpecification_ReturnsNoContentResult()
        {
            // Arrange
            IQueryCollection queryStringValues = new QueryCollection(new Dictionary <string, StringValues>
            {
                { "specificationId", new StringValues(SpecificationId) }
            });

            HttpRequest request = Substitute.For <HttpRequest>();

            request
            .Query
            .Returns(queryStringValues);

            Specification specification = CreateSpecification();

            ILogger logger = CreateLogger();

            ISpecificationsRepository specificationsRepository = CreateSpecificationsRepository();

            specificationsRepository
            .GetSpecificationById(Arg.Is(SpecificationId))
            .Returns(specification);

            specificationsRepository
            .UpdateSpecification(Arg.Is(specification))
            .Returns(HttpStatusCode.OK);

            IJobsApiClient jobsApiClient = CreateJobsApiClient();

            ICacheProvider cacheProvider = CreateCacheProvider();

            SpecificationsService service = CreateService(
                logs: logger,
                specificationsRepository: specificationsRepository,
                jobsApiClient: jobsApiClient,
                cacheProvider: cacheProvider);

            // Act
            IActionResult result = await service.SelectSpecificationForFunding(request);

            // Assert
            result
            .Should()
            .BeOfType <NoContentResult>();

            await jobsApiClient
            .Received(1)
            .CreateJob(Arg.Is <JobCreateModel>(j => j.JobDefinitionId == JobConstants.DefinitionNames.PublishProviderResultsJob && j.SpecificationId == SpecificationId && j.Trigger.Message == $"Selecting specification for funding"));

            await cacheProvider
            .Received(1)
            .RemoveAsync <SpecificationSummary>($"{CacheKeys.SpecificationSummaryById}{specification.Id}");

            await cacheProvider
            .Received(1)
            .RemoveAsync <SpecificationCurrentVersion>($"{CacheKeys.SpecificationCurrentVersionById}{specification.Id}");
        }
        public async Task RegenerateProviderSourceDatasets_GivenSpecificationId_ThenCallJobServiceToProcess()
        {
            // Arrange
            IQueryCollection queryStringValues = new QueryCollection(new Dictionary <string, StringValues>
            {
                { "specificationId", new StringValues(SpecificationId) },
            });

            HttpRequest request = Substitute.For <HttpRequest>();

            request.Query.Returns(queryStringValues);

            IDatasetRepository datasetRepository = CreateDatasetsRepository();

            datasetRepository
            .GetDefinitionSpecificationRelationshipsByQuery(Arg.Any <Expression <Func <DefinitionSpecificationRelationship, bool> > >())
            .Returns(new List <DefinitionSpecificationRelationship>
            {
                new DefinitionSpecificationRelationship {
                    DatasetVersion = new DatasetRelationshipVersion {
                        Id = "DSRV1", Version = 1
                    },
                    Specification = new Common.Models.Reference {
                        Id = SpecificationId, Name = "SpecAbc"
                    }
                }
            });
            datasetRepository
            .GetDatasetsByQuery(Arg.Any <Expression <Func <Dataset, bool> > >())
            .Returns(new List <Dataset>
            {
                new Dataset {
                    Id = "DS1"
                }
            });

            IJobsApiClient jobsApiClient = CreateJobsApiClient();

            DatasetService service = CreateDatasetService(datasetRepository: datasetRepository, jobsApiClient: jobsApiClient);

            // Act
            await service.RegenerateProviderSourceDatasets(request);

            // Assert
            await jobsApiClient
            .Received(1)
            .CreateJob(Arg.Is <JobCreateModel>(j =>
                                               j.JobDefinitionId == "MapDatasetJob" &&
                                               j.Properties.ContainsKey("session-id") &&
                                               j.Properties["session-id"] == SpecificationId));
        }
        public async Task QueueJobs_Called_ReturnsJobs()
        {
            string specificationId = "1234";
            string jobId           = "3456";

            IJobsApiClient jobsApiClient             = Substitute.For <IJobsApiClient>();
            JobManagementResiliencePolicies policies = new JobManagementResiliencePolicies
            {
                JobsApiClient = Policy.NoOpAsync()
            };
            IMessengerService messengerService = Substitute.For <IMessengerService>();
            ILogger           logger           = Substitute.For <ILogger>();

            IEnumerable <Job> jobApiResponse = new List <Job>
            {
                new Job
                {
                    Id = jobId
                }
            };

            IEnumerable <JobCreateModel> jobCreateModel =
                new List <JobCreateModel>
            {
                new JobCreateModel
                {
                    SpecificationId = specificationId
                }
            };

            jobsApiClient
            .CreateJobs(jobCreateModel)
            .Returns(jobApiResponse);

            JobManagement jobManagement = new JobManagement(jobsApiClient, logger, policies, messengerService);

            //Act
            await jobManagement.QueueJobs(jobCreateModel);

            await jobsApiClient
            .Received(1)
            .CreateJobs(jobCreateModel);
        }
        public async Task GetNonCompletedJobsWithinTimeFrame_Called_ReturnsJobSummaries()
        {
            string jobId = "5678";

            DateTimeOffset from = DateTimeOffset.UtcNow.AddDays(-2);
            DateTimeOffset to   = DateTimeOffset.UtcNow.AddDays(-1);

            IJobsApiClient jobsApiClient             = Substitute.For <IJobsApiClient>();
            JobManagementResiliencePolicies policies = new JobManagementResiliencePolicies
            {
                JobsApiClient = Policy.NoOpAsync()
            };
            IMessengerService messengerService = Substitute.For <IMessengerService>();
            ILogger           logger           = Substitute.For <ILogger>();


            JobSummary jobSummary = new JobSummary
            {
                JobId = jobId
            };
            IEnumerable <JobSummary> jobSummaries = new List <JobSummary>
            {
                jobSummary
            };
            ApiResponse <IEnumerable <JobSummary> > jobSummariesApiResponse
                = new ApiResponse <IEnumerable <JobSummary> >(HttpStatusCode.OK, jobSummaries);

            jobsApiClient
            .GetNonCompletedJobsWithinTimeFrame(from, to)
            .Returns(jobSummariesApiResponse);

            JobManagement jobManagement = new JobManagement(jobsApiClient, logger, policies, messengerService);

            //Act
            IEnumerable <JobSummary> result = await jobManagement.GetNonCompletedJobsWithinTimeFrame(from, to);

            Assert.AreEqual(result, jobSummaries);

            await jobsApiClient
            .Received(1)
            .GetNonCompletedJobsWithinTimeFrame(from, to);
        }
        public async Task GetLatestJobForSpecification_Called_ReturnsJobSummary()
        {
            string specificationId = "1234";
            string jobType         = "3456";
            string jobId           = "5678";

            IJobsApiClient jobsApiClient             = Substitute.For <IJobsApiClient>();
            JobManagementResiliencePolicies policies = new JobManagementResiliencePolicies
            {
                JobsApiClient = Policy.NoOpAsync()
            };
            IMessengerService messengerService = Substitute.For <IMessengerService>();
            ILogger           logger           = Substitute.For <ILogger>();

            string[] jobTypes = new string[]
            {
                jobType
            };

            IDictionary <string, JobSummary> jobSummary = new Dictionary <string, JobSummary> {
                { string.Empty, new JobSummary {
                      JobId = jobId
                  } }
            };
            ApiResponse <IDictionary <string, JobSummary> > jobSummaryApiResponse = new ApiResponse <IDictionary <string, JobSummary> >(HttpStatusCode.OK, jobSummary);

            jobsApiClient
            .GetLatestJobsForSpecification(specificationId, jobTypes)
            .Returns(jobSummaryApiResponse);

            JobManagement jobManagement = new JobManagement(jobsApiClient, logger, policies, messengerService);

            //Act
            IDictionary <string, JobSummary> result = await jobManagement.GetLatestJobsForSpecification(specificationId, jobTypes);

            Assert.AreEqual(result, jobSummary);

            await jobsApiClient
            .Received(1)
            .GetLatestJobsForSpecification(specificationId, jobTypes);
        }
        public async Task RetrieveJobAndCheckCanBeProcessed_ApiReturnsIncomplete_ReturnsCorrectly()
        {
            //Arrange
            IJobsApiClient jobsApiClient             = Substitute.For <IJobsApiClient>();
            JobManagementResiliencePolicies policies = new JobManagementResiliencePolicies
            {
                JobsApiClient = Policy.NoOpAsync()
            };
            IMessengerService messengerService = Substitute.For <IMessengerService>();
            ILogger           logger           = Substitute.For <ILogger>();

            JobViewModel jvm = new JobViewModel
            {
                CompletionStatus = null
            };

            ApiResponse <JobViewModel> jobApiResponse = new ApiResponse <JobViewModel>(HttpStatusCode.OK, jvm);

            jobsApiClient
            .GetJobById(Arg.Any <string>())
            .Returns(jobApiResponse);

            JobManagement jobManagement = new JobManagement(jobsApiClient, logger, policies, messengerService);

            string jobId = "3456";

            //Act
            JobViewModel viewModel = await jobManagement.RetrieveJobAndCheckCanBeProcessed(jobId);

            //Assert
            await jobsApiClient
            .Received(1)
            .GetJobById(jobId);

            viewModel
            .Should()
            .Be(jvm);
        }
        public async Task RetrieveJobAndCheckCanBeProcessed_FailsWithJobAlreadyCompleted_LogsAndErrors(ApiResponse <JobViewModel> jobApiResponse,
                                                                                                       string jobId,
                                                                                                       string errorMessage,
                                                                                                       LogEventLevel logEventLevel)
        {
            //Arrange
            IJobsApiClient    jobsApiClient          = Substitute.For <IJobsApiClient>();
            IMessengerService messengerService       = Substitute.For <IMessengerService>();
            JobManagementResiliencePolicies policies = new JobManagementResiliencePolicies
            {
                JobsApiClient = Policy.NoOpAsync()
            };
            ILogger logger = Substitute.For <ILogger>();

            jobsApiClient
            .GetJobById(Arg.Any <string>())
            .Returns(jobApiResponse);

            JobManagement jobManagement = new JobManagement(jobsApiClient, logger, policies, messengerService);

            Func <Task> test = async() => await jobManagement.RetrieveJobAndCheckCanBeProcessed(jobId);

            test
            .Should().Throw <JobAlreadyCompletedException>()
            .Which
            .Message
            .Should().Be(errorMessage);

            await jobsApiClient
            .Received(1)
            .GetJobById(jobId);

            logger
            .Received(1)
            .Write(logEventLevel, errorMessage);
        }
        public async Task UpdateCalculationsForSpecification_GivenModelHasChangedPolicyNameAndSourceCodeContainsCalculationAggregate_SavesChangesEnsuresGenerateAggregationsJobCreated()
        {
            // Arrange
            const string specificationId = "spec-id";

            Models.Specs.SpecificationVersionComparisonModel specificationVersionComparison = new Models.Specs.SpecificationVersionComparisonModel()
            {
                Id      = specificationId,
                Current = new Models.Specs.SpecificationVersion
                {
                    FundingPeriod = new Reference {
                        Id = "fp1"
                    },
                    Name     = "any-name",
                    Policies = new[] { new Models.Specs.Policy {
                                           Id = "pol-id", Name = "policy2"
                                       } }
                },
                Previous = new Models.Specs.SpecificationVersion
                {
                    FundingPeriod = new Reference {
                        Id = "fp1"
                    },
                    Policies = new[] { new Models.Specs.Policy {
                                           Id = "pol-id", Name = "policy1"
                                       } }
                }
            };

            string json = JsonConvert.SerializeObject(specificationVersionComparison);

            Message message = new Message(Encoding.UTF8.GetBytes(json));

            message.UserProperties.Add("user-id", UserId);
            message.UserProperties.Add("user-name", Username);

            ILogger logger = CreateLogger();

            IEnumerable <Calculation> calcs = new[]
            {
                new Calculation
                {
                    SpecificationId = "spec-id",
                    Name            = "any name",
                    Id = "any-id",
                    CalculationSpecification = new Reference("any name", "any-id"),
                    FundingPeriod            = new Reference("18/19", "2018/2019"),
                    CalculationType          = CalculationType.Number,
                    FundingStream            = new Reference("fp1", "fs1-111"),
                    Current = new CalculationVersion
                    {
                        Author        = new Reference(UserId, Username),
                        Date          = DateTimeOffset.Now,
                        PublishStatus = PublishStatus.Draft,
                        SourceCode    = "return Min(calc1)",
                        Version       = 1
                    },
                    Policies = new List <Reference> {
                        new Reference {
                            Id = "pol-id", Name = "policy1"
                        }
                    }
                }
            };

            BuildProject buildProject = new BuildProject
            {
                Id = "build-project-1",
                SpecificationId = specificationId
            };

            ICalculationsRepository calculationsRepository = CreateCalculationsRepository();

            calculationsRepository
            .GetCalculationsBySpecificationId(Arg.Is(specificationId))
            .Returns(calcs);

            IBuildProjectsService buildProjectsService = CreateBuildProjectsService();

            buildProjectsService
            .GetBuildProjectForSpecificationId(Arg.Is(specificationId))
            .Returns(buildProject);

            ISearchRepository <CalculationIndex> searchRepository = CreateSearchRepository();

            IJobsApiClient jobsApiClient = CreateJobsApiClient();

            jobsApiClient
            .CreateJob(Arg.Any <JobCreateModel>())
            .Returns(new Job {
                Id = "job-id-1", JobDefinitionId = JobConstants.DefinitionNames.CreateInstructGenerateAggregationsAllocationJob
            });

            CalculationService service = CreateCalculationService(
                calculationsRepository,
                logger,
                buildProjectsService: buildProjectsService,
                searchRepository: searchRepository,
                jobsApiClient: jobsApiClient);

            // Act
            await service.UpdateCalculationsForSpecification(message);

            // Assert
            await
            jobsApiClient
            .Received(1)
            .CreateJob(Arg.Is <JobCreateModel>(
                           m =>
                           m.InvokerUserDisplayName == Username &&
                           m.InvokerUserId == UserId &&
                           m.JobDefinitionId == JobConstants.DefinitionNames.CreateInstructGenerateAggregationsAllocationJob &&
                           m.Properties["specification-id"] == specificationId &&
                           m.Trigger.EntityId == specificationId &&
                           m.Trigger.EntityType == nameof(Models.Specs.Specification) &&
                           m.Trigger.Message == $"Updating calculations for specification: '{specificationId}'"
                           ));

            logger
            .Received(1)
            .Information(Arg.Is($"New job of type '{JobConstants.DefinitionNames.CreateInstructGenerateAggregationsAllocationJob}' created with id: 'job-id-1'"));
        }
        public async Task UpdateCalculationsForSpecification_GivenModelHasChangedPolicyNameButCreatingJobReturnsNull_LogsError()
        {
            // Arrange
            const string specificationId = "spec-id";

            Models.Specs.SpecificationVersionComparisonModel specificationVersionComparison = new Models.Specs.SpecificationVersionComparisonModel()
            {
                Id      = specificationId,
                Current = new Models.Specs.SpecificationVersion
                {
                    FundingPeriod = new Reference {
                        Id = "fp1"
                    },
                    Name     = "any-name",
                    Policies = new[] { new Models.Specs.Policy {
                                           Id = "pol-id", Name = "policy2"
                                       } }
                },
                Previous = new Models.Specs.SpecificationVersion
                {
                    FundingPeriod = new Reference {
                        Id = "fp1"
                    },
                    Policies = new[] { new Models.Specs.Policy {
                                           Id = "pol-id", Name = "policy1"
                                       } }
                }
            };

            string json = JsonConvert.SerializeObject(specificationVersionComparison);

            Message message = new Message(Encoding.UTF8.GetBytes(json));

            message.UserProperties.Add("user-id", UserId);
            message.UserProperties.Add("user-name", Username);

            ILogger logger = CreateLogger();

            IEnumerable <Calculation> calcs = new[]
            {
                new Calculation
                {
                    SpecificationId = "spec-id",
                    Name            = "any name",
                    Id = "any-id",
                    CalculationSpecification = new Reference("any name", "any-id"),
                    FundingPeriod            = new Reference("18/19", "2018/2019"),
                    CalculationType          = CalculationType.Number,
                    FundingStream            = new Reference("fp1", "fs1-111"),
                    Current = new CalculationVersion
                    {
                        Author        = new Reference(UserId, Username),
                        Date          = DateTimeOffset.Now,
                        PublishStatus = PublishStatus.Draft,
                        SourceCode    = "source code",
                        Version       = 1
                    },
                    Policies = new List <Reference> {
                        new Reference {
                            Id = "pol-id", Name = "policy1"
                        }
                    }
                }
            };

            BuildProject buildProject = new BuildProject
            {
                Id = "build-project-1",
                SpecificationId = specificationId
            };

            ICalculationsRepository calculationsRepository = CreateCalculationsRepository();

            calculationsRepository
            .GetCalculationsBySpecificationId(Arg.Is(specificationId))
            .Returns(calcs);

            IBuildProjectsService buildProjectsService = CreateBuildProjectsService();

            buildProjectsService
            .GetBuildProjectForSpecificationId(Arg.Is(specificationId))
            .Returns(buildProject);

            ISearchRepository <CalculationIndex> searchRepository = CreateSearchRepository();

            IJobsApiClient jobsApiClient = CreateJobsApiClient();

            jobsApiClient
            .CreateJob(Arg.Any <JobCreateModel>())
            .Returns((Job)null);

            CalculationService service = CreateCalculationService(
                calculationsRepository,
                logger,
                buildProjectsService: buildProjectsService,
                searchRepository: searchRepository,
                jobsApiClient: jobsApiClient);

            // Act
            Func <Task> test = async() => await service.UpdateCalculationsForSpecification(message);

            // Assert
            test
            .Should()
            .ThrowExactly <RetriableException>()
            .Which
            .Message
            .Should()
            .Be($"Failed to create job: '{JobConstants.DefinitionNames.CreateInstructAllocationJob} for specification id '{specificationId}'");

            await
            jobsApiClient
            .Received(1)
            .CreateJob(Arg.Is <JobCreateModel>(
                           m =>
                           m.InvokerUserDisplayName == Username &&
                           m.InvokerUserId == UserId &&
                           m.JobDefinitionId == JobConstants.DefinitionNames.CreateInstructAllocationJob &&
                           m.Properties["specification-id"] == specificationId &&
                           m.Trigger.EntityId == specificationId &&
                           m.Trigger.EntityType == nameof(Models.Specs.Specification) &&
                           m.Trigger.Message == $"Updating calculations for specification: '{specificationId}'"
                           ));

            logger
            .Received(1)
            .Error(Arg.Is($"Failed to create job: '{JobConstants.DefinitionNames.CreateInstructAllocationJob} for specification id '{specificationId}'"));
        }
        public async Task UpdatePublishedAllocationLineResultsStatus_GivenBatchingButNoUpdateModel_ReturnsBadRequest()
        {
            //arrange
            IQueryCollection queryStringValues = new QueryCollection(new Dictionary <string, StringValues>
            {
                { "specificationId", new StringValues(specificationId) },
            });

            IEnumerable <UpdatePublishedAllocationLineResultStatusProviderModel> Providers = new[]
            {
                new UpdatePublishedAllocationLineResultStatusProviderModel
                {
                    ProviderId        = "1111",
                    AllocationLineIds = new[] { "AAAAA" }
                },
                new UpdatePublishedAllocationLineResultStatusProviderModel
                {
                    ProviderId        = "1111-1",
                    AllocationLineIds = new[] { "AAAAA" }
                },
                new UpdatePublishedAllocationLineResultStatusProviderModel
                {
                    ProviderId        = "1111-2",
                    AllocationLineIds = new[] { "AAAAA" }
                }
            };

            UpdatePublishedAllocationLineResultStatusModel model = new UpdatePublishedAllocationLineResultStatusModel
            {
                Providers = Providers,
                Status    = AllocationLineStatus.Approved
            };

            string json = JsonConvert.SerializeObject(model);

            byte[]       byteArray = Encoding.UTF8.GetBytes(json);
            MemoryStream stream    = new MemoryStream(byteArray);

            HttpRequest request = Substitute.For <HttpRequest>();

            request
            .Query
            .Returns(queryStringValues);
            request
            .Body
            .Returns(stream);

            ILogger logger = CreateLogger();

            Job newJob = new Job {
                Id = "new-job-id"
            };

            IEnumerable <PublishedProviderResult> publishedProviderResults = CreatePublishedProviderResultsWithDifferentProviders();

            foreach (PublishedProviderResult publishedProviderResult in publishedProviderResults)
            {
                publishedProviderResult.FundingStreamResult.AllocationLineResult.Current.ProfilingPeriods = new[] { new ProfilingPeriod() };
            }

            IJobsApiClient jobsApiClient = CreateJobsApiClient();

            jobsApiClient
            .CreateJob(Arg.Any <JobCreateModel>())
            .Returns(newJob);

            ICacheProvider cacheProvider = CreateCacheProvider();

            PublishedResultsService resultsService = CreateResultsService(logger, jobsApiClient: jobsApiClient, cacheProvider: cacheProvider);

            //Act
            IActionResult actionResult = await resultsService.UpdatePublishedAllocationLineResultsStatus(request);

            //Arrange
            actionResult
            .Should()
            .BeAssignableTo <OkResult>();

            logger
            .Received(1)
            .Information(Arg.Is($"New job: '{JobConstants.DefinitionNames.CreateInstructAllocationLineResultStatusUpdateJob}' created with id: '{newJob.Id}'"));

            await
            cacheProvider
            .Received(1)
            .SetAsync <UpdatePublishedAllocationLineResultStatusModel>(Arg.Any <string>(), Arg.Any <UpdatePublishedAllocationLineResultStatusModel>());

            await
            jobsApiClient
            .Received(1)
            .CreateJob(Arg.Is <JobCreateModel>(m =>
                                               !string.IsNullOrWhiteSpace(m.InvokerUserDisplayName) &&
                                               !string.IsNullOrWhiteSpace(m.InvokerUserId) &&
                                               m.JobDefinitionId == JobConstants.DefinitionNames.CreateInstructAllocationLineResultStatusUpdateJob &&
                                               m.SpecificationId == specificationId &&
                                               m.Properties["specification-id"] == specificationId &&
                                               !string.IsNullOrWhiteSpace(m.Properties["cache-key"]) &&
                                               m.Trigger.EntityId == specificationId &&
                                               m.Trigger.EntityType == "Specification" &&
                                               m.Trigger.Message == $"Updating allocation line results status"
                                               ));
        }
        public async Task CreateAllocationLineResultStatusUpdateJobs_GivenUpateModelWith10ProvidersAndDefaultMaxPartitionSize_CreatesOneChildJob()
        {
            //Arrange
            const string cacheKey = "cache-key";

            Message message = new Message();

            message.UserProperties.Add("jobId", jobId);

            ILogger logger = CreateLogger();

            JobViewModel job = new JobViewModel
            {
                Id = jobId,
                SpecificationId        = specificationId,
                InvokerUserDisplayName = "user-name",
                InvokerUserId          = "user-id",
                CorrelationId          = "coorelation-id",
                Properties             = new Dictionary <string, string>
                {
                    { "cache-key", cacheKey }
                },
                JobDefinitionId = JobConstants.DefinitionNames.CreateInstructAllocationLineResultStatusUpdateJob
            };

            UpdatePublishedAllocationLineResultStatusModel updateModel = new UpdatePublishedAllocationLineResultStatusModel
            {
                Status    = AllocationLineStatus.Approved,
                Providers = CreateProviderAllocationLineResults()
            };

            ApiResponse <JobViewModel> apiResponse = new ApiResponse <JobViewModel>(HttpStatusCode.OK, job);

            IJobsApiClient jobsApiClient = CreateJobsApiClient();

            jobsApiClient
            .GetJobById(jobId)
            .Returns(apiResponse);

            IEnumerable <Job> newJobs = new[]
            {
                new Job()
            };

            jobsApiClient
            .CreateJobs(Arg.Any <IEnumerable <JobCreateModel> >())
            .Returns(newJobs);

            ICacheProvider cacheProvider = CreateCacheProvider();

            cacheProvider
            .GetAsync <UpdatePublishedAllocationLineResultStatusModel>(Arg.Is(cacheKey))
            .Returns(updateModel);

            PublishedResultsService publishedResultsService = CreateResultsService(logger, jobsApiClient: jobsApiClient, cacheProvider: cacheProvider);

            //Act
            await publishedResultsService.CreateAllocationLineResultStatusUpdateJobs(message);

            //Assert
            await
            jobsApiClient
            .Received(1)
            .CreateJobs(Arg.Is <IEnumerable <JobCreateModel> >(
                            m => m.First().Trigger.EntityId == jobId &&
                            m.First().Trigger.EntityType == "Job" &&
                            m.First().Trigger.Message == $"Triggered by parent job" &&
                            m.First().SpecificationId == specificationId &&
                            m.First().ParentJobId == jobId &&
                            m.First().InvokerUserId == job.InvokerUserId &&
                            m.First().InvokerUserDisplayName == job.InvokerUserDisplayName &&
                            m.First().CorrelationId == job.CorrelationId &&
                            !string.IsNullOrWhiteSpace(m.First().MessageBody)));

            await
            cacheProvider
            .Received(1)
            .RemoveAsync <UpdatePublishedAllocationLineResultStatusModel>(Arg.Is(cacheKey));
        }
        public async Task CreateAllocationLineResultStatusUpdateJobs_GivenUpateModelWith10ProvidersAndMaxPartitionSizeOf2_CreatesFiveChildJobs()
        {
            //Arrange
            const string cacheKey = "cache-key";

            Message message = new Message();

            message.UserProperties.Add("jobId", jobId);

            ILogger logger = CreateLogger();

            JobViewModel job = new JobViewModel
            {
                Id = jobId,
                SpecificationId        = specificationId,
                InvokerUserDisplayName = "user-name",
                InvokerUserId          = "user-id",
                CorrelationId          = "coorelation-id",
                Properties             = new Dictionary <string, string>
                {
                    { "cache-key", cacheKey }
                },
                JobDefinitionId = JobConstants.DefinitionNames.CreateInstructAllocationLineResultStatusUpdateJob
            };

            UpdatePublishedAllocationLineResultStatusModel updateModel = new UpdatePublishedAllocationLineResultStatusModel
            {
                Status    = AllocationLineStatus.Approved,
                Providers = CreateProviderAllocationLineResults()
            };

            ApiResponse <JobViewModel> apiResponse = new ApiResponse <JobViewModel>(HttpStatusCode.OK, job);

            IJobsApiClient jobsApiClient = CreateJobsApiClient();

            jobsApiClient
            .GetJobById(jobId)
            .Returns(apiResponse);

            IEnumerable <Job> newJobs = new[]
            {
                new Job(),
                new Job(),
                new Job(),
                new Job(),
                new Job(),
            };

            jobsApiClient
            .CreateJobs(Arg.Any <IEnumerable <JobCreateModel> >())
            .Returns(newJobs);

            ICacheProvider cacheProvider = CreateCacheProvider();

            cacheProvider
            .GetAsync <UpdatePublishedAllocationLineResultStatusModel>(Arg.Is(cacheKey))
            .Returns(updateModel);

            IPublishedProviderResultsSettings settings = CreatePublishedProviderResultsSettings();

            settings
            .UpdateAllocationLineResultStatusBatchCount
            .Returns(2);

            PublishedResultsService publishedResultsService = CreateResultsService(logger, jobsApiClient: jobsApiClient, cacheProvider: cacheProvider, publishedProviderResultsSettings: settings);

            //Act
            await publishedResultsService.CreateAllocationLineResultStatusUpdateJobs(message);

            //Assert
            await
            jobsApiClient
            .Received(1)
            .CreateJobs(Arg.Is <IEnumerable <JobCreateModel> >(m => m.Count() == 5));

            await
            cacheProvider
            .Received(1)
            .RemoveAsync <UpdatePublishedAllocationLineResultStatusModel>(Arg.Is(cacheKey));
        }
        public async Task WaitForJobsToCompleteWithAllJobsSucceeded_ReturnsTrue(bool useServiceBus)
        {
            IJobsApiClient jobsApiClient             = Substitute.For <IJobsApiClient>();
            JobManagementResiliencePolicies policies = new JobManagementResiliencePolicies
            {
                JobsApiClient = Policy.NoOpAsync()
            };
            IMessengerService messengerService = null;

            if (useServiceBus)
            {
                messengerService = Substitute.For <IMessengerService, IServiceBusService>();
            }
            else
            {
                messengerService = Substitute.For <IMessengerService, IQueueService>();
            }

            ILogger logger = Substitute.For <ILogger>();

            JobManagement jobManagement = new JobManagement(jobsApiClient, logger, policies, messengerService);

            var jobId = "3456";
            IEnumerable <Job> jobApiResponse = new List <Job> {
                new Job {
                    Id = jobId
                }
            };

            jobsApiClient
            .GetLatestJobsForSpecification("specificationId", Arg.Is <string[]>(_ => _.Single() == "PopulateScopedProviders"))
            .Returns(new ApiResponse <IDictionary <string, JobSummary> >(HttpStatusCode.OK, new Dictionary <string, JobSummary> {
                { string.Empty, new JobSummary {
                      RunningStatus = RunningStatus.Completed, CompletionStatus = CompletionStatus.Succeeded, JobId = jobId
                  } }
            }));

            messengerService
            .ReceiveMessage("topic/Subscriptions/correlationId", Arg.Any <Predicate <JobSummary> >(), TimeSpan.FromMilliseconds(600000))
            .Returns(new JobSummary
            {
                CompletionStatus = CompletionStatus.Succeeded
            });

            //Act
            bool jobsComplete = await jobManagement.QueueJobAndWait(async() => await Task.Run(() => { return(true); }), "PopulateScopedProviders", "specificationId", "correlationId", "topic");

            //Assert
            if (useServiceBus)
            {
                await((IServiceBusService)messengerService)
                .Received(1)
                .CreateSubscription("topic", "correlationId", Arg.Is <TimeSpan>(_ => _.Days == 1));

                await messengerService
                .Received(1)
                .ReceiveMessage("topic/Subscriptions/correlationId", Arg.Any <Predicate <JobSummary> >(), TimeSpan.FromMilliseconds(600000));
            }
            else
            {
                await jobsApiClient
                .Received(2)
                .GetLatestJobsForSpecification("specificationId", Arg.Is <string[]>(_ => _.Single() == "PopulateScopedProviders"));
            }

            jobsComplete
            .Should()
            .BeTrue();
        }