using JdeScoping.ConfigManager.Services; namespace JdeScoping.ConfigManager.Tests.Services; public class BackupServiceTests { private readonly IFileSystem _fileSystem; private readonly BackupService _sut; public BackupServiceTests() { _fileSystem = Substitute.For(); _sut = new BackupService(_fileSystem); } [Fact] public async Task CreateBackupAsync_CreatesTimestampedBackup() { // Arrange var sourcePath = "/config/appsettings.json"; _fileSystem.FileExists(sourcePath).Returns(true); _fileSystem.GetDirectoryName(sourcePath).Returns("/config"); _fileSystem.GetFileNameWithoutExtension(sourcePath).Returns("appsettings"); _fileSystem.Combine(Arg.Any()).Returns(callInfo => { var paths = callInfo.Arg(); return string.Join("/", paths); }); // Act var backupPath = await _sut.CreateBackupAsync(sourcePath); // Assert backupPath.ShouldStartWith("/config/appsettings."); backupPath.ShouldEndWith(".bak"); await _fileSystem.Received(1).CopyFileAsync(sourcePath, backupPath, Arg.Any()); } [Fact] public async Task CleanupOldBackupsAsync_KeepsOnlySpecifiedCount() { // Arrange var filePath = "/config/appsettings.json"; _fileSystem.GetDirectoryName(filePath).Returns("/config"); _fileSystem.GetFileNameWithoutExtension(filePath).Returns("appsettings"); var backups = Enumerable.Range(1, 15) .Select(i => $"/config/appsettings.2026-01-{i:D2}_120000.bak") .ToArray(); _fileSystem.GetFilesAsync("/config", "appsettings.*.bak", Arg.Any()) .Returns(Task.FromResult(backups)); // Mock GetFileNameWithoutExtension for each backup file foreach (var backup in backups) { var fileName = backup.Split('/').Last().Replace(".bak", ""); _fileSystem.GetFileNameWithoutExtension(backup).Returns(fileName); } // Act await _sut.CleanupOldBackupsAsync(filePath, keepCount: 10); // Assert - should delete 5 oldest backups await _fileSystem.Received(5).DeleteFileAsync(Arg.Any(), Arg.Any()); } }