diff --git a/BuddySave.IntegrationTests/Core/LockManagerTests.cs b/BuddySave.IntegrationTests/Core/LockManagerTests.cs index 4460f68..bf8791b 100644 --- a/BuddySave.IntegrationTests/Core/LockManagerTests.cs +++ b/BuddySave.IntegrationTests/Core/LockManagerTests.cs @@ -6,205 +6,204 @@ using BuddySave.TestTools; using Xunit; -namespace BuddySave.IntegrationTests.Core +namespace BuddySave.IntegrationTests.Core; + +public class LockManagerTests { - public class LockManagerTests + [Theory, AutoMoqData] + public void LockExists_ReturnsFalse_WhenNoLockFile( + GameSave gameSave, + LockManager sut) { - [Theory, AutoMoqData] - public void LockExists_ReturnsFalse_WhenNoLockFile( - GameSave gameSave, - LockManager sut) - { - // Act - var result = sut.LockExists(gameSave); + // Act + var result = sut.LockExists(gameSave); - // Assert - Assert.False(result); - } + // Assert + Assert.False(result); + } - [Theory, AutoMoqData] - public async Task LockExists_ReturnsTrue_WhenLockFileExists( - GameSave gameSave, - LockManager sut) - { - // Arrange - using var lockFile = new TempLockFile(); - await lockFile.Create(); - gameSave.CloudPath = lockFile.CloudPath; + [Theory, AutoMoqData] + public async Task LockExists_ReturnsTrue_WhenLockFileExists( + GameSave gameSave, + LockManager sut) + { + // Arrange + using var lockFile = new TempLockFile(); + await lockFile.Create(); + gameSave.CloudPath = lockFile.CloudPath; - // Act - var result = sut.LockExists(gameSave); + // Act + var result = sut.LockExists(gameSave); - // Assert - Assert.True(result); - } - - [Theory, AutoMoqData] - public async Task LockExistsForUserName_ReturnsFalse_WhenNoLockFile( - GameSave gameSave, - Session session, - LockManager sut) - { - // Act - var result = await sut.LockExists(gameSave, session); + // Assert + Assert.True(result); + } - // Assert - Assert.False(result); - } + [Theory, AutoMoqData] + public async Task LockExistsForUserName_ReturnsFalse_WhenNoLockFile( + GameSave gameSave, + Session session, + LockManager sut) + { + // Act + var result = await sut.LockExists(gameSave, session); - [Theory, AutoMoqData] - public async Task LockExistsForUserName_ReturnsFalse_WhenSaveIsLockedByAnotherUserName( - GameSave gameSave, - Session session, - LockManager sut) - { - // Arrange - using var lockFile = new TempLockFile(); - await lockFile.Create("buddy"); - gameSave.CloudPath = lockFile.CloudPath; - - // Act - var result = await sut.LockExists(gameSave, session); - - // Assert - Assert.False(result); - } + // Assert + Assert.False(result); + } - [Theory, AutoMoqData] - public async Task LockExistsForUserName_ReturnsTrue_WhenSaveIsLockedByCurrentUser( - GameSave gameSave, - Session session, - LockManager sut) - { - // Arrange - using var lockFile = new TempLockFile(); - await lockFile.Create(session.UserName); - gameSave.CloudPath = lockFile.CloudPath; - - // Act - var result = await sut.LockExists(gameSave, session); - - // Assert - Assert.True(result); - } + [Theory, AutoMoqData] + public async Task LockExistsForUserName_ReturnsFalse_WhenSaveIsLockedByAnotherUserName( + GameSave gameSave, + Session session, + LockManager sut) + { + // Arrange + using var lockFile = new TempLockFile(); + await lockFile.Create("buddy"); + gameSave.CloudPath = lockFile.CloudPath; - [Theory, AutoMoqData] - public async Task CreateLock_ThrowsException_WhenLockExists( - GameSave gameSave, - Session session, - LockManager sut) - { - // Arrange - using var lockFile = new TempLockFile(); - await lockFile.Create(); - gameSave.CloudPath = lockFile.CloudPath; + // Act + var result = await sut.LockExists(gameSave, session); - // Act - var act = new Func(async () => await sut.CreateLock(gameSave, session)); + // Assert + Assert.False(result); + } - // Assert - await Assert.ThrowsAnyAsync(act); - } + [Theory, AutoMoqData] + public async Task LockExistsForUserName_ReturnsTrue_WhenSaveIsLockedByCurrentUser( + GameSave gameSave, + Session session, + LockManager sut) + { + // Arrange + using var lockFile = new TempLockFile(); + await lockFile.Create(session.UserName); + gameSave.CloudPath = lockFile.CloudPath; - [Theory, AutoMoqData] - public async Task CreateLock_CreatesLock_WhenLockDoesNotExist( - GameSave gameSave, - Session session, - LockManager sut) - { - // Arrange - using var lockFile = new TempLockFile(); - gameSave.CloudPath = lockFile.CloudPath; + // Act + var result = await sut.LockExists(gameSave, session); - // Act - await sut.CreateLock(gameSave, session); + // Assert + Assert.True(result); + } - // Assert - Assert.True(File.Exists(lockFile.LockPath)); - var expectedFileContent = (await File.ReadAllTextAsync(lockFile.LockPath)).Trim(); - Assert.Equal(expectedFileContent, session.UserName); - } + [Theory, AutoMoqData] + public async Task CreateLock_ThrowsException_WhenLockExists( + GameSave gameSave, + Session session, + LockManager sut) + { + // Arrange + using var lockFile = new TempLockFile(); + await lockFile.Create(); + gameSave.CloudPath = lockFile.CloudPath; - [Theory, AutoMoqData] - public async Task DeleteLock_DeletesLock_WhenLockExists( - GameSave gameSave, - Session session, - LockManager sut) - { - // Arrange - using var lockFile = new TempLockFile(); - await lockFile.Create(session.UserName); - gameSave.CloudPath = lockFile.CloudPath; + // Act + var act = new Func(async () => await sut.CreateLock(gameSave, session)); - // Act - await sut.DeleteLock(gameSave, session); + // Assert + await Assert.ThrowsAnyAsync(act); + } - // Assert - Assert.False(File.Exists(lockFile.LockPath)); - } + [Theory, AutoMoqData] + public async Task CreateLock_CreatesLock_WhenLockDoesNotExist( + GameSave gameSave, + Session session, + LockManager sut) + { + // Arrange + using var lockFile = new TempLockFile(); + gameSave.CloudPath = lockFile.CloudPath; - [Theory, AutoMoqData] - public async Task DeleteLock_DoesNothing_WhenLockDoesNotExist( - GameSave gameSave, - Session session, - LockManager sut) - { - // Arrange - using var lockFile = new TempLockFile(); - gameSave.CloudPath = lockFile.CloudPath; + // Act + await sut.CreateLock(gameSave, session); - // Act - await sut.DeleteLock(gameSave, session); + // Assert + Assert.True(File.Exists(lockFile.LockPath)); + var expectedFileContent = (await File.ReadAllTextAsync(lockFile.LockPath)).Trim(); + Assert.Equal(expectedFileContent, session.UserName); + } - // Assert - Assert.False(File.Exists(lockFile.LockPath)); - } + [Theory, AutoMoqData] + public async Task DeleteLock_DeletesLock_WhenLockExists( + GameSave gameSave, + Session session, + LockManager sut) + { + // Arrange + using var lockFile = new TempLockFile(); + await lockFile.Create(session.UserName); + gameSave.CloudPath = lockFile.CloudPath; + + // Act + await sut.DeleteLock(gameSave, session); + + // Assert + Assert.False(File.Exists(lockFile.LockPath)); + } + + [Theory, AutoMoqData] + public async Task DeleteLock_DoesNothing_WhenLockDoesNotExist( + GameSave gameSave, + Session session, + LockManager sut) + { + // Arrange + using var lockFile = new TempLockFile(); + gameSave.CloudPath = lockFile.CloudPath; + + // Act + await sut.DeleteLock(gameSave, session); + + // Assert + Assert.False(File.Exists(lockFile.LockPath)); + } + + [Theory, AutoMoqData] + public async Task DeleteLock_ThrowsException_WhenLockIsCreatedByAnotherUser( + GameSave gameSave, + Session session, + LockManager sut) + { + // Arrange + using var lockFile = new TempLockFile(); + await lockFile.Create("buddyUser"); + gameSave.CloudPath = lockFile.CloudPath; + + // Act + var act = new Func(async () => await sut.DeleteLock(gameSave, session)); - [Theory, AutoMoqData] - public async Task DeleteLock_ThrowsException_WhenLockIsCreatedByAnotherUser( - GameSave gameSave, - Session session, - LockManager sut) + // Assert + var exception = await Assert.ThrowsAsync(act); + Assert.Equal("Cannot delete lock. Lock is owned by buddyUser.", exception.Message); + } + + private class TempLockFile : IDisposable + { + public TempLockFile() { - // Arrange - using var lockFile = new TempLockFile(); - await lockFile.Create("buddyUser"); - gameSave.CloudPath = lockFile.CloudPath; + CloudPath = Path.GetTempFileName(); + LockPath = CloudPath + ".lock"; + } + + public string LockPath { get; } + + public string CloudPath { get; } - // Act - var act = new Func(async () => await sut.DeleteLock(gameSave, session)); + public void Dispose() + { + File.Delete(LockPath); + } - // Assert - var exception = await Assert.ThrowsAsync(act); - Assert.Equal("Cannot delete lock. Lock is owned by buddyUser.", exception.Message); + public async Task Create() + { + await File.WriteAllTextAsync(LockPath, "This is a test lock"); } - private class TempLockFile : IDisposable + public async Task Create(string contents) { - public TempLockFile() - { - CloudPath = Path.GetTempFileName(); - LockPath = CloudPath + ".lock"; - } - - public string LockPath { get; } - - public string CloudPath { get; } - - public void Dispose() - { - File.Delete(LockPath); - } - - public async Task Create() - { - await File.WriteAllTextAsync(LockPath, "This is a test lock"); - } - - public async Task Create(string contents) - { - await File.WriteAllTextAsync(LockPath, contents); - } + await File.WriteAllTextAsync(LockPath, contents); } } } \ No newline at end of file diff --git a/BuddySave.IntegrationTests/FileManagement/RollingBackupsTests.cs b/BuddySave.IntegrationTests/FileManagement/RollingBackupsTests.cs index e6a9847..01be603 100644 --- a/BuddySave.IntegrationTests/FileManagement/RollingBackupsTests.cs +++ b/BuddySave.IntegrationTests/FileManagement/RollingBackupsTests.cs @@ -8,94 +8,93 @@ using Moq; using Xunit; -namespace BuddySave.IntegrationTests.FileManagement +namespace BuddySave.IntegrationTests.FileManagement; + +public class RollingBackupsTests { - public class RollingBackupsTests + [Theory] + [AutoMoqData] + public void GetMostRecent_ReturnsMostRecentSave( + string gameName, + string saveName, + SaveType saveType, + [Frozen] Mock backupDirectoryProviderMock, + RollingBackups sut) { - [Theory] - [AutoMoqData] - public void GetMostRecent_ReturnsMostRecentSave( - string gameName, - string saveName, - SaveType saveType, - [Frozen] Mock backupDirectoryProviderMock, - RollingBackups sut) + // Arrange + var saveDirectories = new[] { "20220101_010101", "20220101_010102" }; + using var tempDir = new TempDir(saveName, true); + var expectedDir = Path.Combine(tempDir.Path, "20220101_010102"); + backupDirectoryProviderMock + .Setup(x => x.GetRootDirectory(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(tempDir.Path); + + foreach (var dir in saveDirectories) { - // Arrange - var saveDirectories = new[] { "20220101_010101", "20220101_010102" }; - using var tempDir = new TempDir(saveName, true); - var expectedDir = Path.Combine(tempDir.Path, "20220101_010102"); - backupDirectoryProviderMock - .Setup(x => x.GetRootDirectory(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(tempDir.Path); + Directory.CreateDirectory(Path.Combine(tempDir.Path, dir)); + } - foreach (var dir in saveDirectories) - { - Directory.CreateDirectory(Path.Combine(tempDir.Path, dir)); - } + // Act + var result = sut.GetMostRecent(gameName, saveName, saveType); - // Act - var result = sut.GetMostRecent(gameName, saveName, saveType); + // Assert + Assert.Equal(expectedDir, result); + } - // Assert - Assert.Equal(expectedDir, result); - } + [Theory] + [InlineAutoMoqData(SaveType.Cloud)] + [InlineAutoMoqData(SaveType.Local)] + public void Add_CopiesSavesToTimestampedDirectory( + SaveType saveType, + string gameName, + string saveName, + string timestampedDir, + string savePath, + [Frozen] Mock backupDirectoryProviderMock, + [Frozen] Mock saveCopierMock, + RollingBackups sut) + { + // Arrange + backupDirectoryProviderMock.Setup(x => x.GetTimestampedDirectory(gameName, saveName, saveType)) + .Returns(timestampedDir); - [Theory] - [InlineAutoMoqData(SaveType.Cloud)] - [InlineAutoMoqData(SaveType.Local)] - public void Add_CopiesSavesToTimestampedDirectory( - SaveType saveType, - string gameName, - string saveName, - string timestampedDir, - string savePath, - [Frozen] Mock backupDirectoryProviderMock, - [Frozen] Mock saveCopierMock, - RollingBackups sut) - { - // Arrange - backupDirectoryProviderMock.Setup(x => x.GetTimestampedDirectory(gameName, saveName, saveType)) - .Returns(timestampedDir); + // Act + sut.Add(savePath, gameName, saveName, saveType); - // Act - sut.Add(savePath, gameName, saveName, saveType); + // Assert + saveCopierMock.Verify(x => x.CopyOverSaves(saveName, savePath, timestampedDir), Times.Once); + } - // Assert - saveCopierMock.Verify(x => x.CopyOverSaves(saveName, savePath, timestampedDir), Times.Once); - } + [Theory] + [InlineAutoMoqData(SaveType.Cloud)] + [InlineAutoMoqData(SaveType.Local)] + public void Add_DeletesOldestRollingBackup_When_MoreThanTenRollingBackupsExist( + SaveType saveType, + string gameName, + string saveName, + string savePath, + Fixture fixture, + [Frozen] Mock backupDirectoryProviderMock, + RollingBackups sut) + { + // Arrange + using var tempDir = new TempDir(saveName, true); + backupDirectoryProviderMock + .Setup(x => x.GetRootDirectory(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(tempDir.Path); - [Theory] - [InlineAutoMoqData(SaveType.Cloud)] - [InlineAutoMoqData(SaveType.Local)] - public void Add_DeletesOldestRollingBackup_When_MoreThanTenRollingBackupsExist( - SaveType saveType, - string gameName, - string saveName, - string savePath, - Fixture fixture, - [Frozen] Mock backupDirectoryProviderMock, - RollingBackups sut) + var backups = fixture.CreateMany(11); + foreach (var backup in backups) { - // Arrange - using var tempDir = new TempDir(saveName, true); - backupDirectoryProviderMock - .Setup(x => x.GetRootDirectory(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(tempDir.Path); - - var backups = fixture.CreateMany(11); - foreach (var backup in backups) - { - Directory.CreateDirectory(Path.Combine(tempDir.Path, backup)); - } + Directory.CreateDirectory(Path.Combine(tempDir.Path, backup)); + } - var deletedPath = Path.Combine(tempDir.Path, backups.OrderBy(x => x).First()); + var deletedPath = Path.Combine(tempDir.Path, backups.OrderBy(x => x).First()); - // Act - sut.Add(savePath, gameName, saveName, saveType); + // Act + sut.Add(savePath, gameName, saveName, saveType); - // Assert - Assert.False(Directory.Exists(deletedPath)); - } + // Assert + Assert.False(Directory.Exists(deletedPath)); } } \ No newline at end of file diff --git a/BuddySave.IntegrationTests/FileManagement/SaveCopierTests.cs b/BuddySave.IntegrationTests/FileManagement/SaveCopierTests.cs index 409f9ad..1b7ccad 100644 --- a/BuddySave.IntegrationTests/FileManagement/SaveCopierTests.cs +++ b/BuddySave.IntegrationTests/FileManagement/SaveCopierTests.cs @@ -5,161 +5,160 @@ using BuddySave.TestTools; using Xunit; -namespace BuddySave.IntegrationTests.FileManagement +namespace BuddySave.IntegrationTests.FileManagement; + +public class SaveCopierTests { - public class SaveCopierTests + [Theory, AutoMoqData] + public async Task CopyOverSaves_DeletesOldDestinationSave_WhenDestinationSaveExists( + string name, + string sourceDirName, + string destinationDirName, + string oldFile, + string newFile, + SaveCopier sut) { - [Theory, AutoMoqData] - public async Task CopyOverSaves_DeletesOldDestinationSave_WhenDestinationSaveExists( - string name, - string sourceDirName, - string destinationDirName, - string oldFile, - string newFile, - SaveCopier sut) - { - // Arrange - using var destinationSave = PrepareSaveDirectory(destinationDirName, true); - using var sourceSave = PrepareSaveDirectory(sourceDirName, true); + // Arrange + using var destinationSave = PrepareSaveDirectory(destinationDirName, true); + using var sourceSave = PrepareSaveDirectory(sourceDirName, true); - var oldSavePath = await PrepareFile($"{name}.{oldFile}", destinationSave); - await PrepareFile($"{name}.{oldFile}", destinationSave); - await PrepareFile($"{name}.{newFile}", sourceSave); + var oldSavePath = await PrepareFile($"{name}.{oldFile}", destinationSave); + await PrepareFile($"{name}.{oldFile}", destinationSave); + await PrepareFile($"{name}.{newFile}", sourceSave); - // Act - sut.CopyOverSaves(name, sourceSave.Path, destinationSave.Path); + // Act + sut.CopyOverSaves(name, sourceSave.Path, destinationSave.Path); - // Assert - Assert.False(File.Exists(oldSavePath)); - } + // Assert + Assert.False(File.Exists(oldSavePath)); + } - [Theory, AutoMoqData] - public async Task CopyOverSaves_CreatesDestinationDirectory_WhenDestinationSaveDoesNotExist( - string name, - string sourceDirName, - string destinationDirName, - string file, - SaveCopier sut) - { - // Arrange - using var destinationSave = PrepareSaveDirectory(destinationDirName, false); - using var sourceSave = PrepareSaveDirectory(sourceDirName, true); - await PrepareFile($"{name}.{file}", sourceSave); + [Theory, AutoMoqData] + public async Task CopyOverSaves_CreatesDestinationDirectory_WhenDestinationSaveDoesNotExist( + string name, + string sourceDirName, + string destinationDirName, + string file, + SaveCopier sut) + { + // Arrange + using var destinationSave = PrepareSaveDirectory(destinationDirName, false); + using var sourceSave = PrepareSaveDirectory(sourceDirName, true); + await PrepareFile($"{name}.{file}", sourceSave); - // Act - sut.CopyOverSaves(name, sourceSave.Path, destinationSave.Path); + // Act + sut.CopyOverSaves(name, sourceSave.Path, destinationSave.Path); - // Assert - Assert.True(Directory.Exists(destinationSave.Path)); - } + // Assert + Assert.True(Directory.Exists(destinationSave.Path)); + } - [Theory, AutoMoqData] - public async Task CopyOverSaves_CopiesFilesToDestination_WhenSourceHasFiles( - string name, - string sourceDirName, - string destinationDirName, - string file, - SaveCopier sut) - { - // Arrange - using var destinationSave = PrepareSaveDirectory(destinationDirName, false); - using var sourceSave = PrepareSaveDirectory(sourceDirName, true); - await PrepareFile($"{name}.{file}", sourceSave); - var destinationFilePath = Path.Combine(destinationSave.Path, $"{name}.{file}"); + [Theory, AutoMoqData] + public async Task CopyOverSaves_CopiesFilesToDestination_WhenSourceHasFiles( + string name, + string sourceDirName, + string destinationDirName, + string file, + SaveCopier sut) + { + // Arrange + using var destinationSave = PrepareSaveDirectory(destinationDirName, false); + using var sourceSave = PrepareSaveDirectory(sourceDirName, true); + await PrepareFile($"{name}.{file}", sourceSave); + var destinationFilePath = Path.Combine(destinationSave.Path, $"{name}.{file}"); - // Act - sut.CopyOverSaves(name, sourceSave.Path, destinationSave.Path); + // Act + sut.CopyOverSaves(name, sourceSave.Path, destinationSave.Path); - // Assert - Assert.True(File.Exists(destinationFilePath)); - } + // Assert + Assert.True(File.Exists(destinationFilePath)); + } - [Theory, AutoMoqData] - public async Task CopyOverSaves_CopiesOnlyOurNamedFiles_WhenSourceHasManyFiles( - string name, - string sourceDirName, - string destinationDirName, - string[] ourFiles, - string[] otherFiles, - SaveCopier sut) + [Theory, AutoMoqData] + public async Task CopyOverSaves_CopiesOnlyOurNamedFiles_WhenSourceHasManyFiles( + string name, + string sourceDirName, + string destinationDirName, + string[] ourFiles, + string[] otherFiles, + SaveCopier sut) + { + // Arrange + using var destinationSave = PrepareSaveDirectory(destinationDirName, true); + using var sourceSave = PrepareSaveDirectory(sourceDirName, true); + foreach (var file in ourFiles) { - // Arrange - using var destinationSave = PrepareSaveDirectory(destinationDirName, true); - using var sourceSave = PrepareSaveDirectory(sourceDirName, true); - foreach (var file in ourFiles) - { - await PrepareFile($"{name}.{file}", sourceSave); - } - - foreach (var file in otherFiles) - { - await PrepareFile(file, sourceSave); - } - - // Act - sut.CopyOverSaves(name, sourceSave.Path, destinationSave.Path); - - // Assert - Assert.Equal(ourFiles.Length, Directory.GetFiles(destinationSave.Path).Length); + await PrepareFile($"{name}.{file}", sourceSave); } - [Theory, AutoMoqData] - public void ValidateSource_SourceDoesNotExist_Throws( - string name, - string sourceDirName, - SaveCopier sut) + foreach (var file in otherFiles) { - // Act - var act = new Action(() => sut.ValidateSource(name, sourceDirName)); - - // Assert - Assert.ThrowsAny(act); + await PrepareFile(file, sourceSave); } - [Theory, AutoMoqData] - public void ValidateSource_NoSaveFilesExist_Throws( - string name, - string sourceDirName, - SaveCopier sut) - { - // Arrange - using var sourceSave = PrepareSaveDirectory(sourceDirName, true); + // Act + sut.CopyOverSaves(name, sourceSave.Path, destinationSave.Path); - // Act - var act = new Action(() => sut.ValidateSource(name, sourceSave.Path)); + // Assert + Assert.Equal(ourFiles.Length, Directory.GetFiles(destinationSave.Path).Length); + } - // Assert - Assert.ThrowsAny(act); - } + [Theory, AutoMoqData] + public void ValidateSource_SourceDoesNotExist_Throws( + string name, + string sourceDirName, + SaveCopier sut) + { + // Act + var act = new Action(() => sut.ValidateSource(name, sourceDirName)); - [Theory, AutoMoqData] - public async Task ValidateSource_NoSyncedSaveFilesExistInSource_Throws( - string name, - string sourceDirName, - string file, - SaveCopier sut) - { - // Arrange - using var sourceSave = PrepareSaveDirectory(sourceDirName, true); - await PrepareFile(file, sourceSave); + // Assert + Assert.ThrowsAny(act); + } - // Act - var act = new Action(() => sut.ValidateSource(name, sourceSave.Path)); + [Theory, AutoMoqData] + public void ValidateSource_NoSaveFilesExist_Throws( + string name, + string sourceDirName, + SaveCopier sut) + { + // Arrange + using var sourceSave = PrepareSaveDirectory(sourceDirName, true); - // Assert - Assert.ThrowsAny(act); - } + // Act + var act = new Action(() => sut.ValidateSource(name, sourceSave.Path)); - private static TempDir PrepareSaveDirectory(string dirName, bool createDirectory) - { - return new TempDir(dirName, createDirectory); - } + // Assert + Assert.ThrowsAny(act); + } - private static async Task PrepareFile(string file, TempDir save) - { - var filePath = Path.Combine(save.Path, file); - await File.WriteAllTextAsync(filePath, "Test"); - return filePath; - } + [Theory, AutoMoqData] + public async Task ValidateSource_NoSyncedSaveFilesExistInSource_Throws( + string name, + string sourceDirName, + string file, + SaveCopier sut) + { + // Arrange + using var sourceSave = PrepareSaveDirectory(sourceDirName, true); + await PrepareFile(file, sourceSave); + + // Act + var act = new Action(() => sut.ValidateSource(name, sourceSave.Path)); + + // Assert + Assert.ThrowsAny(act); + } + + private static TempDir PrepareSaveDirectory(string dirName, bool createDirectory) + { + return new TempDir(dirName, createDirectory); + } + + private static async Task PrepareFile(string file, TempDir save) + { + var filePath = Path.Combine(save.Path, file); + await File.WriteAllTextAsync(filePath, "Test"); + return filePath; } } \ No newline at end of file diff --git a/BuddySave.TestTools/AutoMoqDataAttribute.cs b/BuddySave.TestTools/AutoMoqDataAttribute.cs index 340a306..8b95714 100644 --- a/BuddySave.TestTools/AutoMoqDataAttribute.cs +++ b/BuddySave.TestTools/AutoMoqDataAttribute.cs @@ -2,14 +2,7 @@ using AutoFixture.AutoMoq; using AutoFixture.Xunit2; -namespace BuddySave.TestTools -{ - public class AutoMoqDataAttribute : AutoDataAttribute - { - public AutoMoqDataAttribute() - : base(() => new Fixture() - .Customize(new AutoMoqCustomization())) - { - } - } -} \ No newline at end of file +namespace BuddySave.TestTools; + +public class AutoMoqDataAttribute() : AutoDataAttribute(() => new Fixture() + .Customize(new AutoMoqCustomization())); \ No newline at end of file diff --git a/BuddySave.TestTools/BuddySave.TestTools.csproj b/BuddySave.TestTools/BuddySave.TestTools.csproj index 52a3886..d9caad5 100644 --- a/BuddySave.TestTools/BuddySave.TestTools.csproj +++ b/BuddySave.TestTools/BuddySave.TestTools.csproj @@ -10,6 +10,7 @@ + diff --git a/BuddySave.TestTools/TempDir.cs b/BuddySave.TestTools/TempDir.cs index 4160823..2a77608 100644 --- a/BuddySave.TestTools/TempDir.cs +++ b/BuddySave.TestTools/TempDir.cs @@ -1,29 +1,28 @@ -namespace BuddySave.TestTools +namespace BuddySave.TestTools; + +public class TempDir : IDisposable { - public class TempDir : IDisposable + public TempDir(string dirName, bool create) { - public TempDir(string dirName, bool create) + Path = System.IO.Path.Combine(System.IO.Path.GetTempPath(), dirName); + if (create) { - Path = System.IO.Path.Combine(System.IO.Path.GetTempPath(), dirName); - if (create) - { - Create(); - } + Create(); } + } - public string Path { get; } + public string Path { get; } - public void Dispose() + public void Dispose() + { + if (Directory.Exists(Path)) { - if (Directory.Exists(Path)) - { - Directory.Delete(Path, true); - } + Directory.Delete(Path, true); } + } - public void Create() - { - Directory.CreateDirectory(Path); - } + public void Create() + { + Directory.CreateDirectory(Path); } } \ No newline at end of file diff --git a/BuddySave.UnitTests/Core/GameSaveSyncManagerTests.cs b/BuddySave.UnitTests/Core/GameSaveSyncManagerTests.cs index fa44aab..d161ab8 100644 --- a/BuddySave.UnitTests/Core/GameSaveSyncManagerTests.cs +++ b/BuddySave.UnitTests/Core/GameSaveSyncManagerTests.cs @@ -4,166 +4,186 @@ using BuddySave.Core.Models; using BuddySave.FileManagement; using BuddySave.TestTools; +using Microsoft.Extensions.Logging; using Moq; -using NLog; using Xunit; -namespace BuddySave.UnitTests.Core +namespace BuddySave.UnitTests.Core; + +public class GameSaveSyncManagerTests { - public class GameSaveSyncManagerTests + [Theory, AutoMoqData] + public void UploadSave_Throws_When_SourceValidationFails( + [Frozen] Mock saveCopierMock, + GameSave save, + GameSaveSyncManager sut) + { + // Arrange + saveCopierMock.Setup(x => x.ValidateSource(It.IsAny(), It.IsAny())).Throws(new Exception()); + + // Act + var act = new Action(() => sut.UploadSave(save)); + + // Assert + Assert.ThrowsAny(act); + } + + [Theory, AutoMoqData] + public void UploadSave_BacksUpCloud_When_CloudHasASave( + [Frozen] Mock backupManagerMock, + GameSave save, + GameSaveSyncManager sut) + { + // Act + sut.UploadSave(save); + + // Assert + 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, + GameSave save, + GameSaveSyncManager sut) + { + // Act + sut.UploadSave(save); + + // Assert + saveCopierMock.Verify(x => x.CopyOverSaves(save.SaveName, save.LocalPath, save.CloudPath), Times.Once); + } + + [Theory, AutoMoqData] + public void UploadSave_RestoresCloudBackup_When_UploadFails( + [Frozen] Mock saveCopierMock, + [Frozen] Mock backupManagerMock, + GameSave save, + GameSaveSyncManager sut) + { + // Arrange + saveCopierMock.Setup(x => x.CopyOverSaves(It.IsAny(), It.IsAny(), It.IsAny())) + .Throws(new Exception()); + + // Act + sut.UploadSave(save); + + // Assert + 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> loggerMock, + GameSave save, + Exception exception, + GameSaveSyncManager sut) + { + // Arrange + saveCopierMock.Setup(x => x.CopyOverSaves(It.IsAny(), It.IsAny(), It.IsAny())) + .Throws(exception); + + // Act + sut.UploadSave(save); + + // Assert + loggerMock.Verify( + m => m.Log( + LogLevel.Error, + It.IsAny(), + It.Is((v, t) => v.ToString()!.Contains("Upload failed.")), + exception, + It.Is>((v, t) => true)) + ); + } + + [Theory, AutoMoqData] + public void DownloadSave_Throws_When_SourceValidationFails( + [Frozen] Mock saveCopierMock, + GameSave save, + GameSaveSyncManager sut) + { + // Arrange + saveCopierMock.Setup(x => x.ValidateSource(It.IsAny(), It.IsAny())).Throws(new Exception()); + + // Act + var act = new Action(() => sut.DownloadSave(save)); + + // Assert + Assert.ThrowsAny(act); + } + + [Theory, AutoMoqData] + public void DownloadSave_BacksUpLocal_When_LocalHasASave( + [Frozen] Mock backupManagerMock, + GameSave save, + GameSaveSyncManager sut) + { + // Act + sut.DownloadSave(save); + + // Assert + backupManagerMock.Verify(x => x.BackupFiles(save.LocalPath, save.GameName, save.SaveName, SaveType.Local), + Times.Once); + } + + [Theory, AutoMoqData] + public void DownloadSave_CopiesCloudSavesToLocal_When_CloudSaveIsValid( + [Frozen] Mock saveCopierMock, + GameSave save, + GameSaveSyncManager sut) + { + // 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 + sut.DownloadSave(save); + + // Assert + 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, + GameSave save, + Exception exception, + GameSaveSyncManager sut) { - [Theory, AutoMoqData] - public void UploadSave_Throws_When_SourceValidationFails( - [Frozen] Mock saveCopierMock, - GameSave save, - GameSaveSyncManager sut) - { - // Arrange - saveCopierMock.Setup(x => x.ValidateSource(It.IsAny(), It.IsAny())).Throws(new Exception()); - - // Act - var act = new Action(() => sut.UploadSave(save)); - - // Assert - Assert.ThrowsAny(act); - } - - [Theory, AutoMoqData] - public void UploadSave_BacksUpCloud_When_CloudHasASave( - [Frozen] Mock backupManagerMock, - GameSave save, - GameSaveSyncManager sut) - { - // Act - sut.UploadSave(save); - - // Assert - 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, - GameSave save, - GameSaveSyncManager sut) - { - // Act - sut.UploadSave(save); - - // Assert - saveCopierMock.Verify(x => x.CopyOverSaves(save.SaveName, save.LocalPath, save.CloudPath), Times.Once); - } - - [Theory, AutoMoqData] - public void UploadSave_RestoresCloudBackup_When_UploadFails( - [Frozen] Mock saveCopierMock, - [Frozen] Mock backupManagerMock, - GameSave save, - GameSaveSyncManager sut) - { - // Arrange - saveCopierMock.Setup(x => x.CopyOverSaves(It.IsAny(), It.IsAny(), It.IsAny())).Throws(new Exception()); - - // Act - sut.UploadSave(save); - - // Assert - 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 loggerMock, - GameSave save, - Exception exception, - GameSaveSyncManager sut) - { - // Arrange - saveCopierMock.Setup(x => x.CopyOverSaves(It.IsAny(), It.IsAny(), It.IsAny())).Throws(exception); - - // Act - sut.UploadSave(save); - - // Assert - loggerMock.Verify(x => x.Error(exception, "Upload failed.")); - } - - [Theory, AutoMoqData] - public void DownloadSave_Throws_When_SourceValidationFails( - [Frozen] Mock saveCopierMock, - GameSave save, - GameSaveSyncManager sut) - { - // Arrange - saveCopierMock.Setup(x => x.ValidateSource(It.IsAny(), It.IsAny())).Throws(new Exception()); - - // Act - var act = new Action(() => sut.DownloadSave(save)); - - // Assert - Assert.ThrowsAny(act); - } - - [Theory, AutoMoqData] - public void DownloadSave_BacksUpLocal_When_LocalHasASave( - [Frozen] Mock backupManagerMock, - GameSave save, - GameSaveSyncManager sut) - { - // Act - sut.DownloadSave(save); - - // Assert - backupManagerMock.Verify(x => x.BackupFiles(save.LocalPath, save.GameName, save.SaveName, SaveType.Local), Times.Once); - } - - [Theory, AutoMoqData] - public void DownloadSave_CopiesCloudSavesToLocal_When_CloudSaveIsValid( - [Frozen] Mock saveCopierMock, - GameSave save, - GameSaveSyncManager sut) - { - // 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 - sut.DownloadSave(save); - - // Assert - 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, - GameSave save, - Exception exception, - GameSaveSyncManager sut) - { - // Arrange - saveCopierMock.Setup(x => x.CopyOverSaves(save.SaveName, save.CloudPath, save.LocalPath)).Throws(exception); - - // Act - sut.DownloadSave(save); - - // Assert - loggerMock.Verify(x => x.Error(exception, "Download failed.")); - } + // Arrange + saveCopierMock.Setup(x => x.CopyOverSaves(save.SaveName, save.CloudPath, save.LocalPath)).Throws(exception); + + // Act + sut.DownloadSave(save); + + // Assert + loggerMock.Verify( + m => m.Log( + LogLevel.Error, + It.IsAny(), + It.Is((v, t) => v.ToString()!.Contains("Download failed.")), + exception, + It.Is>((v, t) => true)) + ); } } \ No newline at end of file diff --git a/BuddySave.UnitTests/Core/GamingSessionTests.cs b/BuddySave.UnitTests/Core/GamingSessionTests.cs index 6df6124..11bcb77 100644 --- a/BuddySave.UnitTests/Core/GamingSessionTests.cs +++ b/BuddySave.UnitTests/Core/GamingSessionTests.cs @@ -9,104 +9,103 @@ using Moq; using Xunit; -namespace BuddySave.UnitTests.Core +namespace BuddySave.UnitTests.Core; + +public class GamingSessionTests { - public class GamingSessionTests + [Theory] + [InlineAutoMoqData((string)null)] + [InlineAutoMoqData("")] + public async Task Run_ThrowsException_WhenNoServerPathIsEmpty( + string serverPath, + GameSave gameSave, + Session session, + ServerParameters serverParameters, + GamingSession sut) { - [Theory] - [InlineAutoMoqData((string)null)] - [InlineAutoMoqData("")] - public async Task Run_ThrowsException_WhenNoServerPathIsEmpty( - string serverPath, - GameSave gameSave, - Session session, - ServerParameters serverParameters, - GamingSession sut) - { - // Arrange - serverParameters.Path = serverPath; + // Arrange + serverParameters.Path = serverPath; - // Act - var act = new Func(() => sut.RunServerWithAutoSave(gameSave, session, serverParameters)); + // Act + var act = new Func(() => sut.RunServerWithAutoSave(gameSave, session, serverParameters)); - // Assert - await Assert.ThrowsAsync(act); - } + // Assert + await Assert.ThrowsAsync(act); + } - [Theory] - [AutoMoqData] - public async Task Run_DoesNotStartTheServer_WhenLoadingSaveFails( - [Frozen] Mock sharedSaveOrchestratorMock, - [Frozen] Mock processProviderMock, - Exception exception, - GameSave gameSave, - Session session, - ServerParameters serverParameters, - GamingSession sut) - { - // Arrange - sharedSaveOrchestratorMock.Setup(x => x.Load(It.IsAny(), It.IsAny())).Throws(exception); + [Theory] + [AutoMoqData] + public async Task Run_DoesNotStartTheServer_WhenLoadingSaveFails( + [Frozen] Mock sharedSaveOrchestratorMock, + [Frozen] Mock processProviderMock, + Exception exception, + GameSave gameSave, + Session session, + ServerParameters serverParameters, + GamingSession sut) + { + // Arrange + sharedSaveOrchestratorMock.Setup(x => x.Load(It.IsAny(), It.IsAny())).Throws(exception); - // Act - var act = new Func(() => sut.RunServerWithAutoSave(gameSave, session, serverParameters)); + // Act + var act = new Func(() => sut.RunServerWithAutoSave(gameSave, session, serverParameters)); - // Assert - await Assert.ThrowsAnyAsync(act); - processProviderMock.Verify(x => x.Start(It.IsAny()), Times.Never()); - } + // Assert + await Assert.ThrowsAnyAsync(act); + processProviderMock.Verify(x => x.Start(It.IsAny()), Times.Never()); + } - [Theory] - [AutoMoqData] - public async Task Run_DoesNotSave_WhenStartingServerFails( - [Frozen] Mock sharedSaveOrchestratorMock, - [Frozen] Mock processProviderMock, - Exception exception, - GameSave gameSave, - Session session, - ServerParameters serverParameters, - GamingSession sut) - { - // Arrange - processProviderMock.Setup(x => x.Start(It.IsAny())).Throws(exception); + [Theory] + [AutoMoqData] + public async Task Run_DoesNotSave_WhenStartingServerFails( + [Frozen] Mock sharedSaveOrchestratorMock, + [Frozen] Mock processProviderMock, + Exception exception, + GameSave gameSave, + Session session, + ServerParameters serverParameters, + GamingSession sut) + { + // Arrange + processProviderMock.Setup(x => x.Start(It.IsAny())).Throws(exception); - // Act - var act = new Func(() => sut.RunServerWithAutoSave(gameSave, session, serverParameters)); + // Act + var act = new Func(() => sut.RunServerWithAutoSave(gameSave, session, serverParameters)); - // Assert - await Assert.ThrowsAnyAsync(act); - sharedSaveOrchestratorMock.Verify(x => x.Save(It.IsAny(), It.IsAny()), Times.Never()); - } + // Assert + await Assert.ThrowsAnyAsync(act); + sharedSaveOrchestratorMock.Verify(x => x.Save(It.IsAny(), It.IsAny()), Times.Never()); + } - [Theory] - [AutoMoqData] - public async Task Run_WaitsForServerStop_WhenServerStarts( - [Frozen] Mock processProviderMock, - GameSave gameSave, - Session session, - ServerParameters serverParameters, - GamingSession sut) - { - // Act - await sut.RunServerWithAutoSave(gameSave, session, serverParameters); + [Theory] + [AutoMoqData] + public async Task Run_WaitsForServerStop_WhenServerStarts( + [Frozen] Mock processProviderMock, + GameSave gameSave, + Session session, + ServerParameters serverParameters, + GamingSession sut) + { + // Act + await sut.RunServerWithAutoSave(gameSave, session, serverParameters); - // Assert - processProviderMock.Verify(x => x.WaitForExitAsync(It.IsAny())); - } + // Assert + processProviderMock.Verify(x => x.WaitForExitAsync(It.IsAny())); + } - [Theory] - [AutoMoqData] - public async Task Run_Saves_WhenServerStops( - [Frozen] Mock sharedSaveOrchestratorMock, - GameSave gameSave, - Session session, - ServerParameters serverParameters, - GamingSession sut) - { - // Act - await sut.RunServerWithAutoSave(gameSave, session, serverParameters); + [Theory] + [AutoMoqData] + public async Task Run_Saves_WhenServerStops( + [Frozen] Mock sharedSaveOrchestratorMock, + GameSave gameSave, + Session session, + ServerParameters serverParameters, + GamingSession sut) + { + // Act + await sut.RunServerWithAutoSave(gameSave, session, serverParameters); - // Assert - sharedSaveOrchestratorMock.Verify(x => x.Save(It.IsAny(), It.IsAny())); - } + // Assert + sharedSaveOrchestratorMock.Verify(x => x.Save(It.IsAny(), It.IsAny())); } } \ No newline at end of file diff --git a/BuddySave.UnitTests/Core/SharedSaveOrchestratorTests.cs b/BuddySave.UnitTests/Core/SharedSaveOrchestratorTests.cs index 1a997da..4f26cbf 100644 --- a/BuddySave.UnitTests/Core/SharedSaveOrchestratorTests.cs +++ b/BuddySave.UnitTests/Core/SharedSaveOrchestratorTests.cs @@ -5,8 +5,8 @@ using BuddySave.Core.Models; using BuddySave.Notifications; using BuddySave.TestTools; +using Microsoft.Extensions.Logging; using Moq; -using NLog; using Xunit; namespace BuddySave.UnitTests.Core; @@ -81,7 +81,7 @@ public async Task Load_LogException_When_DownloadThrowsException( Session session, Exception exception, [Frozen] Mock gameSaveSyncManagerMock, - [Frozen] Mock loggerMock, + [Frozen] Mock> loggerMock, SharedSaveOrchestrator sut) { // Arrange @@ -91,7 +91,14 @@ public async Task Load_LogException_When_DownloadThrowsException( await sut.Load(gameSave, session); // Assert - loggerMock.Verify(x => x.Error(exception, "Error while loading.")); + loggerMock.Verify( + m => m.Log( + LogLevel.Error, + It.IsAny(), + It.Is((v, t) => v.ToString()!.Contains("Error while loading.")), + exception, + It.Is>((v, t) => true)) + ); } [Theory, AutoMoqData] @@ -240,7 +247,7 @@ public async Task Save_LogError_When_UploadThrowsException( Exception exception, [Frozen] Mock gameSaveSyncManagerMock, [Frozen] Mock lockManagerMock, - [Frozen] Mock loggerMock, + [Frozen] Mock> loggerMock, SharedSaveOrchestrator sut) { // Arrange @@ -251,6 +258,13 @@ public async Task Save_LogError_When_UploadThrowsException( await sut.Save(gameSave, session); // Assert - loggerMock.Verify(x => x.Error(exception, "Error while saving.")); + loggerMock.Verify( + m => m.Log( + LogLevel.Error, + It.IsAny(), + It.Is((v, t) => v.ToString()!.Contains("Error while saving.")), + exception, + It.Is>((v, t) => true)) + ); } } \ No newline at end of file diff --git a/BuddySave.UnitTests/FileManagement/BackupManagerTests.cs b/BuddySave.UnitTests/FileManagement/BackupManagerTests.cs index 16c1a16..2244947 100644 --- a/BuddySave.UnitTests/FileManagement/BackupManagerTests.cs +++ b/BuddySave.UnitTests/FileManagement/BackupManagerTests.cs @@ -3,8 +3,8 @@ using BuddySave.Core.Models; using BuddySave.FileManagement; using BuddySave.TestTools; +using Microsoft.Extensions.Logging; using Moq; -using NLog; using Xunit; namespace BuddySave.UnitTests.FileManagement; @@ -18,7 +18,7 @@ public void BackupFiles_DoesNothing_When_SaveDoesNotExist( string savePath, SaveType saveType, [Frozen] Mock saveCopierMock, - [Frozen] Mock loggerMock, + [Frozen] Mock> loggerMock, BackupManager sut) { // Arrange @@ -29,7 +29,14 @@ public void BackupFiles_DoesNothing_When_SaveDoesNotExist( // Assert saveCopierMock.Verify(x => x.CopyOverSaves(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); - loggerMock.Verify(x => x.Info($"Nothing to backup in {savePath}")); + loggerMock.Verify( + m => m.Log( + LogLevel.Information, + It.IsAny(), + It.Is((v, t) => v.ToString()!.Contains($"Nothing to backup in {savePath}")), + It.IsAny(), + It.Is>((v, t) => true)) + ); } [Theory, AutoMoqData] diff --git a/BuddySave/App.cs b/BuddySave/App.cs new file mode 100644 index 0000000..d7c46a6 --- /dev/null +++ b/BuddySave/App.cs @@ -0,0 +1,63 @@ +using BuddySave.Configuration; +using BuddySave.Core; +using BuddySave.Core.Models; +using Microsoft.Extensions.Logging; + +namespace BuddySave; + +internal sealed class App( + ISharedSaveOrchestrator sharedSaveOrchestrator, + IGamingSession gamingSession, + ILogger logger, + IConfigurationLoader configurationLoader) +{ + public async Task Start() + { + logger.LogInformation("Start"); + Console.WriteLine("∞∞∞∞∞∞∞ Buddy Save ∞∞∞∞∞∞∞"); + + try + { + var configuration = await configurationLoader.Load(); + await Run(configuration.GameSave, configuration.Session, configuration.ServerParameters); + } + catch (Exception ex) + { + logger.LogError(ex, "Failed to start main App."); + } + + Console.WriteLine("Bye Buddy! ;)"); + Console.WriteLine("Press any key to exit..."); + Console.ReadKey(); + Console.WriteLine(); + logger.LogInformation("Exit"); + } + + private async Task Run(GameSave gameSave, Session session, ServerParameters serverParameters) + { + var input = string.Empty; + while (!string.Equals(input, "exit", StringComparison.OrdinalIgnoreCase)) + { + Console.WriteLine("Waiting for action command (run, load, save, exit):"); + input = Console.ReadLine(); + + switch (input?.ToLowerInvariant()) + { + case "run": + await gamingSession.RunServerWithAutoSave(gameSave, session, serverParameters); + break; + + case "load": + await sharedSaveOrchestrator.Load(gameSave, session); + break; + + case "save": + await sharedSaveOrchestrator.Save(gameSave, session); + break; + + case "exit": + break; + } + } + } +} \ No newline at end of file diff --git a/BuddySave/BuddySave.csproj b/BuddySave/BuddySave.csproj index 4dd74bc..7cb7a7c 100644 --- a/BuddySave/BuddySave.csproj +++ b/BuddySave/BuddySave.csproj @@ -9,8 +9,12 @@ + + + + diff --git a/BuddySave/Configuration/ConfigurationLoader.cs b/BuddySave/Configuration/ConfigurationLoader.cs index 5d0feb7..6c6bf5f 100644 --- a/BuddySave/Configuration/ConfigurationLoader.cs +++ b/BuddySave/Configuration/ConfigurationLoader.cs @@ -1,20 +1,13 @@ -using Newtonsoft.Json; -using NLog; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; namespace BuddySave.Configuration; -public class ConfigurationLoader : IConfigurationLoader +public class ConfigurationLoader(ILogger logger) : IConfigurationLoader { - private readonly ILogger _logger; - - public ConfigurationLoader(ILogger logger) - { - _logger = logger; - } - public async Task Load() { - _logger.Info("Reading configuration file..."); + logger.LogInformation("Reading configuration file..."); var configFile = await File.ReadAllTextAsync(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.json")); if (string.IsNullOrWhiteSpace(configFile)) { diff --git a/BuddySave/Configuration/IoC.cs b/BuddySave/Configuration/IoC.cs new file mode 100644 index 0000000..e9da097 --- /dev/null +++ b/BuddySave/Configuration/IoC.cs @@ -0,0 +1,45 @@ +using BuddySave.Core; +using BuddySave.FileManagement; +using BuddySave.Notifications; +using BuddySave.System; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using NLog.Extensions.Logging; + +namespace BuddySave.Configuration; + +public static class IoC +{ + public static ServiceProvider Setup() + { + var config = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .Build(); + + var serviceProvider = new ServiceCollection() + .AddTransient() + .AddLogging(loggingBuilder => + { + loggingBuilder.ClearProviders(); + loggingBuilder.SetMinimumLevel(LogLevel.Debug); + loggingBuilder.AddNLog(config); + }) + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .BuildServiceProvider(); + + return serviceProvider; + } +} \ No newline at end of file diff --git a/BuddySave/Core/GameSaveSyncManager.cs b/BuddySave/Core/GameSaveSyncManager.cs index ab58e58..625fd88 100644 --- a/BuddySave/Core/GameSaveSyncManager.cs +++ b/BuddySave/Core/GameSaveSyncManager.cs @@ -1,51 +1,44 @@ using BuddySave.Core.Models; using BuddySave.FileManagement; -using NLog; +using Microsoft.Extensions.Logging; namespace BuddySave.Core; -public class GameSaveSyncManager : IGameSaveSyncManager +public class GameSaveSyncManager( + ILogger logger, + ISaveCopier saveCopier, + IBackupManager backupManager) + : IGameSaveSyncManager { - private readonly ILogger _logger; - private readonly ISaveCopier _saveCopier; - private readonly IBackupManager _backupManager; - - public GameSaveSyncManager(ILogger logger, ISaveCopier saveCopier, IBackupManager backupManager) - { - _logger = logger; - _saveCopier = saveCopier; - _backupManager = backupManager; - } - public void UploadSave(GameSave gameSave) { - _saveCopier.ValidateSource(gameSave.SaveName, gameSave.LocalPath); - _backupManager.BackupFiles(gameSave.CloudPath, gameSave.GameName, gameSave.SaveName, SaveType.Cloud); + saveCopier.ValidateSource(gameSave.SaveName, gameSave.LocalPath); + backupManager.BackupFiles(gameSave.CloudPath, gameSave.GameName, gameSave.SaveName, SaveType.Cloud); try { - _saveCopier.CopyOverSaves(gameSave.SaveName, gameSave.LocalPath, gameSave.CloudPath); + saveCopier.CopyOverSaves(gameSave.SaveName, gameSave.LocalPath, gameSave.CloudPath); } catch (Exception ex) { - _logger.Error(ex, "Upload failed."); - _backupManager.RestoreBackup(gameSave.CloudPath, gameSave.GameName, gameSave.SaveName, SaveType.Cloud); + logger.LogError(ex, "Upload failed."); + backupManager.RestoreBackup(gameSave.CloudPath, gameSave.GameName, gameSave.SaveName, SaveType.Cloud); } } public void DownloadSave(GameSave gameSave) { - _saveCopier.ValidateSource(gameSave.SaveName, gameSave.CloudPath); - _backupManager.BackupFiles(gameSave.LocalPath, gameSave.GameName, gameSave.SaveName, SaveType.Local); + saveCopier.ValidateSource(gameSave.SaveName, gameSave.CloudPath); + backupManager.BackupFiles(gameSave.LocalPath, gameSave.GameName, gameSave.SaveName, SaveType.Local); try { - _saveCopier.CopyOverSaves(gameSave.SaveName, gameSave.CloudPath, gameSave.LocalPath); + saveCopier.CopyOverSaves(gameSave.SaveName, gameSave.CloudPath, gameSave.LocalPath); } catch (Exception ex) { - _logger.Error(ex, "Download failed."); - _backupManager.RestoreBackup(gameSave.LocalPath, gameSave.GameName, gameSave.SaveName, SaveType.Local); + logger.LogError(ex, "Download failed."); + backupManager.RestoreBackup(gameSave.LocalPath, gameSave.GameName, gameSave.SaveName, SaveType.Local); } } } \ No newline at end of file diff --git a/BuddySave/Core/GamingSession.cs b/BuddySave/Core/GamingSession.cs index 6a8b3c0..f7dd374 100644 --- a/BuddySave/Core/GamingSession.cs +++ b/BuddySave/Core/GamingSession.cs @@ -1,23 +1,16 @@ using System.Diagnostics; using BuddySave.Core.Models; using BuddySave.System; -using NLog; +using Microsoft.Extensions.Logging; namespace BuddySave.Core; -public class GamingSession : IGamingSession +public class GamingSession( + ILogger logger, + ISharedSaveOrchestrator sharedSaveOrchestrator, + IProcessProvider processProvider) + : IGamingSession { - private readonly ILogger _logger; - private readonly ISharedSaveOrchestrator _sharedSaveOrchestrator; - private readonly IProcessProvider _processProvider; - - public GamingSession(ILogger logger, ISharedSaveOrchestrator sharedSaveOrchestrator, IProcessProvider processProvider) - { - _logger = logger; - _sharedSaveOrchestrator = sharedSaveOrchestrator; - _processProvider = processProvider; - } - public async Task RunServerWithAutoSave(GameSave gameSave, Session session, ServerParameters serverParameters) { if (string.IsNullOrWhiteSpace(serverParameters.Path)) @@ -25,16 +18,16 @@ public async Task RunServerWithAutoSave(GameSave gameSave, Session session, Serv throw new ArgumentException("No server path provided. Cannot start a gaming session."); } - await _sharedSaveOrchestrator.Load(gameSave, session); + await sharedSaveOrchestrator.Load(gameSave, session); var process = StartServer(serverParameters); await WaitForServerToStop(process); - await _sharedSaveOrchestrator.Save(gameSave, session); + await sharedSaveOrchestrator.Save(gameSave, session); } private Process StartServer(ServerParameters serverParameters) { var workingDirectory = Path.GetDirectoryName(serverParameters.Path); - var startInfo = new ProcessStartInfo() + var startInfo = new ProcessStartInfo { FileName = serverParameters.Path, Arguments = serverParameters.Arguments, @@ -42,18 +35,18 @@ private Process StartServer(ServerParameters serverParameters) UseShellExecute = true }; - var process = _processProvider.Start(startInfo); + var process = processProvider.Start(startInfo); var serverString = string.IsNullOrEmpty(serverParameters.Arguments) ? serverParameters.Path : $"{serverParameters.Path} {serverParameters.Arguments}"; - _logger.Info(@$"Server started, waiting for exit: ""{serverString}"""); + logger.LogInformation(@$"Server started, waiting for exit: ""{serverString}"""); return process; } private async Task WaitForServerToStop(Process process) { - await _processProvider.WaitForExitAsync(process); - _logger.Info("Server exited"); + await processProvider.WaitForExitAsync(process); + logger.LogInformation("Server exited"); } } \ No newline at end of file diff --git a/BuddySave/Core/Models/ServerParameters.cs b/BuddySave/Core/Models/ServerParameters.cs index e49b77c..27700b3 100644 --- a/BuddySave/Core/Models/ServerParameters.cs +++ b/BuddySave/Core/Models/ServerParameters.cs @@ -1,9 +1,8 @@ -namespace BuddySave.Core.Models +namespace BuddySave.Core.Models; + +public class ServerParameters { - public class ServerParameters - { - public string Path { get; set; } + public string Path { get; set; } - public string Arguments { get; set; } - } + public string Arguments { get; set; } } \ No newline at end of file diff --git a/BuddySave/Core/SharedSaveOrchestrator.cs b/BuddySave/Core/SharedSaveOrchestrator.cs index 2a50d0e..242eb77 100644 --- a/BuddySave/Core/SharedSaveOrchestrator.cs +++ b/BuddySave/Core/SharedSaveOrchestrator.cs @@ -1,76 +1,64 @@ using BuddySave.Core.Models; using BuddySave.Notifications; -using NLog; +using Microsoft.Extensions.Logging; namespace BuddySave.Core; -public class SharedSaveOrchestrator : ISharedSaveOrchestrator +public class SharedSaveOrchestrator( + ILogger logger, + IGameSaveSyncManager gameSaveSyncManager, + ILockManager lockManager, + IClientNotifier clientNotifier) + : ISharedSaveOrchestrator { - private readonly ILogger _logger; - private readonly IGameSaveSyncManager _gameSaveSyncManager; - private readonly ILockManager _lockManager; - private readonly IClientNotifier _clientNotifier; - - public SharedSaveOrchestrator( - ILogger logger, - IGameSaveSyncManager gameSaveSyncManager, - ILockManager lockManager, - IClientNotifier clientNotifier) - { - _logger = logger; - _gameSaveSyncManager = gameSaveSyncManager; - _lockManager = lockManager; - _clientNotifier = clientNotifier; - } - public async Task Load(GameSave gameSave, Session session) { - if (_lockManager.LockExists(gameSave)) + if (lockManager.LockExists(gameSave)) { - _clientNotifier.Notify("Game save is locked, your friends are playing!"); + clientNotifier.Notify("Game save is locked, your friends are playing!"); return; } try { - await _lockManager.CreateLock(gameSave, session); - _gameSaveSyncManager.DownloadSave(gameSave); + await lockManager.CreateLock(gameSave, session); + gameSaveSyncManager.DownloadSave(gameSave); } catch (Exception ex) { - _logger.Error(ex, "Error while loading."); - _clientNotifier.Notify("Failed loading game save. Deleting game save lock..."); - await _lockManager.DeleteLock(gameSave, session); - _clientNotifier.Notify("Game save lock released."); + logger.LogError(ex, "Error while loading."); + clientNotifier.Notify("Failed loading game save. Deleting game save lock..."); + await lockManager.DeleteLock(gameSave, session); + clientNotifier.Notify("Game save lock released."); return; } - _clientNotifier.Notify("Game save is prepared! Enjoy Buddy :)"); + clientNotifier.Notify("Game save is prepared! Enjoy Buddy :)"); } public async Task Save(GameSave gameSave, Session session) { - if (!await _lockManager.LockExists(gameSave, session)) + if (!await lockManager.LockExists(gameSave, session)) { - _clientNotifier.Notify($"You don't have a lock on a {gameSave.GameName}, cannot save."); + clientNotifier.Notify($"You don't have a lock on a {gameSave.GameName}, cannot save."); return; } try { - _clientNotifier.Notify("Uploading game save to cloud..."); - _gameSaveSyncManager.UploadSave(gameSave); - _clientNotifier.Notify("Game save uploaded."); + clientNotifier.Notify("Uploading game save to cloud..."); + gameSaveSyncManager.UploadSave(gameSave); + clientNotifier.Notify("Game save uploaded."); } catch (Exception ex) { - _logger.Error(ex, "Error while saving."); - _clientNotifier.Notify("Upload failed."); + logger.LogError(ex, "Error while saving."); + clientNotifier.Notify("Upload failed."); } finally { - await _lockManager.DeleteLock(gameSave, session); - _clientNotifier.Notify("Game save lock released."); + await lockManager.DeleteLock(gameSave, session); + clientNotifier.Notify("Game save lock released."); } } } \ No newline at end of file diff --git a/BuddySave/FileManagement/BackupDirectoryProvider.cs b/BuddySave/FileManagement/BackupDirectoryProvider.cs index b9ab636..cb14390 100644 --- a/BuddySave/FileManagement/BackupDirectoryProvider.cs +++ b/BuddySave/FileManagement/BackupDirectoryProvider.cs @@ -3,19 +3,13 @@ namespace BuddySave.FileManagement; -public class BackupDirectoryProvider : IBackupDirectoryProvider +public class BackupDirectoryProvider(IDateTimeProvider dateTimeProvider) : IBackupDirectoryProvider { private const string BackupDirectoryPrefix = "SavesBackup"; - private readonly IDateTimeProvider _dateTimeProvider; - - public BackupDirectoryProvider(IDateTimeProvider dateTimeProvider) - { - _dateTimeProvider = dateTimeProvider; - } public string GetTimestampedDirectory(string gameName, string saveName, SaveType saveType) { - return Path.Combine(GetRootDirectory(gameName, saveName, saveType), _dateTimeProvider.Now().ToString("yyyyMMdd_HHmmss")); + return Path.Combine(GetRootDirectory(gameName, saveName, saveType), dateTimeProvider.Now().ToString("yyyyMMdd_HHmmss")); } public string GetRootDirectory(string gameName, string saveName, SaveType saveType) diff --git a/BuddySave/FileManagement/BackupManager.cs b/BuddySave/FileManagement/BackupManager.cs index 68507a3..b152a4b 100644 --- a/BuddySave/FileManagement/BackupManager.cs +++ b/BuddySave/FileManagement/BackupManager.cs @@ -1,40 +1,33 @@ using BuddySave.Core.Models; -using NLog; +using Microsoft.Extensions.Logging; namespace BuddySave.FileManagement; -public class BackupManager : IBackupManager +public class BackupManager( + IRollingBackups rollingBackups, + ISaveCopier saveCopier, + ILogger logger) + : IBackupManager { - private readonly IRollingBackups _rollingBackups; - private readonly ISaveCopier _saveCopier; - private readonly ILogger _logger; - - public BackupManager(IRollingBackups rollingBackups, ISaveCopier saveCopier, ILogger logger) - { - _rollingBackups = rollingBackups; - _saveCopier = saveCopier; - _logger = logger; - } - public void BackupFiles(string sourcePath, string gameName, string saveName, SaveType saveType) { try { - _saveCopier.ValidateSource(saveName, sourcePath); + saveCopier.ValidateSource(saveName, sourcePath); } catch { - _logger.Info($"Nothing to backup in {sourcePath}"); + logger.LogInformation("Nothing to backup in {sourcePath}", sourcePath); return; } - _rollingBackups.Add(sourcePath, gameName, saveName, saveType); + rollingBackups.Add(sourcePath, gameName, saveName, saveType); } public void RestoreBackup(string destinationPath, string gameName, string saveName, SaveType saveType) { - var backupDirectory = _rollingBackups.GetMostRecent(gameName, saveName, saveType); - _saveCopier.ValidateSource(saveName, backupDirectory); - _saveCopier.CopyOverSaves(saveName, backupDirectory, destinationPath); + var backupDirectory = rollingBackups.GetMostRecent(gameName, saveName, saveType); + saveCopier.ValidateSource(saveName, backupDirectory); + saveCopier.CopyOverSaves(saveName, backupDirectory, destinationPath); } } \ No newline at end of file diff --git a/BuddySave/FileManagement/RollingBackups.cs b/BuddySave/FileManagement/RollingBackups.cs index ae3af6a..d3247de 100644 --- a/BuddySave/FileManagement/RollingBackups.cs +++ b/BuddySave/FileManagement/RollingBackups.cs @@ -1,32 +1,26 @@ using BuddySave.Core.Models; -using NLog; +using Microsoft.Extensions.Logging; namespace BuddySave.FileManagement; -public class RollingBackups : IRollingBackups +public class RollingBackups( + ILogger logger, + IBackupDirectoryProvider backupDirectoryProvider, + ISaveCopier saveCopier) + : IRollingBackups { private const int MaxNumberOfRollingBackups = 10; - private readonly ILogger _logger; - private readonly IBackupDirectoryProvider _backupDirectoryProvider; - private readonly ISaveCopier _saveCopier; - - public RollingBackups(ILogger logger, IBackupDirectoryProvider backupDirectoryProvider, ISaveCopier saveCopier) - { - _logger = logger; - _backupDirectoryProvider = backupDirectoryProvider; - _saveCopier = saveCopier; - } public string GetMostRecent(string gameName, string saveName, SaveType saveType) { - var savePath = _backupDirectoryProvider.GetRootDirectory(gameName, saveName, saveType); + var savePath = backupDirectoryProvider.GetRootDirectory(gameName, saveName, saveType); return Directory.GetDirectories(savePath).OrderByDescending(x => x).First(); } public void Add(string sourcePath, string gameName, string saveName, SaveType saveType) { - var backupDir = _backupDirectoryProvider.GetTimestampedDirectory(gameName, saveName, saveType); - _saveCopier.CopyOverSaves(saveName, sourcePath, backupDir); + var backupDir = backupDirectoryProvider.GetTimestampedDirectory(gameName, saveName, saveType); + saveCopier.CopyOverSaves(saveName, sourcePath, backupDir); RemoveOldRollingBackup(gameName, saveName, saveType); } @@ -41,7 +35,7 @@ private void RemoveOldRollingBackup(string gameName, string saveName, SaveType s private int GetCount(string gameName, string saveName, SaveType saveType) { - var savePath = _backupDirectoryProvider.GetRootDirectory(gameName, saveName, saveType); + var savePath = backupDirectoryProvider.GetRootDirectory(gameName, saveName, saveType); if (!Directory.Exists(savePath)) { return 0; @@ -54,12 +48,12 @@ private void DeleteOldest(string gameName, string saveName, SaveType saveType) { var oldest = GetOldestPath(gameName, saveName, saveType); Directory.Delete(oldest, true); - _logger.Info($"Old save {oldest} deleted"); + logger.LogInformation("Old save {oldest} deleted", oldest); } private string GetOldestPath(string gameName, string saveName, SaveType saveType) { - var savePath = _backupDirectoryProvider.GetRootDirectory(gameName, saveName, saveType); + var savePath = backupDirectoryProvider.GetRootDirectory(gameName, saveName, saveType); return Directory.GetDirectories(savePath).OrderBy(x => x).First(); } } \ No newline at end of file diff --git a/BuddySave/Notifications/ClientNotifier.cs b/BuddySave/Notifications/ClientNotifier.cs index 5fa6284..aa316ef 100644 --- a/BuddySave/Notifications/ClientNotifier.cs +++ b/BuddySave/Notifications/ClientNotifier.cs @@ -1,18 +1,11 @@ -using NLog; +using Microsoft.Extensions.Logging; namespace BuddySave.Notifications; -public class ClientNotifier : IClientNotifier +public class ClientNotifier(ILogger logger) : IClientNotifier { - private readonly ILogger _logger; - - public ClientNotifier(ILogger logger) - { - _logger = logger; - } - public void Notify(string text) { - _logger.Info(text); + logger.LogInformation(text); } } \ No newline at end of file diff --git a/BuddySave/Program.cs b/BuddySave/Program.cs index cf6403a..5c28d11 100644 --- a/BuddySave/Program.cs +++ b/BuddySave/Program.cs @@ -1,84 +1,14 @@ using BuddySave.Configuration; -using BuddySave.Core; -using BuddySave.Core.Models; -using BuddySave.FileManagement; -using BuddySave.Notifications; -using BuddySave.System; -using NLog; +using Microsoft.Extensions.DependencyInjection; -namespace BuddySave +namespace BuddySave; + +internal static class Program { - internal class Program + private static async Task Main() { - private static readonly ISharedSaveOrchestrator SharedSaveOrchestrator; - private static readonly IGamingSession GamingSession; - private static readonly Logger Logger; - private static readonly IConfigurationLoader ConfigurationLoader; - - static Program() - { - Logger = LogManager.GetLogger("BuddySave"); - ConfigurationLoader = new ConfigurationLoader(Logger); - var saveCopier = new SaveCopier(); - var backupDirectoryProvider = new BackupDirectoryProvider(new DateTimeProvider()); - var rollingBackups = new RollingBackups(Logger, backupDirectoryProvider, saveCopier); - var backupManager = new BackupManager(rollingBackups, saveCopier, Logger); - var gameSaveSyncManager = new GameSaveSyncManager(Logger, saveCopier, backupManager); - var clientNotifier = new ClientNotifier(Logger); - var lockManager = new LockManager(); - var processProvider = new ProcessProvider(); - SharedSaveOrchestrator = new SharedSaveOrchestrator(Logger, gameSaveSyncManager, lockManager, clientNotifier); - GamingSession = new GamingSession(Logger, SharedSaveOrchestrator, processProvider); - } - - private static async Task Main() - { - Logger.Info("Start"); - Console.WriteLine("∞∞∞∞∞∞∞ Buddy Save ∞∞∞∞∞∞∞"); - - try - { - var configuration = await ConfigurationLoader.Load(); - await Run(configuration.GameSave, configuration.Session, configuration.ServerParameters); - } - catch (Exception ex) - { - Logger.Error(ex); - } - - Console.WriteLine("Bye Buddy! ;)"); - Console.WriteLine("Press any key to exit..."); - Console.ReadKey(); - Logger.Info("Exit"); - } - - private static async Task Run(GameSave gameSave, Session session, ServerParameters serverParameters) - { - var input = string.Empty; - while (!string.Equals(input, "exit", StringComparison.OrdinalIgnoreCase)) - { - Console.WriteLine("Waiting for action command (run, load, save, exit):"); - input = Console.ReadLine(); - - switch (input?.ToLowerInvariant()) - { - case "run": - await GamingSession.RunServerWithAutoSave(gameSave, session, serverParameters); - break; - - case "load": - await SharedSaveOrchestrator.Load(gameSave, session); - break; - - case "save": - await SharedSaveOrchestrator.Save(gameSave, session); - - break; - - case "exit": - break; - } - } - } + var serviceProvider = IoC.Setup(); + var app = serviceProvider.GetRequiredService(); + await app.Start(); } } \ No newline at end of file