public async Task ValidateAsync_WhenSpecificationDoesNotContainFundingStreamValidIsFalse() { //Arrange CalculationCreateModel model = CreateModel(); SpecModel.SpecificationSummary specificationSummary = new SpecModel.SpecificationSummary { FundingStreams = new[] { new Reference() } }; ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient(); specificationsApiClient .GetSpecificationSummaryById(Arg.Is(model.SpecificationId)) .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specificationSummary)); CalculationCreateModelValidator validator = CreateValidator(specificationsApiClient: specificationsApiClient); //Act ValidationResult result = await validator.ValidateAsync(model); //Assert result .IsValid .Should() .BeFalse(); result.Errors .Should() .Contain(_ => _.ErrorMessage == "The funding stream id provided is not associated with the provided specification."); }
public async Task RunTests_GivenNoTestResults_LogsAndreturnsEmptyResults() { //Arrange IEnumerable <ProviderResult> providerResults = new[] { new ProviderResult { Provider = new ProviderSummary { Id = ProviderId }, SpecificationId = SpecificationId } }; IEnumerable <TestScenario> scenarios = new[] { new TestScenario() }; IEnumerable <ProviderSourceDataset> providerSourceDatasets = new ProviderSourceDataset[0]; IEnumerable <TestScenarioResult> testScenarioResults = new TestScenarioResult[0]; SpecModel.SpecificationSummary specificationSummary = new SpecModel.SpecificationSummary(); BuildProject buildProject = new BuildProject(); ILogger logger = CreateLogger(); TestEngine testEngine = CreateTestEngine(logger: logger); //Act IEnumerable <TestScenarioResult> results = await testEngine.RunTests(scenarios, providerResults, providerSourceDatasets, testScenarioResults, specificationSummary, buildProject); results .Count() .Should() .Be(0); logger .Received(1) .Warning(Arg.Is($"No test results generated for provider: {ProviderId} on specification: {SpecificationId}")); }
private async Task UpdateSearch(IEnumerable <Calculation> calculations, SpecModel.SpecificationSummary specificationSummary) { await _searchRepository.Index(calculations.Select(_ => { return(CreateCalculationIndexItem(_, specificationSummary)); })); }
public async Task RunTests_GivenNoProviders_LogsAndreturnsEmptyResults() { //Arrange IEnumerable <ProviderResult> providerResults = new ProviderResult[0]; IEnumerable <TestScenario> scenarios = new TestScenario[0]; IEnumerable <ProviderSourceDataset> providerSourceDatasets = new ProviderSourceDataset[0]; IEnumerable <TestScenarioResult> testScenarioResults = new TestScenarioResult[0]; SpecModel.SpecificationSummary specificationSummary = new SpecModel.SpecificationSummary(); BuildProject buildProject = new BuildProject(); ILogger logger = CreateLogger(); TestEngine testEngine = CreateTestEngine(logger: logger); //Act IEnumerable <TestScenarioResult> results = await testEngine.RunTests(scenarios, providerResults, providerSourceDatasets, testScenarioResults, specificationSummary, buildProject); results .Count() .Should() .Be(0); logger .Received(1) .Warning(Arg.Is("No provider results were supplied to execute tests")); }
public async Task <bool?> IsCalculationNameInUse(string specificationId, string calculationName, string existingCalculationId) { Guard.IsNullOrWhiteSpace(specificationId, nameof(specificationId)); Guard.IsNullOrWhiteSpace(calculationName, nameof(calculationName)); ApiResponse <SpecModel.SpecificationSummary> specificationApiResponse = await _specificationsApiClientPolicy.ExecuteAsync(() => _specificationsApiClient.GetSpecificationSummaryById(specificationId)); if (specificationApiResponse == null || !specificationApiResponse.StatusCode.IsSuccess() || specificationApiResponse.Content == null) { return(null); } SpecModel.SpecificationSummary specification = specificationApiResponse.Content; IEnumerable <Calculation> existingCalculations = await _calculationRepositoryPolicy.ExecuteAsync(() => _calculationsRepository.GetCalculationsBySpecificationId(specificationId)); if (!existingCalculations.IsNullOrEmpty()) { string calcSourceName = _typeIdentifierGenerator.GenerateIdentifier(calculationName); foreach (Calculation calculation in existingCalculations) { if (calculation.Id != existingCalculationId && string.Compare(calculation.Current.SourceCodeName, calcSourceName, true) == 0) { return(true); } } } return(false); }
private async Task UpdateCalculationsInCache(SpecModel.SpecificationSummary specificationSummary) { // Invalidate cached calculations for this specification await _cachePolicy.ExecuteAsync(() => _cacheProvider.KeyDeleteAsync <List <CalculationSummaryModel> >($"{CacheKeys.CalculationsSummariesForSpecification}{specificationSummary.Id}")); await _cachePolicy.ExecuteAsync(() => _cacheProvider.KeyDeleteAsync <List <CalculationResponseModel> >($"{CacheKeys.CurrentCalculationsForSpecification}{specificationSummary.Id}")); await _cachePolicy.ExecuteAsync(() => _cacheProvider.KeyDeleteAsync <List <CalculationResponseModel> >($"{CacheKeys.CalculationsMetadataForSpecification}{specificationSummary.Id}")); }
public async Task ApproveAllCalculations_CalcExistsOnGivenSpecification_UpdateBulkPublishStatus() { ApproveAllCalculationsService approveAllCalculationsService = BuildApproveAllCalculationsService(); GivenTheOtherwiseValidMessage(); IEnumerable <Calculation> calculations = new List <Calculation> { NewCalculation(c => c.WithId("calc_1").WithFundingStreamId(_fundingStreamId).WithCurrentVersion(NewCalculationVersion(cv => cv.WithPublishStatus(PublishStatus.Draft)))), NewCalculation(c => c.WithId("calc_2").WithFundingStreamId(_fundingStreamId).WithCurrentVersion(NewCalculationVersion(cv => cv.WithPublishStatus(PublishStatus.Updated)))) }; _calculationsRepositoryMock .Setup(_ => _.GetCalculationsBySpecificationId(_specificationId)) .ReturnsAsync(calculations); SpecModel.SpecificationSummary specificationSummary = new SpecModel.SpecificationSummary() { Id = _specificationId, Name = "spec name", FundingStreams = new[] { new Reference(_fundingStreamId, "funding stream name") } }; _specificationsApiClientMock .Setup(_ => _.GetSpecificationSummaryById(_specificationId)) .ReturnsAsync(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specificationSummary)); await approveAllCalculationsService.Process(_message); _cacheProviderMock.Verify(_ => _.KeyDeleteAsync <List <CalculationSummaryModel> >($"{CacheKeys.CalculationsSummariesForSpecification}{_specificationId}") , Times.Once); _cacheProviderMock.Verify(_ => _.KeyDeleteAsync <List <CalculationResponseModel> >($"{CacheKeys.CurrentCalculationsForSpecification}{_specificationId}") , Times.Once); _cacheProviderMock.Verify(_ => _.KeyDeleteAsync <List <CalculationResponseModel> >($"{CacheKeys.CalculationsMetadataForSpecification}{_specificationId}") , Times.Once); _cacheProviderMock.Verify(_ => _.SetAsync($"{CacheKeys.CurrentCalculation}calc_1", It.IsAny <CalculationResponseModel>(), TimeSpan.FromDays(7), true, null) , Times.Once); _cacheProviderMock.Verify(_ => _.SetAsync($"{CacheKeys.CurrentCalculation}calc_2", It.IsAny <CalculationResponseModel>(), TimeSpan.FromDays(7), true, null) , Times.Once); _searchRepositoryMock.Verify(_ => _.Index(It.Is <IEnumerable <CalculationIndex> >(c => c.FirstOrDefault() != null && c.FirstOrDefault().Status == PublishStatus.Approved.ToString() && c.LastOrDefault() != null && c.LastOrDefault().Status == PublishStatus.Approved.ToString())) , Times.Once); _calculationsRepositoryMock .Verify(_ => _.UpdateCalculations( It.Is <IEnumerable <Calculation> >(c => c.FirstOrDefault() != null && c.FirstOrDefault().Current.PublishStatus == PublishStatus.Approved && c.LastOrDefault() != null && c.LastOrDefault().Current.PublishStatus == PublishStatus.Approved)), Times.Once); }
public async Task ValidateAsync_WhenSourceCodeDoesNotCompile__ValidIsFalse() { //Arrange CalculationCreateModel model = CreateModel(); model.CalculationType = CalculationType.Additional; ICalculationsRepository calculationsRepository = CreateCalculationRepository(); calculationsRepository .GetCalculationsBySpecificationIdAndCalculationName(Arg.Is(model.SpecificationId), Arg.Is(model.Name)) .Returns((Calculation)null); SpecModel.SpecificationSummary specificationSummary = new SpecModel.SpecificationSummary { Name = "spec name", FundingStreams = new[] { new Reference(model.FundingStreamId, "funding stream name") } }; ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient(); specificationsApiClient .GetSpecificationSummaryById(Arg.Is(model.SpecificationId)) .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specificationSummary)); PreviewResponse previewResponse = new PreviewResponse { CompilerOutput = new Build { CompilerMessages = new List <CompilerMessage> { new CompilerMessage { Message = "Failed" } } } }; IPreviewService previewService = CreatePreviewService(previewResponse); CalculationCreateModelValidator validator = CreateValidator( calculationsRepository, previewService: previewService, specificationsApiClient: specificationsApiClient); //Act ValidationResult result = await validator.ValidateAsync(model); //Assert result .IsValid .Should() .BeFalse(); result.Errors .Should() .Contain(_ => _.ErrorMessage == "There are errors in the source code provided"); }
public async Task RunTests_GivenTestResultsReturnedFromExecutorWhereNoStepsExecuted_ReturnsOneIgnoreTestResult() { //Arrange ProviderResult providerResult = new ProviderResult { Provider = new ProviderSummary { Id = ProviderId, Name = "any provider" }, SpecificationId = SpecificationId }; IEnumerable <ProviderResult> providerResults = new[] { providerResult }; IEnumerable <TestScenario> scenarios = new[] { new TestScenario() }; IEnumerable <ProviderSourceDataset> providerSourceDatasets = new ProviderSourceDataset[0]; IEnumerable <TestScenarioResult> testScenarioResults = new TestScenarioResult[0]; SpecModel.SpecificationSummary specificationSummary = new SpecModel.SpecificationSummary { Id = SpecificationId, Name = "spec-name" }; BuildProject buildProject = new BuildProject(); IEnumerable <ScenarioResult> scenarioResults = new[] { new ScenarioResult { Scenario = new Reference("sceanrio=id", "scenario name") } }; ILogger logger = CreateLogger(); IGherkinExecutor gherkinExecutor = CreateGherkinExecutor(); gherkinExecutor .Execute(Arg.Any <ProviderResult>(), Arg.Any <IEnumerable <ProviderSourceDataset> >(), Arg.Any <IEnumerable <TestScenario> >(), Arg.Any <BuildProject>()) .Returns(scenarioResults); TestEngine testEngine = CreateTestEngine(gherkinExecutor, logger); //Act IEnumerable <TestScenarioResult> results = await testEngine.RunTests(scenarios, providerResults, providerSourceDatasets, testScenarioResults, specificationSummary, buildProject); results .Count() .Should() .Be(1); results .First() .TestResult .Should() .Be(TestResult.Ignored); }
public async Task QueryMethodDelegatesToApiClientAndReturnsContentFromResponse() { string specificationId = new RandomString(); ApiSpecificationSummary expectedSpecificationSummary = new ApiSpecificationSummary(); GivenTheApiResponseContentForTheSpecificationId(expectedSpecificationSummary, specificationId); ApiSpecificationSummary specificationSummary = await WhenTheSpecificationSummaryIsQueried(specificationId); specificationSummary .Should() .BeSameAs(expectedSpecificationSummary); }
public async Task <IActionResult> ReIndex() { IEnumerable <DocumentEntity <TestScenario> > testScenarios = await _scenariosRepository.GetAllTestScenarios(); List <ScenarioIndex> testScenarioIndexes = new List <ScenarioIndex>(); Dictionary <string, SpecModel.SpecificationSummary> specifications = new Dictionary <string, SpecModel.SpecificationSummary>(); foreach (DocumentEntity <TestScenario> entity in testScenarios) { TestScenario testScenario = entity.Content; SpecModel.SpecificationSummary specificationSummary = null; if (!specifications.ContainsKey(testScenario.SpecificationId)) { Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary> specificationApiResponse = await _specificationsApiClientPolicy.ExecuteAsync(() => _specificationsApiClient.GetSpecificationSummaryById(testScenario.SpecificationId)); specificationSummary = specificationApiResponse.Content; specifications.Add(testScenario.SpecificationId, specificationSummary); } else { specificationSummary = specifications[testScenario.SpecificationId]; } testScenarioIndexes.Add(new ScenarioIndex() { Id = testScenario.Id, Name = testScenario.Name, Description = testScenario.Current.Description, LastUpdatedDate = entity.UpdatedAt, FundingStreamIds = testScenario.Current?.FundingStreamIds.ToArray(), FundingStreamNames = specificationSummary.FundingStreams.Select(s => s.Name).ToArray(), FundingPeriodId = testScenario.Current?.FundingPeriodId, FundingPeriodName = specificationSummary.FundingPeriod.Name, SpecificationId = testScenario.SpecificationId, SpecificationName = specificationSummary.Name, Status = Enum.GetName(typeof(PublishStatus), testScenario.Current.PublishStatus), }); } await _searchRepository.Index(testScenarioIndexes); return(new OkObjectResult($"Updated {testScenarioIndexes.Count} records")); }
public async Task GetEffectivePermissionsForUser_WhenNotFoundInCacheButSpecificationNotFound_ThenPreconditionFailedResultReturned() { // Arrange ICacheProvider cacheProvider = CreateCacheProvider(); EffectiveSpecificationPermission cachedPermission = null; cacheProvider .GetHashValue <EffectiveSpecificationPermission>(Arg.Is($"{CacheKeys.EffectivePermissions}:{UserId}"), Arg.Is(SpecificationId)) .Returns(cachedPermission); ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient(); SpecModel.SpecificationSummary specificationSummary = null; specificationsApiClient .GetSpecificationSummaryById(Arg.Is(SpecificationId)) .Returns(new ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specificationSummary)); FundingStreamPermissionService service = CreateService( specificationsApiClient: specificationsApiClient, cacheProvider: cacheProvider); // Act IActionResult result = await service.GetEffectivePermissionsForUser(UserId, SpecificationId); // Assert result .Should() .BeOfType <PreconditionFailedResult>() .Which .Value .Should() .Be("Specification not found"); await cacheProvider .Received(1) .GetHashValue <EffectiveSpecificationPermission>(Arg.Is($"{CacheKeys.EffectivePermissions}:{UserId}"), Arg.Is(SpecificationId)); await cacheProvider .Received(0) .SetHashValue( Arg.Is($"{CacheKeys.EffectivePermissions}:{UserId}"), Arg.Is(SpecificationId), Arg.Any <EffectiveSpecificationPermission>()); }
public async Task EditCalculationStatus_GivenNewStatusButUpdatingDbReturnsBadRequest_ReturnsStatusCode400() { //Arrange EditStatusModel CalculationEditStatusModel = new EditStatusModel { PublishStatus = PublishStatus.Approved }; ILogger logger = CreateLogger(); Calculation calculation = CreateCalculation(); ICalculationsRepository CalculationsRepository = CreateCalculationsRepository(); CalculationsRepository .GetCalculationById(Arg.Is(CalculationId)) .Returns(calculation); CalculationsRepository .UpdateCalculation(Arg.Any <Calculation>()) .Returns(HttpStatusCode.BadRequest); SpecModel.SpecificationSummary specificationSummary = new SpecModel.SpecificationSummary(); ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient(); specificationsApiClient .GetSpecificationSummaryById(Arg.Is(calculation.SpecificationId)) .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specificationSummary)); CalculationService service = CreateCalculationService( logger: logger, calculationsRepository: CalculationsRepository, specificationsApiClient: specificationsApiClient); //Act IActionResult result = await service.UpdateCalculationStatus(CalculationId, CalculationEditStatusModel); //Arrange result .Should() .BeAssignableTo <StatusCodeResult>() .Which .StatusCode .Should() .Be(400); }
public override async Task Process(Message message) { Guard.ArgumentNotNull(message, nameof(message)); string specificationId = UserPropertyFrom(message, "specification-id"); IEnumerable <Calculation> calculations = (await _calculationRepositoryPolicy.ExecuteAsync(() => _calculationsRepository.GetCalculationsBySpecificationId(specificationId))) .ToArraySafe(); if (calculations.IsNullOrEmpty()) { string calcNotFoundMessage = $"No calculations found for specification id: {specificationId}"; _logger.Information(calcNotFoundMessage); return; } foreach (Calculation calculation in calculations) { calculation.Current.PublishStatus = Models.Versioning.PublishStatus.Approved; } ApiResponse <SpecModel.SpecificationSummary> specificationApiResponse = await _specificationsApiClientPolicy.ExecuteAsync(() => _specificationsApiClient.GetSpecificationSummaryById(specificationId)); if (!specificationApiResponse.StatusCode.IsSuccess() || specificationApiResponse.Content == null) { throw new NonRetriableException("Specification not found"); } SpecModel.SpecificationSummary specificationSummary = specificationApiResponse.Content; await _calculationRepositoryPolicy.ExecuteAsync(() => _calculationsRepository.UpdateCalculations(calculations)); await UpdateSearch(calculations, specificationSummary); IEnumerable <Task> tasks = calculations.Select(_ => UpdateCalculationInCache(_.ToResponseModel())); await TaskHelper.WhenAllAndThrow(tasks.ToArraySafe()); await UpdateCalculationsInCache(specificationSummary); }
public async Task ValidateAsync_WhenValidModel_ValidIsTrue() { //Arrange CalculationCreateModel model = CreateModel(); ICalculationsRepository calculationsRepository = CreateCalculationRepository(); calculationsRepository .GetCalculationsBySpecificationIdAndCalculationName(Arg.Is(model.SpecificationId), Arg.Is(model.Name)) .Returns((Calculation)null); SpecModel.SpecificationSummary specificationSummary = new SpecModel.SpecificationSummary { Name = "spec name", FundingStreams = new[] { new Reference(model.FundingStreamId, "funding stream name") } }; ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient(); specificationsApiClient .GetSpecificationSummaryById(Arg.Is(model.SpecificationId)) .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specificationSummary)); CalculationCreateModelValidator validator = CreateValidator( calculationsRepository, specificationsApiClient: specificationsApiClient); //Act ValidationResult result = await validator.ValidateAsync(model); //Assert result .IsValid .Should() .BeTrue(); model.SpecificationName .Should() .Be("spec name"); model.FundingStreamName .Should() .Be("funding stream name"); }
private CalculationIndex CreateCalculationIndexItem(Calculation calculation, SpecModel.SpecificationSummary specificationSummary) { return(new CalculationIndex { Id = calculation.Id, SpecificationId = calculation.SpecificationId, SpecificationName = specificationSummary.Name, Name = calculation.Current.Name, ValueType = calculation.Current.ValueType.ToString(), FundingStreamId = calculation.FundingStreamId ?? "N/A", FundingStreamName = specificationSummary.FundingStreams.FirstOrDefault(_ => _.Id == calculation.FundingStreamId)?.Name ?? "N/A", Namespace = calculation.Current.Namespace.ToString(), CalculationType = calculation.Current.CalculationType.ToString(), Description = calculation.Current.Description, WasTemplateCalculation = calculation.Current.WasTemplateCalculation, Status = calculation.Current.PublishStatus.ToString(), LastUpdatedDate = DateTimeOffset.Now }); }
private static ISpecificationsApiClient CreateSpecificationsApiClient() { ISpecificationsApiClient specificationsApiClient = Substitute.For <ISpecificationsApiClient>(); SpecModel.SpecificationSummary specificationSummary = new SpecModel.SpecificationSummary() { TemplateIds = new Dictionary <string, string> { { "fs-1", "2.2" } }, FundingStreams = new List <Reference>() { new Reference("fs-1", "PE and Sports"), }, }; specificationsApiClient .GetSpecificationSummaryById(Arg.Any <string>()) .Returns(new ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specificationSummary)); return(specificationsApiClient); }
public async Task EditCalculationStatus_GivenCalculationIsApprovedButNewStatusIsDraft_UpdatesSearchReturnsOK() { //Arrange EditStatusModel CalculationEditStatusModel = new EditStatusModel { PublishStatus = PublishStatus.Draft }; ILogger logger = CreateLogger(); Calculation calculation = CreateCalculation(); calculation.Current.PublishStatus = PublishStatus.Approved; ICalculationsRepository CalculationsRepository = CreateCalculationsRepository(); CalculationsRepository .GetCalculationById(Arg.Is(CalculationId)) .Returns(calculation); CalculationsRepository .UpdateCalculation(Arg.Any <Calculation>()) .Returns(HttpStatusCode.OK); SpecModel.SpecificationSummary specificationSummary = new SpecModel.SpecificationSummary(); ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient(); specificationsApiClient .GetSpecificationSummaryById(Arg.Is(calculation.SpecificationId)) .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specificationSummary)); ISearchRepository <CalculationIndex> searchRepository = CreateSearchRepository(); CalculationService service = CreateCalculationService( logger: logger, calculationsRepository: CalculationsRepository, searchRepository: searchRepository, specificationsApiClient: specificationsApiClient); //Act IActionResult result = await service.UpdateCalculationStatus(CalculationId, CalculationEditStatusModel); //Arrange result .Should() .BeOfType <BadRequestObjectResult>() .Which .Value .Should() .Be("Publish status can't be changed to Draft from Updated or Approved"); calculation .Current .PublishStatus .Should() .Be(PublishStatus.Approved); await searchRepository .Received(0) .Index(Arg.Any <IEnumerable <CalculationIndex> >()); }
public async Task UpdateCalculationCodeOnCalculationChange_WhenCalculationsFoundReferencingCalculationToBeUpdated_ThenSourceCodeUpdated() { // Arrange ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient(); IVersionRepository <CalculationVersion> versionRepository = CreateCalculationVersionRepository(); IBuildProjectsService buildProjectsService = CreateBuildProjectsService(); ISourceCodeService sourceCodeService = CreateSourceCodeService(); CalculationService service = CreateCalculationService(calculationsRepository: calculationsRepository, specificationsApiClient: specificationsApiClient, calculationVersionRepository: versionRepository, buildProjectsService: buildProjectsService, sourceCodeService: sourceCodeService); const string specificationId = "specId"; const string calculationId = "updatedCalc"; const string originalCodeUpdate = @"Dim test as OriginalNameOptions? OriginalNameOptions.enumName Return Calculations.OriginalName()"; const string newCodeUpdated = @"Dim test as CalculationToUpdateOptions? CalculationToUpdateOptions.enumName Return Calculations.CalculationToUpdate()"; const string originalCodeIgnore = "Return 10"; const string fundingStreamId = "fundingstreamid"; CalculationVersionComparisonModel comparison = new CalculationVersionComparisonModel() { CalculationId = calculationId, CurrentName = "Calculation To Update", PreviousName = "Original Name", SpecificationId = specificationId, CalculationDataType = CalculationDataType.Enum, Namespace = "Calculations" }; Reference user = new Reference("userId", "User Name"); List <Calculation> calculations = new List <Calculation> { new Calculation { Id = calculationId, SpecificationId = specificationId, FundingStreamId = fundingStreamId, Current = new CalculationVersion { SourceCode = originalCodeIgnore, Name = "Calculation to Update", CalculationType = CalculationType.Template, Description = "Calculation Description", DataType = CalculationDataType.Enum } }, new Calculation { Id = "referenceCalc", SpecificationId = specificationId, FundingStreamId = fundingStreamId, Current = new CalculationVersion { SourceCode = originalCodeUpdate, Name = "Calling Calculation To Update", CalculationType = CalculationType.Template, Description = "Calculation Description" } } }; calculationsRepository .GetCalculationsBySpecificationId(Arg.Is(specificationId)) .Returns(calculations); calculationsRepository .UpdateCalculation(Arg.Any <Calculation>()) .Returns(HttpStatusCode.OK); calculationsRepository .GetCalculationById(Arg.Is(calculations[0].Id)) .Returns(calculations[0]); SpecModel.SpecificationSummary specification = new SpecModel.SpecificationSummary() { Id = specificationId, Name = "Specification Name", FundingStreams = new [] { new Reference(fundingStreamId, "fundingStreamName"), } }; specificationsApiClient .GetSpecificationSummaryById(Arg.Is(specificationId)) .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specification)); CalculationVersion calculationVersion = new CalculationVersion { SourceCode = newCodeUpdated, Version = 2 }; versionRepository .CreateVersion(Arg.Is <CalculationVersion>(_ => _.SourceCode == newCodeUpdated), Arg.Any <CalculationVersion>()) .Returns(calculationVersion); buildProjectsService .GetBuildProjectForSpecificationId(specificationId) .Returns(new BuildProject()); sourceCodeService .Compile(Arg.Any <BuildProject>(), Arg.Any <IEnumerable <Calculation> >(), Arg.Any <CompilerOptions>()) .Returns(new Build()); // Act IEnumerable <Calculation> updatedCalculations = await service.UpdateCalculationCodeOnCalculationChange(comparison, user); // Assert updatedCalculations .Should() .HaveCount(1); Calculation calculation = updatedCalculations.Single(); calculation.Current.SourceCode .Should() .Be(newCodeUpdated); calculation.Current.Version .Should() .Be(2); calculation.Id .Should() .Be("referenceCalc"); }
public async Task GetEffectivePermissionsForUser_WhenNotFoundInCacheResultsAreQueriedWithMultipleFundingStreamAndNoPermissionsAreInRepository_ThenOkResultReturnedWithNoPermissions() { // Arrange IUserRepository userRepository = CreateUserRepository(); ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient(); ICacheProvider cacheProvider = CreateCacheProvider(); IMapper mapper = CreateMappingConfiguration(); EffectiveSpecificationPermission cachedPermission = null; cacheProvider .GetHashValue <EffectiveSpecificationPermission>(Arg.Is($"{CacheKeys.EffectivePermissions}:{UserId}"), Arg.Is(SpecificationId)) .Returns(cachedPermission); SpecModel.SpecificationSummary specificationSummary = new SpecModel.SpecificationSummary() { Id = SpecificationId, FundingStreams = new List <Reference>() { new Reference("fs1", "Funding Stream 1"), new Reference("fs2", "Funding Stream 2") } }; specificationsApiClient .GetSpecificationSummaryById(Arg.Is(SpecificationId)) .Returns(new ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specificationSummary)); FundingStreamPermission fs1Permission = null; userRepository .GetFundingStreamPermission(Arg.Is(UserId), Arg.Is("fs1")) .Returns(fs1Permission); FundingStreamPermission fs2Permission = null; userRepository .GetFundingStreamPermission(Arg.Is(UserId), Arg.Is("fs2")) .Returns(fs2Permission); FundingStreamPermissionService service = CreateService(userRepository, specificationsApiClient, cacheProvider: cacheProvider, mapper: mapper); // Act IActionResult result = await service.GetEffectivePermissionsForUser(UserId, SpecificationId); // Assert result .Should() .BeOfType <OkObjectResult>() .Which .Value .Should() .BeEquivalentTo(new EffectiveSpecificationPermission() { UserId = UserId, SpecificationId = SpecificationId, CanApproveFunding = false, CanCreateSpecification = false, CanMapDatasets = false, CanChooseFunding = false, CanEditCalculations = false, CanEditSpecification = false, CanReleaseFunding = false, CanAdministerFundingStream = false, CanApproveSpecification = false, CanCreateQaTests = false, CanEditQaTests = false, CanRefreshFunding = false, }); await cacheProvider .Received(1) .SetHashValue( Arg.Is($"{CacheKeys.EffectivePermissions}:{UserId}"), Arg.Is(SpecificationId), Arg.Is <EffectiveSpecificationPermission>(p => !p.CanApproveFunding && !p.CanChooseFunding && !p.CanCreateSpecification && !p.CanEditCalculations && !p.CanEditSpecification && !p.CanMapDatasets && !p.CanReleaseFunding && !p.CanAdministerFundingStream && !p.CanApproveSpecification && !p.CanCreateQaTests && !p.CanEditQaTests && !p.CanRefreshFunding && p.SpecificationId == SpecificationId && p.UserId == UserId )); }
public async Task EditCalculationStatus_GivenNewStatusOfUpdated_UpdatesSearchReturnsOK() { //Arrange EditStatusModel CalculationEditStatusModel = new EditStatusModel { PublishStatus = PublishStatus.Updated }; ILogger logger = CreateLogger(); Calculation calculation = CreateCalculation(); calculation.Current.PublishStatus = PublishStatus.Approved; CalculationVersion calculationVersion = calculation.Current.Clone() as CalculationVersion; calculationVersion.PublishStatus = PublishStatus.Updated; ICalculationsRepository CalculationsRepository = CreateCalculationsRepository(); CalculationsRepository .GetCalculationById(Arg.Is(CalculationId)) .Returns(calculation); CalculationsRepository .UpdateCalculation(Arg.Any <Calculation>()) .Returns(HttpStatusCode.OK); ISearchRepository <CalculationIndex> searchRepository = CreateSearchRepository(); SpecModel.SpecificationSummary specificationSummary = new SpecModel.SpecificationSummary() { Id = calculation.SpecificationId, Name = "spec name", FundingStreams = new[] { new Reference(calculation.FundingStreamId, "funding stream name") } }; ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient(); specificationsApiClient .GetSpecificationSummaryById(Arg.Is(calculation.SpecificationId)) .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specificationSummary)); IVersionRepository <CalculationVersion> versionRepository = CreateCalculationVersionRepository(); versionRepository .CreateVersion(Arg.Any <CalculationVersion>(), Arg.Any <CalculationVersion>()) .Returns(calculationVersion); Build build = new Build { SourceFiles = new List <SourceFile> { new SourceFile() } }; BuildProject buildProject = new BuildProject(); IBuildProjectsService buildProjectsService = CreateBuildProjectsService(); buildProjectsService .GetBuildProjectForSpecificationId(Arg.Is(calculation.SpecificationId)) .Returns(buildProject); ISourceCodeService sourceCodeService = CreateSourceCodeService(); sourceCodeService .Compile(Arg.Any <BuildProject>(), Arg.Any <IEnumerable <Models.Calcs.Calculation> >(), Arg.Any <CompilerOptions>()) .Returns(build); CalculationService service = CreateCalculationService( logger: logger, calculationsRepository: CalculationsRepository, searchRepository: searchRepository, specificationsApiClient: specificationsApiClient, calculationVersionRepository: versionRepository, sourceCodeService: sourceCodeService, buildProjectsService: buildProjectsService); //Act IActionResult result = await service.UpdateCalculationStatus(CalculationId, CalculationEditStatusModel); //Arrange result .Should() .BeOfType <OkObjectResult>() .Which .Value .Should() .BeOfType <PublishStatusResultModel>() .Which .PublishStatus .Should() .Be(PublishStatus.Updated); calculation .Current .PublishStatus .Should() .Be(PublishStatus.Updated); }
private void GivenTheApiResponseContentForTheSpecificationId(ApiSpecificationSummary specificationSummary, string specificationId) { _specifications.GetSpecificationSummaryById(specificationId) .Returns(new ApiResponse <ApiSpecificationSummary>(HttpStatusCode.OK, specificationSummary)); }
public async Task <IActionResult> ReIndexCalculationProviderResults() { IEnumerable <DocumentEntity <ProviderResult> > providerResults = await _resultsRepositoryPolicy.ExecuteAsync(() => _resultsRepository.GetAllProviderResults()); IList <ProviderCalculationResultsIndex> searchItems = new List <ProviderCalculationResultsIndex>(); Dictionary <string, SpecModel.SpecificationSummary> specifications = new Dictionary <string, SpecModel.SpecificationSummary>(); foreach (DocumentEntity <ProviderResult> documentEntity in providerResults) { ProviderResult providerResult = documentEntity.Content; foreach (CalculationResult calculationResult in providerResult.CalculationResults) { SpecModel.SpecificationSummary specificationSummary = null; if (!specifications.ContainsKey(providerResult.SpecificationId)) { Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary> specificationApiResponse = await _specificationsApiClientPolicy.ExecuteAsync(() => _specificationsApiClient.GetSpecificationSummaryById(providerResult.SpecificationId)); if (!specificationApiResponse.StatusCode.IsSuccess() || specificationApiResponse.Content == null) { throw new InvalidOperationException($"Specification Summary returned null for specification ID '{providerResult.SpecificationId}'"); } specificationSummary = specificationApiResponse.Content; specifications.Add(providerResult.SpecificationId, specificationSummary); } else { specificationSummary = specifications[providerResult.SpecificationId]; } ProviderCalculationResultsIndex searchItem = new ProviderCalculationResultsIndex { SpecificationId = providerResult.SpecificationId, SpecificationName = specificationSummary?.Name, CalculationName = providerResult.CalculationResults.Select(x => x.Calculation.Name).ToArraySafe(), CalculationId = providerResult.CalculationResults.Select(x => x.Calculation.Id).ToArraySafe(), ProviderId = providerResult.Provider.Id, ProviderName = providerResult.Provider.Name, ProviderType = providerResult.Provider.ProviderType, ProviderSubType = providerResult.Provider.ProviderSubType, LocalAuthority = providerResult.Provider.Authority, LastUpdatedDate = documentEntity.UpdatedAt, UKPRN = providerResult.Provider.UKPRN, URN = providerResult.Provider.URN, UPIN = providerResult.Provider.UPIN, EstablishmentNumber = providerResult.Provider.EstablishmentNumber, OpenDate = providerResult.Provider.DateOpened, CalculationResult = providerResult.CalculationResults.Select(m => !string.IsNullOrEmpty(m.Value?.ToString()) ? m.Value.ToString() : "null").ToArraySafe() }; if (_featureToggle.IsExceptionMessagesEnabled()) { searchItem.CalculationException = providerResult.CalculationResults .Where(m => !string.IsNullOrWhiteSpace(m.ExceptionType)) .Select(e => e.Calculation.Id) .ToArraySafe(); searchItem.CalculationExceptionType = providerResult.CalculationResults .Select(m => m.ExceptionType ?? string.Empty) .ToArraySafe(); searchItem.CalculationExceptionMessage = providerResult.CalculationResults .Select(m => m.ExceptionMessage ?? string.Empty) .ToArraySafe(); } searchItems.Add(searchItem); } } const int partitionSize = 500; for (int i = 0; i < searchItems.Count; i += partitionSize) { IEnumerable <ProviderCalculationResultsIndex> partitionedResults = searchItems .Skip(i) .Take(partitionSize); IEnumerable <IndexError> errors = await _resultsSearchRepositoryPolicy.ExecuteAsync(() => _calculationProviderResultsSearchRepository.Index(partitionedResults)); if (errors.Any()) { _logger.Error($"Failed to index calculation provider result documents with errors: { string.Join(";", errors.Select(m => m.ErrorMessage)) }"); return(new InternalServerErrorResult(null)); } } return(new NoContentResult()); }
public async Task <IActionResult> GetEffectivePermissionsForUser(string userId, string specificationId) { if (string.IsNullOrWhiteSpace(userId)) { return(new BadRequestObjectResult($"{nameof(userId)} is empty or null")); } if (string.IsNullOrWhiteSpace(specificationId)) { return(new BadRequestObjectResult($"{nameof(specificationId)} is empty or null")); } EffectiveSpecificationPermission cachedPermissions = await _cacheProviderPolicy.ExecuteAsync(() => _cacheProvider.GetHashValue <EffectiveSpecificationPermission>($"{CacheKeys.EffectivePermissions}:{userId}", specificationId)); if (cachedPermissions != null) { return(new OkObjectResult(cachedPermissions)); } else { ApiResponse <SpecModel.SpecificationSummary> specificationApiResponse = await _specificationsApiClientPolicy.ExecuteAsync(() => _specificationsApiClient.GetSpecificationSummaryById(specificationId)); if (!specificationApiResponse.StatusCode.IsSuccess() || specificationApiResponse.Content == null) { return(new PreconditionFailedResult("Specification not found")); } SpecModel.SpecificationSummary specification = specificationApiResponse.Content; List <FundingStreamPermission> permissionsForUser = new List <FundingStreamPermission>(); foreach (Reference fundingStream in specification.FundingStreams) { FundingStreamPermission permission = await _userRepositoryPolicy.ExecuteAsync(() => _userRepository.GetFundingStreamPermission(userId, fundingStream.Id)); if (permission != null) { permissionsForUser.Add(permission); } else { // Add permission for this funding stream with no permissions - used further down to calculate permissions (required for pessimistic permissions) permissionsForUser.Add(new FundingStreamPermission { UserId = userId, FundingStreamId = fundingStream.Id, CanApproveFunding = false, CanChooseFunding = false, CanCreateSpecification = false, CanEditCalculations = false, CanEditSpecification = false, CanMapDatasets = false, CanReleaseFunding = false, CanAdministerFundingStream = false, CanApproveSpecification = false, CanCreateQaTests = false, CanDeleteCalculations = false, CanDeleteSpecification = false, CanDeleteQaTests = false, CanEditQaTests = false, CanRefreshFunding = false, CanCreateTemplates = false, CanEditTemplates = false, CanDeleteTemplates = false, CanApproveTemplates = false, CanCreateProfilePattern = false, CanEditProfilePattern = false, CanDeleteProfilePattern = false, CanAssignProfilePattern = false, CanApplyCustomProfilePattern = false, CanApproveCalculations = false, CanApproveAnyCalculations = false, CanApproveAllCalculations = false }); } } EffectiveSpecificationPermission specificationPermissions = GeneratePermissions(permissionsForUser, specificationId, userId); string userPermissionHashKey = $"{CacheKeys.EffectivePermissions}:{userId}"; // Does the hash set for this user already exist - used to determine the timeout for the hash set below bool existingHashSetExists = await _cacheProviderPolicy.ExecuteAsync(() => _cacheProvider.HashSetExists(userPermissionHashKey)); // Cache effective permissions for the specification / user await _cacheProviderPolicy.ExecuteAsync(() => _cacheProvider.SetHashValue(userPermissionHashKey, specificationId, specificationPermissions)); // If the hash set does not exist, then set an expiry for the whole hash set. This stops the users permissions being stored indefinitely if (!existingHashSetExists) { await _cacheProviderPolicy.ExecuteAsync(() => _cacheProvider.SetHashExpiry(userPermissionHashKey, DateTime.UtcNow.AddHours(12))); } return(new OkObjectResult(specificationPermissions)); } }
public async Task <IEnumerable <TestScenarioResult> > RunTests(IEnumerable <TestScenario> testScenarios, IEnumerable <ProviderResult> providerResults, IEnumerable <ProviderSourceDataset> sourceDatasets, IEnumerable <TestScenarioResult> currentResults, SpecModel.SpecificationSummary specification, BuildProject buildProject) { Guard.ArgumentNotNull(testScenarios, nameof(testScenarios)); Guard.ArgumentNotNull(providerResults, nameof(providerResults)); Guard.ArgumentNotNull(sourceDatasets, nameof(sourceDatasets)); Guard.ArgumentNotNull(currentResults, nameof(currentResults)); Guard.ArgumentNotNull(specification, nameof(specification)); Guard.ArgumentNotNull(buildProject, nameof(buildProject)); IList <TestScenarioResult> scenarioResults = new List <TestScenarioResult>(); if (!providerResults.Any()) { _logger.Warning("No provider results were supplied to execute tests"); } else if (!testScenarios.Any()) { _logger.Warning("No test scenarios were supplied to execute tests"); } else { foreach (ProviderResult providerResult in providerResults) { IEnumerable <ProviderSourceDataset> providerSourceDatasets = sourceDatasets.Where(m => m.ProviderId == providerResult.Provider.Id); IEnumerable <ScenarioResult> testResults = await RunTests(testScenarios, providerResult, providerSourceDatasets, buildProject); if (!testResults.IsNullOrEmpty()) { foreach (ScenarioResult testResult in testResults) { TestResult status = (testResult.StepsExecuted == 0 || testResult.StepsExecuted < testResult.TotalSteps) ? TestResult.Ignored : testResult.HasErrors ? TestResult.Failed : TestResult.Passed; TestScenarioResult filteredCurrentResults = currentResults.FirstOrDefault(m => m.Provider.Id == providerResult.Provider.Id && m.TestScenario.Id == testResult.Scenario.Id && m.TestResult == status); if (filteredCurrentResults == null) { scenarioResults.Add(new TestScenarioResult { TestResult = status, Specification = new Reference(specification.Id, specification.Name), TestScenario = new Reference(testResult.Scenario.Id, testResult.Scenario.Name), Provider = new Reference(providerResult.Provider.Id, providerResult.Provider.Name) }); } } } else { _logger.Warning($"No test results generated for provider: {providerResult.Provider?.Id} on specification: {providerResult.SpecificationId}"); } } } return(scenarioResults); }
private ScenarioIndex CreateScenarioIndexFromScenario(TestScenario testScenario, SpecModel.SpecificationSummary specification) { return(new ScenarioIndex { Id = testScenario.Id, Name = testScenario.Name, Description = testScenario.Current.Description, SpecificationId = testScenario.SpecificationId, SpecificationName = specification.Name, FundingPeriodId = specification.FundingPeriod.Id, FundingPeriodName = specification.FundingPeriod.Name, FundingStreamIds = specification.FundingStreams?.Select(s => s.Id).ToArray(), FundingStreamNames = specification.FundingStreams?.Select(s => s.Name).ToArray(), Status = testScenario.Current.PublishStatus.ToString(), LastUpdatedDate = DateTimeOffset.Now }); }
public async Task <IActionResult> SaveVersion(CreateNewTestScenarioVersion scenarioVersion, Reference user, string correlationId) { if (scenarioVersion == null) { _logger.Error("A null scenario version was provided"); return(new BadRequestObjectResult("Null or empty calculation Id provided")); } BadRequestObjectResult validationResult = (await _createNewTestScenarioVersionValidator.ValidateAsync(scenarioVersion)).PopulateModelState(); if (validationResult != null) { return(validationResult); } TestScenario testScenario = null; if (!string.IsNullOrEmpty(scenarioVersion.Id)) { testScenario = await _scenariosRepository.GetTestScenarioById(scenarioVersion.Id); } bool saveAsVersion = true; Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary> specificationApiResponse = await _specificationsApiClientPolicy.ExecuteAsync(() => _specificationsApiClient.GetSpecificationSummaryById(scenarioVersion.SpecificationId)); if (!specificationApiResponse.StatusCode.IsSuccess() || specificationApiResponse.Content == null) { _logger.Error($"Unable to find a specification for specification id : {scenarioVersion.SpecificationId}"); return(new StatusCodeResult(412)); } SpecModel.SpecificationSummary specification = specificationApiResponse.Content; if (testScenario == null) { string Id = Guid.NewGuid().ToString(); testScenario = new TestScenario { Id = Id, SpecificationId = specification.Id, Name = scenarioVersion.Name, Current = new TestScenarioVersion { Date = DateTimeOffset.Now.ToLocalTime(), TestScenarioId = Id, PublishStatus = PublishStatus.Draft, Version = 1, Author = user, Gherkin = scenarioVersion.Scenario, Description = scenarioVersion.Description, FundingPeriodId = specification.FundingPeriod.Id, FundingStreamIds = specification.FundingStreams.Select(s => s.Id).ToArraySafe(), } }; } else { testScenario.Name = scenarioVersion.Name; saveAsVersion = !string.Equals(scenarioVersion.Scenario, testScenario.Current.Gherkin) || scenarioVersion.Description != testScenario.Current.Description; TestScenarioVersion newVersion = testScenario.Current.Clone() as TestScenarioVersion; if (saveAsVersion == true) { newVersion.Author = user; newVersion.Gherkin = scenarioVersion.Scenario; newVersion.Description = scenarioVersion.Description; newVersion.FundingStreamIds = specification.FundingStreams.Select(s => s.Id).ToArraySafe(); newVersion.FundingPeriodId = specification.FundingPeriod.Id; newVersion = await _versionRepository.CreateVersion(newVersion, testScenario.Current); testScenario.Current = newVersion; } } HttpStatusCode statusCode = await _scenariosRepository.SaveTestScenario(testScenario); if (!statusCode.IsSuccess()) { _logger.Error($"Failed to save test scenario with status code: {statusCode}"); return(new StatusCodeResult((int)statusCode)); } await _versionRepository.SaveVersion(testScenario.Current); ScenarioIndex scenarioIndex = CreateScenarioIndexFromScenario(testScenario, specification); await _searchRepository.Index(new List <ScenarioIndex> { scenarioIndex }); await _cacheProvider.RemoveAsync <List <TestScenario> >($"{CacheKeys.TestScenarios}{testScenario.SpecificationId}"); await _cacheProvider.RemoveAsync <GherkinParseResult>($"{CacheKeys.GherkinParseResult}{testScenario.Id}"); IEnumerable <Common.ApiClient.Calcs.Models.Calculation> calculations = await _calcsRepositoryPolicy.ExecuteAsync(() => _calcsRepository.GetCurrentCalculationsBySpecificationId(specification.Id)); if (calculations.IsNullOrEmpty()) { _logger.Information($"No calculations found to test for specification id: '{specification.Id}'"); } else { try { JobsModels.Trigger trigger = new JobsModels.Trigger { EntityId = testScenario.Id, EntityType = nameof(TestScenario), Message = $"Saving test scenario: '{testScenario.Id}'" }; bool generateCalculationAggregations = SourceCodeHelpers.HasCalculationAggregateFunctionParameters(calculations.Select(m => m.SourceCode)); JobsModels.Job job = await SendInstructAllocationsToJobService(specification.Id, user, trigger, correlationId, generateCalculationAggregations); _logger.Information($"New job of type '{job.JobDefinitionId}' created with id: '{job.Id}'"); } catch (Exception ex) { _logger.Error(ex, $"Failed to create job of type '{JobConstants.DefinitionNames.CreateInstructAllocationJob}' on specification '{specification.Id}'"); return(new InternalServerErrorResult($"An error occurred attempting to execute calculations prior to running tests on specification '{specification.Id}'")); } } CurrentTestScenario testScenarioResult = await _scenariosRepository.GetCurrentTestScenarioById(testScenario.Id); return(new OkObjectResult(testScenarioResult)); }
public CalculationCreateModelValidator( ICalculationsRepository calculationRepository, IPreviewService previewService, ISpecificationsApiClient specificationsApiClient, ICalcsResiliencePolicies calcsResiliencePolicies) { Guard.ArgumentNotNull(calculationRepository, nameof(calculationRepository)); Guard.ArgumentNotNull(previewService, nameof(previewService)); Guard.ArgumentNotNull(specificationsApiClient, nameof(specificationsApiClient)); Guard.ArgumentNotNull(calcsResiliencePolicies, nameof(calcsResiliencePolicies)); Guard.ArgumentNotNull(calcsResiliencePolicies?.SpecificationsApiClient, nameof(calcsResiliencePolicies.SpecificationsApiClient)); _calculationRepository = calculationRepository; _previewService = previewService; _specificationsApiClient = specificationsApiClient; _specificationsApiClientPolicy = calcsResiliencePolicies.SpecificationsApiClient; CascadeMode = CascadeMode.StopOnFirstFailure; RuleFor(model => model.SpecificationId) .NotEmpty() .NotNull() .WithMessage("Null or empty specification id provided."); RuleFor(model => model.ValueType) .NotNull() .WithMessage("Null value type was provided."); RuleFor(model => model.Name) .Custom((name, context) => { CalculationCreateModel calculationCreateModel = context.ParentContext.InstanceToValidate as CalculationCreateModel; if (string.IsNullOrWhiteSpace(calculationCreateModel.Name)) { context.AddFailure("Null or empty calculation name provided."); } else { if (!string.IsNullOrWhiteSpace(calculationCreateModel.SpecificationId)) { Calculation calculation = _calculationRepository.GetCalculationsBySpecificationIdAndCalculationName(calculationCreateModel.SpecificationId, calculationCreateModel.Name).Result; if (calculation != null) { context.AddFailure($"A calculation already exists with the name: '{calculationCreateModel.Name}' for this specification"); } } } }); RuleFor(model => model.SourceCode) .Custom((sc, context) => { CalculationCreateModel calculationCreateModel = context.ParentContext.InstanceToValidate as CalculationCreateModel; if (string.IsNullOrWhiteSpace(calculationCreateModel.SourceCode)) { context.AddFailure("Null or empty source code provided."); } else { if (calculationCreateModel.CalculationType == CalculationType.Additional) { PreviewRequest previewRequest = new PreviewRequest { SpecificationId = calculationCreateModel.SpecificationId, CalculationId = calculationCreateModel.Id, Name = calculationCreateModel.Name, SourceCode = calculationCreateModel.SourceCode }; IActionResult result = _previewService.Compile(previewRequest).Result; OkObjectResult okObjectResult = result as OkObjectResult; PreviewResponse response = okObjectResult.Value as PreviewResponse; if (response != null) { if (!response.CompilerOutput.CompilerMessages.IsNullOrEmpty()) { context.AddFailure("There are errors in the source code provided"); } } } } }); RuleFor(model => model.FundingStreamId) .Custom((fs, context) => { CalculationCreateModel calculationCreateModel = context.ParentContext.InstanceToValidate as CalculationCreateModel; //only validate funding stream id for template calcs var isTemplateCalculation = calculationCreateModel.CalculationType == CalculationType.Template; if (isTemplateCalculation && string.IsNullOrWhiteSpace(calculationCreateModel.FundingStreamId)) { context.AddFailure("Null or empty funding stream id provided."); } else { ApiResponse <SpecModel.SpecificationSummary> specificationApiResponse = _specificationsApiClientPolicy.ExecuteAsync(() => _specificationsApiClient.GetSpecificationSummaryById(calculationCreateModel.SpecificationId)).GetAwaiter().GetResult(); if (specificationApiResponse == null || !specificationApiResponse.StatusCode.IsSuccess() || specificationApiResponse.Content == null) { context.AddFailure("Failed to find specification for provided specification id."); } else { SpecModel.SpecificationSummary specificationSummary = specificationApiResponse.Content; //I don't want to have to fetch the spec summary again outside of this method to get the name and funding stream so we set them on input model here calculationCreateModel.SpecificationName = specificationSummary.Name; //only validate funding stream ids for template calcs if (!isTemplateCalculation) { return; } Reference fundingStream = specificationSummary.FundingStreams.FirstOrDefault(m => m.Id == calculationCreateModel.FundingStreamId); if (fundingStream == null) { context.AddFailure("The funding stream id provided is not associated with the provided specification."); } else { calculationCreateModel.FundingStreamName = fundingStream.Name; } } } }); }
public async Task ResetCalculationForFieldDefinitionChanges_GivenCalculationRequiresReset_UpdatesCalculationsAndDeletesAssembly() { //Arrange const string specificationId = "spec-id"; IEnumerable <DatasetSpecificationRelationshipViewModel> relationships = new[] { new DatasetSpecificationRelationshipViewModel { Name = "Test Name" } }; IEnumerable <string> currentFieldDefinitionNames = new[] { "Test Field" }; ILogger logger = CreateLogger(); CalculationVersion calculationVersion = new CalculationVersion { SourceCode = "return Datasets.TestName.TestField", Date = DateTimeOffset.Now }; Calculation calculation = new Calculation { Current = calculationVersion, SpecificationId = specificationId, FundingStreamId = "funding stream id", }; IEnumerable <Calculation> calculations = new[] { calculation }; BuildProject buildProject = new BuildProject(); Build build = new Build { SourceFiles = new List <SourceFile>() }; SpecModel.SpecificationSummary specificationSummary = new SpecModel.SpecificationSummary() { Id = specificationId, Name = "Test Spec Name", FundingStreams = new [] { new Reference(calculation.FundingStreamId, "funding stream name") } }; ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); calculationsRepository .GetCalculationsBySpecificationId(Arg.Is(specificationId)) .Returns(calculations); calculationsRepository .UpdateCalculation(Arg.Any <Calculation>()) .Returns(HttpStatusCode.OK); ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient(); specificationsApiClient .GetSpecificationSummaryById(Arg.Is(specificationId)) .Returns(new ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specificationSummary)); IBuildProjectsService buildProjectsService = CreateBuildProjectsService(); buildProjectsService .GetBuildProjectForSpecificationId(Arg.Is(specificationId)) .Returns(buildProject); ISourceCodeService sourceCodeService = CreateSourceCodeService(); sourceCodeService .Compile(Arg.Is(buildProject), Arg.Any <IEnumerable <Calculation> >(), Arg.Any <CompilerOptions>()) .Returns(build); IVersionRepository <CalculationVersion> calculationVersionRepository = CreateCalculationVersionRepository(); calculationVersionRepository .CreateVersion(Arg.Any <CalculationVersion>(), Arg.Any <CalculationVersion>()) .Returns(calculationVersion); ICacheProvider cacheProvider = CreateCacheProvider(); CalculationService calculationService = CreateCalculationService( logger: logger, calculationsRepository: calculationsRepository, specificationsApiClient: specificationsApiClient, buildProjectsService: buildProjectsService, sourceCodeService: sourceCodeService, calculationVersionRepository: calculationVersionRepository, cacheProvider: cacheProvider); //Act await calculationService.ResetCalculationForFieldDefinitionChanges(relationships, specificationId, currentFieldDefinitionNames); //Assert await sourceCodeService .Received(1) .DeleteAssembly(Arg.Is(specificationId)); await cacheProvider .Received(1) .RemoveAsync <List <DatasetSchemaRelationshipModel> >(Arg.Is($"{CacheKeys.DatasetRelationshipFieldsForSpecification}{specificationId}")); }
public async Task UpdateCalculationCodeOnCalculationChange_WhenNoCalculationsFoundReferencingCalculationToBeUpdated_ThenNoCalculationsUpdated() { // Arrange ICalculationsRepository calculationsRepository = CreateCalculationsRepository(); ISpecificationsApiClient specificationsApiClient = CreateSpecificationsApiClient(); IVersionRepository <CalculationVersion> versionRepository = CreateCalculationVersionRepository(); CalculationService service = CreateCalculationService(calculationsRepository: calculationsRepository, specificationsApiClient: specificationsApiClient, calculationVersionRepository: versionRepository); const string specificationId = "specId"; const string calculationId = "updatedCalc"; CalculationVersionComparisonModel comparison = new CalculationVersionComparisonModel() { CalculationId = calculationId, CurrentName = "Calculation To Update", PreviousName = "Original Name", SpecificationId = specificationId, }; Reference user = new Reference("userId", "User Name"); List <Calculation> calculations = new List <Calculation>() { new Calculation { Id = calculationId, SpecificationId = specificationId, Current = new CalculationVersion { SourceCode = "Return 10", Name = "Calculation to Update", CalculationType = CalculationType.Template, Description = "Calculation Description" } }, new Calculation { Id = "referenceCalc", SpecificationId = specificationId, Current = new CalculationVersion { SourceCode = "Return 50", Name = "Calling Calculation To Update", CalculationType = CalculationType.Template, Description = "Calculation Description", } } }; calculationsRepository .GetCalculationsBySpecificationId(Arg.Is(specificationId)) .Returns(calculations); calculationsRepository .UpdateCalculation(Arg.Any <Calculation>()) .Returns(HttpStatusCode.OK); calculationsRepository .GetCalculationById(Arg.Is(calculations[0].Id)) .Returns(calculations[0]); SpecModel.SpecificationSummary specification = new SpecModel.SpecificationSummary() { Id = specificationId, Name = "Specification Name", }; specificationsApiClient .GetSpecificationSummaryById(Arg.Is(specificationId)) .Returns(new Common.ApiClient.Models.ApiResponse <SpecModel.SpecificationSummary>(HttpStatusCode.OK, specification)); CalculationVersion calculationVersion = new CalculationVersion { SourceCode = "Return CalculationToUpdate()", Version = 2 }; versionRepository .CreateVersion(Arg.Any <CalculationVersion>(), Arg.Any <CalculationVersion>()) .Returns(calculationVersion); // Act IEnumerable <Calculation> updatedCalculations = await service.UpdateCalculationCodeOnCalculationChange(comparison, user); // Assert updatedCalculations .Should() .HaveCount(0); }