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); }
public IActionResult Post([FromBody] NpvOptions npvOptions) { if (!ModelState.IsValid) { return(BadRequest(ModelState)); } var result = _nvpCalculator.Calculate(npvOptions); return(Ok(result)); }
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); }
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()); }
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); }
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); } }
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); }