public void CalculateProviderResult_WhenCalculationsAreNotEmpty_ShouldReturnCorrectResult()
        {
            // Arrange
            List <Reference> policySpecificationsForFundingCalc = new List <Reference>()
            {
                new Reference("Spec1", "SpecOne"),
                new Reference("Spec2", "SpecTwo")
            };
            List <Reference> policySpecificationsForNumberCalc = new List <Reference>()
            {
                new Reference("Spec1", "SpecOne"),
            };
            Reference allocationLineReturned = new Reference("allocationLine", "allocation line for Funding Calc and number calc");

            Reference fundingCalcReference = new Reference("CalcF1", "Funding calc 1");
            Reference fundingCalcSpecificationReference = new Reference("FSpect", "FundingSpecification");

            Reference numbercalcReference = new Reference("CalcF2", "Funding calc 2");
            Reference numbercalcSpecificationReference = new Reference("FSpec2", "FundingSpecification2");

            CalculationResult fundingCalcReturned = new CalculationResult()
            {
                CalculationType          = CalculationType.Funding,
                Calculation              = fundingCalcReference,
                AllocationLine           = allocationLineReturned,
                CalculationSpecification = fundingCalcSpecificationReference,
                PolicySpecifications     = policySpecificationsForFundingCalc,
                Value = 10000
            };
            CalculationResult fundingCalcReturned2 = new CalculationResult()
            {
                CalculationType          = CalculationType.Funding,
                Calculation              = numbercalcReference,
                AllocationLine           = allocationLineReturned,
                CalculationSpecification = numbercalcSpecificationReference,
                PolicySpecifications     = policySpecificationsForNumberCalc,
                Value = 20000
            };

            List <CalculationResult> calculationResults = new List <CalculationResult>()
            {
                fundingCalcReturned,
                fundingCalcReturned2
            };
            IAllocationModel mockAllocationModel = Substitute.For <IAllocationModel>();

            mockAllocationModel
            .Execute(Arg.Any <List <ProviderSourceDataset> >(), Arg.Any <ProviderSummary>())
            .Returns(calculationResults);

            CalculationEngine calculationEngine = CreateCalculationEngine();
            ProviderSummary   providerSummary   = CreateDummyProviderSummary();
            BuildProject      buildProject      = CreateBuildProject();

            var nonMatchingCalculationModel = new CalculationSummaryModel()
            {
                Id              = "Non matching calculation",
                Name            = "Non matching calculation",
                CalculationType = CalculationType.Funding
            };
            IEnumerable <CalculationSummaryModel> calculationSummaryModels = new[]
            {
                new CalculationSummaryModel()
                {
                    Id              = fundingCalcReference.Id,
                    Name            = fundingCalcReference.Name,
                    CalculationType = CalculationType.Funding
                },
                new CalculationSummaryModel()
                {
                    Id              = numbercalcReference.Id,
                    Name            = numbercalcReference.Name,
                    CalculationType = CalculationType.Funding
                },
                nonMatchingCalculationModel
            };

            // Act
            var calculateProviderResults = calculationEngine.CalculateProviderResults(mockAllocationModel, buildProject, calculationSummaryModels,
                                                                                      providerSummary, new List <ProviderSourceDataset>());
            ProviderResult result = calculateProviderResults;

            // Assert
            result.Provider.Should().Be(providerSummary);
            result.SpecificationId.Should().BeEquivalentTo(buildProject.SpecificationId);
            result.Id.Should().BeEquivalentTo(GenerateId(providerSummary.Id, buildProject.SpecificationId));
            result.CalculationResults.Should().HaveCount(3);
            result.AllocationLineResults.Should().HaveCount(1);

            AllocationLineResult allocationLine = result.AllocationLineResults[0];

            allocationLine.Value = 30000;

            CalculationResult fundingCalcResult = result.CalculationResults.First(cr => cr.Calculation.Id == fundingCalcReference.Id);

            fundingCalcResult.Calculation.Should().BeEquivalentTo(fundingCalcReference);
            fundingCalcResult.CalculationType.Should().BeEquivalentTo(fundingCalcReturned.CalculationType);
            fundingCalcResult.AllocationLine.Should().BeEquivalentTo(allocationLineReturned);
            fundingCalcResult.CalculationSpecification.Should().BeEquivalentTo(fundingCalcSpecificationReference);
            fundingCalcResult.PolicySpecifications.Should().BeEquivalentTo(policySpecificationsForFundingCalc);
            fundingCalcResult.Value.Should().Be(fundingCalcReturned.Value.Value);

            CalculationResult numberCalcResult = result.CalculationResults.First(cr => cr.Calculation.Id == numbercalcReference.Id);

            numberCalcResult.Calculation.Should().BeEquivalentTo(numbercalcReference);
            numberCalcResult.CalculationType.Should().BeEquivalentTo(fundingCalcReturned2.CalculationType);
            numberCalcResult.AllocationLine.Should().BeEquivalentTo(allocationLineReturned);
            numberCalcResult.CalculationSpecification.Should().BeEquivalentTo(numbercalcSpecificationReference);
            numberCalcResult.PolicySpecifications.Should().BeEquivalentTo(policySpecificationsForNumberCalc);
            numberCalcResult.Value.Should().Be(fundingCalcReturned2.Value.Value);

            CalculationResult nonMatchingCalcResult = result.CalculationResults.First(cr => cr.Calculation.Id == "Non matching calculation");

            nonMatchingCalcResult.Calculation.Should().BeEquivalentTo(new Reference(nonMatchingCalculationModel.Id, nonMatchingCalculationModel.Name));
            nonMatchingCalcResult.CalculationType.Should().BeEquivalentTo(nonMatchingCalculationModel.CalculationType);
            nonMatchingCalcResult.AllocationLine.Should().BeNull();
            nonMatchingCalcResult.CalculationSpecification.Should().BeNull();
            nonMatchingCalcResult.PolicySpecifications.Should().BeNull();
            nonMatchingCalcResult.Value.Should().BeNull();
        }
