public async Task <IActionResult> UpdateBuildProjectRelationships(string specificationId, DatasetRelationshipSummary relationship) { if (string.IsNullOrWhiteSpace(specificationId)) { _logger.Error("No specification Id was provided to UpdateBuildProjectRelationships"); return(new BadRequestObjectResult("Null or empty specification Id provided")); } if (relationship == null) { _logger.Error("A null relationship message was provided to UpdateBuildProjectRelationships"); return(new BadRequestObjectResult("Null relationship provided")); } BuildProject buildProject = await GetBuildProjectForSpecificationId(specificationId); IEnumerable <Models.Calcs.Calculation> calculations = await _calculationsRepository.GetCalculationsBySpecificationId(specificationId); buildProject.Build = _sourceCodeService.Compile(buildProject, calculations ?? Enumerable.Empty <Models.Calcs.Calculation>()); if (!_featureToggle.IsDynamicBuildProjectEnabled()) { await _buildProjectsRepositoryPolicy.ExecuteAsync(() => _buildProjectsRepository.UpdateBuildProject(buildProject)); } await _sourceCodeService.SaveAssembly(buildProject); return(new OkObjectResult(buildProject)); }
public async Task IsCalculationNameValid_WhenCalculationDoesNotExist_ThenReturnsOkResult() { // Arrange string specificationId = "spec1"; ISpecificationRepository specificationRepository = CreateSpecificationRepository(); specificationRepository .GetSpecificationSummaryById(Arg.Is(specificationId)) .Returns(new SpecificationSummary { Id = specificationId }); ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); calculationsRepository .GetCalculationsBySpecificationId(Arg.Is(specificationId)) .Returns(new List <Models.Calcs.Calculation>()); CalculationService service = CreateCalculationService(specificationRepository: specificationRepository, calculationsRepository: calculationsRepository); // Act IActionResult result = await service.IsCalculationNameValid(specificationId, "calc1", null); // Assert result .Should() .BeOfType <OkResult>(); }
public async Task ResetCalculationForFieldDefinitionChanges_GivenNoCalculationsToProcess_LogsAndDoesNotContinue() { //Arrange const string specificationId = "spec-id"; IEnumerable <DatasetSpecificationRelationshipViewModel> relationships = Enumerable.Empty <DatasetSpecificationRelationshipViewModel>(); IEnumerable <string> currentFieldDefinitionNames = Enumerable.Empty <string>(); ILogger logger = CreateLogger(); ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); calculationsRepository .GetCalculationsBySpecificationId(Arg.Is(specificationId)) .Returns(Enumerable.Empty <Calculation>()); CalculationService calculationService = CreateCalculationService(logger: logger, calculationsRepository: calculationsRepository); //Act await calculationService.ResetCalculationForFieldDefinitionChanges(relationships, specificationId, currentFieldDefinitionNames); //Assert logger .Received(1) .Information(Arg.Is($"No calculations found to reset for specification id '{specificationId}'")); }
public async Task IsCalculationNameValid_WhenCalculationDoesNotExist_ThenReturnsOkResult() { // Arrange string specificationId = "spec1"; ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient(); specificationsApiClient .GetSpecificationSummaryById(Arg.Is(specificationId)) .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, new SpecModel.SpecificationSummary { Id = specificationId })); ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); calculationsRepository .GetCalculationsBySpecificationId(Arg.Is(specificationId)) .Returns(new List <Calculation>()); CalculationService service = CreateCalculationService(specificationsApiClient: specificationsApiClient, calculationsRepository: calculationsRepository); // Act IActionResult result = await service.IsCalculationNameValid(specificationId, "calc1", null); // Assert result .Should() .BeOfType <OkResult>(); }
async public Task GetCalculationSummariesForSpecification_WhenCalculationsNotFoundInCacheAndResponseFromRepositoryIsNull_ThenErrorReturned() { // Arrange const string specificationId = "specid"; IQueryCollection queryStringValues = new QueryCollection(new Dictionary <string, StringValues> { { "specificationId", new StringValues(specificationId) } }); HttpRequest request = Substitute.For <HttpRequest>(); request .Query .Returns(queryStringValues); ILogger logger = CreateLogger(); ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); calculationsRepository .GetCalculationsBySpecificationId(Arg.Is(specificationId)) .Returns((IEnumerable <Calculation>)null); ICacheProvider cacheProvider = CreateCacheProvider(); cacheProvider .GetAsync <List <CalculationSummaryModel> >($"{CacheKeys.CalculationsSummariesForSpecification}{specificationId}") .Returns((List <CalculationSummaryModel>)null); CalculationService service = CreateCalculationService(logger: logger, calculationsRepository: calculationsRepository, cacheProvider: cacheProvider); // Act IActionResult result = await service.GetCalculationSummariesForSpecification(request); // Assert result .Should() .BeOfType <InternalServerErrorResult>() .Which .Value .Should() .Be("Calculations from repository returned null"); await calculationsRepository .Received(1) .GetCalculationsBySpecificationId(Arg.Is(specificationId)); await cacheProvider .Received(1) .GetAsync <List <CalculationSummaryModel> >($"{CacheKeys.CalculationsSummariesForSpecification}{specificationId}"); logger .Received(1) .Warning($"Calculations from repository returned null for specification ID of '{specificationId}'"); }
public async Task <SpecificationCalculationRelationships> GetSpecificationCalculationRelationships(string specificationId) { Guard.IsNullOrWhiteSpace(specificationId, nameof(specificationId)); ApiResponse <SpecificationSummary> specificationResponse = await _specificationsResilience.ExecuteAsync(() => _specifications.GetSpecificationSummaryById(specificationId)); SpecificationSummary specificationSummary = specificationResponse?.Content; if (specificationSummary == null) { throw new ArgumentOutOfRangeException(nameof(specificationId), $"No specification with id {specificationId}. Unable to get calculation relationships"); } Calculation[] calculations = (await _calculationsResilience.ExecuteAsync(() => _calculations.GetCalculationsBySpecificationId(specificationId)))?.ToArray(); if (calculations.IsNullOrEmpty()) { throw new ArgumentOutOfRangeException(nameof(specificationId), $"No calculations for specification with id {specificationId}. Unable to get calculation relationships"); } IEnumerable <CalculationRelationship> calculationRelationships = _calculationAnalysis.DetermineRelationshipsBetweenCalculations(calculations); BuildProject buildProject = await _buildProjectsService.GetBuildProjectForSpecificationId(specificationId); IEnumerable <FundingLineCalculationRelationship> fundingLineRelationships = _calculationAnalysis.DetermineRelationshipsBetweenFundingLinesAndCalculations(calculations, buildProject.FundingLines); calculationRelationships = calculationRelationships.Concat(fundingLineRelationships.Select(_ => new CalculationRelationship { CalculationOneId = _.CalculationOneId, CalculationTwoId = _.CalculationTwoId })); IEnumerable <DatasetReference> datasetReferences = _datasetReferenceService.GetDatasetRelationShips(calculations, buildProject.DatasetRelationships); return(new SpecificationCalculationRelationships { Specification = _mapper.Map <Specification>(specificationSummary), Calculations = _mapper.Map <IEnumerable <GraphCalculation> >(calculations), FundingLines = _mapper.Map <IEnumerable <GraphFundingLine> >(buildProject.FundingLines?.Values.SelectMany(_ => _.FundingLines)), CalculationRelationships = calculationRelationships, FundingLineRelationships = fundingLineRelationships, CalculationDataFieldRelationships = datasetReferences.SelectMany(_ => _.Calculations.Select(calc => new CalculationDataFieldRelationship { Calculation = calc, DataField = _.DataField })).Distinct(), DatasetDataFieldRelationships = datasetReferences.Select(_ => new DatasetDataFieldRelationship { Dataset = _.Dataset, DataField = _.DataField }), DatasetDatasetDefinitionRelationships = datasetReferences.Select(_ => new DatasetDatasetDefinitionRelationship { Dataset = _.Dataset, DatasetDefinition = _.DatasetDefinition }) }); }
public void ResetCalculationForFieldDefinitionChanges_GivenCalculationRequiresResetButUpdatingCalculationFails_ThrowsException() { //Arrange const string specificationId = "spec-id"; IEnumerable <DatasetSpecificationRelationshipViewModel> relationships = new[] { new DatasetSpecificationRelationshipViewModel { Name = "Test Name" } }; IEnumerable <string> currentFieldDefinitionNames = new[] { "Test Field" }; ILogger logger = CreateLogger(); IEnumerable <Calculation> calculations = new[] { new Calculation { Current = new CalculationVersion { SourceCode = "return Datasets.TestName.TestField" } } }; ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); calculationsRepository .GetCalculationsBySpecificationId(Arg.Is(specificationId)) .Returns(calculations); calculationsRepository .UpdateCalculation(Arg.Any <Calculation>()) .Returns(HttpStatusCode.BadRequest); CalculationService calculationService = CreateCalculationService(logger: logger, calculationsRepository: calculationsRepository); //Act Func <Task> test = async() => await calculationService.ResetCalculationForFieldDefinitionChanges(relationships, specificationId, currentFieldDefinitionNames); //Assert test .Should() .ThrowExactly <InvalidOperationException>() .Which .Message .Should() .Be($"Update calculation returned status code 'BadRequest' instead of OK"); }
public async Task <IActionResult> UpdateBuildProjectRelationships(HttpRequest request) { request.Query.TryGetValue("specificationId", out Microsoft.Extensions.Primitives.StringValues specId); string specificationId = specId.FirstOrDefault(); if (string.IsNullOrWhiteSpace(specificationId)) { _logger.Error("No specification Id was provided to UpdateBuildProjectRelationships"); return(new BadRequestObjectResult("Null or empty specification Id provided")); } string json = await request.GetRawBodyStringAsync(); DatasetRelationshipSummary relationship = JsonConvert.DeserializeObject <DatasetRelationshipSummary>(json); if (relationship == null) { _logger.Error("A null relationship message was provided to UpdateBuildProjectRelationships"); return(new BadRequestObjectResult("Null relationship provided")); } BuildProject buildProject = await GetBuildProjectForSpecificationId(specificationId); IEnumerable <Models.Calcs.Calculation> calculations = await _calculationsRepository.GetCalculationsBySpecificationId(specificationId); buildProject.Build = _sourceCodeService.Compile(buildProject, calculations ?? Enumerable.Empty <Models.Calcs.Calculation>()); if (!_featureToggle.IsDynamicBuildProjectEnabled()) { await _buildProjectsRepositoryPolicy.ExecuteAsync(() => _buildProjectsRepository.UpdateBuildProject(buildProject)); } await _sourceCodeService.SaveAssembly(buildProject); return(new OkObjectResult(buildProject)); }
public override async Task Process(Message message) { Guard.ArgumentNotNull(message, nameof(message)); string specificationId = UserPropertyFrom(message, "specification-id"); IEnumerable <Calculation> calculations = (await _calculationRepositoryPolicy.ExecuteAsync(() => _calculationsRepository.GetCalculationsBySpecificationId(specificationId))) .ToArraySafe(); if (calculations.IsNullOrEmpty()) { string calcNotFoundMessage = $"No calculations found for specification id: {specificationId}"; _logger.Information(calcNotFoundMessage); return; } foreach (Calculation calculation in calculations) { calculation.Current.PublishStatus = Models.Versioning.PublishStatus.Approved; } ApiResponse <SpecModel.SpecificationSummary> specificationApiResponse = await _specificationsApiClientPolicy.ExecuteAsync(() => _specificationsApiClient.GetSpecificationSummaryById(specificationId)); if (!specificationApiResponse.StatusCode.IsSuccess() || specificationApiResponse.Content == null) { throw new NonRetriableException("Specification not found"); } SpecModel.SpecificationSummary specificationSummary = specificationApiResponse.Content; await _calculationRepositoryPolicy.ExecuteAsync(() => _calculationsRepository.UpdateCalculations(calculations)); await UpdateSearch(calculations, specificationSummary); IEnumerable <Task> tasks = calculations.Select(_ => UpdateCalculationInCache(_.ToResponseModel())); await TaskHelper.WhenAllAndThrow(tasks.ToArraySafe()); await UpdateCalculationsInCache(specificationSummary); }
public async Task IsCalculationNameValid_WhenSameCalculation_ThenReturnsOkResult() { // Arrange string specificationId = "spec1"; string calcName = "calc1"; string calcSpecId = "calc-1"; ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient(); specificationsApiClient .GetSpecificationSummaryById(Arg.Is(specificationId)) .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, new SpecModel.SpecificationSummary { Id = specificationId })); List <Calculation> existingCalcs = new List <Calculation> { new Calculation { Current = new CalculationVersion { Name = calcName, SourceCodeName = calcName }, Id = "calc-1" } }; ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); calculationsRepository .GetCalculationsBySpecificationId(Arg.Is(specificationId)) .Returns(existingCalcs); CalculationService service = CreateCalculationService(specificationsApiClient: specificationsApiClient, calculationsRepository: calculationsRepository); // Act IActionResult result = await service.IsCalculationNameValid(specificationId, calcName, calcSpecId); // Assert result .Should() .BeOfType <OkResult>(); }
public async Task IsCalculationNameValid_WhenSameCalculation_ThenReturnsOkResult() { // Arrange string specificationId = "spec1"; string calcName = "calc1"; string calcSpecId = "calc-spec-id-1"; ISpecificationRepository specificationRepository = CreateSpecificationRepository(); specificationRepository .GetSpecificationSummaryById(Arg.Is(specificationId)) .Returns(new SpecificationSummary { Id = specificationId }); List <Models.Calcs.Calculation> existingCalcs = new List <Models.Calcs.Calculation> { new Models.Calcs.Calculation { Name = calcName, SourceCodeName = calcName, Id = "calc-1", CalculationSpecification = new Common.Models.Reference { Id = calcSpecId } } }; ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); calculationsRepository .GetCalculationsBySpecificationId(Arg.Is(specificationId)) .Returns(existingCalcs); CalculationService service = CreateCalculationService(specificationRepository: specificationRepository, calculationsRepository: calculationsRepository); // Act IActionResult result = await service.IsCalculationNameValid(specificationId, calcName, calcSpecId); // Assert result .Should() .BeOfType <OkResult>(); }
public async Task UpdateCalculationsForSpecification_GivenModelHasChangedFundingPeriodsButCalcculationsCouldNotBeFound_LogsAndReturns() { //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 = "fp2" } }, Previous = new Models.Specs.SpecificationVersion { FundingPeriod = new Reference { Id = "fp1" } } }; string json = JsonConvert.SerializeObject(specificationVersionComparison); Message message = new Message(Encoding.UTF8.GetBytes(json)); ILogger logger = CreateLogger(); ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); calculationsRepository .GetCalculationsBySpecificationId(Arg.Is(specificationId)) .Returns((IEnumerable <Calculation>)null); CalculationService service = CreateCalculationService(calculationsRepository, logger); //Act await service.UpdateCalculationsForSpecification(message); //Assert logger .Received(1) .Information(Arg.Is($"No calculations found for specification id: {specificationId}")); }
public async Task CreateCalculation_GivenValidCalculationWithNullFundingStream_AndSavesLogs() { //Arrange Calculation calculation = CreateCalculation(); calculation.FundingStream = null; IEnumerable <Calculation> calculations = new[] { calculation }; IEnumerable <Models.Specs.Calculation> calculationSpecifications = new[] { new Models.Specs.Calculation { Id = calculation.CalculationSpecification.Id } }; string json = JsonConvert.SerializeObject(calculation); Message message = new Message(Encoding.UTF8.GetBytes(json)); message.UserProperties.Add("user-id", UserId); message.UserProperties.Add("user-name", Username); ICalculationsRepository repository = CreateCalculationsRepository(); repository .CreateDraftCalculation(Arg.Any <Calculation>()) .Returns(HttpStatusCode.Created); repository .GetCalculationsBySpecificationId(Arg.Is("any-spec-id")) .Returns(calculations); Models.Specs.SpecificationSummary specificationSummary = new Models.Specs.SpecificationSummary() { Id = calculation.SpecificationId, Name = "Test Spec Name", }; ISpecificationRepository specificationRepository = CreateSpecificationRepository(); specificationRepository .GetSpecificationSummaryById(Arg.Is(calculation.SpecificationId)) .Returns(specificationSummary); specificationRepository .GetCalculationSpecificationsForSpecification(Arg.Is(calculation.SpecificationId)) .Returns(calculationSpecifications); ILogger logger = CreateLogger(); ISearchRepository <CalculationIndex> searchRepository = CreateSearchRepository(); Build build = new Build { SourceFiles = new List <SourceFile> { new SourceFile() } }; ISourceCodeService sourceCodeService = CreateSourceCodeService(); sourceCodeService .Compile(Arg.Any <BuildProject>(), Arg.Any <IEnumerable <Models.Calcs.Calculation> >(), Arg.Any <CompilerOptions>()) .Returns(build); CalculationService service = CreateCalculationService( calculationsRepository: repository, logger: logger, searchRepository: searchRepository, specificationRepository: specificationRepository, sourceCodeService: sourceCodeService); //Act await service.CreateCalculation(message); //Assert logger .Received(1) .Information($"Calculation with id: {calculation.Id} was successfully saved to Cosmos Db"); await repository .Received(1) .CreateDraftCalculation(Arg.Is <Calculation>(m => m.Id == CalculationId && m.Current.PublishStatus == PublishStatus.Draft && m.Current.Author.Id == UserId && m.Current.Author.Name == Username && m.Current.Date.Date == DateTimeOffset.Now.Date && m.Current.Version == 1 && m.Current.DecimalPlaces == 6 )); await searchRepository .Received(1) .Index(Arg.Is <List <CalculationIndex> >( m => m.First().Id == CalculationId && m.First().Name == "Test Calc Name" && m.First().CalculationSpecificationId == "any-calc-id" && m.First().CalculationSpecificationName == "Test Calc Name" && m.First().SpecificationId == "any-spec-id" && m.First().SpecificationName == "Test Spec Name" && m.First().FundingPeriodId == "18/19" && m.First().FundingPeriodName == "2018/2019" && m.First().FundingStreamId == string.Empty && m.First().FundingStreamName == "No funding stream set" && m.First().AllocationLineId == "test-alloc-id" && m.First().AllocationLineName == "test-alloc-name" && m.First().PolicySpecificationIds.First() == "policy-id" && m.First().PolicySpecificationNames.First() == "policy-name" )); }
async public Task GetCurrentCalculationsForSpecification_WhenCalculationsNotFoundInCache_ThenCalculationsReturnedFromRepository() { //Arrange const string specificationId = "specid"; DateTimeOffset calc1DateTime = DateTimeOffset.Now; DateTimeOffset calc2DateTime = DateTimeOffset.Now; List <Calculation> calculations = new List <Calculation>() { new Calculation() { Id = "one", Current = new CalculationVersion() { SourceCode = "Return 10", PublishStatus = PublishStatus.Draft, Author = new Reference("userId", "User Name"), Version = 1, Date = calc1DateTime, Name = "Calculation Name", CalculationType = CalculationType.Template, }, SpecificationId = specificationId, }, new Calculation() { Id = "two", Current = new CalculationVersion() { SourceCode = "Return 50", PublishStatus = PublishStatus.Approved, Author = new Reference("userId", "User Name"), Version = 5, Date = calc2DateTime, Name = "Calculation Name Two", CalculationType = CalculationType.Template, }, SpecificationId = specificationId, } }; ILogger logger = CreateLogger(); ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); calculationsRepository .GetCalculationsBySpecificationId(Arg.Is(specificationId)) .Returns(calculations); ICacheProvider cacheProvider = CreateCacheProvider(); cacheProvider .GetAsync <List <CalculationResponseModel> >($"{CacheKeys.CurrentCalculationsForSpecification}{specificationId}") .Returns((List <CalculationResponseModel>)null); CalculationService service = CreateCalculationService(logger: logger, calculationsRepository: calculationsRepository, cacheProvider: cacheProvider); //Act IActionResult result = await service.GetCurrentCalculationsForSpecification(specificationId); //Assert result .Should() .BeOfType <OkObjectResult>() .Which .Value .Should() .BeAssignableTo <IEnumerable <CalculationResponseModel> >() .Which .Should() .Contain(m => m.Id == "one" && m.Name == "Calculation Name" && m.SourceCode == "Return 10" && m.CalculationType == CalculationType.Template && m.Version == 1 && m.LastUpdated == calc1DateTime && m.SpecificationId == specificationId && m.PublishStatus == PublishStatus.Draft) .And.Contain(m => m.Id == "two" && m.Name == "Calculation Name Two" && m.SourceCode == "Return 50" && m.CalculationType == CalculationType.Template && m.Version == 5 && m.LastUpdated == calc2DateTime && m.SpecificationId == specificationId && m.PublishStatus == PublishStatus.Approved); await calculationsRepository .Received(1) .GetCalculationsBySpecificationId(Arg.Is(specificationId)); await cacheProvider .Received(1) .GetAsync <List <CalculationResponseModel> >($"{CacheKeys.CurrentCalculationsForSpecification}{specificationId}"); }
async public Task GetCalculationSummariesForSpecification_WhenCalculationsNotFoundInCache_ThenCalculationsReturnedFromRepository() { // Arrange const string specificationId = "specid"; DateTimeOffset calc1DateTime = DateTimeOffset.Now; DateTimeOffset calc2DateTime = DateTimeOffset.Now; List <Calculation> calculations = new List <Calculation>() { new Calculation() { Id = "one", Name = "Calculation Name", Current = new CalculationVersion() { SourceCode = "Return 10", PublishStatus = PublishStatus.Draft, Author = new Reference("userId", "User Name"), Version = 1, Date = calc1DateTime, }, CalculationSpecification = new Reference("specId", "Calculation Specification ID"), CalculationType = CalculationType.Funding, FundingPeriod = new Reference("fp1", "Funding Period 1"), SpecificationId = specificationId, }, new Calculation() { Id = "two", Name = "Calculation Name Two", Current = new CalculationVersion() { SourceCode = "Return 50", PublishStatus = PublishStatus.Published, Author = new Reference("userId", "User Name"), Version = 5, Date = calc2DateTime, }, CalculationSpecification = new Reference("specId", "Calculation Specification ID"), CalculationType = CalculationType.Number, FundingPeriod = new Reference("fp2", "Funding Period Two"), SpecificationId = specificationId, } }; IQueryCollection queryStringValues = new QueryCollection(new Dictionary <string, StringValues> { { "specificationId", new StringValues(specificationId) } }); HttpRequest request = Substitute.For <HttpRequest>(); request .Query .Returns(queryStringValues); ILogger logger = CreateLogger(); ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); calculationsRepository .GetCalculationsBySpecificationId(Arg.Is(specificationId)) .Returns(calculations); ICacheProvider cacheProvider = CreateCacheProvider(); cacheProvider .GetAsync <List <CalculationSummaryModel> >($"{CacheKeys.CalculationsSummariesForSpecification}{specificationId}") .Returns((List <CalculationSummaryModel>)null); CalculationService service = CreateCalculationService(logger: logger, calculationsRepository: calculationsRepository, cacheProvider: cacheProvider); // Act IActionResult result = await service.GetCalculationSummariesForSpecification(request); // Assert result .Should() .BeOfType <OkObjectResult>() .Which .Value .Should() .BeAssignableTo <IEnumerable <CalculationSummaryModel> >() .Which .ShouldAllBeEquivalentTo(new List <CalculationSummaryModel>() { new CalculationSummaryModel() { Id = "one", Name = "Calculation Name", CalculationType = CalculationType.Funding }, new CalculationSummaryModel() { Id = "two", Name = "Calculation Name Two", CalculationType = CalculationType.Number, } }); await calculationsRepository .Received(1) .GetCalculationsBySpecificationId(Arg.Is(specificationId)); await cacheProvider .Received(1) .GetAsync <List <CalculationSummaryModel> >($"{CacheKeys.CalculationsSummariesForSpecification}{specificationId}"); }
public async Task ResetCalculationForFieldDefinitionChanges_GivenCalculationRequiresReset_UpdatesCalculationsAndDeletesAssembly() { //Arrange const string specificationId = "spec-id"; IEnumerable <DatasetSpecificationRelationshipViewModel> relationships = new[] { new DatasetSpecificationRelationshipViewModel { Name = "Test Name" } }; IEnumerable <string> currentFieldDefinitionNames = new[] { "Test Field" }; ILogger logger = CreateLogger(); CalculationVersion calculationVersion = new CalculationVersion { SourceCode = "return Datasets.TestName.TestField", Date = DateTimeOffset.Now }; IEnumerable <Calculation> calculations = new[] { new Calculation { Current = calculationVersion, SpecificationId = specificationId, CalculationSpecification = new Reference("calc-spac-id", "calc spec name"), FundingPeriod = new Reference("fp1", "fp 1"), FundingStream = new Reference("fs1", "fs 1"), Policies = new List <Reference> { new Reference { Id = "policy-1", Name = "policy 1" } } } }; IEnumerable <Models.Specs.Calculation> calculationSpecifications = new[] { new Models.Specs.Calculation { Id = "calc-spec-id" } }; BuildProject buildProject = new BuildProject(); Build build = new Build { SourceFiles = new List <SourceFile>() }; Models.Specs.SpecificationSummary specificationSummary = new Models.Specs.SpecificationSummary(); ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); calculationsRepository .GetCalculationsBySpecificationId(Arg.Is(specificationId)) .Returns(calculations); calculationsRepository .UpdateCalculation(Arg.Any <Calculation>()) .Returns(HttpStatusCode.OK); ISpecificationRepository specificationRepository = CreateSpecificationRepository(); specificationRepository .GetCalculationSpecificationsForSpecification(Arg.Is(specificationId)) .Returns(calculationSpecifications); specificationRepository .GetSpecificationSummaryById(Arg.Is(specificationId)) .Returns(specificationSummary); IBuildProjectsService buildProjectsService = CreateBuildProjectsService(); buildProjectsService .GetBuildProjectForSpecificationId(Arg.Is(specificationId)) .Returns(buildProject); ISourceCodeService sourceCodeService = CreateSourceCodeService(); sourceCodeService .Compile(Arg.Is(buildProject), Arg.Any <IEnumerable <Calculation> >(), Arg.Any <CompilerOptions>()) .Returns(build); IVersionRepository <CalculationVersion> calculationVersionRepository = CreateCalculationVersionRepository(); calculationVersionRepository .CreateVersion(Arg.Any <CalculationVersion>(), Arg.Any <CalculationVersion>()) .Returns(calculationVersion); ICacheProvider cacheProvider = CreateCacheProvider(); CalculationService calculationService = CreateCalculationService( logger: logger, calculationsRepository: calculationsRepository, specificationRepository: specificationRepository, buildProjectsService: buildProjectsService, sourceCodeService: sourceCodeService, calculationVersionRepository: calculationVersionRepository, cacheProvider: cacheProvider); //Act await calculationService.ResetCalculationForFieldDefinitionChanges(relationships, specificationId, currentFieldDefinitionNames); //Assert await sourceCodeService .Received(1) .DeleteAssembly(Arg.Is(specificationId)); await cacheProvider .Received(1) .RemoveAsync <List <DatasetSchemaRelationshipModel> >(Arg.Is($"{CacheKeys.DatasetRelationshipFieldsForSpecification}{specificationId}")); }
public async Task UpdateCalculationCodeOnCalculationChange_WhenCalculationsFoundReferencingCalculationToBeUpdated_ThenSourceCodeUpdated() { // Arrange ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient(); IVersionRepository <CalculationVersion> versionRepository = CreateCalculationVersionRepository(); IBuildProjectsService buildProjectsService = CreateBuildProjectsService(); ISourceCodeService sourceCodeService = CreateSourceCodeService(); CalculationService service = CreateCalculationService(calculationsRepository: calculationsRepository, specificationsApiClient: specificationsApiClient, calculationVersionRepository: versionRepository, buildProjectsService: buildProjectsService, sourceCodeService: sourceCodeService); const string specificationId = "specId"; const string calculationId = "updatedCalc"; const string originalCodeUpdate = @"Dim test as OriginalNameOptions? OriginalNameOptions.enumName Return Calculations.OriginalName()"; const string newCodeUpdated = @"Dim test as CalculationToUpdateOptions? CalculationToUpdateOptions.enumName Return Calculations.CalculationToUpdate()"; const string originalCodeIgnore = "Return 10"; const string fundingStreamId = "fundingstreamid"; CalculationVersionComparisonModel comparison = new CalculationVersionComparisonModel() { CalculationId = calculationId, CurrentName = "Calculation To Update", PreviousName = "Original Name", SpecificationId = specificationId, CalculationDataType = CalculationDataType.Enum, Namespace = "Calculations" }; Reference user = new Reference("userId", "User Name"); List <Calculation> calculations = new List <Calculation> { new Calculation { Id = calculationId, SpecificationId = specificationId, FundingStreamId = fundingStreamId, Current = new CalculationVersion { SourceCode = originalCodeIgnore, Name = "Calculation to Update", CalculationType = CalculationType.Template, Description = "Calculation Description", DataType = CalculationDataType.Enum } }, new Calculation { Id = "referenceCalc", SpecificationId = specificationId, FundingStreamId = fundingStreamId, Current = new CalculationVersion { SourceCode = originalCodeUpdate, Name = "Calling Calculation To Update", CalculationType = CalculationType.Template, Description = "Calculation Description" } } }; calculationsRepository .GetCalculationsBySpecificationId(Arg.Is(specificationId)) .Returns(calculations); calculationsRepository .UpdateCalculation(Arg.Any <Calculation>()) .Returns(HttpStatusCode.OK); calculationsRepository .GetCalculationById(Arg.Is(calculations[0].Id)) .Returns(calculations[0]); SpecModel.SpecificationSummary specification = new SpecModel.SpecificationSummary() { Id = specificationId, Name = "Specification Name", FundingStreams = new [] { new Reference(fundingStreamId, "fundingStreamName"), } }; specificationsApiClient .GetSpecificationSummaryById(Arg.Is(specificationId)) .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specification)); CalculationVersion calculationVersion = new CalculationVersion { SourceCode = newCodeUpdated, Version = 2 }; versionRepository .CreateVersion(Arg.Is <CalculationVersion>(_ => _.SourceCode == newCodeUpdated), Arg.Any <CalculationVersion>()) .Returns(calculationVersion); buildProjectsService .GetBuildProjectForSpecificationId(specificationId) .Returns(new BuildProject()); sourceCodeService .Compile(Arg.Any <BuildProject>(), Arg.Any <IEnumerable <Calculation> >(), Arg.Any <CompilerOptions>()) .Returns(new Build()); // Act IEnumerable <Calculation> updatedCalculations = await service.UpdateCalculationCodeOnCalculationChange(comparison, user); // Assert updatedCalculations .Should() .HaveCount(1); Calculation calculation = updatedCalculations.Single(); calculation.Current.SourceCode .Should() .Be(newCodeUpdated); calculation.Current.Version .Should() .Be(2); calculation.Id .Should() .Be("referenceCalc"); }
public async Task UpdateCalculationCodeOnCalculationChange_WhenNoCalculationsFoundReferencingCalculationToBeUpdated_ThenNoCalculationsUpdated() { // Arrange ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient(); IVersionRepository <CalculationVersion> versionRepository = CreateCalculationVersionRepository(); CalculationService service = CreateCalculationService(calculationsRepository: calculationsRepository, specificationsApiClient: specificationsApiClient, calculationVersionRepository: versionRepository); const string specificationId = "specId"; const string calculationId = "updatedCalc"; CalculationVersionComparisonModel comparison = new CalculationVersionComparisonModel() { CalculationId = calculationId, CurrentName = "Calculation To Update", PreviousName = "Original Name", SpecificationId = specificationId, }; Reference user = new Reference("userId", "User Name"); List <Calculation> calculations = new List <Calculation>() { new Calculation { Id = calculationId, SpecificationId = specificationId, Current = new CalculationVersion { SourceCode = "Return 10", Name = "Calculation to Update", CalculationType = CalculationType.Template, Description = "Calculation Description" } }, new Calculation { Id = "referenceCalc", SpecificationId = specificationId, Current = new CalculationVersion { SourceCode = "Return 50", Name = "Calling Calculation To Update", CalculationType = CalculationType.Template, Description = "Calculation Description", } } }; calculationsRepository .GetCalculationsBySpecificationId(Arg.Is(specificationId)) .Returns(calculations); calculationsRepository .UpdateCalculation(Arg.Any <Calculation>()) .Returns(HttpStatusCode.OK); calculationsRepository .GetCalculationById(Arg.Is(calculations[0].Id)) .Returns(calculations[0]); SpecModel.SpecificationSummary specification = new SpecModel.SpecificationSummary() { Id = specificationId, Name = "Specification Name", }; specificationsApiClient .GetSpecificationSummaryById(Arg.Is(specificationId)) .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specification)); CalculationVersion calculationVersion = new CalculationVersion { SourceCode = "Return CalculationToUpdate()", Version = 2 }; versionRepository .CreateVersion(Arg.Any <CalculationVersion>(), Arg.Any <CalculationVersion>()) .Returns(calculationVersion); // Act IEnumerable <Calculation> updatedCalculations = await service.UpdateCalculationCodeOnCalculationChange(comparison, user); // Assert updatedCalculations .Should() .HaveCount(0); }
public async Task <IActionResult> Compile(PreviewRequest previewRequest) { if (previewRequest == null) { _logger.Error("A null preview request was supplied"); return(new BadRequestObjectResult("A null preview request was provided")); } FluentValidation.Results.ValidationResult validationResult = await _previewRequestValidator.ValidateAsync(previewRequest); if (!validationResult.IsValid) { string errors = string.Join(";", validationResult.Errors.Select(m => m.ErrorMessage).ToArraySafe()); _logger.Warning($"The preview request failed to validate with errors: {errors}"); return(new BadRequestObjectResult("The preview request failed to validate")); } Task <IEnumerable <Calculation> > calculationsTask = _calculationsRepository.GetCalculationsBySpecificationId(previewRequest.SpecificationId); Task <BuildProject> buildProjectTask = _buildProjectsService.GetBuildProjectForSpecificationId(previewRequest.SpecificationId); Task <CompilerOptions> compilerOptionsTask = _calculationsRepository.GetCompilerOptions(previewRequest.SpecificationId); await TaskHelper.WhenAllAndThrow(calculationsTask, buildProjectTask, compilerOptionsTask); BuildProject buildProject = buildProjectTask.Result; if (buildProject == null) { _logger.Warning($"Build project for specification '{previewRequest.SpecificationId}' could not be found"); return(new PreconditionFailedResult($"Build project for specification '{previewRequest.SpecificationId}' could not be found")); } List <Calculation> allSpecCalculations = new List <Calculation>(calculationsTask.Result); Calculation calculationToPreview = allSpecCalculations.SingleOrDefault(m => m.Id == previewRequest.CalculationId); if (calculationToPreview == null) { calculationToPreview = GenerateTemporaryCalculationForPreview(previewRequest); allSpecCalculations.Add(calculationToPreview); } else { ApplyChangesToCurrentCalculationForPreview(previewRequest, calculationToPreview); } Build buildForDatasetAggregationCheck = await CheckDatasetValidAggregations(previewRequest); if (buildForDatasetAggregationCheck != null && buildForDatasetAggregationCheck.CompilerMessages.Any(m => m.Severity == Severity.Error)) { PreviewResponse response = new PreviewResponse { Calculation = calculationToPreview.ToResponseModel(), CompilerOutput = buildForDatasetAggregationCheck }; return(new OkObjectResult(response)); } CompilerOptions compilerOptions = compilerOptionsTask.Result ?? new CompilerOptions { SpecificationId = buildProject.SpecificationId }; return(await GenerateAndCompile(buildProject, calculationToPreview, allSpecCalculations, compilerOptions, previewRequest)); }
public async Task ProcessChanges_GivenChangeModelWithAggregableFieldChanges_CallsResetCalculationForFieldDefinitionChanges(FieldDefinitionChangeType fieldDefinitionChangeType) { //Arrange string definitionId = "df-id-1"; string specificationId = "spec-1"; IEnumerable <Calculation> calculations = new[] { new Calculation { Current = new CalculationVersion { SourceCode = "return Sum(Datasets.TestRelationship.TestField1)" } } }; DatasetDefinitionChanges datasetDefinitionChanges = new DatasetDefinitionChanges { Id = definitionId, }; FieldDefinitionChanges fieldDefinitionChanges = new FieldDefinitionChanges { FieldDefinition = new FieldDefinition { Id = "field1" }, ExistingFieldDefinition = new FieldDefinition { Name = "test field 1" } }; fieldDefinitionChanges.ChangeTypes.Add(fieldDefinitionChangeType); TableDefinitionChanges tableDefinitionChanges = new TableDefinitionChanges(); tableDefinitionChanges.FieldChanges.Add(fieldDefinitionChanges); datasetDefinitionChanges.TableDefinitionChanges.Add(tableDefinitionChanges); string json = JsonConvert.SerializeObject(datasetDefinitionChanges); Message message = new Message(Encoding.UTF8.GetBytes(json)); ILogger logger = CreateLogger(); IEnumerable <string> relationshipSpecificationIds = new[] { specificationId }; IEnumerable <Common.ApiClient.DataSets.Models.DatasetSpecificationRelationshipViewModel> relationshipViewModels = new[] { new Common.ApiClient.DataSets.Models.DatasetSpecificationRelationshipViewModel { Name = "Test Relationship" } }; IEnumerable <DatasetSpecificationRelationshipViewModel> relationshipViewModel = new[] { new DatasetSpecificationRelationshipViewModel { Name = "Test Relationship" } }; IDatasetsApiClient datasetRepository = CreateDatasetRepository(); datasetRepository .GetSpecificationIdsForRelationshipDefinitionId(Arg.Is(definitionId)) .Returns(new ApiResponse <IEnumerable <string> >(HttpStatusCode.OK, relationshipSpecificationIds)); datasetRepository .GetCurrentRelationshipsBySpecificationIdAndDatasetDefinitionId(Arg.Is(specificationId), Arg.Is(definitionId)) .Returns(new ApiResponse <IEnumerable <Common.ApiClient.DataSets.Models.DatasetSpecificationRelationshipViewModel> >(HttpStatusCode.OK, relationshipViewModels)); ICalculationService calculationService = CreateCalculationService(); ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); calculationsRepository .GetCalculationsBySpecificationId(Arg.Is(specificationId)) .Returns(calculations); IMapper mapper = CreateMapper(); DatasetDefinitionFieldChangesProcessor processor = CreateProcessor( logger: logger, datasetRepository: datasetRepository, calculationService: calculationService, calculationsRepository: calculationsRepository, mapper: mapper); //Act await processor.Run(message); //Assert await calculationService .Received(1) .ResetCalculationForFieldDefinitionChanges(Arg.Is <IEnumerable <Models.Datasets.ViewModels.DatasetSpecificationRelationshipViewModel> >(_ => _.First().Id == relationshipViewModel.First().Id), Arg.Is(specificationId), Arg.Is <IEnumerable <string> >(m => m.First() == "test field 1")); }
private async Task ProcessFieldChanges(string datasetDefinitionId, IEnumerable <FieldDefinitionChanges> fieldChanges, IEnumerable <string> relationshipSpecificationIds) { Guard.IsNullOrWhiteSpace(datasetDefinitionId, nameof(datasetDefinitionId)); Guard.ArgumentNotNull(fieldChanges, nameof(fieldChanges)); Guard.ArgumentNotNull(relationshipSpecificationIds, nameof(relationshipSpecificationIds)); IEnumerable <IGrouping <string, FieldDefinitionChanges> > groupedFieldChanges = fieldChanges.GroupBy(f => f.FieldDefinition.Id); IList <FieldDefinitionChanges> fieldDefinitionChanges = new List <FieldDefinitionChanges>(); bool shouldResetCalculation = false; foreach (IGrouping <string, FieldDefinitionChanges> grouping in groupedFieldChanges) { FieldDefinitionChanges fieldDefinitionChange = grouping.FirstOrDefault(m => m.ChangeTypes.Any( c => c == FieldDefinitionChangeType.FieldName) || m.RequiresRemap); if (fieldDefinitionChange != null) { fieldDefinitionChanges.Add(fieldDefinitionChange); } shouldResetCalculation = true; } if (!shouldResetCalculation) { return; } foreach (string specificationId in relationshipSpecificationIds) { IEnumerable <DatasetSpecificationRelationshipViewModel> relationships = await _datasetRepositoryPolicy.ExecuteAsync(() => _datasetRepository.GetCurrentRelationshipsBySpecificationIdAndDatasetDefinitionId(specificationId, datasetDefinitionId)); if (relationships.IsNullOrEmpty()) { throw new RetriableException($"No relationships found for specificationId '{specificationId}' and dataset definition id '{datasetDefinitionId}'"); } IEnumerable <Calculation> calculations = (await _calculationsRepositoryPolicy.ExecuteAsync(() => _calculationsRepository.GetCalculationsBySpecificationId(specificationId))).ToList(); IEnumerable <string> aggregateParameters = calculations.SelectMany(m => SourceCodeHelpers.GetDatasetAggregateFunctionParameters(m.Current.SourceCode)); HashSet <string> fieldNames = new HashSet <string>(); foreach (FieldDefinitionChanges changes in fieldDefinitionChanges) { //Check if only aggregable changes if (!changes.ChangeTypes.Contains(FieldDefinitionChangeType.FieldType) && !changes.ChangeTypes.Contains(FieldDefinitionChangeType.FieldName)) { foreach (DatasetSpecificationRelationshipViewModel datasetSpecificationRelationshipViewModel in relationships) { if (aggregateParameters.Contains($"Datasets.{VisualBasicTypeGenerator.GenerateIdentifier(datasetSpecificationRelationshipViewModel.Name)}.{VisualBasicTypeGenerator.GenerateIdentifier(changes.ExistingFieldDefinition.Name)}")) { fieldNames.Add(changes.ExistingFieldDefinition.Name); } } } else { fieldNames.Add(changes.ExistingFieldDefinition.Name); } } if (fieldNames.Any()) { await _calculationService.ResetCalculationForFieldDefinitionChanges(relationships, specificationId, fieldNames); } } }
public async Task UpdateCalculationCodeOnCalculationSpecificationChange_WhenNoCalculationsFoundReferencingCalculationToBeUpdated_ThenNoCalculationsUpdated() { // Arrange ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); ISpecificationRepository specificationRepository = CreateSpecificationRepository(); IVersionRepository <CalculationVersion> versionRepository = CreateCalculationVersionRepository(); ICalculationCodeReferenceUpdate calculationCodeReferenceUpdate = FakeCalculationCodeReferenceUpdate(); CalculationService service = CreateCalculationService(calculationsRepository: calculationsRepository, specificationRepository: specificationRepository, calculationVersionRepository: versionRepository, calculationCodeReferenceUpdate: calculationCodeReferenceUpdate); const string specificationId = "specId"; const string calculationId = "updatedCalc"; Models.Specs.CalculationVersionComparisonModel comparison = new Models.Specs.CalculationVersionComparisonModel() { CalculationId = calculationId, Current = new Models.Specs.Calculation { Id = "calcSpec1", Name = "Calculation to update", CalculationType = Models.Specs.CalculationType.Funding, }, Previous = new Models.Specs.Calculation { Id = "calcSpec1", Name = "Original Name", CalculationType = Models.Specs.CalculationType.Funding, }, SpecificationId = specificationId, }; Reference user = new Reference("userId", "User Name"); List <Calculation> calculations = new List <Calculation>() { new Calculation { Id = calculationId, Name = "Calculation to Update", SpecificationId = specificationId, FundingPeriod = new Reference("fp1", "Funding Period"), CalculationSpecification = new Reference("calcSpec1", "Calculation to Update"), CalculationType = CalculationType.Funding, Description = "Calculation Description", BuildProjectId = "bpC1", Policies = new List <Reference>(), Current = new CalculationVersion { SourceCode = "Return 10", DecimalPlaces = 6, } }, new Calculation { Id = "referenceCalc", Name = "Calling Calculation To Update", SpecificationId = specificationId, FundingPeriod = new Reference("fp1", "Funding Period"), CalculationSpecification = new Reference("calcSpec1", "Calculation to Update"), CalculationType = CalculationType.Funding, Description = "Calculation Description", BuildProjectId = "bpC1", Policies = new List <Reference>(), Current = new CalculationVersion { SourceCode = "Return 50", DecimalPlaces = 6, } } }; calculationsRepository .GetCalculationsBySpecificationId(Arg.Is(specificationId)) .Returns(calculations); calculationsRepository .UpdateCalculation(Arg.Any <Calculation>()) .Returns(HttpStatusCode.OK); calculationsRepository .GetCalculationById(Arg.Is(calculations[0].Id)) .Returns(calculations[0]); Models.Specs.SpecificationSummary specification = new Models.Specs.SpecificationSummary() { Id = specificationId, Name = "Specification Name", }; specificationRepository .GetSpecificationSummaryById(Arg.Is(specificationId)) .Returns(specification); CalculationVersion calculationVersion = new CalculationVersion { SourceCode = "Return CalculationToUpdate()", Version = 2 }; versionRepository .CreateVersion(Arg.Any <CalculationVersion>(), Arg.Any <CalculationVersion>()) .Returns(calculationVersion); // Act IEnumerable <Calculation> updatedCalculations = await service.UpdateCalculationCodeOnCalculationSpecificationChange(comparison, user); // Assert updatedCalculations .Should() .HaveCount(0); calculationCodeReferenceUpdate .Received(calculations.Count) .ReplaceSourceCodeReferences(Arg.Any <string>(), VisualBasicTypeGenerator.GenerateIdentifier(comparison.Previous.Name), VisualBasicTypeGenerator.GenerateIdentifier(comparison.Current.Name)); foreach (Calculation calculation in calculations) { calculationCodeReferenceUpdate .Received(1) .ReplaceSourceCodeReferences(calculation.Current.SourceCode, VisualBasicTypeGenerator.GenerateIdentifier(comparison.Previous.Name), VisualBasicTypeGenerator.GenerateIdentifier(comparison.Current.Name)); } }
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 UpdateCalculationsForSpecification_GivenModelHasChangedPolicyNameAndGraphEnabled_SavesChangesEnsuresJobCreated() { // Arrange const string specificationId = "spec-id"; SpecificationVersionComparisonModel specificationVersionComparison = new SpecificationVersionComparisonModel() { Id = specificationId, Current = new SpecificationVersion { FundingPeriod = new Reference { Id = "fp1" }, Name = "any-name" }, Previous = new SpecificationVersion { FundingPeriod = new Reference { Id = "fp1" } } }; 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", Id = "any-id", Current = new CalculationVersion { Author = new Reference(UserId, Username), Date = DateTimeOffset.Now, PublishStatus = PublishStatus.Draft, SourceCode = "source code", Version = 1, Name = "any name", CalculationType = CalculationType.Template, } } }; 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(); IJobManagement jobManagement = CreateJobManagement(); jobManagement .QueueJob(Arg.Is <JobCreateModel>(_ => _.JobDefinitionId == JobConstants.DefinitionNames.GenerateGraphAndInstructAllocationJob)) .Returns(new Job { Id = "job-id-1", JobDefinitionId = JobConstants.DefinitionNames.GenerateGraphAndInstructAllocationJob }); jobManagement .QueueJob(Arg.Is <JobCreateModel>(_ => _.JobDefinitionId == JobConstants.DefinitionNames.ReIndexSpecificationCalculationRelationshipsJob)) .Returns(new Job { Id = "job-id-2", JobDefinitionId = JobConstants.DefinitionNames.ReIndexSpecificationCalculationRelationshipsJob }); IMapper mapper = Substitute.For <IMapper>(); ICalculationsFeatureFlag calculationsFeatureFlag = CreateCalculationsFeatureFlag(true); CalculationService service = CreateCalculationService( calculationsRepository, logger, buildProjectsService: buildProjectsService, searchRepository: searchRepository, jobManagement: jobManagement, calculationsFeatureFlag: calculationsFeatureFlag); // Act await service.Run(message); // Assert await jobManagement .Received(1) .QueueJob(Arg.Is <JobCreateModel>( m => m.InvokerUserDisplayName == Username && m.InvokerUserId == UserId && m.JobDefinitionId == JobConstants.DefinitionNames.ReIndexSpecificationCalculationRelationshipsJob && m.Properties["specification-id"] == specificationId && m.Trigger.EntityId == specificationId && m.Trigger.EntityType == "Specification" && m.Trigger.Message == $"Updating calculations for specification: '{specificationId}'" )); await jobManagement .Received(1) .QueueJob(Arg.Is <JobCreateModel>( m => m.InvokerUserDisplayName == Username && m.InvokerUserId == UserId && m.JobDefinitionId == JobConstants.DefinitionNames.GenerateGraphAndInstructAllocationJob && m.Properties["specification-id"] == specificationId && m.Trigger.EntityId == specificationId && m.Trigger.EntityType == "Specification" && m.Trigger.Message == $"Updating calculations for specification: '{specificationId}'" )); logger .Received(1) .Information(Arg.Is($"New job of type '{JobConstants.DefinitionNames.GenerateGraphAndInstructAllocationJob}' created with id: 'job-id-1'")); logger .Received(1) .Information(Arg.Is($"New job of type '{JobConstants.DefinitionNames.ReIndexSpecificationCalculationRelationshipsJob}' created with id: 'job-id-2'")); }
public async Task UpdateCalculationsForSpecification_GivenModelHasChangedFundingStreams_SetsTheAllocationLineAndFundingStreamToNull() { //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", FundingStreams = new List <Reference> { new Reference { Id = "fs2" } } }, Previous = new Models.Specs.SpecificationVersion { FundingPeriod = new Reference { Id = "fp1" }, FundingStreams = new List <Reference> { new Reference { Id = "fs1" } } } }; string json = JsonConvert.SerializeObject(specificationVersionComparison); Message message = new Message(Encoding.UTF8.GetBytes(json)); 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("fs1", "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>() } }; BuildProject buildProject = new BuildProject(); 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" }); CalculationService service = CreateCalculationService(calculationsRepository, logger, buildProjectsService: buildProjectsService, searchRepository: searchRepository, jobsApiClient: jobsApiClient); //Act await service.UpdateCalculationsForSpecification(message); //Assert calcs .First() .FundingStream .Should() .BeNull(); calcs .First() .AllocationLine .Should() .BeNull(); await searchRepository .Received(1) .Index(Arg.Is <IEnumerable <CalculationIndex> >(c => c.First().Id == calcs.First().Id&& c.First().FundingStreamId == "" && c.First().FundingStreamName == "No funding stream set")); }
public async Task CreateCalculation_GivenValidCalculation_ButFailedToSave_DoesNotUpdateSearch() { //Arrange Calculation calculation = CreateCalculation(); IEnumerable <Calculation> calculations = new[] { calculation }; IEnumerable <Models.Specs.Calculation> calculationSpecifications = new[] { new Models.Specs.Calculation { Id = calculation.CalculationSpecification.Id } }; string json = JsonConvert.SerializeObject(calculation); Message message = new Message(Encoding.UTF8.GetBytes(json)); message.UserProperties.Add("user-id", UserId); message.UserProperties.Add("user-name", Username); ICalculationsRepository repository = CreateCalculationsRepository(); repository .CreateDraftCalculation(Arg.Any <Calculation>()) .Returns(HttpStatusCode.BadRequest); repository .GetCalculationsBySpecificationId(Arg.Is("any-spec-id")) .Returns(calculations); ILogger logger = CreateLogger(); ISearchRepository <CalculationIndex> searchRepository = CreateSearchRepository(); Models.Specs.SpecificationSummary specificationSummary = new Models.Specs.SpecificationSummary() { Id = calculation.SpecificationId, Name = "Test Spec Name", }; ISpecificationRepository specificationRepository = CreateSpecificationRepository(); specificationRepository .GetSpecificationSummaryById(Arg.Is(calculation.SpecificationId)) .Returns(specificationSummary); specificationRepository .GetCalculationSpecificationsForSpecification(Arg.Is(calculation.SpecificationId)) .Returns(calculationSpecifications); CalculationService service = CreateCalculationService(calculationsRepository: repository, logger: logger, searchRepository: searchRepository, specificationRepository: specificationRepository); //Act await service.CreateCalculation(message); //Assert logger .Received(1) .Error($"There was problem creating a new calculation with id {calculation.Id} in Cosmos Db with status code 400"); await repository .Received(1) .CreateDraftCalculation(Arg.Is <Calculation>(m => m.Id == CalculationId && m.Current.PublishStatus == PublishStatus.Draft && m.Current.Author.Id == UserId && m.Current.Author.Name == Username && m.Current.Date.Date == DateTimeOffset.Now.Date && m.Current.Version == 1 && m.Current.DecimalPlaces == 6 )); await searchRepository .DidNotReceive() .Index(Arg.Any <List <CalculationIndex> >()); }
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 ProcessChanges_GivenChangeModelWithAggregableFieldChangesButNoAggregateParanetersFound_DoesNotResetCalculationForFieldDefinitionChanges(FieldDefinitionChangeType fieldDefinitionChangeType) { //Arrange string definitionId = "df-id-1"; string specificationId = "spec-1"; IEnumerable <Calculation> calculations = new[] { new Calculation { Current = new CalculationVersion { SourceCode = "return 2 + Datasets.TestRelationship.TestField1" } } }; DatasetDefinitionChanges datasetDefinitionChanges = new DatasetDefinitionChanges { Id = definitionId, }; FieldDefinitionChanges fieldDefinitionChanges = new FieldDefinitionChanges { FieldDefinition = new FieldDefinition { Id = "field1" }, ExistingFieldDefinition = new FieldDefinition { Name = "test field 1" } }; fieldDefinitionChanges.ChangeTypes.Add(fieldDefinitionChangeType); TableDefinitionChanges tableDefinitionChanges = new TableDefinitionChanges(); tableDefinitionChanges.FieldChanges.Add(fieldDefinitionChanges); datasetDefinitionChanges.TableDefinitionChanges.Add(tableDefinitionChanges); string json = JsonConvert.SerializeObject(datasetDefinitionChanges); Message message = new Message(Encoding.UTF8.GetBytes(json)); ILogger logger = CreateLogger(); IEnumerable <string> relationshipSpecificationIds = new[] { specificationId }; IEnumerable <DatasetSpecificationRelationshipViewModel> relationshipViewModels = new[] { new DatasetSpecificationRelationshipViewModel { Name = "Test Relationship" } }; IDatasetRepository datasetRepository = CreateDatasetRepository(); datasetRepository .GetRelationshipSpecificationIdsByDatasetDefinitionId(Arg.Is(definitionId)) .Returns(relationshipSpecificationIds); datasetRepository .GetCurrentRelationshipsBySpecificationIdAndDatasetDefinitionId(Arg.Is(specificationId), Arg.Is(definitionId)) .Returns(relationshipViewModels); ICalculationService calculationService = CreateCalculationService(); ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); calculationsRepository .GetCalculationsBySpecificationId(Arg.Is(specificationId)) .Returns(calculations); DatasetDefinitionFieldChangesProcessor processor = CreateProcessor( logger: logger, datasetRepository: datasetRepository, calculationService: calculationService, calculationsRepository: calculationsRepository); //Act await processor.ProcessChanges(message); //Assert await calculationService .DidNotReceive() .ResetCalculationForFieldDefinitionChanges(Arg.Any <IEnumerable <DatasetSpecificationRelationshipViewModel> >(), Arg.Any <string>(), Arg.Any <IEnumerable <string> >()); }
private async Task EnsureAllExistingCalculationsModified(TemplateMappingItem[] mappingsWithCalculations, SpecificationSummary specification, string correlationId, Reference author, IDictionary <uint, Calculation> uniqueTemplateCalculations, int startingItemCount) { if (!mappingsWithCalculations.Any()) { return; } bool madeChanges = false; IEnumerable <Models.Calcs.Calculation> existingCalculations = await _calculationsRepositoryPolicy.ExecuteAsync(() => _calculationsRepository.GetCalculationsBySpecificationId(specification.Id)); IDictionary <string, Models.Calcs.Calculation> existingCalculationsById = existingCalculations.ToDictionary(_ => _.Id); // filter out unchanged calculations (Calculation TemplateCalculation, Models.Calcs.Calculation Calculation)[] AllCalculation = GetAllCalculations(mappingsWithCalculations, uniqueTemplateCalculations, existingCalculationsById).ToArray();
private void AndTheCalculations(IEnumerable <Calculation> calculations) { _calculationsRepository.GetCalculationsBySpecificationId(_specificationId) .Returns(calculations); }