public void GenerateCalculations_GiveEnumCalculationType() { Calculation _1619 = NewCalculation(_ => _.WithFundingStream(NewReference(rf => rf.WithId("1619"))) .WithName("MethodologyType") .WithValueType(CalculationValueType.String) .WithDataType(CalculationDataType.Enum) .WithAllowedEnumTypeValues(new[] { "Type1", "Type2", "Type3" }) .WithSourceCodeName("One") .WithCalculationNamespaceType(CalculationNamespace.Template) .WithSourceCode("return Nothing")); FundingLine _1619fl = NewFundingLine(_ => _.WithCalculations(new[] { NewFundingLineCalculation(_ => _.WithId(1) .WithCalculationNamespaceType(CalculationNamespace.Template)) }) .WithId(2) .WithName("Two") .WithSourceCodeName("Two") .WithNamespace("1619")); IDictionary <string, Funding> fundingLines = new Dictionary <string, Funding> { { "1619", NewFunding(_ => _.WithFundingLines(new[] { _1619fl })) } }; IEnumerable <SourceFile> results = new CalculationTypeGenerator(new CompilerOptions(), _fundingLineRoundingSettings.Object).GenerateCalcs(new[] { _1619 }, fundingLines).ToList(); results.Should().HaveCount(1); results.First().SourceCode.Should().Contain("Public Enum MethodologyTypeOptions"); results.First().SourceCode.Should().Contain("Public One As Func(Of MethodologyTypeOptions?) = Nothing"); results.First().SourceCode.Should().Contain("Return If(String.IsNullOrWhiteSpace(existingCalculationResultString), Nothing, CType([Enum].Parse(GetType(MethodologyTypeOptions), existingCalculationResultString), MethodologyTypeOptions?))"); results.First().SourceCode.Should().Contain($"calculationContext.DictionaryStringValues.Add(\"{_1619.Id}\", executedUserCodeCalculationResult?.ToString())"); }
public void GenerateCalculations_GivenSpaceInEnumReturnsValidEnumCalculationType() { Calculation _1619 = NewCalculation(_ => _.WithFundingStream(NewReference(rf => rf.WithId("1619"))) .WithName("MethodologyType") .WithValueType(CalculationValueType.String) .WithDataType(CalculationDataType.Enum) .WithAllowedEnumTypeValues(new[] { "Type1 ", "Type2", "Type3" }) .WithSourceCodeName("One") .WithCalculationNamespaceType(CalculationNamespace.Template) .WithSourceCode("return Nothing")); FundingLine _1619fl = NewFundingLine(_ => _.WithCalculations(new[] { NewFundingLineCalculation(_ => _.WithId(1) .WithCalculationNamespaceType(CalculationNamespace.Template)) }) .WithId(2) .WithName("Two") .WithSourceCodeName("Two") .WithNamespace("1619")); IDictionary <string, Funding> fundingLines = new Dictionary <string, Funding> { { "1619", NewFunding(_ => _.WithFundingLines(new[] { _1619fl })) } }; IEnumerable <SourceFile> results = new CalculationTypeGenerator(new CompilerOptions(), _fundingLineRoundingSettings.Object).GenerateCalcs(new[] { _1619 }, fundingLines).ToList(); results.Should().HaveCount(1); results.First().SourceCode.Should().Contain("Enum MethodologyTypeOptions"); results.First().SourceCode.Should().Contain("Public One As Func(Of MethodologyTypeOptions?) = Nothing"); }
public void GenerateCalculations_GivenEnumCalculation_ThenEnumCreated() { Calculation _1619 = NewCalculation(_ => _.WithFundingStream(NewReference(rf => rf.WithId("1619"))) .WithSourceCodeName("One") .WithCalculationNamespaceType(CalculationNamespace.Template) .WithSourceCode("return 456") .WithName("EnumName") .WithDataType(CalculationDataType.Enum) .WithAllowedEnumTypeValues(new[] { "Option1", "Option2" })); FundingLine _1619fl = NewFundingLine(_ => _.WithCalculations(new[] { NewFundingLineCalculation(_ => _.WithId(1) .WithCalculationNamespaceType(CalculationNamespace.Template)) }) .WithId(1) .WithName("One") .WithSourceCodeName("One") .WithNamespace("1619")); IDictionary <string, Funding> fundingLines = new Dictionary <string, Funding> { { "1619", NewFunding(_ => _.WithFundingLines(new[] { _1619fl })) }, }; IEnumerable <SourceFile> results = new CalculationTypeGenerator(new CompilerOptions(), _fundingLineRoundingSettings.Object).GenerateCalcs(new[] { _1619 }, fundingLines); results.Should().HaveCount(1); results.First() .SourceCode .Should() .Contain( @$ "Enum EnumNameOptions <Description(Description:=" "Option1" ")> Option1 <Description(Description:=" "Option2" ")> Option2 End Enum"); }
public void GenerateCalculations_GivenCalculationsInDifferentNamespaces_ThenInnerClassPerNamespaceCreated() { Calculation _1619 = NewCalculation(_ => _.WithFundingStream(NewReference(rf => rf.WithId("1619"))) .WithSourceCodeName("One") .WithCalculationNamespaceType(CalculationNamespace.Template) .WithSourceCode("return 456")); Calculation psg = NewCalculation(_ => _.WithFundingStream(NewReference(rf => rf.WithId("PSG"))) .WithCalculationNamespaceType(CalculationNamespace.Template) .WithSourceCodeName("One") .WithSourceCode("return Calculations.One() + 100")); Calculation additionalOne = NewCalculation(_ => _.WithFundingStream(NewReference()) .WithCalculationNamespaceType(CalculationNamespace.Additional) .WithSourceCodeName("One") .WithSourceCode("return DSG.One() + Calculations.Two() + 34")); Calculation additionalTwo = NewCalculation(_ => _.WithFundingStream(NewReference()) .WithCalculationNamespaceType(CalculationNamespace.Additional) .WithSourceCodeName("Two") .WithSourceCode("return 789")); Dictionary <string, Funding> fundingLines = new Dictionary <string, Funding>(); IEnumerable <SourceFile> results = new CalculationTypeGenerator(new CompilerOptions(), _fundingLineRoundingSettings.Object).GenerateCalcs(new[] { _1619, psg, additionalOne, additionalTwo }, fundingLines); results.Should().HaveCount(1); results.First() .SourceCode .Should() .ContainAll("Public Class PSGCalculations", "Public Class _1619Calculations", "Public Class AdditionalCalculations"); }
private void PopulateCachedCalculationAggregationsBatch(IEnumerable <ProviderResult> providerResults, Dictionary <string, List <decimal> > cachedCalculationAggregationsBatch, GenerateAllocationMessageProperties messageProperties) { if (cachedCalculationAggregationsBatch == null) { _logger.Error($"Cached calculation aggregations not found for key: {messageProperties.CalculationsAggregationsBatchCacheKey}"); throw new Exception($"Cached calculation aggregations not found for key: {messageProperties.CalculationsAggregationsBatchCacheKey}"); } IEnumerable <string> calculationsToAggregate = messageProperties.CalculationsToAggregate; foreach (ProviderResult providerResult in providerResults) { IEnumerable <CalculationResult> calculationResultsForAggregation = providerResult.CalculationResults.Where(m => calculationsToAggregate.Contains(VisualBasicTypeGenerator.GenerateIdentifier(m.Calculation.Name), StringComparer.InvariantCultureIgnoreCase)); foreach (CalculationResult calculationResult in calculationResultsForAggregation) { string calculationReferenceName = CalculationTypeGenerator.GenerateIdentifier(calculationResult.Calculation.Name.Trim()); string calcNameFromCalcsToAggregate = messageProperties.CalculationsToAggregate.FirstOrDefault(m => string.Equals(m, calculationReferenceName, StringComparison.InvariantCultureIgnoreCase)); if (!string.IsNullOrWhiteSpace(calcNameFromCalcsToAggregate) && cachedCalculationAggregationsBatch.ContainsKey(calculationReferenceName)) { cachedCalculationAggregationsBatch[calcNameFromCalcsToAggregate].Add(calculationResult.Value.HasValue ? calculationResult.Value.Value : 0); } } } }
public void GenerateIdentifier_IdentifiersSubstituted(string input, string expected) { // Act string result = CalculationTypeGenerator.GenerateIdentifier(input); // Assert result .Should() .Be(expected); }
public void QuoteAggregateFunctionCalls_QuotesAsExpected(string input, string expected) { //Act string result = CalculationTypeGenerator.QuoteAggregateFunctionCalls(input); //Assert result .Should() .Be(expected); }
public void GenerateCalculations_GivenNoCalculations_ThenSingleInnerClassForAdditionalCreated() { Dictionary <string, Funding> fundingLines = new Dictionary <string, Funding>(); IEnumerable <SourceFile> results = new CalculationTypeGenerator(new CompilerOptions(), _fundingLineRoundingSettings.Object).GenerateCalcs(Enumerable.Empty <Calculation>(), fundingLines); results.Should().HaveCount(1); results.First() .SourceCode .Should() .ContainAll("Public Class AdditionalCalculations"); }
public void GenerateCalcs_InvalidSourceCodeNormaliseWhitespaceFails_ReturnsError() { CompilerOptions compilerOptions = new CompilerOptions(); CalculationTypeGenerator calculationTypeGenerator = new CalculationTypeGenerator(compilerOptions, _fundingLineRoundingSettings.Object); string badCode = @"Dim Filter as Decimal Dim APTPhase as String Dim CensusPhase as String Dim Result as Decimal Filter = FILTERSBSAcademiesFilter() APTPhase = Datasets.APTInputsAndAdjustments.Phase() CensusPhase = Datasets.CensusPupilCharacteristics.Phase() If Filter = 0 then Return Exclude() Else If string.isnullorempty(APTPhase) then Result = 0 Else If CensusPhase = ""PRIMARY"" then Result = 1 Else If CensusPhase = ""MIDDLE-DEEMED PRIMARY"" then Result = 2 Else If CensusPhase = ""SECONDARY"" then Result = 3 Else If CensusPhase = ""MIDDLE-DEEMED SECONDARY"" then Result = 4 End If End If End If Return Result + 0"; IEnumerable <Calculation> calculations = new[] { new Calculation { Current = new CalculationVersion { SourceCode = badCode, SourceCodeName = "Broken" } } }; Dictionary <string, Funding> fundingLines = new Dictionary <string, Funding>(); Action generate = () => calculationTypeGenerator.GenerateCalcs(calculations, fundingLines).ToList(); generate .Should() .Throw <Exception>() .And.Message .Should() .StartWith("Error compiling source code. Please check your code's structure is valid. "); }
public void GenerateCalcs_GivenCalculationsAndCompilerOptionsUseLegacyCodeIsSet_TheEnsuresCorrectInheritanceStatement(bool useLegacyCode, string expectedInheritsStatement) { // Arrange CompilerOptions compilerOptions = new CompilerOptions { UseLegacyCode = useLegacyCode, }; CalculationTypeGenerator calculationTypeGenerator = new CalculationTypeGenerator(compilerOptions); // Act IEnumerable <SourceFile> results = calculationTypeGenerator.GenerateCalcs(new List <Calculation>()); // Assert results.Should().HaveCount(1); results.First().SourceCode.Should().Contain(expectedInheritsStatement); }
public void GenerateCalcs_GivenCalculationsAndCompilerOptionsOff_ThenOptionsGenerated() { // Arrange List <Calculation> calculations = new List <Calculation>(); CompilerOptions compilerOptions = new CompilerOptions { OptionStrictEnabled = false }; CalculationTypeGenerator calculationTypeGenerator = new CalculationTypeGenerator(compilerOptions); // Act IEnumerable <SourceFile> results = calculationTypeGenerator.GenerateCalcs(calculations); // Assert results.Should().HaveCount(1); results.First().SourceCode.Should().StartWith("Option Strict Off"); }
public void GenerateCalculations_GivenNoAdditionalCalculations_ThenFundingLineMembersCreated() { Calculation _1619 = NewCalculation(_ => _.WithFundingStream(NewReference(rf => rf.WithId("DSG"))) .WithSourceCodeName("One") .WithCalculationNamespaceType(CalculationNamespace.Template) .WithSourceCode("return 456")); Calculation psg = NewCalculation(_ => _.WithFundingStream(NewReference(rf => rf.WithId("PSG"))) .WithCalculationNamespaceType(CalculationNamespace.Template) .WithSourceCodeName("Two") .WithSourceCode("return _1619.One() + 100")); FundingLine _1619fl = NewFundingLine(_ => _.WithCalculations(new[] { NewFundingLineCalculation(_ => _.WithId(1) .WithCalculationNamespaceType(CalculationNamespace.Template)) }) .WithId(1) .WithName("One") .WithSourceCodeName("One") .WithNamespace("1619")); FundingLine psgfl = NewFundingLine(_ => _.WithCalculations(new[] { NewFundingLineCalculation(_ => _.WithId(1) .WithCalculationNamespaceType(CalculationNamespace.Template)) }) .WithId(2) .WithName("Two") .WithSourceCodeName("Two") .WithNamespace("PSG")); IDictionary <string, Funding> fundingLines = new Dictionary <string, Funding> { { "1619", NewFunding(_ => _.WithFundingLines(new[] { _1619fl })) }, { "PSG", NewFunding(_ => _.WithFundingLines(new[] { psgfl })) } }; IEnumerable <SourceFile> results = new CalculationTypeGenerator(new CompilerOptions(), _fundingLineRoundingSettings.Object).GenerateCalcs(new[] { _1619, psg }, fundingLines); results.Should().HaveCount(1); results.First() .SourceCode .Should() .ContainAll( "<FundingLine(FundingStream:=\"1619\", Id:=\"1\", Name:=\"One\")>", "Public One As Func(Of decimal?) = Nothing", "<FundingLine(FundingStream:=\"PSG\", Id:=\"2\", Name:=\"Two\")>", "Public Two As Func(Of decimal?) = Nothing", "_1619.FundingLines.One()"); }
public void GenerateCalculations_GivenNoAdditionalCalculations_ThenSingleInnerClassForAdditionalStillCreated() { Calculation _1619 = NewCalculation(_ => _.WithFundingStream(NewReference(rf => rf.WithId("1619"))) .WithSourceCodeName("One") .WithCalculationNamespaceType(CalculationNamespace.Template) .WithSourceCode("return 456")); Calculation psg = NewCalculation(_ => _.WithFundingStream(NewReference(rf => rf.WithId("PSG"))) .WithCalculationNamespaceType(CalculationNamespace.Template) .WithSourceCodeName("One") .WithSourceCode("return DSG.One() + 100")); FundingLine _1619fl = NewFundingLine(_ => _.WithCalculations(new[] { NewFundingLineCalculation(_ => _.WithId(1) .WithCalculationNamespaceType(CalculationNamespace.Template)) }) .WithId(1) .WithName("One") .WithSourceCodeName("One") .WithNamespace("1619")); FundingLine psgfl = NewFundingLine(_ => _.WithCalculations(new[] { NewFundingLineCalculation(_ => _.WithId(1) .WithCalculationNamespaceType(CalculationNamespace.Template)) }) .WithId(2) .WithName("One") .WithSourceCodeName("One") .WithNamespace("PSG")); IDictionary <string, Funding> fundingLines = new Dictionary <string, Funding> { { "1619", NewFunding(_ => _.WithFundingLines(new[] { _1619fl })) }, { "PSG", NewFunding(_ => _.WithFundingLines(new[] { psgfl })) } }; IEnumerable <SourceFile> results = new CalculationTypeGenerator(new CompilerOptions(), _fundingLineRoundingSettings.Object).GenerateCalcs(new[] { _1619, psg }, fundingLines); results.Should().HaveCount(1); results.First() .SourceCode .Should() .ContainAll("Public Class PSGCalculations", "Public Class _1619Calculations", "Public Class AdditionalCalculations", "Public Class PSGFundingLines", "Public Class _1619FundingLines"); }
public void GenerateCalcs_GivenCalculationsAndCompilerOptionsStrictOn_ThenOptionStrictGenerated() { // Arrange List <Calculation> calculations = new List <Calculation>(); Dictionary <string, Funding> fundingLines = new Dictionary <string, Funding>(); CompilerOptions compilerOptions = new CompilerOptions { OptionStrictEnabled = true }; CalculationTypeGenerator calculationTypeGenerator = new CalculationTypeGenerator(compilerOptions, _fundingLineRoundingSettings.Object); // Act IEnumerable <SourceFile> results = calculationTypeGenerator.GenerateCalcs(calculations, fundingLines); // Assert results.Should().HaveCount(1); results.First().SourceCode.Should().StartWith("Option Strict On"); }
public void GenerateCalcs_MissingSourceCodeName_ReturnsError() { string id = "42"; CompilerOptions compilerOptions = new CompilerOptions(); CalculationTypeGenerator calculationTypeGenerator = new CalculationTypeGenerator(compilerOptions); IEnumerable <Calculation> calculations = new[] { new Calculation { Current = new CalculationVersion { SourceCode = "Return 1" }, Id = id } }; Action generate = () => calculationTypeGenerator.GenerateCalcs(calculations).ToList(); generate .Should() .Throw <Exception>() .And.Message .Should() .Be($"Calculation source code name is not populated for calc {id}"); }
public void GenerateCalcs_ThenEntryFunctionGenerated() { // Arrange Dictionary <string, Funding> fundingLines = new Dictionary <string, Funding>(); CompilerOptions compilerOptions = new CompilerOptions { OptionStrictEnabled = false }; CalculationTypeGenerator calculationTypeGenerator = new CalculationTypeGenerator(compilerOptions, _fundingLineRoundingSettings.Object); // Act IEnumerable <SourceFile> results = calculationTypeGenerator.GenerateCalcs(new List <Calculation>(), fundingLines); // Assert results.Should().HaveCount(1); // Assert results.Should().HaveCount(1); results.First() .SourceCode .Should(). Contain("Public MainCalc As Func(Of Boolean, (CalculationResults As Dictionary(Of String, String()), FundingLineResults As Dictionary(Of String, String()))) = Function(allCalculations)"); }
public void GenerateCalcs_MissingSourceCodeName_ReturnsError() { string id = "42"; CompilerOptions compilerOptions = new CompilerOptions(); CalculationTypeGenerator calculationTypeGenerator = new CalculationTypeGenerator(compilerOptions, _fundingLineRoundingSettings.Object); IEnumerable <Calculation> calculations = new[] { new Calculation { Current = new CalculationVersion { SourceCode = "Return 1", Namespace = CalculationNamespace.Additional }, Id = id } }; Dictionary <string, Funding> fundingLines = new Dictionary <string, Funding>(); Action generate = () => calculationTypeGenerator.GenerateCalcs(calculations, fundingLines).ToList(); generate .Should() .Throw <Exception>() .And.Message .Should() .Be($"Calculation source code name is not populated for calc {id}"); }
private async Task <IEnumerable <Job> > CreateGenerateAllocationJobs(JobViewModel parentJob, IEnumerable <IDictionary <string, string> > jobProperties) { HashSet <string> calculationsToAggregate = new HashSet <string>(); if (parentJob.JobDefinitionId == JobConstants.DefinitionNames.CreateInstructGenerateAggregationsAllocationJob) { string calculationAggregatesCacheKeyPrefix = $"{CacheKeys.CalculationAggregations}{parentJob.SpecificationId}"; await _cacheProvider.RemoveByPatternAsync(calculationAggregatesCacheKeyPrefix); IEnumerable <Models.Calcs.Calculation> calculations = await _calculationsRepository.GetCalculationsBySpecificationId(parentJob.SpecificationId); foreach (Models.Calcs.Calculation calculation in calculations) { IEnumerable <string> aggregateParameters = SourceCodeHelpers.GetCalculationAggregateFunctionParameters(calculation.Current.SourceCode); if (aggregateParameters.IsNullOrEmpty()) { continue; } foreach (string aggregateParameter in aggregateParameters) { Models.Calcs.Calculation referencedCalculation = calculations.FirstOrDefault(m => string.Equals(CalculationTypeGenerator.GenerateIdentifier(m.Name.Trim()), aggregateParameter.Trim(), StringComparison.InvariantCultureIgnoreCase)); if (referencedCalculation != null) { calculationsToAggregate.Add(aggregateParameter); } } } } IList <JobCreateModel> jobCreateModels = new List <JobCreateModel>(); Trigger trigger = new Trigger { EntityId = parentJob.Id, EntityType = nameof(Job), Message = $"Triggered by parent job with id: '{parentJob.Id}" }; int batchNumber = 1; int batchCount = jobProperties.Count(); string calcsToAggregate = string.Join(",", calculationsToAggregate); foreach (IDictionary <string, string> properties in jobProperties) { properties.Add("batch-number", batchNumber.ToString()); properties.Add("batch-count", batchCount.ToString()); properties.Add("calculations-to-aggregate", calcsToAggregate); JobCreateModel jobCreateModel = new JobCreateModel { InvokerUserDisplayName = parentJob.InvokerUserDisplayName, InvokerUserId = parentJob.InvokerUserId, JobDefinitionId = parentJob.JobDefinitionId == JobConstants.DefinitionNames.CreateInstructAllocationJob ? JobConstants.DefinitionNames.CreateAllocationJob : JobConstants.DefinitionNames.GenerateCalculationAggregationsJob, SpecificationId = parentJob.SpecificationId, Properties = properties, ParentJobId = parentJob.Id, Trigger = trigger, CorrelationId = parentJob.CorrelationId }; batchNumber++; jobCreateModels.Add(jobCreateModel); } return(await _jobsApiClientPolicy.ExecuteAsync(() => _jobsApiClient.CreateJobs(jobCreateModels))); }