diff --git a/src/GovUk.Education.ExploreEducationStatistics.Content.Api.Tests/Controllers/ThemeControllerCachingTests.cs b/src/GovUk.Education.ExploreEducationStatistics.Content.Api.Tests/Controllers/ThemeControllerCachingTests.cs new file mode 100644 index 00000000000..a284f8f4642 --- /dev/null +++ b/src/GovUk.Education.ExploreEducationStatistics.Content.Api.Tests/Controllers/ThemeControllerCachingTests.cs @@ -0,0 +1,97 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using GovUk.Education.ExploreEducationStatistics.Common.Cache; +using GovUk.Education.ExploreEducationStatistics.Common.Tests.Extensions; +using GovUk.Education.ExploreEducationStatistics.Common.Tests.Fixtures; +using GovUk.Education.ExploreEducationStatistics.Content.Api.Cache; +using GovUk.Education.ExploreEducationStatistics.Content.Api.Controllers; +using GovUk.Education.ExploreEducationStatistics.Content.Services.Interfaces; +using GovUk.Education.ExploreEducationStatistics.Content.Services.Interfaces.Cache; +using GovUk.Education.ExploreEducationStatistics.Content.Services.ViewModels; +using Moq; +using NCrontab; +using Xunit; +using static GovUk.Education.ExploreEducationStatistics.Common.Cache.CronSchedules; +using static GovUk.Education.ExploreEducationStatistics.Common.Services.CollectionUtils; +using static GovUk.Education.ExploreEducationStatistics.Common.Tests.Utils.MockUtils; +using static Moq.MockBehavior; +using static Newtonsoft.Json.JsonConvert; + +namespace GovUk.Education.ExploreEducationStatistics.Content.Api.Tests.Controllers; + +[Collection(CacheServiceTests)] +public class ThemeControllerCachingTests : CacheServiceTestFixture +{ + private readonly IList _themes = ListOf( + new ThemeViewModel(Guid.NewGuid(), "slug1", "title1", "summary1"), + new ThemeViewModel(Guid.NewGuid(), "slug2", "title2", "summary2")); + + [Fact] + public async Task ListThemes_NoCachedEntryExists() + { + var themeService = new Mock(Strict); + + MemoryCacheService + .Setup(s => s.GetItem( + new ListThemesCacheKey(), + typeof(IList))) + .Returns(null); + + var expectedCacheConfiguration = new MemoryCacheConfiguration( + 10, CrontabSchedule.Parse(HalfHourlyExpirySchedule)); + + MemoryCacheService + .Setup(s => s.SetItem( + new ListThemesCacheKey(), + _themes, + ItIs.DeepEqualTo(expectedCacheConfiguration), + null)); + + themeService + .Setup(s => s.ListThemes()) + .ReturnsAsync(_themes); + + var controller = BuildController(themeService.Object); + + var result = await controller.ListThemes(); + + VerifyAllMocks(MemoryCacheService, themeService); + + Assert.Equal(_themes, result); + } + + [Fact] + public async Task ListThemes_CachedEntryExists() + { + MemoryCacheService + .Setup(s => s.GetItem( + new ListThemesCacheKey(), + typeof(IList))) + .Returns(_themes); + + var controller = BuildController(); + + var result = await controller.ListThemes(); + + VerifyAllMocks(MemoryCacheService); + + Assert.Equal(_themes, result); + } + + [Fact] + public void ThemeViewModelList_SerializeAndDeserialize() + { + var converted = DeserializeObject>(SerializeObject(_themes)); + converted.AssertDeepEqualTo(_themes); + } + + private static ThemeController BuildController(IThemeService? themeService = null) + { + return new( + Mock.Of(), + themeService ?? Mock.Of(Strict) + ); + } +} diff --git a/src/GovUk.Education.ExploreEducationStatistics.Content.Api/Cache/ListThemesCacheKey.cs b/src/GovUk.Education.ExploreEducationStatistics.Content.Api/Cache/ListThemesCacheKey.cs new file mode 100644 index 00000000000..7bf2f175b21 --- /dev/null +++ b/src/GovUk.Education.ExploreEducationStatistics.Content.Api/Cache/ListThemesCacheKey.cs @@ -0,0 +1,8 @@ +using GovUk.Education.ExploreEducationStatistics.Common.Cache.Interfaces; + +namespace GovUk.Education.ExploreEducationStatistics.Content.Api.Cache; + +public record ListThemesCacheKey : IMemoryCacheKey +{ + public string Key => GetType().Name; +} \ No newline at end of file diff --git a/src/GovUk.Education.ExploreEducationStatistics.Content.Api/Controllers/ThemeController.cs b/src/GovUk.Education.ExploreEducationStatistics.Content.Api/Controllers/ThemeController.cs index be5a50409e8..7f28acd503b 100644 --- a/src/GovUk.Education.ExploreEducationStatistics.Content.Api/Controllers/ThemeController.cs +++ b/src/GovUk.Education.ExploreEducationStatistics.Content.Api/Controllers/ThemeController.cs @@ -2,11 +2,14 @@ using System.Collections.Generic; using System.Net.Mime; using System.Threading.Tasks; +using GovUk.Education.ExploreEducationStatistics.Common.Cache; using GovUk.Education.ExploreEducationStatistics.Common.Extensions; +using GovUk.Education.ExploreEducationStatistics.Content.Api.Cache; using GovUk.Education.ExploreEducationStatistics.Content.Services.Interfaces; using GovUk.Education.ExploreEducationStatistics.Content.Services.Interfaces.Cache; using GovUk.Education.ExploreEducationStatistics.Content.Services.ViewModels; using Microsoft.AspNetCore.Mvc; +using static GovUk.Education.ExploreEducationStatistics.Common.Cache.CronSchedules; namespace GovUk.Education.ExploreEducationStatistics.Content.Api.Controllers { @@ -33,6 +36,7 @@ public async Task>> GetMethodo .HandleFailuresOrOk(); } + [MemoryCache(typeof(ListThemesCacheKey), durationInSeconds: 10, expiryScheduleCron: HalfHourlyExpirySchedule)] [HttpGet("themes")] public async Task> ListThemes() {