示例#1
0
        public void MoreThan20ResultShouldOnlyReturn20Records(
            double[] expectedNpvResult, decimal lowerDiscount, decimal upperDiscount,
            decimal increment, decimal outflow, double[] inflows)
        {
            var sut = new NpvCalculator();

            //Validation has been tested on project NpvApi.Dtos.Test so will not test again here.
            var npvOptions = new NpvOptions
            {
                RateOption = new RateOption
                {
                    LowerDiscount     = lowerDiscount,
                    UpperDiscount     = upperDiscount,
                    DiscountIncrement = increment
                },
                Outflow = outflow
            };

            foreach (var inflow in inflows.ToList())
            {
                npvOptions.Inflows.Add(new Inflow()
                {
                    Value = (decimal)inflow
                });
            }

            var result = sut.Calculate(npvOptions);

            Assert.Equal(20, result.Count);
        }
示例#2
0
        public IActionResult Post([FromBody] NpvOptions npvOptions)
        {
            if (!ModelState.IsValid)
            {
                return(BadRequest(ModelState));
            }

            var result = _nvpCalculator.Calculate(npvOptions);

            return(Ok(result));
        }
示例#3
0
        public IList <NpvResult> Calculate(NpvOptions options)
        {
            decimal lowerDiscount  = options.RateOption.LowerDiscount;
            decimal upperDiscount  = options.RateOption.UpperDiscount;
            decimal increment      = options.RateOption.DiscountIncrement;
            decimal initialOutflow = -options.Outflow;

            var inflows = options.Inflows.Select(i => i.Value).ToList();


            var result    = new List <NpvResult>();
            int loopCount = 0;

            do
            {
                var npv = (double)initialOutflow;

                for (int i = 0; i < inflows.Count(); i++)
                {
                    var denom = Math.Pow((1 + (double)lowerDiscount / 100), i + 1);
                    npv += (double)inflows[i] / denom;
                }
                result.Add(new NpvResult()
                {
                    DiscountRate = lowerDiscount,
                    //Round result to 2 digits
                    NetPresentValue = (decimal)Math.Round(npv, 2)
                });

                lowerDiscount += increment;

                //The max results we will return to user to prevent
                //malicious user putting some edge case input to make calculation do very long loop
                loopCount++;
                if (loopCount >= MaxLoopCount)
                {
                    break;
                }

                //If increment = 0 means user only want to calculate one result instead of serial of results
                //In this case normally should set lowerDiscount = higherDiscount, but user specify
                //different value for lower or higherDiscount we just assume we will use the lowerDiscount
                //and make sure we break out of the loop. Negtive should already been checked by ValidationAttribute and should never happen.
                if (increment <= 0)
                {
                    break;
                }
            }while (lowerDiscount <= upperDiscount);

            return(result);
        }
示例#4
0
        public void InvalidInputShouldReturnBadRequestStatusWithModelErrors(double[] expectedNpvResult,
                                                                            decimal lowerDiscount, decimal upperDiscount, decimal increment, decimal outflow,
                                                                            double[] inflows)
        {
            //Arrange
            var stubCalculator = Substitute.For <INpvCalculator>();
            var controller     = new NpvController(stubCalculator);

            //Validation testing has been done on project NpvApi.Dtos.Test so will not test again here.
            var npvOptions = new NpvOptions
            {
                RateOption = new RateOption
                {
                    LowerDiscount = lowerDiscount,
                    UpperDiscount = upperDiscount
                },
                Outflow = outflow
            };

            foreach (var inflow in inflows.ToList())
            {
                npvOptions.Inflows.Add(new Inflow()
                {
                    Value = (decimal)inflow
                });
            }

            const string errorPropertyKey   = "UpperDiscount";
            const string errorPropertyValue = "Required";

            //Add the error to model state to mock 400 BadRequest
            controller.ModelState.AddModelError(errorPropertyKey, errorPropertyValue);

            //Act
            var actionResult     = controller.Post(npvOptions);
            var badRequestResult = actionResult as BadRequestObjectResult;

            //Assert response should contain the model errors
            Assert.NotNull(badRequestResult);
            Assert.Equal(400, badRequestResult.StatusCode);

            var responseContent = badRequestResult.Value as SerializableError;

            Assert.NotNull(responseContent);
            Assert.Equal(1, responseContent.Keys.Count);
            Assert.Equal(errorPropertyKey, responseContent.Keys.First());
            Assert.Equal(errorPropertyValue, (responseContent.Values.First() as string[]).First());
        }
