mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
test: add unit tests for DeezerMetadataService and LocalLibraryService
- Add DeezerMetadataServiceTests with mocked HTTP responses - Add LocalLibraryServiceTests for song ID parsing and registration - Configure xUnit test project with Moq and MVC Testing packages
This commit is contained in:
198
octo-fiesta.Tests/DeezerMetadataServiceTests.cs
Normal file
198
octo-fiesta.Tests/DeezerMetadataServiceTests.cs
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
using octo_fiesta.Services;
|
||||||
|
using octo_fiesta.Models;
|
||||||
|
using Moq;
|
||||||
|
using Moq.Protected;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace octo_fiesta.Tests;
|
||||||
|
|
||||||
|
public class DeezerMetadataServiceTests
|
||||||
|
{
|
||||||
|
private readonly Mock<IHttpClientFactory> _httpClientFactoryMock;
|
||||||
|
private readonly Mock<HttpMessageHandler> _httpMessageHandlerMock;
|
||||||
|
private readonly DeezerMetadataService _service;
|
||||||
|
|
||||||
|
public DeezerMetadataServiceTests()
|
||||||
|
{
|
||||||
|
_httpMessageHandlerMock = new Mock<HttpMessageHandler>();
|
||||||
|
var httpClient = new HttpClient(_httpMessageHandlerMock.Object);
|
||||||
|
|
||||||
|
_httpClientFactoryMock = new Mock<IHttpClientFactory>();
|
||||||
|
_httpClientFactoryMock.Setup(f => f.CreateClient(It.IsAny<string>())).Returns(httpClient);
|
||||||
|
|
||||||
|
_service = new DeezerMetadataService(_httpClientFactoryMock.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SearchSongsAsync_ReturnsListOfSongs()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var deezerResponse = new
|
||||||
|
{
|
||||||
|
data = new[]
|
||||||
|
{
|
||||||
|
new
|
||||||
|
{
|
||||||
|
id = 123456,
|
||||||
|
title = "Test Song",
|
||||||
|
duration = 180,
|
||||||
|
track_position = 1,
|
||||||
|
artist = new { id = 789, name = "Test Artist" },
|
||||||
|
album = new { id = 456, title = "Test Album", cover_medium = "https://example.com/cover.jpg" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SetupHttpResponse(JsonSerializer.Serialize(deezerResponse));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _service.SearchSongsAsync("test query", 20);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.Single(result);
|
||||||
|
Assert.Equal("ext-deezer-123456", result[0].Id);
|
||||||
|
Assert.Equal("Test Song", result[0].Title);
|
||||||
|
Assert.Equal("Test Artist", result[0].Artist);
|
||||||
|
Assert.Equal("Test Album", result[0].Album);
|
||||||
|
Assert.Equal(180, result[0].Duration);
|
||||||
|
Assert.False(result[0].IsLocal);
|
||||||
|
Assert.Equal("deezer", result[0].ExternalProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SearchAlbumsAsync_ReturnsListOfAlbums()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var deezerResponse = new
|
||||||
|
{
|
||||||
|
data = new[]
|
||||||
|
{
|
||||||
|
new
|
||||||
|
{
|
||||||
|
id = 456789,
|
||||||
|
title = "Test Album",
|
||||||
|
nb_tracks = 12,
|
||||||
|
release_date = "2023-01-15",
|
||||||
|
cover_medium = "https://example.com/album.jpg",
|
||||||
|
artist = new { id = 123, name = "Test Artist" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SetupHttpResponse(JsonSerializer.Serialize(deezerResponse));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _service.SearchAlbumsAsync("test album", 20);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.Single(result);
|
||||||
|
Assert.Equal("ext-deezer-456789", result[0].Id);
|
||||||
|
Assert.Equal("Test Album", result[0].Title);
|
||||||
|
Assert.Equal("Test Artist", result[0].Artist);
|
||||||
|
Assert.Equal(12, result[0].SongCount);
|
||||||
|
Assert.Equal(2023, result[0].Year);
|
||||||
|
Assert.False(result[0].IsLocal);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SearchArtistsAsync_ReturnsListOfArtists()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var deezerResponse = new
|
||||||
|
{
|
||||||
|
data = new[]
|
||||||
|
{
|
||||||
|
new
|
||||||
|
{
|
||||||
|
id = 789012,
|
||||||
|
name = "Test Artist",
|
||||||
|
nb_album = 5,
|
||||||
|
picture_medium = "https://example.com/artist.jpg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SetupHttpResponse(JsonSerializer.Serialize(deezerResponse));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _service.SearchArtistsAsync("test artist", 20);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.Single(result);
|
||||||
|
Assert.Equal("ext-deezer-789012", result[0].Id);
|
||||||
|
Assert.Equal("Test Artist", result[0].Name);
|
||||||
|
Assert.Equal(5, result[0].AlbumCount);
|
||||||
|
Assert.False(result[0].IsLocal);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SearchAllAsync_ReturnsAllTypes()
|
||||||
|
{
|
||||||
|
// This test would need multiple HTTP calls mocked, simplified for now
|
||||||
|
var emptyResponse = JsonSerializer.Serialize(new { data = Array.Empty<object>() });
|
||||||
|
SetupHttpResponse(emptyResponse);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _service.SearchAllAsync("test");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.NotNull(result.Songs);
|
||||||
|
Assert.NotNull(result.Albums);
|
||||||
|
Assert.NotNull(result.Artists);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetSongAsync_WithDeezerProvider_ReturnsSong()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var deezerResponse = new
|
||||||
|
{
|
||||||
|
id = 123456,
|
||||||
|
title = "Test Song",
|
||||||
|
duration = 200,
|
||||||
|
track_position = 3,
|
||||||
|
artist = new { id = 789, name = "Test Artist" },
|
||||||
|
album = new { id = 456, title = "Test Album", cover_medium = "https://example.com/cover.jpg" }
|
||||||
|
};
|
||||||
|
|
||||||
|
SetupHttpResponse(JsonSerializer.Serialize(deezerResponse));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _service.GetSongAsync("deezer", "123456");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.Equal("ext-deezer-123456", result.Id);
|
||||||
|
Assert.Equal("Test Song", result.Title);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetSongAsync_WithNonDeezerProvider_ReturnsNull()
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var result = await _service.GetSongAsync("spotify", "123456");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupHttpResponse(string content, HttpStatusCode statusCode = HttpStatusCode.OK)
|
||||||
|
{
|
||||||
|
_httpMessageHandlerMock
|
||||||
|
.Protected()
|
||||||
|
.Setup<Task<HttpResponseMessage>>(
|
||||||
|
"SendAsync",
|
||||||
|
ItExpr.IsAny<HttpRequestMessage>(),
|
||||||
|
ItExpr.IsAny<CancellationToken>())
|
||||||
|
.ReturnsAsync(new HttpResponseMessage
|
||||||
|
{
|
||||||
|
StatusCode = statusCode,
|
||||||
|
Content = new StringContent(content)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
155
octo-fiesta.Tests/LocalLibraryServiceTests.cs
Normal file
155
octo-fiesta.Tests/LocalLibraryServiceTests.cs
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
using octo_fiesta.Services;
|
||||||
|
using octo_fiesta.Models;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
namespace octo_fiesta.Tests;
|
||||||
|
|
||||||
|
public class LocalLibraryServiceTests : IDisposable
|
||||||
|
{
|
||||||
|
private readonly LocalLibraryService _service;
|
||||||
|
private readonly string _testDownloadPath;
|
||||||
|
|
||||||
|
public LocalLibraryServiceTests()
|
||||||
|
{
|
||||||
|
_testDownloadPath = Path.Combine(Path.GetTempPath(), "octo-fiesta-tests-" + Guid.NewGuid());
|
||||||
|
Directory.CreateDirectory(_testDownloadPath);
|
||||||
|
|
||||||
|
var configuration = new ConfigurationBuilder()
|
||||||
|
.AddInMemoryCollection(new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
["Library:DownloadPath"] = _testDownloadPath
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_service = new LocalLibraryService(configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (Directory.Exists(_testDownloadPath))
|
||||||
|
{
|
||||||
|
Directory.Delete(_testDownloadPath, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ParseSongId_WithExternalId_ReturnsCorrectParts()
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var (isExternal, provider, externalId) = _service.ParseSongId("ext-deezer-123456");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(isExternal);
|
||||||
|
Assert.Equal("deezer", provider);
|
||||||
|
Assert.Equal("123456", externalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ParseSongId_WithLocalId_ReturnsNotExternal()
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var (isExternal, provider, externalId) = _service.ParseSongId("local-789");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(isExternal);
|
||||||
|
Assert.Null(provider);
|
||||||
|
Assert.Null(externalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ParseSongId_WithNumericId_ReturnsNotExternal()
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var (isExternal, provider, externalId) = _service.ParseSongId("12345");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(isExternal);
|
||||||
|
Assert.Null(provider);
|
||||||
|
Assert.Null(externalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetLocalPathForExternalSongAsync_WhenNotRegistered_ReturnsNull()
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var result = await _service.GetLocalPathForExternalSongAsync("deezer", "nonexistent");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterDownloadedSongAsync_ThenGetLocalPath_ReturnsPath()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var song = new Song
|
||||||
|
{
|
||||||
|
Id = "ext-deezer-123456",
|
||||||
|
Title = "Test Song",
|
||||||
|
Artist = "Test Artist",
|
||||||
|
Album = "Test Album",
|
||||||
|
ExternalProvider = "deezer",
|
||||||
|
ExternalId = "123456"
|
||||||
|
};
|
||||||
|
var localPath = Path.Combine(_testDownloadPath, "test-song.mp3");
|
||||||
|
|
||||||
|
// Create the file
|
||||||
|
await File.WriteAllTextAsync(localPath, "fake audio content");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _service.RegisterDownloadedSongAsync(song, localPath);
|
||||||
|
var result = await _service.GetLocalPathForExternalSongAsync("deezer", "123456");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(localPath, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetLocalPathForExternalSongAsync_WhenFileDeleted_ReturnsNull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var song = new Song
|
||||||
|
{
|
||||||
|
Id = "ext-deezer-999999",
|
||||||
|
Title = "Deleted Song",
|
||||||
|
Artist = "Test Artist",
|
||||||
|
Album = "Test Album",
|
||||||
|
ExternalProvider = "deezer",
|
||||||
|
ExternalId = "999999"
|
||||||
|
};
|
||||||
|
var localPath = Path.Combine(_testDownloadPath, "deleted-song.mp3");
|
||||||
|
|
||||||
|
// Create and then delete the file
|
||||||
|
await File.WriteAllTextAsync(localPath, "fake audio content");
|
||||||
|
await _service.RegisterDownloadedSongAsync(song, localPath);
|
||||||
|
File.Delete(localPath);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _service.GetLocalPathForExternalSongAsync("deezer", "999999");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RegisterDownloadedSongAsync_WithNullProvider_DoesNothing()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var song = new Song
|
||||||
|
{
|
||||||
|
Id = "local-123",
|
||||||
|
Title = "Local Song",
|
||||||
|
Artist = "Local Artist",
|
||||||
|
Album = "Local Album",
|
||||||
|
ExternalProvider = null,
|
||||||
|
ExternalId = null
|
||||||
|
};
|
||||||
|
var localPath = Path.Combine(_testDownloadPath, "local-song.mp3");
|
||||||
|
|
||||||
|
// Act - should not throw
|
||||||
|
await _service.RegisterDownloadedSongAsync(song, localPath);
|
||||||
|
|
||||||
|
// Assert - nothing to assert, just checking it doesn't throw
|
||||||
|
Assert.True(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
28
octo-fiesta.Tests/octo-fiesta.Tests.csproj
Normal file
28
octo-fiesta.Tests/octo-fiesta.Tests.csproj
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<RootNamespace>octo_fiesta.Tests</RootNamespace>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="coverlet.collector" Version="6.0.2" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||||
|
<PackageReference Include="Moq" Version="4.20.72" />
|
||||||
|
<PackageReference Include="xunit" Version="2.9.2" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Using Include="Xunit" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\octo-fiesta\octo-fiesta.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -2,15 +2,44 @@
|
|||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "octo-fiesta", "octo-fiesta\octo-fiesta.csproj", "{C56EF44B-3AD5-4D4C-A513-726DA8A0E225}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "octo-fiesta", "octo-fiesta\octo-fiesta.csproj", "{C56EF44B-3AD5-4D4C-A513-726DA8A0E225}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "octo-fiesta.Tests", "octo-fiesta.Tests\octo-fiesta.Tests.csproj", "{72E3A16E-7020-4EE0-95D4-FB8FA027ED12}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
Release|Any CPU = Release|Any CPU
|
Release|Any CPU = Release|Any CPU
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{C56EF44B-3AD5-4D4C-A513-726DA8A0E225}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{C56EF44B-3AD5-4D4C-A513-726DA8A0E225}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{C56EF44B-3AD5-4D4C-A513-726DA8A0E225}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{C56EF44B-3AD5-4D4C-A513-726DA8A0E225}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C56EF44B-3AD5-4D4C-A513-726DA8A0E225}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{C56EF44B-3AD5-4D4C-A513-726DA8A0E225}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{C56EF44B-3AD5-4D4C-A513-726DA8A0E225}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{C56EF44B-3AD5-4D4C-A513-726DA8A0E225}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
{C56EF44B-3AD5-4D4C-A513-726DA8A0E225}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{C56EF44B-3AD5-4D4C-A513-726DA8A0E225}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{C56EF44B-3AD5-4D4C-A513-726DA8A0E225}.Release|Any CPU.Build.0 = Release|Any CPU
|
{C56EF44B-3AD5-4D4C-A513-726DA8A0E225}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C56EF44B-3AD5-4D4C-A513-726DA8A0E225}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{C56EF44B-3AD5-4D4C-A513-726DA8A0E225}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{C56EF44B-3AD5-4D4C-A513-726DA8A0E225}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{C56EF44B-3AD5-4D4C-A513-726DA8A0E225}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{72E3A16E-7020-4EE0-95D4-FB8FA027ED12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{72E3A16E-7020-4EE0-95D4-FB8FA027ED12}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{72E3A16E-7020-4EE0-95D4-FB8FA027ED12}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{72E3A16E-7020-4EE0-95D4-FB8FA027ED12}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{72E3A16E-7020-4EE0-95D4-FB8FA027ED12}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{72E3A16E-7020-4EE0-95D4-FB8FA027ED12}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{72E3A16E-7020-4EE0-95D4-FB8FA027ED12}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{72E3A16E-7020-4EE0-95D4-FB8FA027ED12}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{72E3A16E-7020-4EE0-95D4-FB8FA027ED12}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{72E3A16E-7020-4EE0-95D4-FB8FA027ED12}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{72E3A16E-7020-4EE0-95D4-FB8FA027ED12}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{72E3A16E-7020-4EE0-95D4-FB8FA027ED12}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
Reference in New Issue
Block a user