diff --git a/BuddySave.IntegrationTests/FileManagement/LatestSaveTypeProviderTests.cs b/BuddySave.IntegrationTests/FileManagement/LatestSaveTypeProviderTests.cs new file mode 100644 index 0000000..e8b2de8 --- /dev/null +++ b/BuddySave.IntegrationTests/FileManagement/LatestSaveTypeProviderTests.cs @@ -0,0 +1,86 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using AutoFixture.Xunit2; +using BuddySave.Core.Models; +using BuddySave.FileManagement; +using BuddySave.System; +using BuddySave.TestTools; +using Moq; +using Xunit; + +namespace BuddySave.IntegrationTests.FileManagement; + +public class LatestSaveTypeProviderTests +{ + [Theory, AutoMoqData] + public async Task ReturnsLocalSaveType_When_LocalSaveIsNewer( + [Frozen] Mock fileInfoProviderMock, + GameSave gameSave, + LatestSaveTypeProvider sut) + { + // Arrange + var localFileInfo = await PrepareFile(SaveType.Local.ToString(), gameSave.SaveName, DateTime.Now); + var cloudFileInfo = await PrepareFile(SaveType.Cloud.ToString(), gameSave.SaveName, DateTime.Now.AddDays(-1)); + + fileInfoProviderMock.Setup(x => x.Get(gameSave.LocalPath, gameSave.SaveName)).Returns(localFileInfo); + fileInfoProviderMock.Setup(x => x.Get(gameSave.CloudPath, gameSave.SaveName)).Returns(cloudFileInfo); + + // Act + var result = sut.Get(gameSave); + + // Assert + Assert.Equal(SaveType.Local, result); + } + + [Theory, AutoMoqData] + public async Task ReturnsCloudSaveType_When_CloudSaveIsNewer( + [Frozen] Mock fileInfoProviderMock, + GameSave gameSave, + LatestSaveTypeProvider sut) + { + // Arrange + var localFileInfo = await PrepareFile(SaveType.Local.ToString(), gameSave.SaveName, DateTime.Now.AddDays(-1)); + var cloudFileInfo = await PrepareFile(SaveType.Cloud.ToString(), gameSave.SaveName, DateTime.Now); + + fileInfoProviderMock.Setup(x => x.Get(gameSave.LocalPath, gameSave.SaveName)).Returns(localFileInfo); + fileInfoProviderMock.Setup(x => x.Get(gameSave.CloudPath, gameSave.SaveName)).Returns(cloudFileInfo); + + // Act + var result = sut.Get(gameSave); + + // Assert + Assert.Equal(SaveType.Cloud, result); + } + + [Theory, AutoMoqData] + public async Task ReturnsCloudSaveType_When_LocalSaveTimeIsEqual( + [Frozen] Mock fileInfoProviderMock, + GameSave gameSave, + LatestSaveTypeProvider sut) + { + // Arrange + var localFileInfo = await PrepareFile(SaveType.Local.ToString(), gameSave.SaveName, DateTime.Now); + var cloudFileInfo = await PrepareFile(SaveType.Cloud.ToString(), gameSave.SaveName, DateTime.Now); + + fileInfoProviderMock.Setup(x => x.Get(gameSave.LocalPath, gameSave.SaveName)).Returns(localFileInfo); + fileInfoProviderMock.Setup(x => x.Get(gameSave.CloudPath, gameSave.SaveName)).Returns(cloudFileInfo); + + // Act + var result = sut.Get(gameSave); + + // Assert + Assert.Equal(SaveType.Cloud, result); + } + + private static async Task PrepareFile(string directoryName, string file, DateTime lastWriteTimeUtc) + { + var saveDirectory = new TempDir(directoryName, true); + var filePath = Path.Combine(saveDirectory.Path, file); + await File.WriteAllTextAsync(filePath, "Test"); + return new FileInfo(filePath) + { + LastWriteTimeUtc = lastWriteTimeUtc + }; + } +} \ No newline at end of file diff --git a/BuddySave.TestTools/TempDir.cs b/BuddySave.TestTools/TempDir.cs index 2a77608..ba063fa 100644 --- a/BuddySave.TestTools/TempDir.cs +++ b/BuddySave.TestTools/TempDir.cs @@ -21,7 +21,7 @@ public void Dispose() } } - public void Create() + private void Create() { Directory.CreateDirectory(Path); } diff --git a/BuddySave.UnitTests/Core/GameSaveSyncManagerTests.cs b/BuddySave.UnitTests/Core/GameSaveSyncManagerTests.cs index d161ab8..7f1d260 100644 --- a/BuddySave.UnitTests/Core/GameSaveSyncManagerTests.cs +++ b/BuddySave.UnitTests/Core/GameSaveSyncManagerTests.cs @@ -3,6 +3,7 @@ using BuddySave.Core; using BuddySave.Core.Models; using BuddySave.FileManagement; +using BuddySave.Notifications; using BuddySave.TestTools; using Microsoft.Extensions.Logging; using Moq; @@ -28,6 +29,100 @@ public void UploadSave_Throws_When_SourceValidationFails( Assert.ThrowsAny(act); } + [Theory, AutoMoqData] + public void UploadSave_DoesNotUploadSave_When_CloudSaveIsNewer( + [Frozen] Mock saveCopierMock, + [Frozen] Mock latestSaveTypeProviderMock, + GameSave save, + GameSaveSyncManager sut) + { + // Arrange + latestSaveTypeProviderMock.Setup(x => x.Get(save)).Returns(SaveType.Cloud); + + // Act + sut.UploadSave(save); + + // Assert + saveCopierMock.Verify(x => x.CopyOverSaves(save.SaveName, save.LocalPath, save.CloudPath), Times.Never); + } + + [Theory, AutoMoqData] + public void UploadSave_NotifyClient_When_CloudSaveIsNewer( + [Frozen] Mock latestSaveTypeProviderMock, + [Frozen] Mock clientNotifierMock, + GameSave save, + GameSaveSyncManager sut) + { + // Arrange + latestSaveTypeProviderMock.Setup(x => x.Get(save)).Returns(SaveType.Cloud); + + // Act + sut.UploadSave(save); + + // Assert + clientNotifierMock.Verify(x => x.Notify("Newer save found in Cloud, uploading game save skipped!!"), Times.Once); + } + + [Theory, AutoMoqData] + public void UploadSave_LogInformation_When_CloudSaveIsNewer( + [Frozen] Mock latestSaveTypeProviderMock, + [Frozen] Mock> loggerMock, + GameSave save, + GameSaveSyncManager sut) + { + // Arrange + latestSaveTypeProviderMock.Setup(x => x.Get(save)).Returns(SaveType.Cloud); + + // Act + sut.UploadSave(save); + + // Assert + loggerMock.Verify( + m => m.Log( + LogLevel.Information, + It.IsAny(), + It.Is((v, t) => + v.ToString()!.Contains("Newer save found in Cloud, uploading game save skipped!!")), + It.IsAny(), + It.Is>((v, t) => true)), + Times.Once); + } + + [Theory, AutoMoqData] + public void UploadSave_CopiesOverSaves_When_LocalSaveIsNewer( + [Frozen] Mock saveCopierMock, + [Frozen] Mock latestSaveTypeProviderMock, + GameSave save, + GameSaveSyncManager sut) + { + // Arrange + latestSaveTypeProviderMock.Setup(x => x.Get(save)).Returns(SaveType.Local); + + // Act + sut.UploadSave(save); + + // Assert + saveCopierMock.Verify(x => x.CopyOverSaves(save.SaveName, save.LocalPath, save.CloudPath), Times.Once); + } + + [Theory, AutoMoqData] + public void UploadSave_NotifyClient_When_SaveIsUploaded( + [Frozen] Mock latestSaveTypeProviderMock, + [Frozen] Mock clientNotifierMock, + GameSave save, + GameSaveSyncManager sut) + { + // Arrange + latestSaveTypeProviderMock.Setup(x => x.Get(save)).Returns(SaveType.Local); + + // Act + sut.UploadSave(save); + + // Assert + clientNotifierMock.Verify(x => x.Notify("Uploading game save to cloud..."), Times.Once); + clientNotifierMock.Verify(x => x.Notify("Game save uploaded."), Times.Once); + } + [Theory, AutoMoqData] public void UploadSave_BacksUpCloud_When_CloudHasASave( [Frozen] Mock backupManagerMock, @@ -38,16 +133,19 @@ public void UploadSave_BacksUpCloud_When_CloudHasASave( sut.UploadSave(save); // Assert - backupManagerMock.Verify(x => x.BackupFiles(save.CloudPath, save.GameName, save.SaveName, SaveType.Cloud), - Times.Once); + backupManagerMock.Verify(x => x.BackupFiles(save.CloudPath, save.GameName, save.SaveName, SaveType.Cloud), Times.Once); } [Theory, AutoMoqData] public void UploadSave_CopiesLocalSavesToCloud_When_LocalSaveIsValid( [Frozen] Mock saveCopierMock, + [Frozen] Mock latestSaveTypeProviderMock, GameSave save, GameSaveSyncManager sut) { + // Arrange + latestSaveTypeProviderMock.Setup(x => x.Get(save)).Returns(SaveType.Local); + // Act sut.UploadSave(save); @@ -59,45 +157,70 @@ public void UploadSave_CopiesLocalSavesToCloud_When_LocalSaveIsValid( public void UploadSave_RestoresCloudBackup_When_UploadFails( [Frozen] Mock saveCopierMock, [Frozen] Mock backupManagerMock, + [Frozen] Mock latestSaveTypeProviderMock, GameSave save, GameSaveSyncManager sut) { // Arrange saveCopierMock.Setup(x => x.CopyOverSaves(It.IsAny(), It.IsAny(), It.IsAny())) .Throws(new Exception()); + latestSaveTypeProviderMock.Setup(x => x.Get(save)).Returns(SaveType.Local); // Act - sut.UploadSave(save); + var act = new Action(() => sut.UploadSave(save)); // Assert - backupManagerMock.Verify(x => x.RestoreBackup(save.CloudPath, save.GameName, save.SaveName, SaveType.Cloud), - Times.Once); + Assert.Throws(act); + backupManagerMock.Verify(x => x.RestoreBackup(save.CloudPath, save.GameName, save.SaveName, SaveType.Cloud), Times.Once); } [Theory, AutoMoqData] public void UploadSave_LogsError_When_UploadFails( + [Frozen] Mock saveCopierMock, + [Frozen] Mock clientNotifierMock, + [Frozen] Mock latestSaveTypeProviderMock, + GameSave save, + Exception exception, + GameSaveSyncManager sut) + { + // Arrange + saveCopierMock.Setup(x => x.CopyOverSaves(It.IsAny(), It.IsAny(), It.IsAny())).Throws(exception); + latestSaveTypeProviderMock.Setup(x => x.Get(save)).Returns(SaveType.Local); + + // Act + var act = new Action(() => sut.UploadSave(save)); + + // Assert + Assert.Throws(act); + clientNotifierMock.Verify(x => x.Notify("Upload failed."), Times.Once); + } + + [Theory, AutoMoqData] + public void UploadSave_NotifyClient_When_UploadFails( [Frozen] Mock saveCopierMock, [Frozen] Mock> loggerMock, + [Frozen] Mock latestSaveTypeProviderMock, GameSave save, Exception exception, GameSaveSyncManager sut) { // Arrange - saveCopierMock.Setup(x => x.CopyOverSaves(It.IsAny(), It.IsAny(), It.IsAny())) - .Throws(exception); + saveCopierMock.Setup(x => x.CopyOverSaves(It.IsAny(), It.IsAny(), It.IsAny())).Throws(exception); + latestSaveTypeProviderMock.Setup(x => x.Get(save)).Returns(SaveType.Local); // Act - sut.UploadSave(save); + var act = new Action(() => sut.UploadSave(save)); // Assert + Assert.Throws(act); loggerMock.Verify( m => m.Log( LogLevel.Error, It.IsAny(), It.Is((v, t) => v.ToString()!.Contains("Upload failed.")), exception, - It.Is>((v, t) => true)) - ); + It.Is>((v, t) => true)), + Times.Once); } [Theory, AutoMoqData] @@ -126,8 +249,7 @@ public void DownloadSave_BacksUpLocal_When_LocalHasASave( sut.DownloadSave(save); // Assert - backupManagerMock.Verify(x => x.BackupFiles(save.LocalPath, save.GameName, save.SaveName, SaveType.Local), - Times.Once); + backupManagerMock.Verify(x => x.BackupFiles(save.LocalPath, save.GameName, save.SaveName, SaveType.Local), Times.Once); } [Theory, AutoMoqData] @@ -144,46 +266,157 @@ public void DownloadSave_CopiesCloudSavesToLocal_When_CloudSaveIsValid( } [Theory, AutoMoqData] - public void DownloadSave_RestoresLocalBackup_When_DownloadFails( + public void DownloadSave_NotifiesClient_When_CloudSaveIsValid( + [Frozen] Mock clientNotifierMock, + GameSave save, + GameSaveSyncManager sut) + { + // Act + sut.DownloadSave(save); + + // Assert + clientNotifierMock.Verify(x => x.Notify("Downloading game save from cloud..."), Times.Once); + clientNotifierMock.Verify(x => x.Notify("Game save downloaded."), Times.Once); + } + + [Theory, AutoMoqData] + public void DownloadSave_DoNotDownloadSave_When_LocalSaveIsNewer( + [Frozen] Mock latestSaveTypeProviderMock, [Frozen] Mock saveCopierMock, - [Frozen] Mock backupManagerMock, GameSave save, GameSaveSyncManager sut) { // Arrange - saveCopierMock.Setup(x => x.CopyOverSaves(save.SaveName, save.CloudPath, save.LocalPath)) - .Throws(new Exception("Download error")); + latestSaveTypeProviderMock.Setup(x => x.Get(save)).Returns(SaveType.Local); // Act sut.DownloadSave(save); // Assert - backupManagerMock.Verify(x => x.RestoreBackup(save.LocalPath, save.GameName, save.SaveName, SaveType.Local), + saveCopierMock.Verify(x => x.CopyOverSaves(save.SaveName, save.CloudPath, save.LocalPath), Times.Never); + } + + [Theory, AutoMoqData] + public void DownloadSave_NotifiesClient_When_LocalSaveIsNewer( + [Frozen] Mock latestSaveTypeProviderMock, + [Frozen] Mock clientNotifierMock, + GameSave save, + GameSaveSyncManager sut) + { + // Arrange + latestSaveTypeProviderMock.Setup(x => x.Get(save)).Returns(SaveType.Local); + + // Act + sut.DownloadSave(save); + + // Assert + clientNotifierMock.Verify(x => x.Notify("Newer Local game save was found, downloading game save skipped!!"), Times.Once); + } + + [Theory, AutoMoqData] + public void DownloadSave_LogsInformation_When_LocalSaveIsNewer( + [Frozen] Mock latestSaveTypeProviderMock, + [Frozen] Mock> loggerMock, + GameSave save, + GameSaveSyncManager sut) + { + // Arrange + latestSaveTypeProviderMock.Setup(x => x.Get(save)).Returns(SaveType.Local); + + // Act + sut.DownloadSave(save); + + // Assert + loggerMock.Verify( + m => m.Log( + LogLevel.Information, + It.IsAny(), + It.Is((v, t) => + v.ToString()!.Contains("Newer Local game save was found, downloading game save skipped!!")), + It.IsAny(), + It.Is>((v, t) => true)), Times.Once); } + [Theory, AutoMoqData] + public void DownloadSave_CopiesOverSaves_When_CloudSaveIsNewer( + [Frozen] Mock latestSaveTypeProviderMock, + [Frozen] Mock saveCopierMock, + GameSave save, + GameSaveSyncManager sut) + { + // Arrange + latestSaveTypeProviderMock.Setup(x => x.Get(save)).Returns(SaveType.Cloud); + + // Act + sut.DownloadSave(save); + + // Assert + saveCopierMock.Verify(x => x.CopyOverSaves(save.SaveName, save.CloudPath, save.LocalPath), Times.Once); + } + + [Theory, AutoMoqData] + public void DownloadSave_RestoresLocalBackup_When_DownloadFails( + [Frozen] Mock saveCopierMock, + [Frozen] Mock backupManagerMock, + GameSave save, + GameSaveSyncManager sut) + { + // Arrange + saveCopierMock.Setup(x => x.CopyOverSaves(save.SaveName, save.CloudPath, save.LocalPath)).Throws(new Exception("Download error")); + + // Act + var act = new Action(() => sut.DownloadSave(save)); + + // Assert + Assert.Throws(act); + backupManagerMock.Verify(x => x.RestoreBackup(save.LocalPath, save.GameName, save.SaveName, SaveType.Local), Times.Once); + } + [Theory, AutoMoqData] public void DownloadSave_LogsError_When_DownloadFails( [Frozen] Mock saveCopierMock, [Frozen] Mock> loggerMock, + [Frozen] Mock latestSaveTypeProviderMock, GameSave save, Exception exception, GameSaveSyncManager sut) { // Arrange - saveCopierMock.Setup(x => x.CopyOverSaves(save.SaveName, save.CloudPath, save.LocalPath)).Throws(exception); + saveCopierMock.Setup(x => x.CopyOverSaves(It.IsAny(), It.IsAny(), It.IsAny())).Throws(exception); + latestSaveTypeProviderMock.Setup(x => x.Get(save)).Returns(SaveType.Cloud); // Act - sut.DownloadSave(save); + var act = new Action(() => sut.DownloadSave(save)); // Assert + Assert.Throws(act); loggerMock.Verify( m => m.Log( LogLevel.Error, It.IsAny(), It.Is((v, t) => v.ToString()!.Contains("Download failed.")), exception, - It.Is>((v, t) => true)) - ); + It.Is>((v, t) => true)), + Times.Once); + } + + [Theory, AutoMoqData] + public void DownloadSave_NotifiesClient_When_DownloadFails( + [Frozen] Mock saveCopierMock, + [Frozen] Mock clientNotifierMock, + GameSave save, + Exception exception, + GameSaveSyncManager sut) + { + // Arrange + saveCopierMock.Setup(x => x.CopyOverSaves(save.SaveName, save.CloudPath, save.LocalPath)).Throws(exception); + + // Act + var act = new Action(() => sut.DownloadSave(save)); + + // Assert + Assert.Throws(act); + clientNotifierMock.Verify(x => x.Notify("Download failed."), Times.Once); } } \ No newline at end of file diff --git a/BuddySave.UnitTests/Core/SharedSaveOrchestratorTests.cs b/BuddySave.UnitTests/Core/SharedSaveOrchestratorTests.cs index 914601d..854ebd4 100644 --- a/BuddySave.UnitTests/Core/SharedSaveOrchestratorTests.cs +++ b/BuddySave.UnitTests/Core/SharedSaveOrchestratorTests.cs @@ -233,7 +233,6 @@ public async Task Save_UploadSaveToCloud_When_LockExists( Session session, [Frozen] Mock gameSaveSyncManagerMock, [Frozen] Mock lockManagerMock, - [Frozen] Mock clientNotifierMock, SharedSaveOrchestrator sut) { // Arrange @@ -243,8 +242,6 @@ public async Task Save_UploadSaveToCloud_When_LockExists( await sut.Save(gameSave, session); // Assert - clientNotifierMock.Verify(x => x.Notify("Uploading game save to cloud..."), Times.Once); - clientNotifierMock.Verify(x => x.Notify("Game save uploaded."), Times.Once); gameSaveSyncManagerMock.Verify(x => x.UploadSave(gameSave), Times.Once); } @@ -305,7 +302,7 @@ public async Task Save_NotifyClient_When_UploadFails( await sut.Save(gameSave, session); // Assert - clientNotifierMock.Verify(x => x.Notify("Upload failed."), Times.Once); + clientNotifierMock.Verify(x => x.Notify("Error while saving."), Times.Once); } [Theory, AutoMoqData] diff --git a/BuddySave/Configuration/IoC.cs b/BuddySave/Configuration/IoC.cs index e9da097..99b44d4 100644 --- a/BuddySave/Configuration/IoC.cs +++ b/BuddySave/Configuration/IoC.cs @@ -38,6 +38,8 @@ public static ServiceProvider Setup() .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() + .AddSingleton() .BuildServiceProvider(); return serviceProvider; diff --git a/BuddySave/Core/GameSaveSyncManager.cs b/BuddySave/Core/GameSaveSyncManager.cs index 625fd88..93ed0f4 100644 --- a/BuddySave/Core/GameSaveSyncManager.cs +++ b/BuddySave/Core/GameSaveSyncManager.cs @@ -1,5 +1,6 @@ using BuddySave.Core.Models; using BuddySave.FileManagement; +using BuddySave.Notifications; using Microsoft.Extensions.Logging; namespace BuddySave.Core; @@ -7,7 +8,9 @@ namespace BuddySave.Core; public class GameSaveSyncManager( ILogger logger, ISaveCopier saveCopier, - IBackupManager backupManager) + IBackupManager backupManager, + ILatestSaveTypeProvider latestSaveTypeProvider, + IClientNotifier clientNotifier) : IGameSaveSyncManager { public void UploadSave(GameSave gameSave) @@ -17,12 +20,25 @@ public void UploadSave(GameSave gameSave) try { + if (latestSaveTypeProvider.Get(gameSave) is SaveType.Cloud) + { + const string skippedLogMessage = "Newer save found in Cloud, uploading game save skipped!!"; + logger.LogInformation(skippedLogMessage); + clientNotifier.Notify(skippedLogMessage); + return; + } + + clientNotifier.Notify("Uploading game save to cloud..."); saveCopier.CopyOverSaves(gameSave.SaveName, gameSave.LocalPath, gameSave.CloudPath); + clientNotifier.Notify("Game save uploaded."); } catch (Exception ex) { - logger.LogError(ex, "Upload failed."); + const string failedLogMessage = "Upload failed."; + logger.LogError(ex, failedLogMessage); + clientNotifier.Notify(failedLogMessage); backupManager.RestoreBackup(gameSave.CloudPath, gameSave.GameName, gameSave.SaveName, SaveType.Cloud); + throw; } } @@ -33,12 +49,25 @@ public void DownloadSave(GameSave gameSave) try { + if (latestSaveTypeProvider.Get(gameSave) is SaveType.Local) + { + const string skippedLogMessage = "Newer Local game save was found, downloading game save skipped!!"; + logger.LogInformation(skippedLogMessage); + clientNotifier.Notify(skippedLogMessage); + return; + } + + clientNotifier.Notify("Downloading game save from cloud..."); saveCopier.CopyOverSaves(gameSave.SaveName, gameSave.CloudPath, gameSave.LocalPath); + clientNotifier.Notify("Game save downloaded."); } catch (Exception ex) { - logger.LogError(ex, "Download failed."); + const string failedLogMessage = "Download failed."; + logger.LogError(ex, failedLogMessage); + clientNotifier.Notify(failedLogMessage); backupManager.RestoreBackup(gameSave.LocalPath, gameSave.GameName, gameSave.SaveName, SaveType.Local); + throw; } } } \ No newline at end of file diff --git a/BuddySave/Core/SharedSaveOrchestrator.cs b/BuddySave/Core/SharedSaveOrchestrator.cs index 8bdfd4b..9c34722 100644 --- a/BuddySave/Core/SharedSaveOrchestrator.cs +++ b/BuddySave/Core/SharedSaveOrchestrator.cs @@ -49,14 +49,13 @@ public async Task Save(GameSave gameSave, Session session) try { - clientNotifier.Notify("Uploading game save to cloud..."); gameSaveSyncManager.UploadSave(gameSave); - clientNotifier.Notify("Game save uploaded."); } catch (Exception ex) { - logger.LogError(ex, "Error while saving."); - clientNotifier.Notify("Upload failed."); + const string errorLogMessage = "Error while saving."; + logger.LogError(ex, errorLogMessage); + clientNotifier.Notify(errorLogMessage); } finally { diff --git a/BuddySave/FileManagement/ILatestSaveTypeProvider.cs b/BuddySave/FileManagement/ILatestSaveTypeProvider.cs new file mode 100644 index 0000000..8f5f8c3 --- /dev/null +++ b/BuddySave/FileManagement/ILatestSaveTypeProvider.cs @@ -0,0 +1,8 @@ +using BuddySave.Core.Models; + +namespace BuddySave.FileManagement; + +public interface ILatestSaveTypeProvider +{ + public SaveType Get(GameSave gameSave); +} \ No newline at end of file diff --git a/BuddySave/FileManagement/LatestSaveTypeProvider.cs b/BuddySave/FileManagement/LatestSaveTypeProvider.cs new file mode 100644 index 0000000..084962e --- /dev/null +++ b/BuddySave/FileManagement/LatestSaveTypeProvider.cs @@ -0,0 +1,14 @@ +using BuddySave.Core.Models; +using BuddySave.System; + +namespace BuddySave.FileManagement; + +public class LatestSaveTypeProvider(IFileInfoProvider fileInfoProvider) : ILatestSaveTypeProvider +{ + public SaveType Get(GameSave gameSave) + { + var latestLocalSaveInfo = fileInfoProvider.Get(gameSave.LocalPath, gameSave.SaveName); + var latestCloudSaveInfo = fileInfoProvider.Get(gameSave.CloudPath, gameSave.SaveName); + return latestCloudSaveInfo.LastWriteTimeUtc >= latestLocalSaveInfo.LastWriteTimeUtc ? SaveType.Cloud : SaveType.Local; + } +} \ No newline at end of file diff --git a/BuddySave/System/FileInfoProvider.cs b/BuddySave/System/FileInfoProvider.cs new file mode 100644 index 0000000..262bc8c --- /dev/null +++ b/BuddySave/System/FileInfoProvider.cs @@ -0,0 +1,12 @@ +using BuddySave.Core.Models; + +namespace BuddySave.System; + +public class FileInfoProvider : IFileInfoProvider +{ + public FileInfo Get(string path, string saveName) + { + var directoryInfo = new DirectoryInfo(path); + return directoryInfo.GetFiles($"{saveName}.*").OrderByDescending(x => x.LastWriteTime).First(); + } +} \ No newline at end of file diff --git a/BuddySave/System/IFileInfoProvider.cs b/BuddySave/System/IFileInfoProvider.cs new file mode 100644 index 0000000..c0e22c3 --- /dev/null +++ b/BuddySave/System/IFileInfoProvider.cs @@ -0,0 +1,6 @@ +namespace BuddySave.System; + +public interface IFileInfoProvider +{ + FileInfo Get(string path, string saveName); +} \ No newline at end of file