Пример #1
0
        public async Task CreateAdditionalCalculation_GivenCreateJobReturnsNull_ReturnsInternalServerError()
        {
            //Arrange
            CalculationCreateModel model = CreateCalculationCreateModel();

            Reference author = CreateAuthor();

            ICalculationsRepository calculationsRepository = CreateCalculationsRepository();

            calculationsRepository
            .CreateDraftCalculation(Arg.Any <Calculation>())
            .Returns(HttpStatusCode.OK);

            IVersionRepository <CalculationVersion> versionRepository = CreateCalculationVersionRepository();

            ISearchRepository <CalculationIndex> searchRepository = CreateSearchRepository();

            IJobManagement jobManagement = CreateJobManagement();

            jobManagement
            .QueueJob(Arg.Any <JobCreateModel>())
            .Returns((Job)null);

            ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient();

            specificationsApiClient
            .GetSpecificationSummaryById(Arg.Is(SpecificationId))
            .Returns(new ApiResponse <SpecificationSummary>(
                         HttpStatusCode.OK,
                         new SpecificationSummary {
                Id = SpecificationId
            }
                         ));

            ILogger logger = CreateLogger();

            CalculationService calculationService = CreateCalculationService(
                calculationsRepository: calculationsRepository,
                calculationVersionRepository: versionRepository,
                searchRepository: searchRepository,
                jobManagement: jobManagement,
                logger: logger,
                specificationsApiClient: specificationsApiClient);

            //Act
            IActionResult result = await calculationService.CreateAdditionalCalculation(SpecificationId, model, author, CorrelationId);

            //Assert
            result
            .Should()
            .BeOfType <InternalServerErrorResult>()
            .Which
            .Value
            .Should()
            .Be($"Failed to create job of type '{JobConstants.DefinitionNames.CreateInstructAllocationJob}' on specification '{SpecificationId}'");

            logger
            .Received(1)
            .Error(Arg.Is($"Failed to create job of type '{JobConstants.DefinitionNames.CreateInstructAllocationJob}' on specification '{SpecificationId}'"));
        }
Пример #2
0
        public async Task CreateAdditionalCalculation_GivenSavingDraftCalcFails_ReturnsInternalServerErrorResult()
        {
            //Arrange
            string correlationId = "any-id";

            CalculationCreateModel model = CreateCalculationCreateModel();

            Reference author = CreateAuthor();

            ICalculationsRepository calculationsRepository = CreateCalculationsRepository();

            calculationsRepository
            .CreateDraftCalculation(Arg.Any <Calculation>())
            .Returns(HttpStatusCode.BadRequest);

            ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient();

            specificationsApiClient
            .GetSpecificationSummaryById(Arg.Is(SpecificationId))
            .Returns(new ApiResponse <SpecificationSummary>(
                         HttpStatusCode.OK,
                         new SpecificationSummary {
                Id = SpecificationId
            }
                         ));

            ILogger logger = CreateLogger();

            CalculationService calculationService = CreateCalculationService(logger: logger, calculationsRepository: calculationsRepository, specificationsApiClient: specificationsApiClient);

            string errorMessage = $"There was problem creating a new calculation with name {CalculationName} in Cosmos Db with status code 400";

            //Act
            IActionResult result = await calculationService.CreateAdditionalCalculation(SpecificationId, model, author, correlationId);

            //Assert
            result
            .Should()
            .BeAssignableTo <InternalServerErrorResult>()
            .Which
            .Value
            .Should()
            .Be(errorMessage);

            logger
            .Received(1)
            .Error(Arg.Is(errorMessage));
        }
        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 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"
                       ));
        }
