private IEnumerable <StatementSyntax> CreateNamespaceFunctionPointers(IEnumerable <Calculation> calculations)
        {
            foreach (Calculation calculation in calculations)
            {
                StringBuilder sourceCode = new StringBuilder();

                CalculationVersion currentCalculationVersion = calculation.Current;

                if (string.IsNullOrWhiteSpace(currentCalculationVersion.SourceCodeName))
                {
                    throw new InvalidOperationException($"Calculation source code name is not populated for calc {calculation.Id}");
                }

                // Add attributes to describe calculation and calculation specification
                sourceCode.AppendLine($"<Calculation(Id := \"{calculation.Id}\", Name := \"{calculation.Name}\", CalculationDataType := \"{calculation.Current.DataType}\")>");

                // Add attribute for calculation description
                if (currentCalculationVersion.Description.IsNotNullOrWhitespace())
                {
                    sourceCode.AppendLine($"<Description(Description := \"{currentCalculationVersion.Description?.Replace("\"", "\"\"")}\")>");
                }

                sourceCode.AppendLine($"Public {currentCalculationVersion.SourceCodeName} As Func(Of {GetDataType(calculation.Current.DataType, calculation.Name)}) = Nothing");

                sourceCode.AppendLine();

                yield return(ParseSourceCodeToStatementSyntax(sourceCode));
            }
        }
Example #2
0
        public void CalculationVersionCalculationViewModel_MapsAsExpected(CalculationVersion source, CalculationViewModel destination)
        {
            // Arrange
            var     config = new MapperConfiguration(c => c.AddProfile <FrontEndMappingProfile>());
            IMapper mapper = new Mapper(config);

            //Act
            var result = mapper.Map(source, destination);

            //Assert
            result?.Description
            .Should().Be(destination?.Description);

            if (result?.LastModified != null)
            {
                result.LastModified
                .Should().Be(destination.LastModified);
            }

            result?.LastModifiedByName
            .Should().Be(destination?.LastModifiedByName);

            result?.SourceCode
            .Should().Be(destination?.SourceCode);

            result?.CalculationType
            .Should().Be(destination?.CalculationType);

            result?.PublishStatus
            .Should().Be(destination?.PublishStatus);

            //Final deep check - do this last so we can get descriptive errors before, but this'll catch anything else that's slipped through
            JsonConvert.SerializeObject(destination)
            .Should().Be(JsonConvert.SerializeObject(result));
        }