示例#5
0
        public void ValidInputShouldCallApplicationServiceWithCorrectDataAndReturnOk(double[] expectedNpvResult,
                                                                                     decimal lowerDiscount, decimal upperDiscount, decimal increment, decimal outflow,
                                                                                     double[] inflows)
        {
            //Autofixture does not support Asp.net Core so can not use AutoData for
            //NSubstitute and just use InlineData instead

            var mockCalculator = Substitute.For <INpvCalculator>();
            var controller     = new NpvController(mockCalculator);

            //Validation testing has been done on project NpvApi.Dtos.Test so will not test again here.
            var npvOptions = new NpvOptions
            {
                RateOption = new RateOption
                {
                    LowerDiscount = lowerDiscount,
                    UpperDiscount = upperDiscount
                },
                Outflow = outflow
            };

            foreach (var inflow in inflows.ToList())
            {
                npvOptions.Inflows.Add(new Inflow()
                {
                    Value = (decimal)inflow
                });
            }
            var actionResult = controller.Post(npvOptions);

            mockCalculator.Received().Calculate(npvOptions);

            var badRequestResult = actionResult as OkObjectResult;

            //Assert response should contain the model errors
            Assert.NotNull(badRequestResult);
            Assert.Equal(200, badRequestResult.StatusCode);
        }
示例#6
0
        public void ValidInputShouldGetCorrectNpvResults(double[] expectedNpvResult,
                                                         decimal lowerDiscount, decimal upperDiscount, decimal increment, decimal outflow,
                                                         double[] inflows)
        {
            var sut = new NpvCalculator();

            //Validation testing has been done on project NpvApi.Dtos.Test so will not test again here.
            var npvOptions = new NpvOptions
            {
                RateOption = new RateOption
                {
                    LowerDiscount     = lowerDiscount,
                    UpperDiscount     = upperDiscount,
                    DiscountIncrement = increment
                },
                Outflow = outflow
            };

            foreach (var inflow in inflows.ToList())
            {
                npvOptions.Inflows.Add(new Inflow()
                {
                    Value = (decimal)inflow
                });
            }

            var result = sut.Calculate(npvOptions);

            Assert.Equal(expectedNpvResult.Length, result.Count);

            for (int i = 0; i < expectedNpvResult.Length; i++)
            {
                var expected = expectedNpvResult[i];
                var actual   = (double)result[i].NetPresentValue;
                Assert.Equal(expected, actual);
            }
        }
示例#7
0
        public void RateIncrement0ShouldOnlyReturn1ResultBack(
            double[] expectedNpvResult, decimal lowerDiscount, decimal upperDiscount,
            decimal increment, decimal outflow, double[] inflows)
        {
            //This case need to check the result set which is different than the previous one\
            //so just keep it seperate.
            var sut = new NpvCalculator();

            var npvOptions = new NpvOptions
            {
                RateOption = new RateOption
                {
                    LowerDiscount     = lowerDiscount,
                    UpperDiscount     = upperDiscount,
                    DiscountIncrement = increment
                },
                Outflow = outflow
            };

            foreach (var inflow in inflows.ToList())
            {
                npvOptions.Inflows.Add(new Inflow()
                {
                    Value = (decimal)inflow
                });
            }

            var result = sut.Calculate(npvOptions);

            Assert.Equal(1, result.Count);

            var expected = expectedNpvResult.First();
            var actual   = (double)result.First().NetPresentValue;

            Assert.Equal(expected, actual);
        }