Пример #5
0
        public async Task CreateAdditionalCalculation_GivenCalcSaves_ReturnsOKObjectResult()
        {
            //Arrange
            string cacheKey = $"{CacheKeys.CalculationsMetadataForSpecification}{SpecificationId}";

            CalculationCreateModel model = CreateCalculationCreateModel();

            Reference author = CreateAuthor();

            ICalculationsRepository calculationsRepository = CreateCalculationsRepository();

            calculationsRepository
            .CreateDraftCalculation(Arg.Any <Calculation>())
            .Returns(HttpStatusCode.OK);

            IVersionRepository <CalculationVersion> versionRepository = CreateCalculationVersionRepository();

            ISearchRepository <CalculationIndex> searchRepository = CreateSearchRepository();

            IJobManagement jobManagement = CreateJobManagement();

            jobManagement
            .QueueJob(Arg.Any <JobCreateModel>())
            .Returns(new Job {
                Id = "job-id-1"
            });

            ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient();

            specificationsApiClient
            .GetSpecificationSummaryById(Arg.Is(SpecificationId))
            .Returns(new ApiResponse <SpecificationSummary>(
                         HttpStatusCode.OK,
                         new SpecificationSummary {
                Id            = SpecificationId,
                FundingPeriod = new FundingPeriod {
                    Id = FundingPeriodId
                }
            }
                         ));

            ILogger logger = CreateLogger();

            ICacheProvider cacheProvider = CreateCacheProvider();

            ICodeContextCache codeContextCache = Substitute.For <ICodeContextCache>();

            IResultsApiClient resultsApiClient = CreateResultsApiClient();

            CalculationService calculationService = CreateCalculationService(
                calculationsRepository: calculationsRepository,
                calculationVersionRepository: versionRepository,
                searchRepository: searchRepository,
                jobManagement: jobManagement,
                logger: logger,
                cacheProvider: cacheProvider,
                specificationsApiClient: specificationsApiClient,
                codeContextCache: codeContextCache,
                resultsApiClient: resultsApiClient);

            IEnumerable <CalculationIndex> indexedCalculations = null;

            await
            searchRepository
            .Index(Arg.Do <IEnumerable <CalculationIndex> >(m =>
                                                            indexedCalculations = m
                                                            ));

            CalculationVersion savedCalculationVersion = null;

            await
            versionRepository
            .SaveVersion(Arg.Do <CalculationVersion>(m => savedCalculationVersion = m));

            //Act
            IActionResult result = await calculationService.CreateAdditionalCalculation(SpecificationId, model, author, CorrelationId);

            //Assert
            result
            .Should()
            .BeAssignableTo <OkObjectResult>();

            Calculation calculation = (result as OkObjectResult).Value as Calculation;

            await
            jobManagement
            .Received(1)
            .QueueJob(Arg.Is <JobCreateModel>(
                          m =>
                          m.InvokerUserDisplayName == Username &&
                          m.InvokerUserId == UserId &&
                          m.JobDefinitionId == JobConstants.DefinitionNames.CreateInstructAllocationJob &&
                          m.Properties["specification-id"] == SpecificationId
                          ));

            logger
            .Received(1)
            .Information(Arg.Is($"New job of type '{JobConstants.DefinitionNames.CreateInstructAllocationJob}' created with id: 'job-id-1'"));


            await
            versionRepository
            .Received(1)
            .SaveVersion(Arg.Is <CalculationVersion>(m =>
                                                     m.PublishStatus == Models.Versioning.PublishStatus.Draft &&
                                                     m.Author.Id == UserId &&
                                                     m.Author.Name == Username &&
                                                     m.Date.Date == DateTimeOffset.Now.Date &&
                                                     m.Version == 1 &&
                                                     m.SourceCode == model.SourceCode &&
                                                     m.Description == model.Description &&
                                                     m.ValueType == model.ValueType &&
                                                     m.CalculationType == CalculationType.Additional &&
                                                     m.WasTemplateCalculation == false &&
                                                     m.Namespace == CalculationNamespace.Additional &&
                                                     m.Name == model.Name &&
                                                     m.SourceCodeName == new VisualBasicTypeIdentifierGenerator().GenerateIdentifier(model.Name) &&
                                                     m.DataType == CalculationDataType.Decimal
                                                     ));


            await searchRepository
            .Received(1)
            .Index(Arg.Any <IEnumerable <CalculationIndex> >());

            indexedCalculations
            .Should()
            .BeEquivalentTo(new List <CalculationIndex>()
            {
                new CalculationIndex()
                {
                    CalculationType   = "Additional",
                    Description       = "test description",
                    FundingStreamId   = "fs-1",
                    FundingStreamName = model.FundingStreamName,
                    Id                     = model.Id,
                    Name                   = model.Name,
                    Namespace              = "Additional",
                    SpecificationId        = "spec-id-1",
                    SpecificationName      = "spec-id-1_specificationName",
                    Status                 = "Draft",
                    ValueType              = "Currency",
                    WasTemplateCalculation = false,
                    LastUpdatedDate        = savedCalculationVersion.Date,
                }
            });

            //!string.IsNullOrWhiteSpace(m.First().Id) &&
            //m.First().Name == model.Name &&
            //m.First().SpecificationId == SpecificationId &&
            //m.First().SpecificationName == model.SpecificationName &&
            //m.First().ValueType == model.ValueType.ToString() &&
            //m.First().CalculationType == CalculationType.Additional.ToString() &&
            //m.First().Namespace == CalculationNamespace.Additional.ToString() &&
            //m.First().FundingStreamId == model.FundingStreamId &&
            //m.First().FundingStreamName == model.FundingStreamName &&
            //m.First().WasTemplateCalculation == false &&
            //m.First().Description == model.Description &&
            //m.First().Status == calculation.Current.PublishStatus.ToString()

            await
            cacheProvider
            .Received(1)
            .RemoveAsync <List <CalculationMetadata> >(Arg.Is(cacheKey));

            await codeContextCache
            .Received(1)
            .QueueCodeContextCacheUpdate(SpecificationId);
        }
