public async Task SaveFundingTemplate_GivenInvalidTemplateDueToProfilePeriods_BadRequest()
        {
            //Arrange
            string template        = CreateJsonFile("CalculateFunding.Services.Policy.Resources.LogicalModelTemplate.json");
            string fundingStreamId = "PES";
            string templateVersion = "1.5";
            string fundingPeriodId = "AY-2020";

            FundingTemplateValidationResult validationResult = new FundingTemplateValidationResult
            {
                TemplateVersion = templateVersion,
                FundingStreamId = fundingStreamId,
                SchemaVersion   = "1.0",
            };

            ITemplateMetadataResolver templateMetadataResolver = CreateMetadataResolver("1.0");

            IFundingTemplateValidationService fundingTemplateValidationService = CreateFundingTemplateValidationService();

            fundingTemplateValidationService
            .ValidateFundingTemplate(Arg.Is(template), Arg.Is(fundingStreamId), Arg.Is(fundingPeriodId), Arg.Is(templateVersion))
            .Returns(validationResult);

            IFundingTemplateRepository fundingTemplateRepository = CreateFundingTemplateRepository();

            ILogger logger = CreateLogger();

            ICacheProvider cacheProvider = CreateCacheProvider();

            FundingTemplateService fundingTemplateService = CreateFundingTemplateService(
                logger,
                fundingTemplateValidationService: fundingTemplateValidationService,
                fundingTemplateRepository: fundingTemplateRepository,
                cacheProvider: cacheProvider,
                templateMetadataResolver: templateMetadataResolver);

            //Act
            IActionResult result = await fundingTemplateService.SaveFundingTemplate(createdAtActionName, createdAtControllerName, template, fundingStreamId, fundingPeriodId, templateVersion);

            //Assert
            result
            .Should()
            .BeAssignableTo <BadRequestObjectResult>();

            BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult;

            SerializableError validationResults = badRequestObjectResult.Value as SerializableError;

            validationResults
            .Count()
            .Should()
            .Be(1);

            ((string[])validationResults["DistributionPeriods"])[0]
            .Should()
            .Be("Funding line : 'Total funding line' has values for the distribution periods");
        }
        public async Task SaveFundingTemplate_GivenTemplateButDidNotValidate_ReturnsBadRequest()
        {
            //Arrange
            const string template        = "a template";
            string       fundingStreamId = NewRandomString();
            string       templateVersion = NewRandomString();
            string       fundingPeriodId = NewRandomString();

            FundingTemplateValidationResult validationResult = new FundingTemplateValidationResult();

            validationResult.Errors.Add(new ValidationFailure("prop1", "an error"));
            validationResult.Errors.Add(new ValidationFailure("prop2", "another error"));

            IFundingTemplateValidationService fundingTemplateValidationService = CreateFundingTemplateValidationService();

            fundingTemplateValidationService
            .ValidateFundingTemplate(Arg.Is(template), Arg.Is(fundingStreamId), Arg.Is(fundingPeriodId), Arg.Is(templateVersion))
            .Returns(validationResult);

            FundingTemplateService fundingTemplateService = CreateFundingTemplateService(fundingTemplateValidationService: fundingTemplateValidationService);

            //Act
            IActionResult result = await fundingTemplateService.SaveFundingTemplate(createdAtActionName, createdAtControllerName, template, fundingStreamId, fundingPeriodId, templateVersion);

            //Assert
            result
            .Should()
            .BeAssignableTo <BadRequestObjectResult>();

            BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult;

            badRequestObjectResult
            .Value
            .Should()
            .BeOfType <SerializableError>();

            SerializableError modelState = badRequestObjectResult.Value as SerializableError;

            modelState
            .Should()
            .NotBeNull();

            modelState
            .Values
            .Should()
            .HaveCount(2);
        }
        public async Task SaveFundingTemplate_GivenEmptyTemplate_ReturnsBadRequest(string template)
        {
            //Arrange
            string fundingStreamId = NewRandomString();
            string templateVersion = NewRandomString();
            string fundingPeriodId = NewRandomString();
            FundingTemplateService fundingTemplateService = CreateFundingTemplateService();

            //Act
            IActionResult result = await fundingTemplateService.SaveFundingTemplate(createdAtActionName, createdAtControllerName, template, fundingStreamId, fundingPeriodId, templateVersion);

            //Assert
            result
            .Should()
            .BeAssignableTo <BadRequestObjectResult>()
            .Which
            .Value
            .Should()
            .Be("Null or empty funding template was provided.");
        }
        public async Task SaveFundingTemplate_GivenValidTemplateAndSaves_InvalidatesCacheReturnsCreatedAtActionResult()
        {
            //Arrange
            string template        = CreateJsonFile("CalculateFunding.Services.Policy.Resources.LogicalModelTemplateNoProfilePeriods.json");
            string fundingStreamId = "PES";
            string templateVersion = "1.5";
            string fundingPeriodId = "AY-2020";

            FundingTemplateValidationResult validationResult = new FundingTemplateValidationResult
            {
                TemplateVersion = templateVersion,
                FundingStreamId = fundingStreamId,
                SchemaVersion   = "1.0",
                FundingPeriodId = fundingPeriodId
            };

            string cacheKey = $"{CacheKeys.FundingTemplatePrefix}{validationResult.FundingStreamId}-{validationResult.FundingPeriodId}-{validationResult.TemplateVersion}".ToLowerInvariant();

            ITemplateMetadataResolver templateMetadataResolver = CreateMetadataResolver("1.0");

            IFundingTemplateValidationService fundingTemplateValidationService = CreateFundingTemplateValidationService();

            fundingTemplateValidationService
            .ValidateFundingTemplate(Arg.Is(template), Arg.Is(fundingStreamId), Arg.Is(fundingPeriodId), Arg.Is(templateVersion))
            .Returns(validationResult);

            IFundingTemplateRepository fundingTemplateRepository = CreateFundingTemplateRepository();

            ILogger logger = CreateLogger();

            ICacheProvider cacheProvider = CreateCacheProvider();

            FundingTemplateService fundingTemplateService = CreateFundingTemplateService(
                logger,
                fundingTemplateValidationService: fundingTemplateValidationService,
                fundingTemplateRepository: fundingTemplateRepository,
                cacheProvider: cacheProvider,
                templateMetadataResolver: templateMetadataResolver);

            //Act
            IActionResult result = await fundingTemplateService.SaveFundingTemplate(createdAtActionName, createdAtControllerName, template, fundingStreamId, fundingPeriodId, templateVersion);

            //Assert
            result
            .Should()
            .BeAssignableTo <CreatedAtActionResult>();

            CreatedAtActionResult actionResult = result as CreatedAtActionResult;

            actionResult
            .ActionName
            .Should()
            .Be(createdAtActionName);

            actionResult
            .ControllerName
            .Should()
            .Be(createdAtControllerName);

            actionResult
            .RouteValues["fundingStreamId"].ToString()
            .Should()
            .Be("PES");

            actionResult
            .RouteValues["fundingPeriodId"].ToString()
            .Should()
            .Be("AY-2020");

            actionResult
            .RouteValues["templateVersion"].ToString()
            .Should()
            .Be("1.5");

            await
            cacheProvider
            .Received(1)
            .RemoveAsync <string>(Arg.Is(cacheKey));

            await
            cacheProvider
            .Received(1)
            .RemoveAsync <FundingTemplateContents>(Arg.Is($"{CacheKeys.FundingTemplateContents}pes:ay-2020:1.5"));

            await cacheProvider
            .Received(1)
            .RemoveAsync <TemplateMetadataContents>($"{CacheKeys.FundingTemplateContentMetadata}pes:ay-2020:1.5");

            await cacheProvider
            .Received(1)
            .RemoveAsync <TemplateMetadataDistinctContents>($"{CacheKeys.FundingTemplateContentMetadataDistinct}pes:ay-2020:1.5");
        }
        public async Task SaveFundingTemplate_GivenValidTemplateButFailedToSaveToBlobStorage_ReturnsInternalServerError()
        {
            //Arrange
            const string template        = "a template";
            string       fundingStreamId = NewRandomString();
            string       templateVersion = NewRandomString();
            string       fundingPeriodId = NewRandomString();

            FundingTemplateValidationResult validationResult = new FundingTemplateValidationResult
            {
                TemplateVersion = "1.8",
                FundingStreamId = "PES",
                SchemaVersion   = "1.0",
                FundingPeriodId = "AY-2020"
            };

            string blobName = $"{validationResult.FundingStreamId}/{validationResult.FundingPeriodId}/{validationResult.TemplateVersion}.json";

            IFundingTemplateValidationService fundingTemplateValidationService = CreateFundingTemplateValidationService();

            fundingTemplateValidationService
            .ValidateFundingTemplate(Arg.Is(template), Arg.Is(fundingStreamId), Arg.Is(fundingPeriodId), Arg.Is(templateVersion))
            .Returns(validationResult);

            IFundingTemplateRepository fundingTemplateRepository = CreateFundingTemplateRepository();

            fundingTemplateRepository
            .When(x => x.SaveFundingTemplateVersion(Arg.Is(blobName), Arg.Any <byte[]>()))
            .Do(x => { throw new Exception(); });

            ILogger logger = CreateLogger();

            ITemplateMetadataGenerator templateMetadataGenerator = CreateMetadataGenerator();

            templateMetadataGenerator.Validate(Arg.Is <string>(template))
            .Returns(new FluentValidation.Results.ValidationResult());

            ITemplateMetadataResolver templateMetadataResolver = CreateMetadataResolver("1.0", templateMetadataGenerator);

            FundingTemplateService fundingTemplateService = CreateFundingTemplateService(
                logger,
                fundingTemplateValidationService: fundingTemplateValidationService,
                fundingTemplateRepository: fundingTemplateRepository,
                templateMetadataResolver: templateMetadataResolver);

            //Act
            IActionResult result = await fundingTemplateService.SaveFundingTemplate(createdAtActionName, createdAtControllerName, template, fundingStreamId, fundingPeriodId, templateVersion);

            //Assert
            result
            .Should()
            .BeAssignableTo <InternalServerErrorResult>()
            .Which
            .Value
            .Should()
            .Be("Error occurred uploading funding template");

            logger
            .Received(1)
            .Error(Arg.Any <NonRetriableException>(), Arg.Is($"Failed to save funding template '{blobName}' to blob storage"));
        }