示例#2
0
        private async Task <IActionResult> GenerateAndCompile(BuildProject buildProject,
                                                              Calculation calculationToPreview,
                                                              IEnumerable <Calculation> calculations,
                                                              CompilerOptions compilerOptions,
                                                              PreviewRequest previewRequest)
        {
            PreviewProviderCalculationResponseModel previewProviderCalculation = null;

            Build compilerOutput = _sourceCodeService.Compile(buildProject, calculations, compilerOptions);

            if (compilerOutput.SourceFiles != null)
            {
                await _sourceCodeService.SaveSourceFiles(compilerOutput.SourceFiles, buildProject.SpecificationId, SourceCodeType.Preview);
            }

            if (compilerOutput.Success)
            {
                _logger.Information($"Build compiled successfully for calculation id {calculationToPreview.Id}");

                string calculationIdentifier = $"{_typeIdentifierGenerator.GenerateIdentifier(calculationToPreview.Namespace)}.{_typeIdentifierGenerator.GenerateIdentifier(calculationToPreview.Name)}";

                IDictionary <string, string> functions = _sourceCodeService.GetCalculationFunctions(compilerOutput.SourceFiles);
                IDictionary <string, string> calculationIdentifierMap = calculations
                                                                        .Select(_ => new
                {
                    Identifier = $"{_typeIdentifierGenerator.GenerateIdentifier(_.Namespace)}.{_typeIdentifierGenerator.GenerateIdentifier(_.Name)}",
                    CalcName   = _.Name
                })
                                                                        .ToDictionary(d => d.Identifier, d => d.CalcName);

                if (!functions.ContainsKey(calculationIdentifier))
                {
                    compilerOutput.Success = false;
                    compilerOutput.CompilerMessages.Add(new CompilerMessage {
                        Message = $"{calculationIdentifier} is not an aggregable field", Severity = Severity.Error
                    });
                }
                else
                {
                    if (previewRequest != null)
                    {
                        if (!SourceCodeHelpers.HasReturn(previewRequest.SourceCode))
                        {
                            compilerOutput.Success = false;
                            compilerOutput.CompilerMessages.Add(new CompilerMessage {
                                Message = $"{calculationIdentifier} must have a return statement so that a calculation result will be returned", Severity = Severity.Error
                            });
                        }
                        else
                        {
                            IEnumerable <string> aggregateParameters = SourceCodeHelpers.GetCalculationAggregateFunctionParameters(previewRequest.SourceCode);

                            bool continueChecking = true;

                            if (!aggregateParameters.IsNullOrEmpty())
                            {
                                foreach (string aggregateParameter in aggregateParameters)
                                {
                                    if (!functions.ContainsKey(aggregateParameter))
                                    {
                                        compilerOutput.Success = false;
                                        compilerOutput.CompilerMessages.Add(new CompilerMessage {
                                            Message = $"{aggregateParameter} is not an aggregable field", Severity = Severity.Error
                                        });
                                        continueChecking = false;
                                        break;
                                    }

                                    if (calculationIdentifierMap.ContainsKey(aggregateParameter))
                                    {
                                        Calculation calculation = calculations.SingleOrDefault(_ => _.Name == calculationIdentifierMap[aggregateParameter]);

                                        if (calculation.Current.DataType != CalculationDataType.Decimal)
                                        {
                                            compilerOutput.Success = false;
                                            compilerOutput.CompilerMessages.Add(new CompilerMessage
                                            {
                                                Message =
                                                    $"Only decimal fields can be used on aggregation. {aggregateParameter} has data type of {calculation.Current.DataType}",
                                                Severity = Severity.Error
                                            });
                                            continueChecking = false;
                                            break;
                                        }
                                    }
                                }

                                if (continueChecking)
                                {
                                    if (SourceCodeHelpers.IsCalcReferencedInAnAggregate(functions, calculationIdentifier))
                                    {
                                        compilerOutput.Success = false;
                                        compilerOutput.CompilerMessages.Add(new CompilerMessage {
                                            Message = $"{calculationIdentifier} is already referenced in an aggregation that would cause nesting", Severity = Severity.Error
                                        });
                                    }
                                    else if (SourceCodeHelpers.CheckSourceForExistingCalculationAggregates(functions, previewRequest.SourceCode))
                                    {
                                        compilerOutput.Success = false;
                                        compilerOutput.CompilerMessages.Add(new CompilerMessage {
                                            Message = $"{calculationIdentifier} cannot reference another calc that is being aggregated", Severity = Severity.Error
                                        });
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                _logger.Information($"Build did not compile successfully for calculation id {calculationToPreview.Id}");
            }

            LogMessages(compilerOutput, buildProject, calculationToPreview);

            if (!string.IsNullOrEmpty(previewRequest.ProviderId))
            {
                CalculationSummaryModel calculationSummaryModel = calculationToPreview.ToSummaryModel();
                CalcEngineModels.CalculationSummaryModel model  = _mapper.Map <CalcEngineModels.CalculationSummaryModel>(calculationSummaryModel);

                CalcEngineModels.PreviewCalculationRequest previewCalculationRequest = new CalcEngineModels.PreviewCalculationRequest
                {
                    AssemblyContent = compilerOutput.Assembly,
                    PreviewCalculationSummaryModel = model
                };

                ApiResponse <CalcEngineProviderResult> previewCalcResultApiResponse =
                    await _calcEngineApiClientPolicy.ExecuteAsync(
                        () => _calcEngineApiClient.PreviewCalculationResults(
                            previewRequest.SpecificationId,
                            previewRequest.ProviderId,
                            previewCalculationRequest));

                if (previewCalcResultApiResponse.StatusCode.IsSuccess())
                {
                    CalcEngineProviderResult calcEngineProviderResult = previewCalcResultApiResponse.Content;

                    previewProviderCalculation = new PreviewProviderCalculationResponseModel
                    {
                        ProviderName      = calcEngineProviderResult.Provider.Name,
                        CalculationResult = _mapper.Map <CalculationResult>(
                            calcEngineProviderResult.CalculationResults.SingleOrDefault(_ => _.Calculation?.Id == calculationToPreview.Id)),
                    };
                }
            }

            return(new OkObjectResult(new PreviewResponse
            {
                Calculation = calculationToPreview.ToResponseModel(),
                CompilerOutput = compilerOutput,
                PreviewProviderCalculation = previewProviderCalculation
            }));
        }
示例#3
0
        public ProviderResult CalculateProviderResults(
            IAllocationModel model,
            string specificationId,
            IEnumerable <CalculationSummaryModel> calculations,
            ProviderSummary provider,
            IDictionary <string, ProviderSourceDataset> providerSourceDatasets,
            IEnumerable <CalculationAggregation> aggregations = null)
        {
            var stopwatch = new Stopwatch();

            stopwatch.Start();

            CalculationResultContainer calculationResultContainer = model.Execute(providerSourceDatasets, provider, aggregations);

            IEnumerable <CalculationResult> calculationResultItems = calculationResultContainer.CalculationResults;

            stopwatch.Stop();

            IDictionary <string, double> metrics = new Dictionary <string, double>()
            {
                { "calculation-provider-calcsMs", stopwatch.ElapsedMilliseconds },
                { "calculation-provider-calcsTotal", calculations.AnyWithNullCheck() ? calculations.Count() : 0 },
                { "calculation-provider-exceptions", calculationResultItems.AnyWithNullCheck() ? calculationResultItems.Count(c => !string.IsNullOrWhiteSpace(c.ExceptionMessage)) : 0 },
            };

            _telemetry.TrackEvent("CalculationRunProvider",
                                  new Dictionary <string, string>()
            {
                { "specificationId", specificationId },
            },
                                  metrics
                                  );

            if (calculationResultItems.AnyWithNullCheck() && calculationResultItems.Count() > 0)
            {
                _logger.Verbose($"Processed results for {calculationResultItems.Count()} calcs in {stopwatch.ElapsedMilliseconds}ms ({stopwatch.ElapsedMilliseconds / calculationResultItems.Count(): 0.0000}ms)");
            }
            else
            {
                _logger.Information("There are no calculations to executed for specification ID {specificationId}", specificationId);
            }

            byte[] plainTextBytes = System.Text.Encoding.UTF8.GetBytes($"{provider.Id}-{specificationId}");

            ProviderResult providerResult = new ProviderResult
            {
                Id              = Convert.ToBase64String(plainTextBytes),
                Provider        = provider,
                SpecificationId = specificationId
            };

            if (calculationResultItems.AnyWithNullCheck())
            {
                foreach (CalculationResult calcResult in calculationResultItems)
                {
                    CalculationSummaryModel calculationSummaryModel = calculations.First(c => c.Id == calcResult.Calculation.Id);

                    calcResult.CalculationType     = calculationSummaryModel.CalculationType;
                    calcResult.CalculationDataType = calculationSummaryModel.CalculationValueType.ToCalculationDataType();

                    if (calcResult.CalculationDataType == CalculationDataType.Decimal && Decimal.Equals(decimal.MinValue, calcResult.Value))
                    {
                        // The default for the calculation is to return Decimal.MinValue - if this is the case, then subsitute a 0 value as the result, instead of the negative number.
                        calcResult.Value = 0;
                    }
                }
            }

            //we need a stable sort of results to enable the cache checks by overall SHA hash on the results json
            providerResult.CalculationResults = calculationResultContainer.CalculationResults?.OrderBy(_ => _.Calculation.Id).ToList();
            providerResult.FundingLineResults = calculationResultContainer.FundingLineResults.OrderBy(_ => _.FundingLine.Id).ToList();

            return(providerResult);
        }
        public async Task PreviewCalculationResult_GivenCachedAggregateValuesExist_CalculateProviderResultsCallReceived()
        {
            IAllocationModel allocationModel = Substitute.For <IAllocationModel>();

            _calculationEngine
            .GenerateAllocationModel(Arg.Any <Assembly>())
            .Returns(allocationModel);

            ProviderVersionSearchResult providerVersionSearchResult = new ProviderVersionSearchResult
            {
                UKPRN = providerId
            };

            IEnumerable <string> dataDefinitionRelationshipIds = new List <string>();

            SpecificationSummary specificationSummary = new SpecificationSummary
            {
                DataDefinitionRelationshipIds = dataDefinitionRelationshipIds,
                ProviderVersionId             = providerVersionId
            };

            _specificationsApiClient
            .GetSpecificationSummaryById(Arg.Is(specificationId))
            .Returns(new ApiResponse <SpecificationSummary>(HttpStatusCode.OK, specificationSummary));

            _providersApiClient
            .GetProviderByIdFromProviderVersion(Arg.Is(providerVersionId), Arg.Is(providerId))
            .Returns(new ApiResponse <ProviderVersionSearchResult>(HttpStatusCode.OK, providerVersionSearchResult));

            CalculationSummaryModel previewCalculationSummaryModel = new CalculationSummaryModel();

            IEnumerable <CalculationSummaryModel> calculationSummaryModels = new List <CalculationSummaryModel>
            {
                new CalculationSummaryModel(),
                new CalculationSummaryModel()
            };

            List <CalculationSummaryModel> expectedCalculationSummaryModels = calculationSummaryModels.ToList();

            expectedCalculationSummaryModels.Add(previewCalculationSummaryModel);

            _calculationsRepository
            .GetCalculationSummariesForSpecification(Arg.Is(specificationId))
            .Returns(calculationSummaryModels);

            Dictionary <string, ProviderSourceDataset> sourceDatasets = new Dictionary <string, ProviderSourceDataset>();

            Dictionary <string, Dictionary <string, ProviderSourceDataset> > providerSourceDatasets = new Dictionary <string, Dictionary <string, ProviderSourceDataset> >
            {
                { providerId, sourceDatasets }
            };

            _providerSourceDatasetsRepository
            .GetProviderSourceDatasetsByProviderIdsAndRelationshipIds(
                Arg.Is(specificationId),
                Arg.Is <IEnumerable <string> >(_ => _ != null && _.Count() == 1 && _.FirstOrDefault() == providerId),
                Arg.Is <IEnumerable <string> >(_ => _ != null && _.SequenceEqual(dataDefinitionRelationshipIds)))
            .Returns(providerSourceDatasets);

            IEnumerable <CalculationAggregation> calculationAggregations = new List <CalculationAggregation>();

            _calculationAggregationService
            .BuildAggregations(Arg.Is <BuildAggregationRequest>(_ => _ != null && _.SpecificationId == specificationId))
            .Returns(calculationAggregations);

            PreviewCalculationRequest previewCalculationRequest = new PreviewCalculationRequest
            {
                AssemblyContent = MockData.GetMockAssembly(),
                PreviewCalculationSummaryModel = previewCalculationSummaryModel
            };

            IActionResult actionResult =
                await _calculationEnginePreviewService.PreviewCalculationResult(specificationId, providerId, previewCalculationRequest);

            _calculationEngine
            .Received(1)
            .CalculateProviderResults(
                Arg.Is(allocationModel),
                specificationId,
                Arg.Is <IEnumerable <CalculationSummaryModel> >(_ => _.SequenceEqual(expectedCalculationSummaryModels)),
                Arg.Is <ProviderSummary>(_ => _.UKPRN == providerId),
                Arg.Is <Dictionary <string, ProviderSourceDataset> >(_ => _.SequenceEqual(sourceDatasets)),
                Arg.Is <IEnumerable <CalculationAggregation> >(_ => _.SequenceEqual(calculationAggregations)));
        }
        public void CalculateProviderResult_WhenCalculationsAreNotEmpty_ShouldReturnCorrectResult()
        {
            // Arrange

            List <Reference> policySpecificationsForFundingCalc = new List <Reference>()
            {
                new Reference("Spec1", "SpecOne"),
                new Reference("Spec2", "SpecTwo")
            };

            Reference fundingCalcReference = new Reference("CalcF1", "Funding calc 1");

            Reference numbercalcReference = new Reference("CalcF2", "Funding calc 2");

            Reference booleancalcReference = new Reference("CalcF3", "Funding calc 3");

            Reference fundingLineCalcReference = new Reference("FL1", "Funding line calc 1");

            CalculationResult fundingCalcReturned = new CalculationResult()
            {
                Calculation = fundingCalcReference,
                Value       = 10000
            };
            CalculationResult fundingCalcReturned2 = new CalculationResult()
            {
                Calculation = numbercalcReference,
                Value       = 20000
            };
            CalculationResult fundingCalcReturned3 = new CalculationResult()
            {
                Calculation = booleancalcReference,
                Value       = true
            };

            CalculationResultContainer calculationResultContainer = new CalculationResultContainer();

            List <CalculationResult> calculationResults = new List <CalculationResult>()
            {
                fundingCalcReturned,
                fundingCalcReturned2,
                fundingCalcReturned3
            };

            calculationResultContainer.CalculationResults = calculationResults;

            string fundingStreamId = "FS1";

            FundingLineResult fundingLineResult = new FundingLineResult
            {
                Value       = 1000,
                FundingLine = fundingLineCalcReference,
                FundingLineFundingStreamId = fundingStreamId
            };

            List <FundingLineResult> fundingLineResults = new List <FundingLineResult>
            {
                fundingLineResult
            };

            calculationResultContainer.FundingLineResults = fundingLineResults;

            IAllocationModel mockAllocationModel = Substitute.For <IAllocationModel>();

            mockAllocationModel
            .Execute(Arg.Any <Dictionary <string, ProviderSourceDataset> >(), Arg.Any <ProviderSummary>())
            .Returns(calculationResultContainer);

            CalculationEngine calculationEngine = CreateCalculationEngine();
            ProviderSummary   providerSummary   = CreateDummyProviderSummary();
            BuildProject      buildProject      = CreateBuildProject();

            var nonMatchingCalculationModel = new CalculationSummaryModel()
            {
                Id                   = "Non matching calculation",
                Name                 = "Non matching calculation",
                CalculationType      = CalculationType.Template,
                CalculationValueType = CalculationValueType.Number
            };
            IEnumerable <CalculationSummaryModel> calculationSummaryModels = new[]
            {
                new CalculationSummaryModel()
                {
                    Id                   = fundingCalcReference.Id,
                    Name                 = fundingCalcReference.Name,
                    CalculationType      = CalculationType.Template,
                    CalculationValueType = CalculationValueType.Number
                },
                new CalculationSummaryModel()
                {
                    Id                   = numbercalcReference.Id,
                    Name                 = numbercalcReference.Name,
                    CalculationType      = CalculationType.Template,
                    CalculationValueType = CalculationValueType.Number
                },
                new CalculationSummaryModel()
                {
                    Id                   = booleancalcReference.Id,
                    Name                 = booleancalcReference.Name,
                    CalculationType      = CalculationType.Template,
                    CalculationValueType = CalculationValueType.Boolean
                },
                nonMatchingCalculationModel
            };

            // Act
            var calculateProviderResults = calculationEngine.CalculateProviderResults(mockAllocationModel, buildProject.SpecificationId, calculationSummaryModels,
                                                                                      providerSummary, new Dictionary <string, ProviderSourceDataset>());
            ProviderResult result = calculateProviderResults;

            // Assert
            result.Provider.Should().Be(providerSummary);
            result.SpecificationId.Should().BeEquivalentTo(buildProject.SpecificationId);
            result.Id.Should().BeEquivalentTo(GenerateId(providerSummary.Id, buildProject.SpecificationId));
            result.CalculationResults.Should().HaveCount(3);
            result.FundingLineResults.Should().HaveCount(1);

            CalculationResult fundingCalcResult = result.CalculationResults.First(cr => cr.Calculation.Id == fundingCalcReference.Id);

            fundingCalcResult.Calculation.Should().BeEquivalentTo(fundingCalcReference);
            fundingCalcResult.CalculationType.Should().BeEquivalentTo(CalculationType.Template);
            fundingCalcResult.Value.Should().Be(fundingCalcReturned.Value);
            fundingCalcResult.CalculationDataType.Should().Be(CalculationDataType.Decimal);

            CalculationResult numberCalcResult = result.CalculationResults.First(cr => cr.Calculation.Id == numbercalcReference.Id);

            numberCalcResult.Calculation.Should().BeEquivalentTo(numbercalcReference);
            numberCalcResult.CalculationType.Should().BeEquivalentTo(CalculationType.Template);
            numberCalcResult.Value.Should().Be(fundingCalcReturned2.Value);
            numberCalcResult.CalculationDataType.Should().Be(CalculationDataType.Decimal);

            CalculationResult booleanCalcResult = result.CalculationResults.First(cr => cr.Calculation.Id == booleancalcReference.Id);

            booleanCalcResult.Calculation.Should().BeEquivalentTo(booleancalcReference);
            booleanCalcResult.CalculationType.Should().BeEquivalentTo(CalculationType.Template);
            booleanCalcResult.Value.Should().Be(fundingCalcReturned3.Value);
            booleanCalcResult.CalculationDataType.Should().Be(CalculationDataType.Boolean);

            FundingLineResult fundingLineCalcResult = result.FundingLineResults.First(cr => cr.FundingLine.Id == fundingLineCalcReference.Id);

            fundingLineCalcResult.FundingLine.Should().BeEquivalentTo(fundingLineCalcReference);
            fundingLineCalcResult.Value.Should().Be(fundingLineResult.Value);
            fundingLineCalcResult.FundingLineFundingStreamId.Should().Be(fundingStreamId);
        }