Example #3
0
        public async Task GetCalculationByName_GivenSpecificationExistsAndCalculationExists_ReturnsSuccess()
        {
            //Arrange
            CalculationVersion calculationVersion = new CalculationVersion
            {
                Name = CalculationName,
                Date = new DateTimeOffset(2013, 1, 2, 3, 4, 55, TimeSpan.Zero),
            };

            Calculation calc = new Calculation {
                Current = calculationVersion
            };

            CalculationGetModel model = new CalculationGetModel
            {
                SpecificationId = SpecificationId,
                Name            = CalculationName
            };

            ILogger logger = CreateLogger();

            ICalculationsRepository calculationsRepository = CreateCalculationsRepository();

            calculationsRepository
            .GetCalculationsBySpecificationIdAndCalculationName(Arg.Is(SpecificationId), Arg.Is(CalculationName))
            .Returns(calc);

            CalculationService service = CreateCalculationService(calculationsRepository: calculationsRepository, logger: logger);

            //Act
            IActionResult result = await service.GetCalculationByName(model);

            //Assert
            result
            .Should()
            .BeOfType <OkObjectResult>()
            .Which
            .Value
            .Should()
            .BeEquivalentTo(new CalculationResponseModel()
            {
                Name        = CalculationName,
                SourceCode  = "Return 0",
                LastUpdated = new DateTimeOffset(2013, 1, 2, 3, 4, 55, TimeSpan.Zero),
            });
        }
 public static CalculationVersionResponseModel ToResponseModel(this CalculationVersion version)
 {
     return(new CalculationVersionResponseModel()
     {
         Author = version.Author,
         CalculationId = version.Id,
         CalculationType = version.CalculationType,
         LastUpdated = version.Date,
         Name = version.Name,
         Namespace = version.Namespace,
         PublishStatus = version.PublishStatus,
         SourceCode = version.SourceCode,
         SourceCodeName = version.SourceCodeName,
         Version = version.Version,
         WasTemplateCalculation = version.WasTemplateCalculation,
         Description = version.Description,
     });
 }
        public async Task EditCalculationStatus_GivenNewStatusOfUpdated_UpdatesSearchReturnsOK()
        {
            //Arrange
            EditStatusModel CalculationEditStatusModel = new EditStatusModel
            {
                PublishStatus = PublishStatus.Updated
            };

            string json = JsonConvert.SerializeObject(CalculationEditStatusModel);

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

            HttpContext context = Substitute.For <HttpContext>();

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

            IQueryCollection queryStringValues = new QueryCollection(new Dictionary <string, StringValues>
            {
                { "calculationId", new StringValues(CalculationId) },
            });

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

            request
            .HttpContext
            .Returns(context);

            ILogger logger = CreateLogger();

            Calculation calculation = CreateCalculation();

            calculation.Current.PublishStatus = PublishStatus.Approved;

            CalculationVersion calculationVersion = calculation.Current.Clone() as CalculationVersion;

            calculationVersion.PublishStatus = PublishStatus.Updated;

            ICalculationsRepository CalculationsRepository = CreateCalculationsRepository();

            CalculationsRepository
            .GetCalculationById(Arg.Is(CalculationId))
            .Returns(calculation);

            CalculationsRepository
            .UpdateCalculation(Arg.Any <Calculation>())
            .Returns(HttpStatusCode.OK);


            ISearchRepository <CalculationIndex> searchRepository = CreateSearchRepository();

            Models.Specs.SpecificationSummary specificationSummary = new Models.Specs.SpecificationSummary();

            ISpecificationRepository specificationRepository = CreateSpecificationRepository();

            specificationRepository
            .GetSpecificationSummaryById(Arg.Is(calculation.SpecificationId))
            .Returns(specificationSummary);

            IVersionRepository <CalculationVersion> versionRepository = CreateCalculationVersionRepository();

            versionRepository
            .CreateVersion(Arg.Any <CalculationVersion>(), Arg.Any <CalculationVersion>())
            .Returns(calculationVersion);

            Build build = new Build
            {
                SourceFiles = new List <SourceFile> {
                    new SourceFile()
                }
            };

            BuildProject buildProject = new BuildProject();

            IBuildProjectsService buildProjectsService = CreateBuildProjectsService();

            buildProjectsService
            .GetBuildProjectForSpecificationId(Arg.Is(calculation.SpecificationId))
            .Returns(buildProject);

            ISourceCodeService sourceCodeService = CreateSourceCodeService();

            sourceCodeService
            .Compile(Arg.Any <BuildProject>(), Arg.Any <IEnumerable <Models.Calcs.Calculation> >(), Arg.Any <CompilerOptions>())
            .Returns(build);

            CalculationService service = CreateCalculationService(
                logger: logger, calculationsRepository: CalculationsRepository, searchRepository: searchRepository,
                specificationRepository: specificationRepository, calculationVersionRepository: versionRepository,
                sourceCodeService: sourceCodeService, buildProjectsService: buildProjectsService);

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

            //Arrange
            result
            .Should()
            .BeOfType <OkObjectResult>()
            .Which
            .Value
            .Should()
            .BeOfType <PublishStatusResultModel>()
            .Which
            .PublishStatus
            .Should()
            .Be(PublishStatus.Updated);

            calculation
            .Current
            .PublishStatus
            .Should()
            .Be(PublishStatus.Updated);

            await
            searchRepository
            .Received(1)
            .Index(Arg.Is <IEnumerable <CalculationIndex> >(m => m.First().Status == "Updated"));
        }
        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 OnGet_WhencalculationVersionsListReturned()
        {
            // Arrange
            ICalculationsApiClient calcsClient = Substitute.For <ICalculationsApiClient>();
            ISpecsApiClient        specsClient = Substitute.For <ISpecsApiClient>();
            IMapper mapper = MappingHelper.CreateFrontEndMapper();

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

            Calculation expectedCalculation = new Calculation()
            {
                CalculationSpecification = new Reference("1", "Calc spec"),
                Id   = "2",
                Name = "Specs Calculation",
                FundingPeriodName = "2018/19",
                SpecificationId   = "1",
                PublishStatus     = PublishStatus.Draft,
                LastModifiedBy    = new Reference("1", "Matt Vallily"),
                SourceCode        = "Public Function GetProductResult(rid As String) As Decimal 'change to As String if text product  Dim result As Decimal = 0 'change to As String if text product     Dim P04_Learners As Decimal = products.1819_Additional_Funding.P04_Learner_Numbers     Dim P03_Rate As Decimal = products.1819_Additional_Funding.P03_Maths_Top_Up_Rate     result = P03_Rate * P04_learners     Return result End Function",
                Version           = 4
            };

            IEnumerable <int> versions = new List <int> {
                1, 2
            };
            string calculationId = "1";

            Clients.SpecsClient.Models.CalculationCurrentVersion specCalculation = new Clients.SpecsClient.Models.CalculationCurrentVersion()
            {
                Id             = "1",
                Name           = "Test spec",
                Description    = "Test description",
                AllocationLine = new Reference("1", "Test Allocation")
            };

            // build two versions for feeding the left and right panel
            CalculationVersion calver1 = new CalculationVersion()
            {
                DecimalPlaces = 2,
                Version       = "1",
                Date          = new DateTime(2018, 1, 1, 10, 23, 34),
                Author        = new Reference("1", "Clifford"),
                Status        = "Draft",
                SourceCode    = "Test"
            };

            CalculationVersion calver2 = new CalculationVersion()
            {
                DecimalPlaces = 2,
                Version       = "2",
                Date          = new DateTime(2018, 1, 1, 10, 23, 34),
                Author        = new Reference("2", "Clifford"),
                Status        = "Draft",
                SourceCode    = "Test"
            };

            IEnumerable <CalculationVersion> calcVerArray = new List <CalculationVersion> {
                calver1, calver2
            };

            calcVerArray.AsEnumerable();
            calcsClient
            .GetCalculationById(calculationId)
            .Returns(new ApiResponse <Clients.CalcsClient.Models.Calculation>(System.Net.HttpStatusCode.OK, expectedCalculation));

            specsClient
            .GetCalculationById("1", calculationId)
            .Returns(new ApiResponse <CalculateFunding.Frontend.Clients.SpecsClient.Models.CalculationCurrentVersion>(System.Net.HttpStatusCode.OK, specCalculation));  // CalculateFunding.Frontend.Clients.SpecsClient.Models

            calcsClient
            .GetMultipleVersionsByCalculationId(versions, calculationId)
            .Returns(new ApiResponse <IEnumerable <CalculationVersion> >(System.Net.HttpStatusCode.OK, calcVerArray));

            DiffCalculationModel diffCalcPageModel = new DiffCalculationModel(specsClient, calcsClient, mapper);

            // Act
            IActionResult result = await diffCalcPageModel.OnGet(versions, calculationId);

            // Assert
            result.Should().NotBeNull();

            diffCalcPageModel.RightCalculationDiffModel.Version.Should().Be(calver2.Version);
            diffCalcPageModel.LeftCalcualationDiffModel.Version.Should().Be(calver1.Version);
        }
        public async Task OnGet_WhenCalculationExists_ThenCalculationReturned()
        {
            // Arrange
            ICalculationsApiClient calcsClient = Substitute.For <ICalculationsApiClient>();
            ISpecsApiClient        specsClient = Substitute.For <ISpecsApiClient>();
            IMapper mapper = MappingHelper.CreateFrontEndMapper();

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

            string calculationId = "1";

            Calculation expectedCalculation = new Calculation()
            {
                Id   = "1",
                Name = "Specs Calculation",
                FundingPeriodName        = "2018/19",
                SpecificationId          = "3",
                PublishStatus            = PublishStatus.Draft,
                LastModifiedBy           = new Reference("1", "Matt Vallily"),
                SourceCode               = "Public Function GetProductResult(rid As String) As Decimal 'change to As String if text product     Dim result As Decimal = 0 'change to As String if text product     Dim P04_Learners As Decimal = products.1819_Additional_Funding.P04_Learner_Numbers     Dim P03_Rate As Decimal = products.1819_Additional_Funding.P03_Maths_Top_Up_Rate     result = P03_Rate * P04_learners     Return result End Function",
                Version                  = 4,
                CalculationSpecification = new Reference("1", "Test Spec")
            };

            calcsClient
            .GetCalculationById(calculationId)
            .Returns(new ApiResponse <Calculation>(System.Net.HttpStatusCode.OK, expectedCalculation));

            Clients.SpecsClient.Models.CalculationCurrentVersion specCalculation = new Clients.SpecsClient.Models.CalculationCurrentVersion()
            {
                Id             = "1",
                Name           = "Test spec",
                Description    = "Test description",
                AllocationLine = new Reference("1", "Test Allocation")
            };

            specsClient
            .GetCalculationById(expectedCalculation.SpecificationId, calculationId)
            .Returns(new ApiResponse <Clients.SpecsClient.Models.CalculationCurrentVersion>(System.Net.HttpStatusCode.OK, specCalculation));

            CalculationVersion calcsVersion1 = new CalculationVersion()
            {
                Status        = "Draft",
                Version       = "1",
                DecimalPlaces = 4,
                Date          = new DateTime(2018, 1, 1, 12, 34, 45, 03),
                Author        = new Reference("1", "Matt Vallily"),
                SourceCode    = "Public Function GetProductResult(rid As String) As Decimal 'change to As String if text product     Dim result As Decimal = 0 'change to As String if text product     Dim P04_Learners As Decimal = products.1819_Additional_Funding.P04_Learner_Numbers     Dim P03_Rate As Decimal = products.1819_Additional_Funding.P03_Maths_Top_Up_Rate     result = P03_Rate * P04_learners     Return result End Function",
            };

            CalculationVersion calcsVersion2 = new CalculationVersion()
            {
                Status        = "Draft",
                Version       = "2",
                DecimalPlaces = 4,
                Date          = new DateTime(2018, 1, 2, 12, 34, 45, 03),
                Author        = new Reference("1", "Matt Vallily"),
                SourceCode    = "Public Function GetProductResult(rid As String) As Decimal 'change to As String if text product     Dim result As Decimal = 0 'change to As String if text product     Dim P04_Learners As Decimal = products.1819_Additional_Funding.P04_Learner_Numbers     Dim P03_Rate As Decimal = products.1819_Additional_Funding.P03_Maths_Top_Up_Rate     result = P03_Rate * P04_learners     Return result End Function",
            };

            CalculationVersion calcsVersion3 = new CalculationVersion()
            {
                Status        = "Draft",
                Version       = "3",
                DecimalPlaces = 4,
                Date          = new DateTime(2018, 1, 3, 12, 34, 45, 03),
                Author        = new Reference("1", "Matt Vallily"),
                SourceCode    = "Public Function GetProductResult(rid As String) As Decimal 'change to As String if text product     Dim result As Decimal = 0 'change to As String if text product     Dim P04_Learners As Decimal = products.1819_Additional_Funding.P04_Learner_Numbers     Dim P03_Rate As Decimal = products.1819_Additional_Funding.P03_Maths_Top_Up_Rate     result = P03_Rate * P04_learners     Return result End Function",
            };

            IEnumerable <CalculationVersion> calculationVersion = new List <CalculationVersion>()
            {
                calcsVersion1,
                calcsVersion2,
                calcsVersion3
            };

            calcsClient
            .GetAllVersionsByCalculationId(calculationId)
            .Returns(new ApiResponse <IEnumerable <CalculationVersion> >(System.Net.HttpStatusCode.OK, calculationVersion));

            ComparePageModel comparePageModel = new ComparePageModel(specsClient, calcsClient, mapper);

            // Act
            IActionResult result = await comparePageModel.OnGet(calculationId);

            // Assert
            result.Should().NotBeNull();

            comparePageModel.Calculation.Should().NotBeNull();

            comparePageModel.Calculation.Description.Should().Be(specCalculation.Description);

            comparePageModel.Calculation.Name.Should().Be(expectedCalculation.Name);

            comparePageModel.Calculations.Select(f => f.Version).Should().BeInDescendingOrder();

            comparePageModel.Calculations.Should().HaveCount(3);

            ViewModels.Calculations.CalculationVersionViewModel firstCalculation = comparePageModel.Calculations.First();

            firstCalculation.Version.Should().Be(calcsVersion3.Version);
        }
