diff --git a/src/GovUk.Education.ExploreEducationStatistics.Admin.Tests/Controllers/Api/Methodologies/MethodologyControllerTests.cs b/src/GovUk.Education.ExploreEducationStatistics.Admin.Tests/Controllers/Api/Methodologies/MethodologyControllerTests.cs index 45d00645e8e..0daf7afcf3b 100644 --- a/src/GovUk.Education.ExploreEducationStatistics.Admin.Tests/Controllers/Api/Methodologies/MethodologyControllerTests.cs +++ b/src/GovUk.Education.ExploreEducationStatistics.Admin.Tests/Controllers/Api/Methodologies/MethodologyControllerTests.cs @@ -177,7 +177,7 @@ public async Task ListLatestMethodologyVersions_Returns_Ok() var methodologyService = new Mock(Strict); methodologyService - .Setup(s => s.ListLatestMethodologyVersions(_id)) + .Setup(s => s.ListLatestMethodologyVersions(_id, false)) .ReturnsAsync(ListOf(new MethodologyVersionSummaryViewModel())); var controller = SetupMethodologyController(methodologyService.Object); diff --git a/src/GovUk.Education.ExploreEducationStatistics.Admin.Tests/Services/ManageContent/ManageContentPageServiceTests.cs b/src/GovUk.Education.ExploreEducationStatistics.Admin.Tests/Services/ManageContent/ManageContentPageServiceTests.cs index d9bf5aaa18f..9cbd61266ca 100644 --- a/src/GovUk.Education.ExploreEducationStatistics.Admin.Tests/Services/ManageContent/ManageContentPageServiceTests.cs +++ b/src/GovUk.Education.ExploreEducationStatistics.Admin.Tests/Services/ManageContent/ManageContentPageServiceTests.cs @@ -10,6 +10,7 @@ using GovUk.Education.ExploreEducationStatistics.Admin.ViewModels; using GovUk.Education.ExploreEducationStatistics.Common.Model; using GovUk.Education.ExploreEducationStatistics.Common.Services.Interfaces.Security; +using GovUk.Education.ExploreEducationStatistics.Common.Tests.Extensions; using GovUk.Education.ExploreEducationStatistics.Common.Tests.Utils; using GovUk.Education.ExploreEducationStatistics.Common.Utils; using GovUk.Education.ExploreEducationStatistics.Content.Model; @@ -148,19 +149,7 @@ public async Task GetManageContentPageViewModel() { Id = Guid.NewGuid(), AlternativeTitle = "Methodology 2 title", - Status = MethodologyApprovalStatus.Approved, - }, - new MethodologyVersion - { - Id = Guid.NewGuid(), - AlternativeTitle = "Methodology should not appear 1", Status = MethodologyApprovalStatus.Draft, - }, - new MethodologyVersion - { - Id = Guid.NewGuid(), - AlternativeTitle = "Methodology should not appear 2", - Status = MethodologyApprovalStatus.HigherLevelReview, } ); @@ -249,7 +238,7 @@ await contentDbContext.ReleaseContentSections.AddRangeAsync( var result = await service.GetManageContentPageViewModel(release.Id); - Assert.True(result.IsRight); + var viewModel = result.AssertRight(); dataBlockService.Verify(mock => mock.GetUnattachedDataBlocks(release.Id), Times.Once); @@ -257,9 +246,9 @@ await contentDbContext.ReleaseContentSections.AddRangeAsync( releaseFileService.Verify(mock => mock.ListAll(release.Id, Ancillary, FileType.Data), Times.Once); - Assert.Equal(unattachedDataBlocks, result.Right.UnattachedDataBlocks); + Assert.Equal(unattachedDataBlocks, viewModel.UnattachedDataBlocks); - var contentRelease = result.Right.Release; + var contentRelease = viewModel.Release; Assert.NotNull(contentRelease); Assert.Equal(release.Id, contentRelease.Id); @@ -367,6 +356,168 @@ await contentDbContext.ReleaseContentSections.AddRangeAsync( MockUtils.VerifyAllMocks(dataBlockService, methodologyVersionRepository, releaseFileService); } + [Fact] + public async Task GetManageContentPageViewModel_IsPrerelease() + { + var publication = new Publication + { + Contact = new Contact(), + Slug = "test-publication", + Title = "Publication", + Topic = new Topic + { + Theme = new Theme(), + } + }; + + var release = new Release + { + NextReleaseDate = new PartialDate {Day = "9", Month = "9", Year = "2040"}, + PreReleaseAccessList = "Test access list", + Publication = publication, + PublishScheduled = DateTime.Parse("2020-09-08T23:00:00.00Z", styles: DateTimeStyles.AdjustToUniversal), + Published = null, + ReleaseName = "2020", + Slug = "2020-21", + TimePeriodCoverage = AcademicYear, + Type = ReleaseType.OfficialStatistics, + }; + + var previousMethodologyVersion = new MethodologyVersion + { + Id = Guid.NewGuid(), + AlternativeTitle = "Methodology 3 title", + // Previous versions should always be approved - so no status set + }; + + var methodologyVersions = AsList( + new MethodologyVersion + { + // in result because approved + Id = Guid.NewGuid(), + AlternativeTitle = "Methodology 1 title", + Status = MethodologyApprovalStatus.Approved, + }, + new MethodologyVersion + { + // in result because approved + Id = Guid.NewGuid(), + AlternativeTitle = "Methodology 2 title", + Status = MethodologyApprovalStatus.Approved, + }, + previousMethodologyVersion, // in result because amendment of this version is not Approved + new MethodologyVersion + { + Id = Guid.NewGuid(), + AlternativeTitle = "Methodology should be filtered 1", + Status = MethodologyApprovalStatus.Draft, + PreviousVersion = previousMethodologyVersion, + }, + new MethodologyVersion + { + // not in result because not Approved and no previous version + Id = Guid.NewGuid(), + AlternativeTitle = "Methodology should be filtered 2", + Status = MethodologyApprovalStatus.HigherLevelReview, + PreviousVersion = null, + } + ); + + var contentDbContextId = Guid.NewGuid().ToString(); + await using (var contentDbContext = InMemoryApplicationDbContext(contentDbContextId)) + { + await contentDbContext.Publications.AddAsync(publication); + await contentDbContext.Releases.AddAsync(release); + await contentDbContext.MethodologyVersions.AddRangeAsync(methodologyVersions); + await contentDbContext.ReleaseContentSections.AddRangeAsync( + new() + { + Release = release, + ContentSection = new() + { + Type = ContentSectionType.Headlines + } + }, + new() + { + Release = release, + ContentSection = new() + { + Type = ContentSectionType.KeyStatisticsSecondary + } + }, + new() + { + Release = release, + ContentSection = new() + { + Type = ContentSectionType.ReleaseSummary + } + }, + new() + { + Release = release, + ContentSection = new() + { + Type = ContentSectionType.RelatedDashboards + } + }, + new() + { + Release = release, + ContentSection = new() + { + Type = ContentSectionType.Generic, + } + }); + await contentDbContext.SaveChangesAsync(); + } + + var dataBlockService = new Mock(MockBehavior.Strict); + var methodologyVersionRepository = new Mock(MockBehavior.Strict); + var releaseFileService = new Mock(MockBehavior.Strict); + + dataBlockService.Setup(mock => + mock.GetUnattachedDataBlocks(release.Id)) + .ReturnsAsync(new List()); + + methodologyVersionRepository.Setup(mock => + mock.GetLatestVersionByPublication(publication.Id)) + .ReturnsAsync(methodologyVersions); + + releaseFileService.Setup(mock => + mock.ListAll(release.Id, Ancillary, FileType.Data)) + .ReturnsAsync(new List()); + + await using (var contentDbContext = InMemoryApplicationDbContext(contentDbContextId)) + { + var service = SetupManageContentPageService(contentDbContext: contentDbContext, + dataBlockService: dataBlockService.Object, + methodologyVersionRepository: methodologyVersionRepository.Object, + releaseFileService: releaseFileService.Object); + + var result = await service.GetManageContentPageViewModel( + release.Id, isPrerelease: true); + + var viewModel = result.AssertRight(); + + var contentRelease = viewModel.Release; + + var contentPublication = contentRelease.Publication; + Assert.NotNull(contentPublication); + + Assert.Equal(3, contentPublication.Methodologies.Count); + Assert.Equal(methodologyVersions[0].Id, contentPublication.Methodologies[0].Id); + Assert.Equal("Methodology 1 title", contentPublication.Methodologies[0].Title); + Assert.Equal(methodologyVersions[1].Id, contentPublication.Methodologies[1].Id); + Assert.Equal("Methodology 2 title", contentPublication.Methodologies[1].Title); + Assert.Equal(methodologyVersions[2].Id, contentPublication.Methodologies[2].Id); + Assert.Equal("Methodology 3 title", contentPublication.Methodologies[2].Title); + } + + MockUtils.VerifyAllMocks(dataBlockService, methodologyVersionRepository, releaseFileService); + } + [Fact] public async Task GetManageContentPageViewModel_MapsBlocksCorrectly() { @@ -507,7 +658,7 @@ await contentDbContext.ReleaseContentSections.AddRangeAsync( var result = await service.GetManageContentPageViewModel(release.Id); - Assert.True(result.IsRight); + var viewModel = result.AssertRight(); dataBlockService.Verify(mock => mock.GetUnattachedDataBlocks(release.Id), Times.Once); @@ -515,9 +666,9 @@ await contentDbContext.ReleaseContentSections.AddRangeAsync( releaseFileService.Verify(mock => mock.ListAll(release.Id, Ancillary, FileType.Data), Times.Once); - Assert.Equal(unattachedDataBlocks, result.Right.UnattachedDataBlocks); + Assert.Equal(unattachedDataBlocks, viewModel.UnattachedDataBlocks); - var contentRelease = result.Right.Release; + var contentRelease = viewModel.Release; var contentReleaseSummary = contentRelease.SummarySection; @@ -581,6 +732,7 @@ private static ManageContentPageService SetupManageContentPageService( IUserService? userService = null) { return new( + contentDbContext, contentPersistenceHelper ?? new PersistenceHelper(contentDbContext), mapper ?? MapperUtils.AdminMapper(), dataBlockService ?? new Mock().Object, diff --git a/src/GovUk.Education.ExploreEducationStatistics.Admin.Tests/Services/Methodologies/MethodologyServiceTests.cs b/src/GovUk.Education.ExploreEducationStatistics.Admin.Tests/Services/Methodologies/MethodologyServiceTests.cs index 3ac454ec3d2..8cb457a26d9 100644 --- a/src/GovUk.Education.ExploreEducationStatistics.Admin.Tests/Services/Methodologies/MethodologyServiceTests.cs +++ b/src/GovUk.Education.ExploreEducationStatistics.Admin.Tests/Services/Methodologies/MethodologyServiceTests.cs @@ -1143,7 +1143,7 @@ public async Task ListLatestMethodologyVersions() Version = 0, AlternativeTitle = "Methodology 1 Version 1", Published = new DateTime(2021, 1, 1), - Status = MethodologyApprovalStatus.Approved + Status = MethodologyApprovalStatus.Approved, } } }; @@ -1158,7 +1158,7 @@ public async Task ListLatestMethodologyVersions() Version = 0, AlternativeTitle = "Methodology 2 Version 1", Published = new DateTime(2021, 1, 1), - Status = MethodologyApprovalStatus.Approved + Status = MethodologyApprovalStatus.Approved, }, new() { @@ -1166,10 +1166,11 @@ public async Task ListLatestMethodologyVersions() Version = 1, AlternativeTitle = "Methodology 2 Version 2", Published = null, - Status = MethodologyApprovalStatus.Draft + Status = MethodologyApprovalStatus.Draft, } } }; + methodology2.Versions[1].PreviousVersion = methodology2.Versions[0]; var methodology3 = new Methodology { @@ -1181,7 +1182,7 @@ public async Task ListLatestMethodologyVersions() Version = 0, AlternativeTitle = "Methodology 3 Version 1", Published = new DateTime(2021, 1, 1), - Status = MethodologyApprovalStatus.Approved + Status = MethodologyApprovalStatus.Approved, }, new() { @@ -1189,13 +1190,27 @@ public async Task ListLatestMethodologyVersions() Version = 1, AlternativeTitle = "Methodology 3 Version 2", Published = new DateTime(2022, 1, 1), - Status = MethodologyApprovalStatus.Approved + Status = MethodologyApprovalStatus.Approved, } } }; + methodology3.Versions[1].PreviousVersion = methodology3.Versions[0]; - methodology2.Versions[1].PreviousVersionId = methodology2.Versions[0].Id; - methodology3.Versions[1].PreviousVersionId = methodology3.Versions[0].Id; + var methodology4 = new Methodology + { + Versions = new List + { + new() + { + Id = Guid.NewGuid(), + Version = 0, + AlternativeTitle = "Methodology 4 Version 1", + Published = new DateTime(2021, 1, 1), + Status = MethodologyApprovalStatus.Draft, + PreviousVersion = null, + }, + } + }; var publication = new Publication { @@ -1204,19 +1219,24 @@ public async Task ListLatestMethodologyVersions() new() { Owner = false, - Methodology = methodology2 + Methodology = methodology2, }, new() { Owner = true, - Methodology = methodology1 + Methodology = methodology1, }, new() { Owner = false, - Methodology = methodology3 - } - } + Methodology = methodology3, + }, + new() + { + Owner = true, + Methodology = methodology4, + }, + }, }; var contextId = Guid.NewGuid().ToString(); @@ -1231,11 +1251,12 @@ public async Task ListLatestMethodologyVersions() { var service = SetupMethodologyService(contentDbContext); - var result = await service.ListLatestMethodologyVersions(publication.Id); + var result = await service.ListLatestMethodologyVersions( + publication.Id); var viewModels = result.AssertRight(); // Check that the latest versions of the methodologies are returned in title order - Assert.Equal(3, viewModels.Count); + Assert.Equal(4, viewModels.Count); Assert.Equal(methodology1.Versions[0].Id, viewModels[0].Id); Assert.False(viewModels[0].Amendment); @@ -1263,6 +1284,175 @@ public async Task ListLatestMethodologyVersions() Assert.Equal("Methodology 3 Version 2", viewModels[2].Title); Assert.Equal(methodology3.Id, viewModels[2].MethodologyId); Assert.Equal(methodology3.Versions[0].Id, viewModels[2].PreviousVersionId); + + Assert.Equal(methodology4.Versions[0].Id, viewModels[3].Id); + Assert.False(viewModels[3].Amendment); + Assert.True(viewModels[3].Owned); + Assert.Equal(new DateTime(2021, 1, 1), viewModels[3].Published); + Assert.Equal(MethodologyApprovalStatus.Draft, viewModels[3].Status); + Assert.Equal("Methodology 4 Version 1", viewModels[3].Title); + Assert.Equal(methodology4.Id, viewModels[3].MethodologyId); + Assert.Null(viewModels[3].PreviousVersionId); + } + } + [Fact] + public async Task ListLatestMethodologyVersions_IsPrerelease() + { + var methodology1 = new Methodology + { + Versions = new List + { + new() + { + // Version in result because latest is approved + Id = Guid.NewGuid(), + Version = 0, + AlternativeTitle = "Methodology 1 Version 1", + Published = new DateTime(2021, 1, 1), + Status = MethodologyApprovalStatus.Approved, + } + } + }; + + var methodology2 = new Methodology + { + Versions = new List + { + new() + { + // This version is in results because latest is draft, so previous is used + Id = Guid.NewGuid(), + Version = 0, + AlternativeTitle = "Methodology 2 Version 1", + Published = new DateTime(2021, 1, 1), + Status = MethodologyApprovalStatus.Approved, + }, + new() + { + Id = Guid.NewGuid(), + Version = 1, + AlternativeTitle = "Methodology 2 Version 2", + Published = null, + Status = MethodologyApprovalStatus.Draft, + } + } + }; + methodology2.Versions[1].PreviousVersion = methodology2.Versions[0]; + + var methodology3 = new Methodology + { + Versions = new List + { + new() + { + Id = Guid.NewGuid(), + Version = 0, + AlternativeTitle = "Methodology 3 Version 1", + Published = new DateTime(2021, 1, 1), + Status = MethodologyApprovalStatus.Approved, + }, + new() + { + // This is in results because it is latest approved + Id = Guid.NewGuid(), + Version = 1, + AlternativeTitle = "Methodology 3 Version 2", + Published = new DateTime(2022, 1, 1), + Status = MethodologyApprovalStatus.Approved, + } + } + }; + methodology3.Versions[1].PreviousVersion = methodology3.Versions[0]; + + var methodology4 = new Methodology + { + Versions = new List + { + new() + { + // Not in results because draft and no previous + Id = Guid.NewGuid(), + Version = 0, + AlternativeTitle = "Methodology 4 Version 1", + Published = new DateTime(2021, 1, 1), + Status = MethodologyApprovalStatus.Draft, + PreviousVersion = null, + }, + } + }; + + var publication = new Publication + { + Methodologies = new List + { + new() + { + Owner = false, + Methodology = methodology2, + }, + new() + { + Owner = true, + Methodology = methodology1, + }, + new() + { + Owner = false, + Methodology = methodology3, + }, + new() + { + Owner = true, + Methodology = methodology4, + }, + }, + }; + + var contextId = Guid.NewGuid().ToString(); + + await using (var contentDbContext = InMemoryApplicationDbContext(contextId)) + { + await contentDbContext.Publications.AddAsync(publication); + await contentDbContext.SaveChangesAsync(); + } + + await using (var contentDbContext = InMemoryApplicationDbContext(contextId)) + { + var service = SetupMethodologyService(contentDbContext); + + var result = await service.ListLatestMethodologyVersions( + publication.Id, isPrerelease: true); + var viewModels = result.AssertRight(); + + // Check that the latest versions of the methodologies are returned in title order + Assert.Equal(3, viewModels.Count); + + Assert.Equal(methodology1.Versions[0].Id, viewModels[0].Id); + Assert.False(viewModels[0].Amendment); + Assert.True(viewModels[0].Owned); + Assert.Equal(new DateTime(2021, 1, 1), viewModels[0].Published); + Assert.Equal(MethodologyApprovalStatus.Approved, viewModels[0].Status); + Assert.Equal("Methodology 1 Version 1", viewModels[0].Title); + Assert.Equal(methodology1.Id, viewModels[0].MethodologyId); + Assert.Null(viewModels[0].PreviousVersionId); + + Assert.Equal(methodology2.Versions[0].Id, viewModels[1].Id); + Assert.False(viewModels[1].Amendment); + Assert.False(viewModels[1].Owned); + Assert.Equal(new DateTime(2021, 1, 1), viewModels[1].Published); + Assert.Equal(MethodologyApprovalStatus.Approved, viewModels[1].Status); + Assert.Equal("Methodology 2 Version 1", viewModels[1].Title); + Assert.Equal(methodology2.Id, viewModels[1].MethodologyId); + Assert.Null(viewModels[1].PreviousVersionId); + + Assert.Equal(methodology3.Versions[1].Id, viewModels[2].Id); + Assert.False(viewModels[2].Amendment); + Assert.False(viewModels[2].Owned); + Assert.Equal(new DateTime(2022, 1, 1), viewModels[2].Published); + Assert.Equal(MethodologyApprovalStatus.Approved, viewModels[2].Status); + Assert.Equal("Methodology 3 Version 2", viewModels[2].Title); + Assert.Equal(methodology3.Id, viewModels[2].MethodologyId); + Assert.Equal(methodology3.Versions[0].Id, viewModels[2].PreviousVersionId); } } @@ -1280,10 +1470,13 @@ public async Task ListLatestMethodologyVersions_VerifyPermissions() Versions = new List { new() - } - } - } - } + { + Status = MethodologyApprovalStatus.Approved, + }, + }, + }, + }, + }, }; var contextId = Guid.NewGuid().ToString(); @@ -2343,14 +2536,14 @@ public async Task GetMethodologyStatuses_NoStatuses() public class ListUsersMethodologyVersionsForApprovalForPublicationRoles { private readonly DataFixture _fixture = new(); - + [Fact] public async Task UserIsApproverOnOwningPublication_Included() { var publication = _fixture .DefaultPublication() .Generate(); - + var methodology = _fixture .DefaultMethodology() .WithOwningPublication(publication) @@ -2384,19 +2577,19 @@ public async Task UserIsApproverOnOwningPublication_Included() var methodologyForApproval = Assert.Single(methodologyVersionsForApproval); Assert.Equal(methodology.Versions[0].Id, methodologyForApproval.Id); - + // Assert that we have a populated view model, including the owning Publication details. Assert.Equal(publication.Title, methodologyForApproval.OwningPublication.Title); } } - + [Fact] public async Task MethodologyVersionNotInHigherReview_NotIncluded() { var publication = _fixture .DefaultPublication() .Generate(); - + // Generate 2 Methodologies that are not in Higher Review. var methodologies = _fixture .DefaultMethodology() @@ -2433,14 +2626,14 @@ public async Task MethodologyVersionNotInHigherReview_NotIncluded() Assert.Empty(result.AssertRight()); } } - + [Fact] public async Task UserIsApproverButOnAdoptingPublication_NotIncluded() { var publication = _fixture .DefaultPublication() .Generate(); - + // Create a Methodology that has only been adopted by the User's Publication. var methodology = _fixture .DefaultMethodology() @@ -2474,14 +2667,14 @@ public async Task UserIsApproverButOnAdoptingPublication_NotIncluded() Assert.Empty(result.AssertRight()); } } - + [Fact] public async Task UserIsOnlyOwnerOnOwningPublication_NotIncluded() { var publication = _fixture .DefaultPublication() .Generate(); - + var methodology = _fixture .DefaultMethodology() .WithOwningPublication(publication) @@ -2514,7 +2707,7 @@ public async Task UserIsOnlyOwnerOnOwningPublication_NotIncluded() Assert.Empty(result.AssertRight()); } } - + [Fact] public async Task DifferentUserIsApproverOnOwningPublication_NotIncluded() { @@ -2524,7 +2717,7 @@ public async Task DifferentUserIsApproverOnOwningPublication_NotIncluded() var publication = _fixture .DefaultPublication() .Generate(); - + var methodology = _fixture .DefaultMethodology() .WithOwningPublication(publication) @@ -2557,21 +2750,21 @@ public async Task DifferentUserIsApproverOnOwningPublication_NotIncluded() } } } - + public class ListUsersMethodologyVersionsForApprovalForReleaseRoles { private readonly DataFixture _fixture = new(); - + [Fact] public async Task UserIsApproverOnOwningPublicationRelease_Included() { var release = _fixture.DefaultRelease().Generate(); - + var publication = _fixture .DefaultPublication() .WithReleases(ListOf(release)) .Generate(); - + var methodology = _fixture .DefaultMethodology() .WithOwningPublication(publication) @@ -2605,21 +2798,21 @@ public async Task UserIsApproverOnOwningPublicationRelease_Included() var methodologyForApproval = Assert.Single(methodologyVersionsForApproval); Assert.Equal(methodology.Versions[0].Id, methodologyForApproval.Id); - + // Assert that we have a populated view model, including the owning Publication details. Assert.Equal(publication.Title, methodologyForApproval.OwningPublication.Title); } } - + [Fact] public async Task UserIsApproverOnOwningPublicationRelease_MethodologyVersionNotInHigherReview_NotIncluded() { var release = _fixture.DefaultRelease().Generate(); - + var publication = _fixture .DefaultPublication() .Generate(); - + // Generate 2 Methodologies that are not in Higher Review. var methodologies = _fixture .DefaultMethodology() @@ -2633,7 +2826,7 @@ public async Task UserIsApproverOnOwningPublicationRelease_MethodologyVersionNot .WithApprovalStatus(MethodologyApprovalStatus.Approved) .Generate(1))) .GenerateList(); - + var releaseRoleForUser = _fixture .DefaultUserReleaseRole() .WithUser(User) @@ -2656,16 +2849,16 @@ public async Task UserIsApproverOnOwningPublicationRelease_MethodologyVersionNot Assert.Empty(result.AssertRight()); } } - + [Fact] public async Task UserIsReleaseApproverOnAdoptingPublication_NotIncluded() { var release = _fixture.DefaultRelease().Generate(); - + var publication = _fixture .DefaultPublication() .Generate(); - + // Create a Methodology that has only been adopted by the User's Publication. var methodology = _fixture .DefaultMethodology() @@ -2675,7 +2868,7 @@ public async Task UserIsReleaseApproverOnAdoptingPublication_NotIncluded() .WithApprovalStatus(MethodologyApprovalStatus.HigherLevelReview) .Generate(1)) .Generate(); - + var releaseRoleForUser = _fixture .DefaultUserReleaseRole() .WithUser(User) @@ -2699,16 +2892,16 @@ public async Task UserIsReleaseApproverOnAdoptingPublication_NotIncluded() Assert.Empty(result.AssertRight()); } } - + [Fact] public async Task UserIsOnlyContributorOnOwningPublicationRelease_NotIncluded() { var release = _fixture.DefaultRelease().Generate(); - + var publication = _fixture .DefaultPublication() .Generate(); - + var methodology = _fixture .DefaultMethodology() .WithOwningPublication(publication) @@ -2741,7 +2934,7 @@ public async Task UserIsOnlyContributorOnOwningPublicationRelease_NotIncluded() Assert.Empty(result.AssertRight()); } } - + [Fact] public async Task DifferentUserIsApproverOnOwningPublicationRelease_NotIncluded() { @@ -2749,11 +2942,11 @@ public async Task DifferentUserIsApproverOnOwningPublicationRelease_NotIncluded( var otherUser = new User(); var release = _fixture.DefaultRelease().Generate(); - + var publication = _fixture .DefaultPublication() .Generate(); - + var methodology = _fixture .DefaultMethodology() .WithOwningPublication(publication) @@ -2790,12 +2983,12 @@ public async Task DifferentUserIsApproverOnOwningPublicationRelease_NotIncluded( public async Task UserIsPublicationAndReleaseApprover_NoDuplication() { var release = _fixture.DefaultRelease().Generate(); - + var publication = _fixture .DefaultPublication() .WithReleases(ListOf(release)) .Generate(); - + var methodology = _fixture .DefaultMethodology() .WithOwningPublication(publication) @@ -2804,14 +2997,14 @@ public async Task UserIsPublicationAndReleaseApprover_NoDuplication() .WithApprovalStatus(MethodologyApprovalStatus.HigherLevelReview) .GenerateList(1)) .Generate(); - + var publicationRoleForUser = _fixture .DefaultUserPublicationRole() .WithUser(User) .WithPublication(publication) .WithRole(PublicationRole.Approver) .Generate(); - + var releaseRoleForUser = _fixture .DefaultUserReleaseRole() .WithUser(User) @@ -2841,7 +3034,7 @@ public async Task UserIsPublicationAndReleaseApprover_NoDuplication() } } } - + private static MethodologyService SetupMethodologyService( ContentDbContext contentDbContext, IPersistenceHelper? persistenceHelper = null, diff --git a/src/GovUk.Education.ExploreEducationStatistics.Admin/Controllers/Api/ManageContent/ManageContentPageController.cs b/src/GovUk.Education.ExploreEducationStatistics.Admin/Controllers/Api/ManageContent/ManageContentPageController.cs index 592bf306ab2..975b3de9805 100644 --- a/src/GovUk.Education.ExploreEducationStatistics.Admin/Controllers/Api/ManageContent/ManageContentPageController.cs +++ b/src/GovUk.Education.ExploreEducationStatistics.Admin/Controllers/Api/ManageContent/ManageContentPageController.cs @@ -21,10 +21,11 @@ public ManageContentPageController(IManageContentPageService manageContentPageSe } [HttpGet("release/{releaseId:guid}/content")] - public async Task> GetManageContentPageData(Guid releaseId) + public async Task> GetManageContentPageData(Guid releaseId, + [FromQuery] bool isPrerelease = false) { return await _manageContentPageService - .GetManageContentPageViewModel(releaseId) + .GetManageContentPageViewModel(releaseId, isPrerelease) .HandleFailuresOrOk(); } } diff --git a/src/GovUk.Education.ExploreEducationStatistics.Admin/Controllers/Api/Methodologies/MethodologyController.cs b/src/GovUk.Education.ExploreEducationStatistics.Admin/Controllers/Api/Methodologies/MethodologyController.cs index b01ee7a40a0..aaf6846713b 100644 --- a/src/GovUk.Education.ExploreEducationStatistics.Admin/Controllers/Api/Methodologies/MethodologyController.cs +++ b/src/GovUk.Education.ExploreEducationStatistics.Admin/Controllers/Api/Methodologies/MethodologyController.cs @@ -78,10 +78,12 @@ public async Task>> GetUnpublishedReleasesUs } [HttpGet("publication/{publicationId:guid}/methodologies")] - public async Task>> ListLatestMethodologyVersions(Guid publicationId) + public async Task>> ListLatestMethodologyVersions( + Guid publicationId, + [FromQuery] bool isPrerelease = false) { return await _methodologyService - .ListLatestMethodologyVersions(publicationId) + .ListLatestMethodologyVersions(publicationId, isPrerelease) .HandleFailuresOrOk(); } diff --git a/src/GovUk.Education.ExploreEducationStatistics.Admin/Services/Interfaces/ManageContent/IManageContentPageService.cs b/src/GovUk.Education.ExploreEducationStatistics.Admin/Services/Interfaces/ManageContent/IManageContentPageService.cs index 048fc7be7c3..0c12fb28164 100644 --- a/src/GovUk.Education.ExploreEducationStatistics.Admin/Services/Interfaces/ManageContent/IManageContentPageService.cs +++ b/src/GovUk.Education.ExploreEducationStatistics.Admin/Services/Interfaces/ManageContent/IManageContentPageService.cs @@ -8,6 +8,7 @@ namespace GovUk.Education.ExploreEducationStatistics.Admin.Services.Interfaces.M { public interface IManageContentPageService { - Task> GetManageContentPageViewModel(Guid releaseId); + Task> GetManageContentPageViewModel( + Guid releaseId, bool isPrerelease = false); } -} \ No newline at end of file +} diff --git a/src/GovUk.Education.ExploreEducationStatistics.Admin/Services/Interfaces/Methodologies/IMethodologyService.cs b/src/GovUk.Education.ExploreEducationStatistics.Admin/Services/Interfaces/Methodologies/IMethodologyService.cs index cd1b979270a..85de87db8ff 100644 --- a/src/GovUk.Education.ExploreEducationStatistics.Admin/Services/Interfaces/Methodologies/IMethodologyService.cs +++ b/src/GovUk.Education.ExploreEducationStatistics.Admin/Services/Interfaces/Methodologies/IMethodologyService.cs @@ -27,7 +27,9 @@ Task>> GetAdoptableMethod Task> GetMethodology(Guid methodologyVersionId); - Task>> ListLatestMethodologyVersions(Guid publicationId); + Task>> ListLatestMethodologyVersions( + Guid publicationId, + bool isPrerelease = false); Task>> GetUnpublishedReleasesUsingMethodology( Guid methodologyVersionId); diff --git a/src/GovUk.Education.ExploreEducationStatistics.Admin/Services/ManageContent/ManageContentPageService.cs b/src/GovUk.Education.ExploreEducationStatistics.Admin/Services/ManageContent/ManageContentPageService.cs index 0f2c1d3779d..d6e99af35f6 100644 --- a/src/GovUk.Education.ExploreEducationStatistics.Admin/Services/ManageContent/ManageContentPageService.cs +++ b/src/GovUk.Education.ExploreEducationStatistics.Admin/Services/ManageContent/ManageContentPageService.cs @@ -9,6 +9,7 @@ using GovUk.Education.ExploreEducationStatistics.Admin.Services.Interfaces.Security; using GovUk.Education.ExploreEducationStatistics.Admin.ViewModels; using GovUk.Education.ExploreEducationStatistics.Admin.ViewModels.ManageContent; +using GovUk.Education.ExploreEducationStatistics.Common.Extensions; using GovUk.Education.ExploreEducationStatistics.Common.Model; using GovUk.Education.ExploreEducationStatistics.Common.Model.Chart; using GovUk.Education.ExploreEducationStatistics.Common.Services.Interfaces.Security; @@ -24,6 +25,7 @@ namespace GovUk.Education.ExploreEducationStatistics.Admin.Services.ManageConten { public class ManageContentPageService : IManageContentPageService { + private readonly ContentDbContext _contentDbContext; private readonly IPersistenceHelper _persistenceHelper; private readonly IMapper _mapper; private readonly IDataBlockService _dataBlockService; @@ -32,6 +34,7 @@ public class ManageContentPageService : IManageContentPageService private readonly IUserService _userService; public ManageContentPageService( + ContentDbContext contentDbContext, IPersistenceHelper persistenceHelper, IMapper mapper, IDataBlockService dataBlockService, @@ -39,6 +42,7 @@ public ManageContentPageService( IReleaseFileService releaseFileService, IUserService userService) { + _contentDbContext = contentDbContext; _persistenceHelper = persistenceHelper; _mapper = mapper; _dataBlockService = dataBlockService; @@ -48,7 +52,7 @@ public ManageContentPageService( } public async Task> GetManageContentPageViewModel( - Guid releaseId) + Guid releaseId, bool isPrerelease = false) { return await _persistenceHelper .CheckEntityExists(releaseId, HydrateReleaseQuery) @@ -62,17 +66,39 @@ public async Task> GetManageCon { var (release, unattachedDataBlocks, files) = releaseBlocksAndFiles; - var methodologyVersions = await _methodologyVersionRepository - .GetLatestVersionByPublication(release.PublicationId); + var methodologyVersions = + await _methodologyVersionRepository.GetLatestVersionByPublication(release.PublicationId); - var approvedMethodologyVersions = methodologyVersions - .Where(mv => mv.Approved) - .ToList(); + if (isPrerelease) + { + // Get latest approved version + methodologyVersions = await methodologyVersions + .ToAsyncEnumerable() + .SelectAwait(async version => + { + if (version.Status == MethodologyApprovalStatus.Approved) + { + return version; + } + + if (version.PreviousVersionId == null) + { + return null; + } + + // If there is a previous version, it must be approved, because cannot + // create an amendment for an unpublished version + return await _contentDbContext.MethodologyVersions + .FirstAsync(mv => mv.Id == version.PreviousVersionId); + }) + .WhereNotNull() + .ToListAsync(); + } var releaseViewModel = _mapper.Map(release); releaseViewModel.DownloadFiles = files.ToList(); releaseViewModel.Publication.Methodologies = - _mapper.Map>(approvedMethodologyVersions); + _mapper.Map>(methodologyVersions); // TODO EES-3319 - remove backwards-compatibility for Map Configuration without its // own Boundary Level selection diff --git a/src/GovUk.Education.ExploreEducationStatistics.Admin/Services/Methodologies/MethodologyService.cs b/src/GovUk.Education.ExploreEducationStatistics.Admin/Services/Methodologies/MethodologyService.cs index 04b6e2a3997..d368cd9f4e9 100644 --- a/src/GovUk.Education.ExploreEducationStatistics.Admin/Services/Methodologies/MethodologyService.cs +++ b/src/GovUk.Education.ExploreEducationStatistics.Admin/Services/Methodologies/MethodologyService.cs @@ -151,12 +151,16 @@ public async Task> GetMethodol .OnSuccess(BuildMethodologyVersionViewModel); } - public async Task>> ListLatestMethodologyVersions(Guid publicationId) + public async Task>> + ListLatestMethodologyVersions( + Guid publicationId, + bool isPrerelease = false) { return await _persistenceHelper.CheckEntityExists(publicationId, - q => q.Include(p => p.Methodologies) - .ThenInclude(p => p.Methodology) - .ThenInclude(p => p.Versions)) + q => q.Include(publication => publication.Methodologies) + .ThenInclude(publicationMethodology => publicationMethodology.Methodology) + .ThenInclude(methodology => methodology.Versions) + .ThenInclude(versions => versions.PreviousVersion)) .OnSuccess(publication => _userService.CheckCanViewPublication(publication)) .OnSuccess(async publication => { @@ -164,25 +168,40 @@ public async Task> .ToAsyncEnumerable() .SelectAwait(async publicationMethodology => { - var latestVersion = publicationMethodology.Methodology.LatestVersion(); + var methodologyVersion = publicationMethodology.Methodology.LatestVersion(); + + if (isPrerelease && methodologyVersion.Status != MethodologyApprovalStatus.Approved) + { + // Get latest approved version + if (methodologyVersion.PreviousVersion == null) + { + return null; + } + + // If there is a previous version, it must be approved, because cannot + // create an amendment for an unpublished version + methodologyVersion = methodologyVersion.PreviousVersion; + } + var permissions = await PermissionsUtils.GetMethodologyVersionPermissions(_userService, - latestVersion, + methodologyVersion, publicationMethodology); return new MethodologyVersionSummaryViewModel { - Id = latestVersion.Id, - Amendment = latestVersion.Amendment, + Id = methodologyVersion.Id, + Amendment = methodologyVersion.Amendment, Owned = publicationMethodology.Owner, - Published = latestVersion.Published, - Status = latestVersion.Status, - Title = latestVersion.Title, - MethodologyId = latestVersion.MethodologyId, - PreviousVersionId = latestVersion.PreviousVersionId, + Published = methodologyVersion.Published, + Status = methodologyVersion.Status, + Title = methodologyVersion.Title, + MethodologyId = methodologyVersion.MethodologyId, + PreviousVersionId = methodologyVersion.PreviousVersionId, Permissions = permissions, }; }) + .WhereNotNull() .OrderBy(viewModel => viewModel.Title) .ToListAsync(); }); @@ -436,17 +455,17 @@ public async Task>> ListU .Distinct(); var methodologiesToApprove = await _context - .MethodologyVersions - .Where(methodologyVersion => - methodologyVersion.Status == MethodologyApprovalStatus.HigherLevelReview - && methodologyVersion.Methodology.Publications.Any( - publicationMethodology => - publicationMethodology.Owner - && publicationIdsForApproval.Contains(publicationMethodology.PublicationId))) - .ToListAsync(); + .MethodologyVersions + .Where(methodologyVersion => + methodologyVersion.Status == MethodologyApprovalStatus.HigherLevelReview + && methodologyVersion.Methodology.Publications.Any( + publicationMethodology => + publicationMethodology.Owner + && publicationIdsForApproval.Contains(publicationMethodology.PublicationId))) + .ToListAsync(); return (await methodologiesToApprove - .SelectAsync(BuildMethodologyVersionViewModel)) + .SelectAsync(BuildMethodologyVersionViewModel)) .ToList(); } diff --git a/src/explore-education-statistics-admin/src/pages/release/pre-release/PreReleaseContentPage.tsx b/src/explore-education-statistics-admin/src/pages/release/pre-release/PreReleaseContentPage.tsx index 22cf0023952..a90ceb2ed28 100644 --- a/src/explore-education-statistics-admin/src/pages/release/pre-release/PreReleaseContentPage.tsx +++ b/src/explore-education-statistics-admin/src/pages/release/pre-release/PreReleaseContentPage.tsx @@ -14,7 +14,7 @@ const PreReleaseContentPage = ({ const { releaseId } = match.params; const { value: content, isLoading } = useAsyncHandledRetry( - () => releaseContentService.getContent(releaseId), + () => releaseContentService.getContent(releaseId, true), [releaseId], ); diff --git a/src/explore-education-statistics-admin/src/pages/release/pre-release/PreReleaseMethodologiesPage.tsx b/src/explore-education-statistics-admin/src/pages/release/pre-release/PreReleaseMethodologiesPage.tsx index 7e5126e7169..20adc95abb3 100644 --- a/src/explore-education-statistics-admin/src/pages/release/pre-release/PreReleaseMethodologiesPage.tsx +++ b/src/explore-education-statistics-admin/src/pages/release/pre-release/PreReleaseMethodologiesPage.tsx @@ -32,7 +32,7 @@ const PreReleaseMethodologiesPage = ({ const { value: model, isLoading } = useAsyncHandledRetry(async () => { const [externalMethodology, latestMethodologyVersions] = await Promise.all([ publicationService.getExternalMethodology(publicationId), - methodologyService.listLatestMethodologyVersions(publicationId), + methodologyService.listLatestMethodologyVersions(publicationId, true), ]); return { diff --git a/src/explore-education-statistics-admin/src/services/methodologyService.ts b/src/explore-education-statistics-admin/src/services/methodologyService.ts index 3b38f427dd8..18d19c69b63 100644 --- a/src/explore-education-statistics-admin/src/services/methodologyService.ts +++ b/src/explore-education-statistics-admin/src/services/methodologyService.ts @@ -85,8 +85,11 @@ const methodologyService = { listLatestMethodologyVersions( publicationId: string, + isPrerelease = false, ): Promise { - return client.get(`/publication/${publicationId}/methodologies`); + return client.get(`/publication/${publicationId}/methodologies`, { + params: { isPrerelease }, + }); }, listMethodologiesForApproval(): Promise { diff --git a/src/explore-education-statistics-admin/src/services/releaseContentService.ts b/src/explore-education-statistics-admin/src/services/releaseContentService.ts index 4ce07027a50..bdd266519ef 100644 --- a/src/explore-education-statistics-admin/src/services/releaseContentService.ts +++ b/src/explore-education-statistics-admin/src/services/releaseContentService.ts @@ -37,8 +37,10 @@ export interface ContentBlockAttachRequest { } const releaseContentService = { - getContent(releaseId: string): Promise { - return client.get(`/release/${releaseId}/content`); + getContent(releaseId: string, isPrerelease = false): Promise { + return client.get(`/release/${releaseId}/content`, { + params: { isPrerelease }, + }); }, addContentSection( releaseId: string, diff --git a/tests/robot-tests/tests/admin_and_public_2/bau/publish_amend_and_cancel.robot b/tests/robot-tests/tests/admin_and_public_2/bau/publish_amend_and_cancel.robot index 860887fc134..abe10d810b7 100644 --- a/tests/robot-tests/tests/admin_and_public_2/bau/publish_amend_and_cancel.robot +++ b/tests/robot-tests/tests/admin_and_public_2/bau/publish_amend_and_cancel.robot @@ -180,6 +180,8 @@ Publish the scheduled release set suite variable ${EXPECTED_PUBLISHED_DATE} ... ${publish_date_day} ${publish_date_month_word} ${publish_date_year} + user waits for caches to expire + Verify newly published release is on Find Statistics page user checks publication is on find statistics page ${PUBLICATION_NAME}