diff --git a/src/Umbraco.Infrastructure/Sync/SyncBootStateAccessor.cs b/src/Umbraco.Infrastructure/Sync/SyncBootStateAccessor.cs index e2180cfbcd8a..7daee38b1a23 100644 --- a/src/Umbraco.Infrastructure/Sync/SyncBootStateAccessor.cs +++ b/src/Umbraco.Infrastructure/Sync/SyncBootStateAccessor.cs @@ -1,6 +1,8 @@ +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Sync; @@ -9,7 +11,7 @@ namespace Umbraco.Cms.Infrastructure.Sync; public class SyncBootStateAccessor : ISyncBootStateAccessor { private readonly ICacheInstructionService _cacheInstructionService; - private readonly LastSyncedFileManager _lastSyncedFileManager; + private readonly ILastSyncedManager _lastSyncedManager; private readonly ILogger _logger; private GlobalSettings _globalSettings; @@ -19,24 +21,53 @@ public class SyncBootStateAccessor : ISyncBootStateAccessor public SyncBootStateAccessor( ILogger logger, - LastSyncedFileManager lastSyncedFileManager, IOptionsMonitor globalSettings, - ICacheInstructionService cacheInstructionService) + ICacheInstructionService cacheInstructionService, + ILastSyncedManager lastSyncedManager) { _logger = logger; - _lastSyncedFileManager = lastSyncedFileManager; + _lastSyncedManager = lastSyncedManager; _globalSettings = globalSettings.CurrentValue; _cacheInstructionService = cacheInstructionService; globalSettings.OnChange(x => _globalSettings = x); } + [Obsolete("Please use the constructor without LastSyncedFileManager. Scheduled for removal in Umbraco 18.")] + public SyncBootStateAccessor( + ILogger logger, + LastSyncedFileManager lastSyncedFileManager, + IOptionsMonitor globalSettings, + ICacheInstructionService cacheInstructionService, + ILastSyncedManager lastSyncedManager) + : this( + logger, + globalSettings, + cacheInstructionService, + lastSyncedManager) + { + } + + [Obsolete("Please use the constructor with ILastSyncedManager. Scheduled for removal in Umbraco 18.")] + public SyncBootStateAccessor( + ILogger logger, + LastSyncedFileManager lastSyncedFileManager, + IOptionsMonitor globalSettings, + ICacheInstructionService cacheInstructionService) + : this( + logger, + globalSettings, + cacheInstructionService, + StaticServiceProvider.Instance.GetRequiredService()) + { + } + public SyncBootState GetSyncBootState() => LazyInitializer.EnsureInitialized( ref _syncBootState, ref _syncBootStateReady, ref _syncBootStateLock, - () => InitializeColdBootState(_lastSyncedFileManager.LastSyncedId)); + () => InitializeColdBootState(_lastSyncedManager.GetLastSyncedExternalAsync().GetAwaiter().GetResult() ?? -1)); private SyncBootState InitializeColdBootState(int lastId) { diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Sync/SyncBootStateAccessorTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Sync/SyncBootStateAccessorTest.cs new file mode 100644 index 000000000000..76a0ff0fa4c4 --- /dev/null +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Sync/SyncBootStateAccessorTest.cs @@ -0,0 +1,64 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using NUnit.Framework; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Sync; +using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; +using Umbraco.Cms.Infrastructure.Scoping; +using Umbraco.Cms.Infrastructure.Sync; +using Umbraco.Cms.Tests.Common.Testing; +using Umbraco.Cms.Tests.Integration.Testing; + +namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Sync; + +[TestFixture] +[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] +public class SyncBootStateAccessorTest : UmbracoIntegrationTest +{ + private ILastSyncedManager LastSyncedManager => GetRequiredService(); + + private SyncBootStateAccessor CreateSyncBootStateAccessor() => new( + GetRequiredService>(), + GetRequiredService>(), + GetRequiredService(), + LastSyncedManager); + + [Test] + public void Returns_ColdBoot_When_No_Last_Synced_Id() + { + // Arrange - the database is fresh with no last synced id saved + var syncBootStateAccessor = CreateSyncBootStateAccessor(); + + // Act + var result = syncBootStateAccessor.GetSyncBootState(); + + // Assert + Assert.AreEqual(SyncBootState.ColdBoot, result); + } + + [Test] + public async Task Returns_WarmBoot_When_Last_Synced_Id_Exists() + { + // Arrange - create a cache instruction (ID is auto-incremented starting at 1) + // and save the last synced external id matching it + using (var scope = ScopeProvider.CreateScope()) + { + var repo = new CacheInstructionRepository((IScopeAccessor)ScopeProvider); + repo.Add(new CacheInstruction(0, DateTime.Now, "{}", "Test", 1)); + scope.Complete(); + } + + // The auto-incremented ID will be 1 in a fresh database + await LastSyncedManager.SaveLastSyncedExternalAsync(1); + + var syncBootStateAccessor = CreateSyncBootStateAccessor(); + + // Act + var result = syncBootStateAccessor.GetSyncBootState(); + + // Assert + Assert.AreEqual(SyncBootState.WarmBoot, result); + } +}