Пример #6
0
        public async Task <CreateCalculationResponse> CreateCalculation(string specificationId,
                                                                        CalculationCreateModel model,
                                                                        CalculationNamespace calculationNamespace,
                                                                        CalculationType calculationType,
                                                                        Reference author,
                                                                        string correlationId,
                                                                        CalculationDataType calculationDataType = CalculationDataType.Decimal,
                                                                        bool initiateCalcRun = true,
                                                                        IEnumerable <string> allowedEnumTypeValues = null)
        {
            Guard.ArgumentNotNull(model, nameof(model));
            Guard.ArgumentNotNull(author, nameof(author));

            if (string.IsNullOrWhiteSpace(model.Id))
            {
                model.Id = Guid.NewGuid().ToString();
            }

            model.SpecificationId = specificationId;
            model.CalculationType = calculationType;

            ValidationResult validationResult = await _calculationCreateModelValidator.ValidateAsync(model);

            if (!validationResult.IsValid)
            {
                return(new CreateCalculationResponse
                {
                    ValidationResult = validationResult,
                    ErrorType = CreateCalculationErrorType.InvalidRequest
                });
            }

            Calculation calculation = new Calculation
            {
                Id = model.Id,
                FundingStreamId = model.FundingStreamId,
                SpecificationId = model.SpecificationId
            };

            CalculationVersion calculationVersion = new CalculationVersion
            {
                CalculationId          = calculation.Id,
                PublishStatus          = PublishStatus.Draft,
                Author                 = author,
                Date                   = DateTimeOffset.Now.ToLocalTime(),
                Version                = 1,
                SourceCode             = model.SourceCode,
                Description            = model.Description,
                ValueType              = model.ValueType.Value,
                CalculationType        = calculationType,
                WasTemplateCalculation = false,
                Namespace              = calculationNamespace,
                Name                   = model.Name,
                DataType               = calculationDataType,
                AllowedEnumTypeValues  = allowedEnumTypeValues != null ? new List <string>(allowedEnumTypeValues) : Enumerable.Empty <string>()
            };

            calculation.Current = calculationVersion;

            bool?nameValidResult = await _calculationNameInUseCheck.IsCalculationNameInUse(calculation.SpecificationId, calculation.Name, null);

            if (nameValidResult == true)
            {
                string error =
                    $"Calculation with the same generated source code name already exists in this specification. Calculation Name {calculation.Name} and Specification {calculation.SpecificationId}";

                _logger.Error(error);

                return(new CreateCalculationResponse
                {
                    ErrorMessage = error,
                    ErrorType = CreateCalculationErrorType.InvalidRequest
                });
            }

            calculation.Current.SourceCodeName = _typeIdentifierGenerator.GenerateIdentifier(calculation.Name);

            HttpStatusCode result = await _calculationRepositoryPolicy.ExecuteAsync(() => _calculationsRepository.CreateDraftCalculation(calculation));

            if (result.IsSuccess())
            {
                await _calculationVersionsRepositoryPolicy.ExecuteAsync(() => _calculationVersionRepository.SaveVersion(calculationVersion));

                await UpdateSearch(calculation, model.SpecificationName, model.FundingStreamName);

                string cacheKey = $"{CacheKeys.CalculationsMetadataForSpecification}{specificationId}";

                await _cachePolicy.ExecuteAsync(() => _cacheProvider.RemoveAsync <List <CalculationMetadata> >(cacheKey));

                if (!initiateCalcRun)
                {
                    return(new CreateCalculationResponse
                    {
                        Succeeded = true,
                        Calculation = calculation
                    });
                }

                try
                {
                    Job job = await SendInstructAllocationsToJobService(calculation.SpecificationId, author.Id, author.Name, new Trigger
                    {
                        EntityId   = calculation.Id,
                        EntityType = nameof(Calculation),
                        Message    = $"Saving calculation: '{calculation.Id}' for specification: '{calculation.SpecificationId}'"
                    }, correlationId);

                    if (job != null)
                    {
                        _logger.Information($"New job of type '{JobConstants.DefinitionNames.CreateInstructAllocationJob}' created with id: '{job.Id}'");

                        return(new CreateCalculationResponse
                        {
                            Succeeded = true,
                            Calculation = calculation
                        });
                    }
                    else
                    {
                        string errorMessage = $"Failed to create job of type '{JobConstants.DefinitionNames.CreateInstructAllocationJob}' on specification '{calculation.SpecificationId}'";

                        _logger.Error(errorMessage);

                        return(new CreateCalculationResponse
                        {
                            ErrorType = CreateCalculationErrorType.Exception,
                            ErrorMessage = errorMessage
                        });
                    }
                }
                catch (Exception ex)
                {
                    return(new CreateCalculationResponse
                    {
                        ErrorMessage = ex.Message,
                        ErrorType = CreateCalculationErrorType.Exception
                    });
                }
            }
            else
            {
                string errorMessage = $"There was problem creating a new calculation with name {calculation.Name} in Cosmos Db with status code {(int)result}";

                _logger.Error(errorMessage);

                return(new CreateCalculationResponse
                {
                    ErrorMessage = errorMessage,
                    ErrorType = CreateCalculationErrorType.Exception
                });
            }
        }