diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml index 4f55358..5a19686 100644 --- a/.github/workflows/pr-validation.yml +++ b/.github/workflows/pr-validation.yml @@ -27,7 +27,7 @@ jobs: run: dotnet build --configuration Release -warnaserror - name: Run unit tests - run: dotnet test --no-build --verbosity normal + run: dotnet test --configuration Release --verbosity normal - name: Validate mapping JSON files run: | diff --git a/drum-midi-remapper.sln b/drum-midi-remapper.sln index d7542fa..1f8712f 100644 --- a/drum-midi-remapper.sln +++ b/drum-midi-remapper.sln @@ -13,6 +13,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Managers", "src\Managers\Ma EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CLI", "src\CLI\CLI.csproj", "{36E27E57-4986-4178-98F8-3B4F6D985E2C}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Managers.Tests", "tests\Managers.Tests\Managers.Tests.csproj", "{04A8B018-1954-48B8-BAAF-E17385114845}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Models.Tests", "tests\Models.Tests\Models.Tests.csproj", "{C3C581BD-E5DE-4CC4-B9A5-9F76602C69A4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Services.Tests", "tests\Services.Tests\Services.Tests.csproj", "{3CECB972-EFFD-422B-9109-4D0D5AB6441E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -71,6 +79,42 @@ Global {36E27E57-4986-4178-98F8-3B4F6D985E2C}.Release|x64.Build.0 = Release|Any CPU {36E27E57-4986-4178-98F8-3B4F6D985E2C}.Release|x86.ActiveCfg = Release|Any CPU {36E27E57-4986-4178-98F8-3B4F6D985E2C}.Release|x86.Build.0 = Release|Any CPU + {04A8B018-1954-48B8-BAAF-E17385114845}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04A8B018-1954-48B8-BAAF-E17385114845}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04A8B018-1954-48B8-BAAF-E17385114845}.Debug|x64.ActiveCfg = Debug|Any CPU + {04A8B018-1954-48B8-BAAF-E17385114845}.Debug|x64.Build.0 = Debug|Any CPU + {04A8B018-1954-48B8-BAAF-E17385114845}.Debug|x86.ActiveCfg = Debug|Any CPU + {04A8B018-1954-48B8-BAAF-E17385114845}.Debug|x86.Build.0 = Debug|Any CPU + {04A8B018-1954-48B8-BAAF-E17385114845}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04A8B018-1954-48B8-BAAF-E17385114845}.Release|Any CPU.Build.0 = Release|Any CPU + {04A8B018-1954-48B8-BAAF-E17385114845}.Release|x64.ActiveCfg = Release|Any CPU + {04A8B018-1954-48B8-BAAF-E17385114845}.Release|x64.Build.0 = Release|Any CPU + {04A8B018-1954-48B8-BAAF-E17385114845}.Release|x86.ActiveCfg = Release|Any CPU + {04A8B018-1954-48B8-BAAF-E17385114845}.Release|x86.Build.0 = Release|Any CPU + {C3C581BD-E5DE-4CC4-B9A5-9F76602C69A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C3C581BD-E5DE-4CC4-B9A5-9F76602C69A4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3C581BD-E5DE-4CC4-B9A5-9F76602C69A4}.Debug|x64.ActiveCfg = Debug|Any CPU + {C3C581BD-E5DE-4CC4-B9A5-9F76602C69A4}.Debug|x64.Build.0 = Debug|Any CPU + {C3C581BD-E5DE-4CC4-B9A5-9F76602C69A4}.Debug|x86.ActiveCfg = Debug|Any CPU + {C3C581BD-E5DE-4CC4-B9A5-9F76602C69A4}.Debug|x86.Build.0 = Debug|Any CPU + {C3C581BD-E5DE-4CC4-B9A5-9F76602C69A4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C3C581BD-E5DE-4CC4-B9A5-9F76602C69A4}.Release|Any CPU.Build.0 = Release|Any CPU + {C3C581BD-E5DE-4CC4-B9A5-9F76602C69A4}.Release|x64.ActiveCfg = Release|Any CPU + {C3C581BD-E5DE-4CC4-B9A5-9F76602C69A4}.Release|x64.Build.0 = Release|Any CPU + {C3C581BD-E5DE-4CC4-B9A5-9F76602C69A4}.Release|x86.ActiveCfg = Release|Any CPU + {C3C581BD-E5DE-4CC4-B9A5-9F76602C69A4}.Release|x86.Build.0 = Release|Any CPU + {3CECB972-EFFD-422B-9109-4D0D5AB6441E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3CECB972-EFFD-422B-9109-4D0D5AB6441E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3CECB972-EFFD-422B-9109-4D0D5AB6441E}.Debug|x64.ActiveCfg = Debug|Any CPU + {3CECB972-EFFD-422B-9109-4D0D5AB6441E}.Debug|x64.Build.0 = Debug|Any CPU + {3CECB972-EFFD-422B-9109-4D0D5AB6441E}.Debug|x86.ActiveCfg = Debug|Any CPU + {3CECB972-EFFD-422B-9109-4D0D5AB6441E}.Debug|x86.Build.0 = Debug|Any CPU + {3CECB972-EFFD-422B-9109-4D0D5AB6441E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3CECB972-EFFD-422B-9109-4D0D5AB6441E}.Release|Any CPU.Build.0 = Release|Any CPU + {3CECB972-EFFD-422B-9109-4D0D5AB6441E}.Release|x64.ActiveCfg = Release|Any CPU + {3CECB972-EFFD-422B-9109-4D0D5AB6441E}.Release|x64.Build.0 = Release|Any CPU + {3CECB972-EFFD-422B-9109-4D0D5AB6441E}.Release|x86.ActiveCfg = Release|Any CPU + {3CECB972-EFFD-422B-9109-4D0D5AB6441E}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -80,5 +124,8 @@ Global {E5A71B17-7DDC-452E-AE20-141AFDAE906A} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} {92335A36-F5AE-45AA-B691-5054C217E33A} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} {36E27E57-4986-4178-98F8-3B4F6D985E2C} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} + {04A8B018-1954-48B8-BAAF-E17385114845} = {0AB3BF05-4346-4AA6-1389-037BE0695223} + {C3C581BD-E5DE-4CC4-B9A5-9F76602C69A4} = {0AB3BF05-4346-4AA6-1389-037BE0695223} + {3CECB972-EFFD-422B-9109-4D0D5AB6441E} = {0AB3BF05-4346-4AA6-1389-037BE0695223} EndGlobalSection EndGlobal diff --git a/tests/Managers.Tests/Implementations/CliArgumentManagerTest.cs b/tests/Managers.Tests/Implementations/CliArgumentManagerTest.cs new file mode 100644 index 0000000..315bb9a --- /dev/null +++ b/tests/Managers.Tests/Implementations/CliArgumentManagerTest.cs @@ -0,0 +1,50 @@ +using Models; + +namespace Managers.Tests.Implementations +{ + public class CliArgumentManagerTest + { + [Fact] + public async Task Execute_ValidArguments_ReturnsRemapVariables() + { + var manager = new CliArgumentManager(); + var args = new[] { "GuitarPro", "LogicPro", "test.mid" }; + + var result = await manager.Execute(args); + + Assert.Equal(DrumMapType.GuitarPro, result.SourceMapType); + Assert.Equal(DrumMapType.LogicPro, result.TargetMapType); + Assert.Equal("test.mid", result.MidiPath); + } + + [Fact] + public async Task Execute_InsufficientArguments_ThrowsNullReferenceException() + { + var manager = new CliArgumentManager(); + var args = new[] { "GuitarPro", "LogicPro" }; + + var ex = await Assert.ThrowsAsync(() => manager.Execute(args)); + Assert.Contains("Insufficient arguments", ex.Message); + } + + [Fact] + public async Task Execute_InvalidSourceMap_ThrowsArgumentException() + { + var manager = new CliArgumentManager(); + var args = new[] { "InvalidMap", "LogicPro", "test.mid" }; + + var ex = await Assert.ThrowsAsync(() => manager.Execute(args)); + Assert.Contains("Invalid source map", ex.Message); + } + + [Fact] + public async Task Execute_InvalidTargetMap_ThrowsArgumentException() + { + var manager = new CliArgumentManager(); + var args = new[] { "GuitarPro", "InvalidMap", "test.mid" }; + + var ex = await Assert.ThrowsAsync(() => manager.Execute(args)); + Assert.Contains("Invalid target map", ex.Message); + } + } +} \ No newline at end of file diff --git a/tests/Managers.Tests/Implementations/MidiMapManagerTests.cs b/tests/Managers.Tests/Implementations/MidiMapManagerTests.cs new file mode 100644 index 0000000..b2243bd --- /dev/null +++ b/tests/Managers.Tests/Implementations/MidiMapManagerTests.cs @@ -0,0 +1,43 @@ +using Moq; +using Services.Contracts; +using Models; + +namespace Managers.Tests.Implementations; + +public class MidiMapManagerTest +{ + private const DrumMapType SOURCE = DrumMapType.GuitarPro; + private const DrumMapType TARGET = DrumMapType.StevenSlate; + private const string FILENAME = "test.mid"; + + [Fact] + public async Task RemapMidi_CallsServicesWithCorrectParameters() + { + // Arrange + var mockMapLoader = new Mock(); + var mockMidiFileService = new Mock(); + + var sourceMap = new DrumMap(); + var targetMap = new DrumMap(); + + var variables = new RemapVariables + { + SourceMapType = SOURCE, + TargetMapType = TARGET, + MidiPath = FILENAME + }; + + mockMapLoader.Setup(m => m.LoadAsync(SOURCE)).ReturnsAsync(sourceMap); + mockMapLoader.Setup(m => m.LoadAsync(TARGET)).ReturnsAsync(targetMap); + + var manager = new MidiMapManager(mockMapLoader.Object, mockMidiFileService.Object); + + // Act + await manager.RemapMidi(variables); + + // Assert + mockMapLoader.Verify(m => m.LoadAsync(SOURCE), Times.Once); + mockMapLoader.Verify(m => m.LoadAsync(TARGET), Times.Once); + mockMidiFileService.Verify(m => m.RemapAsync(sourceMap, targetMap, "test.mid"), Times.Once); + } +} \ No newline at end of file diff --git a/tests/Managers.Tests/Managers.Tests.csproj b/tests/Managers.Tests/Managers.Tests.csproj new file mode 100644 index 0000000..53741e2 --- /dev/null +++ b/tests/Managers.Tests/Managers.Tests.csproj @@ -0,0 +1,27 @@ + + + + net9.0 + enable + enable + false + + + + + + + + + + + + + + + + + + + + diff --git a/tests/Models.Tests/DrumMapTest.cs b/tests/Models.Tests/DrumMapTest.cs new file mode 100644 index 0000000..c45bf1a --- /dev/null +++ b/tests/Models.Tests/DrumMapTest.cs @@ -0,0 +1,59 @@ +namespace Models.Tests; + +public class DrumMapTest +{ + [Fact] + public void DrumMap_DefaultConstructor_InitializesProperties() + { + var drumMap = new DrumMap(); + + Assert.NotNull(drumMap.Name); + Assert.Equal(string.Empty, drumMap.Name); + Assert.NotNull(drumMap.Mapping); + Assert.Empty(drumMap.Mapping); + } + + [Fact] + public void DrumMap_CanSetName() + { + var drumMap = new DrumMap + { + Name = "My Drum Map" + }; + + Assert.Equal("My Drum Map", drumMap.Name); + } + + [Fact] + public void DrumMap_CanAddMapping() + { + var drumMap = new DrumMap(); + drumMap.Mapping["Kick"] = 36; + drumMap.Mapping["Snare"] = 38; + + Assert.Equal(2, drumMap.Mapping.Count); + Assert.Equal(36, drumMap.Mapping["Kick"]); + Assert.Equal(38, drumMap.Mapping["Snare"]); + } + + [Fact] + public void DrumMap_CanUpdateMapping() + { + var drumMap = new DrumMap(); + drumMap.Mapping["Kick"] = 36; + drumMap.Mapping["Kick"] = 35; + + Assert.Single(drumMap.Mapping); + Assert.Equal(35, drumMap.Mapping["Kick"]); + } + + [Fact] + public void DrumMap_CanRemoveMapping() + { + var drumMap = new DrumMap(); + drumMap.Mapping["Kick"] = 36; + drumMap.Mapping.Remove("Kick"); + + Assert.Empty(drumMap.Mapping); + } +} \ No newline at end of file diff --git a/tests/Models.Tests/DrumMapTypeTest.cs b/tests/Models.Tests/DrumMapTypeTest.cs new file mode 100644 index 0000000..d337357 --- /dev/null +++ b/tests/Models.Tests/DrumMapTypeTest.cs @@ -0,0 +1,34 @@ +namespace Models.Tests; + +public class DrumMapTypeTest +{ + [Fact] + public void DrumMapType_ContainsExpectedValues() + { + Assert.Equal(0, (int)DrumMapType.StevenSlate); + Assert.Equal(1, (int)DrumMapType.GuitarPro); + Assert.Equal(2, (int)DrumMapType.LogicPro); + Assert.Equal(3, (int)DrumMapType.ProTools); + } + + [Theory] + [InlineData(DrumMapType.StevenSlate)] + [InlineData(DrumMapType.GuitarPro)] + [InlineData(DrumMapType.LogicPro)] + [InlineData(DrumMapType.ProTools)] + public void DrumMapType_IsDefined(DrumMapType type) + { + Assert.True(Enum.IsDefined(type)); + } + + [Theory] + [InlineData("StevenSlate", DrumMapType.StevenSlate)] + [InlineData("GuitarPro", DrumMapType.GuitarPro)] + [InlineData("LogicPro", DrumMapType.LogicPro)] + [InlineData("ProTools", DrumMapType.ProTools)] + public void DrumMapType_ParseString_ReturnsCorrectEnum(string name, DrumMapType expected) + { + var parsed = (DrumMapType)Enum.Parse(typeof(DrumMapType), name); + Assert.Equal(expected, parsed); + } +} \ No newline at end of file diff --git a/tests/Models.Tests/Models.Tests.csproj b/tests/Models.Tests/Models.Tests.csproj new file mode 100644 index 0000000..bb1b65b --- /dev/null +++ b/tests/Models.Tests/Models.Tests.csproj @@ -0,0 +1,25 @@ + + + + net9.0 + enable + enable + false + + + + + + + + + + + + + + + + + + diff --git a/tests/Models.Tests/RemapVariablesTest.cs b/tests/Models.Tests/RemapVariablesTest.cs new file mode 100644 index 0000000..4c1dfea --- /dev/null +++ b/tests/Models.Tests/RemapVariablesTest.cs @@ -0,0 +1,64 @@ +namespace Models.Tests; + +public class RemapVariablesTest +{ + [Fact] + public void RemapVariables_DefaultConstructor_InitializesProperties() + { + var remapVariables = new RemapVariables + { + SourceMapType = DrumMapType.GuitarPro, + TargetMapType = DrumMapType.LogicPro, + MidiPath = "test.mid" + }; + + Assert.Equal(DrumMapType.GuitarPro, remapVariables.SourceMapType); + Assert.Equal(DrumMapType.LogicPro, remapVariables.TargetMapType); + Assert.Equal("test.mid", remapVariables.MidiPath); + } + + [Fact] + public void RemapVariables_CanSetSourceMapType() + { + var remapVariables = new RemapVariables + { + SourceMapType = DrumMapType.LogicPro, + TargetMapType = DrumMapType.GuitarPro, + MidiPath = "file.mid" + }; + + remapVariables.SourceMapType = DrumMapType.GuitarPro; + + Assert.Equal(DrumMapType.GuitarPro, remapVariables.SourceMapType); + } + + [Fact] + public void RemapVariables_CanSetTargetMapType() + { + var remapVariables = new RemapVariables + { + SourceMapType = DrumMapType.GuitarPro, + TargetMapType = DrumMapType.LogicPro, + MidiPath = "file.mid" + }; + + remapVariables.TargetMapType = DrumMapType.GuitarPro; + + Assert.Equal(DrumMapType.GuitarPro, remapVariables.TargetMapType); + } + + [Fact] + public void RemapVariables_CanSetMidiPath() + { + var remapVariables = new RemapVariables + { + SourceMapType = DrumMapType.GuitarPro, + TargetMapType = DrumMapType.LogicPro, + MidiPath = "old.mid" + }; + + remapVariables.MidiPath = "new.mid"; + + Assert.Equal("new.mid", remapVariables.MidiPath); + } +} \ No newline at end of file diff --git a/tests/Services.Tests/Extensions/MidiFileExtensionsTest.cs b/tests/Services.Tests/Extensions/MidiFileExtensionsTest.cs new file mode 100644 index 0000000..c9da3b2 --- /dev/null +++ b/tests/Services.Tests/Extensions/MidiFileExtensionsTest.cs @@ -0,0 +1,116 @@ +using Melanchall.DryWetMidi.Common; +using Melanchall.DryWetMidi.Core; +using Models; + +namespace Services.Extensions.Tests +{ + public class MidiFileExtensionsTest + { + private DrumMap CreateDrumMap(Dictionary mapping) + { + return new DrumMap { Mapping = mapping }; + } + + private MidiFile CreateMidiFileWithNotes(params int[] noteNumbers) + { + var trackChunk = new TrackChunk(); + foreach (var note in noteNumbers) + { + trackChunk.Events.Add(new NoteOnEvent((SevenBitNumber)note, (SevenBitNumber)100)); + trackChunk.Events.Add(new NoteOffEvent((SevenBitNumber)note, (SevenBitNumber)0)); + } + return new MidiFile(trackChunk); + } + + [Fact] + public void RemapNotes_MapsNotesAccordingToDrumMaps() + { + // Arrange + var sourceMap = CreateDrumMap(new Dictionary + { + { "Kick", 36 }, + { "Snare", 38 } + }); + var targetMap = CreateDrumMap(new Dictionary + { + { "Kick", 40 }, + { "Snare", 41 } + }); + var midiFile = CreateMidiFileWithNotes(36, 38); + + // Act + midiFile.RemapNotes(sourceMap, targetMap); + + // Assert + var notes = new List(); + foreach (var trackChunk in midiFile.GetTrackChunks()) + { + foreach (var midiEvent in trackChunk.Events) + { + if (midiEvent is NoteOnEvent noteOn) + notes.Add(noteOn.NoteNumber); + } + } + Assert.Contains(40, notes); // Kick mapped + Assert.Contains(41, notes); // Snare mapped + Assert.DoesNotContain(36, notes); + Assert.DoesNotContain(38, notes); + } + + [Fact] + public void RemapNotes_IgnoresNotesNotInSourceMap() + { + // Arrange + var sourceMap = CreateDrumMap(new Dictionary + { + { "Kick", 36 } + }); + var targetMap = CreateDrumMap(new Dictionary + { + { "Kick", 40 } + }); + var midiFile = CreateMidiFileWithNotes(36, 38); // 38 is not in sourceMap + + // Act + midiFile.RemapNotes(sourceMap, targetMap); + + // Assert + var notes = new List(); + foreach (var trackChunk in midiFile.GetTrackChunks()) + { + foreach (var midiEvent in trackChunk.Events) + { + if (midiEvent is NoteOnEvent noteOn) + notes.Add(noteOn.NoteNumber); + } + } + Assert.Contains(40, notes); // Kick mapped + Assert.Contains(38, notes); // Snare not mapped, remains + } + + [Fact] + public void RemapNotes_DoesNothingIfNoMapping() + { + // Arrange + var sourceMap = CreateDrumMap(new Dictionary()); + var targetMap = CreateDrumMap(new Dictionary()); + var midiFile = CreateMidiFileWithNotes(36, 38); + + // Act + midiFile.RemapNotes(sourceMap, targetMap); + + // Assert + var notes = new List(); + foreach (var trackChunk in midiFile.GetTrackChunks()) + { + foreach (var midiEvent in trackChunk.Events) + { + if (midiEvent is NoteOnEvent noteOn) + notes.Add(noteOn.NoteNumber); + } + } + Assert.Contains(36, notes); + Assert.Contains(38, notes); + } + } +} \ No newline at end of file diff --git a/tests/Services.Tests/Implementation/MapLoaderServiceTest.cs b/tests/Services.Tests/Implementation/MapLoaderServiceTest.cs new file mode 100644 index 0000000..fdf5a51 --- /dev/null +++ b/tests/Services.Tests/Implementation/MapLoaderServiceTest.cs @@ -0,0 +1,86 @@ +using System.Reflection; +using System.Text.Json; +using Models; +using Services.Implementations; +using Moq; + +namespace Services.Tests.Implementation; + +public class MapLoaderServiceTest +{ + [Fact] + public async Task LoadAsync_ValidType_ReturnsDrumMap() + { + // Arrange + var type = DrumMapType.ProTools; + var resourceName = $"Services.Resources.Maps.{type}.json"; + var drumMap = new DrumMap { Name = "TestMap" }; + var json = JsonSerializer.Serialize(drumMap); + + var assemblyMock = new Mock(); + var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(json)); + assemblyMock.Setup(a => a.GetManifestResourceStream(resourceName)).Returns(stream); + + var service = new MapLoaderServiceTestable(assemblyMock.Object); + + // Act + var result = await service.LoadAsync(type); + + // Assert + Assert.NotNull(result); + Assert.Equal("TestMap", result.Name); + } + + [Fact] + public async Task LoadAsync_ResourceNotFound_ThrowsFileNotFoundException() + { + // Arrange + var type = DrumMapType.GuitarPro; + var resourceName = $"Services.Resources.Maps.{type}.json"; + var assemblyMock = new Mock(); + assemblyMock.Setup(a => a.GetManifestResourceStream(resourceName)).Returns((Stream?)null); + + var service = new MapLoaderServiceTestable(assemblyMock.Object); + + // Act & Assert + await Assert.ThrowsAsync(() => service.LoadAsync(type)); + } + + [Fact] + public async Task LoadAsync_InvalidJson_ThrowsException() + { + // Arrange + var type = DrumMapType.LogicPro; + var resourceName = $"Services.Resources.Maps.{type}.json"; + var invalidJson = "{ invalid json }"; + var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(invalidJson)); + var assemblyMock = new Mock(); + assemblyMock.Setup(a => a.GetManifestResourceStream(resourceName)).Returns(stream); + + var service = new MapLoaderServiceTestable(assemblyMock.Object); + + // Act & Assert + await Assert.ThrowsAsync(() => service.LoadAsync(type)); + } + + // Helper class to inject mocked Assembly + private class MapLoaderServiceTestable : MapLoaderService + { + private readonly Assembly _testAssembly; + + public MapLoaderServiceTestable(Assembly testAssembly) + { + _testAssembly = testAssembly; + } + + public new async Task LoadAsync(DrumMapType type) + { + var resourceName = $"Services.Resources.Maps.{type}.json"; + using Stream? stream = _testAssembly.GetManifestResourceStream(resourceName) ?? + throw new FileNotFoundException($"Embedded map not found: {resourceName}"); + + return await JsonSerializer.DeserializeAsync(stream) + ?? throw new Exception($"Error deserializing map: {type}"); + } + } +} \ No newline at end of file diff --git a/tests/Services.Tests/Implementation/MidiFileServicesTest.cs b/tests/Services.Tests/Implementation/MidiFileServicesTest.cs new file mode 100644 index 0000000..6bbea23 --- /dev/null +++ b/tests/Services.Tests/Implementation/MidiFileServicesTest.cs @@ -0,0 +1,58 @@ + +using Melanchall.DryWetMidi.Core; +using Models; +using Services.Implementations; + + +namespace Services.Tests.Implementation +{ + public class MidiFileServiceTest + { + [Fact] + public async Task RemapAsync_FileDoesNotExist_ThrowsFileNotFoundException() + { + var service = new MidiFileService(); + var sourceMap = new DrumMap(); + var targetMap = new DrumMap(); + string fakePath = "nonexistent.mid"; + + var ex = await Assert.ThrowsAsync(() => + service.RemapAsync(sourceMap, targetMap, fakePath)); + Assert.Contains("MIDI file not found", ex.Message); + } + + [Fact] + public async Task RemapAsync_ValidFile_RemapsAndSavesFile() + { + // Arrange + var service = new MidiFileService(); + var sourceMap = new DrumMap(); + var targetMap = new DrumMap(); + string tempFile = Path.GetTempFileName(); + string midiPath = Path.ChangeExtension(tempFile, ".mid"); + + // Create a simple MIDI file + var midiFile = new MidiFile(); + midiFile.Write(midiPath); + + try + { + // Act + await service.RemapAsync(sourceMap, targetMap, midiPath); + + // Assert + string outputPath = Path.Combine(Directory.GetCurrentDirectory(), Path.GetFileName(midiPath)); + Assert.True(File.Exists(outputPath)); + } + finally + { + if (File.Exists(midiPath)) + File.Delete(midiPath); + + string outputPath = Path.Combine(Directory.GetCurrentDirectory(), Path.GetFileName(midiPath)); + if (File.Exists(outputPath)) + File.Delete(outputPath); + } + } + } +} \ No newline at end of file diff --git a/tests/Services.Tests/Services.Tests.csproj b/tests/Services.Tests/Services.Tests.csproj new file mode 100644 index 0000000..120a457 --- /dev/null +++ b/tests/Services.Tests/Services.Tests.csproj @@ -0,0 +1,27 @@ + + + + net9.0 + enable + enable + false + + + + + + + + + + + + + + + + + + + +