Example #9
0
        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);
        }
Example #10
0
        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");
        }
Example #11
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);
        }
        public async Task EditCalculationStatus_GivenNewStatusOfUpdated_UpdatesSearchReturnsOK()
        {
            //Arrange
            EditStatusModel CalculationEditStatusModel = new EditStatusModel
            {
                PublishStatus = PublishStatus.Updated
            };

            ILogger logger = CreateLogger();

            Calculation calculation = CreateCalculation();

            calculation.Current.PublishStatus = PublishStatus.Approved;

            CalculationVersion calculationVersion = calculation.Current.Clone() as CalculationVersion;

            calculationVersion.PublishStatus = PublishStatus.Updated;

            ICalculationsRepository CalculationsRepository = CreateCalculationsRepository();

            CalculationsRepository
            .GetCalculationById(Arg.Is(CalculationId))
            .Returns(calculation);

            CalculationsRepository
            .UpdateCalculation(Arg.Any <Calculation>())
            .Returns(HttpStatusCode.OK);


            ISearchRepository <CalculationIndex> searchRepository = CreateSearchRepository();

            SpecModel.SpecificationSummary specificationSummary = new SpecModel.SpecificationSummary()
            {
                Id             = calculation.SpecificationId,
                Name           = "spec name",
                FundingStreams = new[]
                {
                    new Reference(calculation.FundingStreamId, "funding stream name")
                }
            };

            ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient();

            specificationsApiClient
            .GetSpecificationSummaryById(Arg.Is(calculation.SpecificationId))
            .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specificationSummary));

            IVersionRepository <CalculationVersion> versionRepository = CreateCalculationVersionRepository();

            versionRepository
            .CreateVersion(Arg.Any <CalculationVersion>(), Arg.Any <CalculationVersion>())
            .Returns(calculationVersion);

            Build build = new Build
            {
                SourceFiles = new List <SourceFile> {
                    new SourceFile()
                }
            };

            BuildProject buildProject = new BuildProject();

            IBuildProjectsService buildProjectsService = CreateBuildProjectsService();

            buildProjectsService
            .GetBuildProjectForSpecificationId(Arg.Is(calculation.SpecificationId))
            .Returns(buildProject);

            ISourceCodeService sourceCodeService = CreateSourceCodeService();

            sourceCodeService
            .Compile(Arg.Any <BuildProject>(), Arg.Any <IEnumerable <Models.Calcs.Calculation> >(), Arg.Any <CompilerOptions>())
            .Returns(build);

            CalculationService service = CreateCalculationService(
                logger: logger, calculationsRepository: CalculationsRepository, searchRepository: searchRepository,
                specificationsApiClient: specificationsApiClient, calculationVersionRepository: versionRepository,
                sourceCodeService: sourceCodeService, buildProjectsService: buildProjectsService);

            //Act
            IActionResult result = await service.UpdateCalculationStatus(CalculationId, CalculationEditStatusModel);

            //Arrange
            result
            .Should()
            .BeOfType <OkObjectResult>()
            .Which
            .Value
            .Should()
            .BeOfType <PublishStatusResultModel>()
            .Which
            .PublishStatus
            .Should()
            .Be(PublishStatus.Updated);

            calculation
            .Current
            .PublishStatus
            .Should()
            .Be(PublishStatus.Updated);
        }
Example #13
0
        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));
            }
        }
Example #14
0
        public CalculationBuilder WithCurrentVersion(CalculationVersion calculationVersion)
        {
            _calculationVersion = calculationVersion;

            return(this);
        }
Example #15
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
                });
            }
        }