mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
feat: playlist implementation
This commit is contained in:
@@ -24,6 +24,7 @@ public class DeezerDownloadServiceTests : IDisposable
|
||||
private readonly Mock<ILocalLibraryService> _localLibraryServiceMock;
|
||||
private readonly Mock<IMusicMetadataService> _metadataServiceMock;
|
||||
private readonly Mock<ILogger<DeezerDownloadService>> _loggerMock;
|
||||
private readonly Mock<IServiceProvider> _serviceProviderMock;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly string _testDownloadPath;
|
||||
|
||||
@@ -41,6 +42,7 @@ public class DeezerDownloadServiceTests : IDisposable
|
||||
_localLibraryServiceMock = new Mock<ILocalLibraryService>();
|
||||
_metadataServiceMock = new Mock<IMusicMetadataService>();
|
||||
_loggerMock = new Mock<ILogger<DeezerDownloadService>>();
|
||||
_serviceProviderMock = new Mock<IServiceProvider>();
|
||||
|
||||
_configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(new Dictionary<string, string?>
|
||||
@@ -90,6 +92,7 @@ public class DeezerDownloadServiceTests : IDisposable
|
||||
_metadataServiceMock.Object,
|
||||
subsonicSettings,
|
||||
deezerSettings,
|
||||
_serviceProviderMock.Object,
|
||||
_loggerMock.Object);
|
||||
}
|
||||
|
||||
|
||||
@@ -580,4 +580,226 @@ public class DeezerMetadataServiceTests
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Playlist Tests
|
||||
|
||||
[Fact]
|
||||
public async Task SearchPlaylistsAsync_ReturnsListOfPlaylists()
|
||||
{
|
||||
// Arrange
|
||||
var deezerResponse = new
|
||||
{
|
||||
data = new[]
|
||||
{
|
||||
new
|
||||
{
|
||||
id = 12345,
|
||||
title = "Chill Vibes",
|
||||
nb_tracks = 50,
|
||||
picture_medium = "https://example.com/playlist1.jpg",
|
||||
user = new { name = "Test User" }
|
||||
},
|
||||
new
|
||||
{
|
||||
id = 67890,
|
||||
title = "Workout Mix",
|
||||
nb_tracks = 30,
|
||||
picture_medium = "https://example.com/playlist2.jpg",
|
||||
user = new { name = "Gym Buddy" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SetupHttpResponse(JsonSerializer.Serialize(deezerResponse));
|
||||
|
||||
// Act
|
||||
var result = await _service.SearchPlaylistsAsync("chill");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal("Chill Vibes", result[0].Name);
|
||||
Assert.Equal(50, result[0].TrackCount);
|
||||
Assert.Equal("pl-deezer-12345", result[0].Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SearchPlaylistsAsync_WithLimit_RespectsLimit()
|
||||
{
|
||||
// Arrange
|
||||
var deezerResponse = new
|
||||
{
|
||||
data = new[]
|
||||
{
|
||||
new
|
||||
{
|
||||
id = 12345,
|
||||
title = "Playlist 1",
|
||||
nb_tracks = 10,
|
||||
picture_medium = "https://example.com/p1.jpg",
|
||||
user = new { name = "User 1" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SetupHttpResponse(JsonSerializer.Serialize(deezerResponse));
|
||||
|
||||
// Act
|
||||
var result = await _service.SearchPlaylistsAsync("test", 1);
|
||||
|
||||
// Assert
|
||||
Assert.Single(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SearchPlaylistsAsync_WithEmptyResults_ReturnsEmptyList()
|
||||
{
|
||||
// Arrange
|
||||
var deezerResponse = new
|
||||
{
|
||||
data = new object[] { }
|
||||
};
|
||||
|
||||
SetupHttpResponse(JsonSerializer.Serialize(deezerResponse));
|
||||
|
||||
// Act
|
||||
var result = await _service.SearchPlaylistsAsync("nonexistent");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetPlaylistAsync_WithValidId_ReturnsPlaylist()
|
||||
{
|
||||
// Arrange
|
||||
var deezerResponse = new
|
||||
{
|
||||
id = 12345,
|
||||
title = "Best Of Jazz",
|
||||
description = "The best jazz tracks",
|
||||
nb_tracks = 100,
|
||||
picture_medium = "https://example.com/jazz.jpg",
|
||||
user = new { name = "Jazz Lover" }
|
||||
};
|
||||
|
||||
SetupHttpResponse(JsonSerializer.Serialize(deezerResponse));
|
||||
|
||||
// Act
|
||||
var result = await _service.GetPlaylistAsync("deezer", "12345");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal("Best Of Jazz", result.Name);
|
||||
Assert.Equal(100, result.TrackCount);
|
||||
Assert.Equal("pl-deezer-12345", result.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetPlaylistAsync_WithWrongProvider_ReturnsNull()
|
||||
{
|
||||
// Act
|
||||
var result = await _service.GetPlaylistAsync("qobuz", "12345");
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetPlaylistTracksAsync_ReturnsListOfSongs()
|
||||
{
|
||||
// Arrange
|
||||
var deezerResponse = new
|
||||
{
|
||||
tracks = new
|
||||
{
|
||||
data = new[]
|
||||
{
|
||||
new
|
||||
{
|
||||
id = 111,
|
||||
title = "Track 1",
|
||||
duration = 200,
|
||||
track_position = 1,
|
||||
disk_number = 1,
|
||||
artist = new
|
||||
{
|
||||
id = 999,
|
||||
name = "Artist A"
|
||||
},
|
||||
album = new
|
||||
{
|
||||
id = 888,
|
||||
title = "Album X",
|
||||
release_date = "2020-01-15",
|
||||
cover_medium = "https://example.com/cover.jpg"
|
||||
}
|
||||
},
|
||||
new
|
||||
{
|
||||
id = 222,
|
||||
title = "Track 2",
|
||||
duration = 180,
|
||||
track_position = 2,
|
||||
disk_number = 1,
|
||||
artist = new
|
||||
{
|
||||
id = 777,
|
||||
name = "Artist B"
|
||||
},
|
||||
album = new
|
||||
{
|
||||
id = 666,
|
||||
title = "Album Y",
|
||||
release_date = "2021-05-20",
|
||||
cover_medium = "https://example.com/cover2.jpg"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SetupHttpResponse(JsonSerializer.Serialize(deezerResponse));
|
||||
|
||||
// Act
|
||||
var result = await _service.GetPlaylistTracksAsync("deezer", "12345");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Equal("Track 1", result[0].Title);
|
||||
Assert.Equal("Artist A", result[0].Artist);
|
||||
Assert.Equal("ext-deezer-111", result[0].Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetPlaylistTracksAsync_WithWrongProvider_ReturnsEmptyList()
|
||||
{
|
||||
// Act
|
||||
var result = await _service.GetPlaylistTracksAsync("qobuz", "12345");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetPlaylistTracksAsync_WithEmptyPlaylist_ReturnsEmptyList()
|
||||
{
|
||||
// Arrange
|
||||
var deezerResponse = new
|
||||
{
|
||||
tracks = new
|
||||
{
|
||||
data = new object[] { }
|
||||
}
|
||||
};
|
||||
|
||||
SetupHttpResponse(JsonSerializer.Serialize(deezerResponse));
|
||||
|
||||
// Act
|
||||
var result = await _service.GetPlaylistTracksAsync("deezer", "12345");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
375
octo-fiesta.Tests/PlaylistIdHelperTests.cs
Normal file
375
octo-fiesta.Tests/PlaylistIdHelperTests.cs
Normal file
@@ -0,0 +1,375 @@
|
||||
using octo_fiesta.Services.Common;
|
||||
using Xunit;
|
||||
|
||||
namespace octo_fiesta.Tests;
|
||||
|
||||
public class PlaylistIdHelperTests
|
||||
{
|
||||
#region IsExternalPlaylist Tests
|
||||
|
||||
[Fact]
|
||||
public void IsExternalPlaylist_WithValidPlaylistId_ReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var id = "pl-deezer-123456";
|
||||
|
||||
// Act
|
||||
var result = PlaylistIdHelper.IsExternalPlaylist(id);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsExternalPlaylist_WithValidQobuzPlaylistId_ReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var id = "pl-qobuz-789012";
|
||||
|
||||
// Act
|
||||
var result = PlaylistIdHelper.IsExternalPlaylist(id);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsExternalPlaylist_WithUpperCasePrefix_ReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var id = "PL-deezer-123456";
|
||||
|
||||
// Act
|
||||
var result = PlaylistIdHelper.IsExternalPlaylist(id);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsExternalPlaylist_WithRegularAlbumId_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var id = "ext-deezer-album-123456";
|
||||
|
||||
// Act
|
||||
var result = PlaylistIdHelper.IsExternalPlaylist(id);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsExternalPlaylist_WithNullId_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
string? id = null;
|
||||
|
||||
// Act
|
||||
var result = PlaylistIdHelper.IsExternalPlaylist(id);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsExternalPlaylist_WithEmptyString_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var id = "";
|
||||
|
||||
// Act
|
||||
var result = PlaylistIdHelper.IsExternalPlaylist(id);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsExternalPlaylist_WithRandomString_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var id = "random-string-123";
|
||||
|
||||
// Act
|
||||
var result = PlaylistIdHelper.IsExternalPlaylist(id);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ParsePlaylistId Tests
|
||||
|
||||
[Fact]
|
||||
public void ParsePlaylistId_WithValidDeezerPlaylistId_ReturnsProviderAndExternalId()
|
||||
{
|
||||
// Arrange
|
||||
var id = "pl-deezer-123456";
|
||||
|
||||
// Act
|
||||
var (provider, externalId) = PlaylistIdHelper.ParsePlaylistId(id);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("deezer", provider);
|
||||
Assert.Equal("123456", externalId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsePlaylistId_WithValidQobuzPlaylistId_ReturnsProviderAndExternalId()
|
||||
{
|
||||
// Arrange
|
||||
var id = "pl-qobuz-789012";
|
||||
|
||||
// Act
|
||||
var (provider, externalId) = PlaylistIdHelper.ParsePlaylistId(id);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("qobuz", provider);
|
||||
Assert.Equal("789012", externalId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsePlaylistId_WithExternalIdContainingDashes_ParsesCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var id = "pl-deezer-abc-def-123";
|
||||
|
||||
// Act
|
||||
var (provider, externalId) = PlaylistIdHelper.ParsePlaylistId(id);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("deezer", provider);
|
||||
Assert.Equal("abc-def-123", externalId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsePlaylistId_WithInvalidFormatNoProvider_ThrowsArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
var id = "pl-123456";
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<ArgumentException>(() => PlaylistIdHelper.ParsePlaylistId(id));
|
||||
Assert.Contains("Invalid playlist ID format", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsePlaylistId_WithNonPlaylistId_ThrowsArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
var id = "ext-deezer-album-123456";
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<ArgumentException>(() => PlaylistIdHelper.ParsePlaylistId(id));
|
||||
Assert.Contains("Invalid playlist ID format", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsePlaylistId_WithNullId_ThrowsArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
string? id = null;
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentException>(() => PlaylistIdHelper.ParsePlaylistId(id!));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsePlaylistId_WithEmptyString_ThrowsArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
var id = "";
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentException>(() => PlaylistIdHelper.ParsePlaylistId(id));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParsePlaylistId_WithOnlyPrefix_ThrowsArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
var id = "pl-";
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<ArgumentException>(() => PlaylistIdHelper.ParsePlaylistId(id));
|
||||
Assert.Contains("Invalid playlist ID format", exception.Message);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region CreatePlaylistId Tests
|
||||
|
||||
[Fact]
|
||||
public void CreatePlaylistId_WithValidDeezerProviderAndId_ReturnsCorrectFormat()
|
||||
{
|
||||
// Arrange
|
||||
var provider = "deezer";
|
||||
var externalId = "123456";
|
||||
|
||||
// Act
|
||||
var result = PlaylistIdHelper.CreatePlaylistId(provider, externalId);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("pl-deezer-123456", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreatePlaylistId_WithValidQobuzProviderAndId_ReturnsCorrectFormat()
|
||||
{
|
||||
// Arrange
|
||||
var provider = "qobuz";
|
||||
var externalId = "789012";
|
||||
|
||||
// Act
|
||||
var result = PlaylistIdHelper.CreatePlaylistId(provider, externalId);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("pl-qobuz-789012", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreatePlaylistId_WithUpperCaseProvider_ConvertsToLowerCase()
|
||||
{
|
||||
// Arrange
|
||||
var provider = "DEEZER";
|
||||
var externalId = "123456";
|
||||
|
||||
// Act
|
||||
var result = PlaylistIdHelper.CreatePlaylistId(provider, externalId);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("pl-deezer-123456", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreatePlaylistId_WithMixedCaseProvider_ConvertsToLowerCase()
|
||||
{
|
||||
// Arrange
|
||||
var provider = "DeEzEr";
|
||||
var externalId = "123456";
|
||||
|
||||
// Act
|
||||
var result = PlaylistIdHelper.CreatePlaylistId(provider, externalId);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("pl-deezer-123456", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreatePlaylistId_WithExternalIdContainingDashes_PreservesDashes()
|
||||
{
|
||||
// Arrange
|
||||
var provider = "deezer";
|
||||
var externalId = "abc-def-123";
|
||||
|
||||
// Act
|
||||
var result = PlaylistIdHelper.CreatePlaylistId(provider, externalId);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("pl-deezer-abc-def-123", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreatePlaylistId_WithNullProvider_ThrowsArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
string? provider = null;
|
||||
var externalId = "123456";
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<ArgumentException>(() => PlaylistIdHelper.CreatePlaylistId(provider!, externalId));
|
||||
Assert.Contains("Provider cannot be null or empty", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreatePlaylistId_WithEmptyProvider_ThrowsArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
var provider = "";
|
||||
var externalId = "123456";
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<ArgumentException>(() => PlaylistIdHelper.CreatePlaylistId(provider, externalId));
|
||||
Assert.Contains("Provider cannot be null or empty", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreatePlaylistId_WithNullExternalId_ThrowsArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
var provider = "deezer";
|
||||
string? externalId = null;
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<ArgumentException>(() => PlaylistIdHelper.CreatePlaylistId(provider, externalId!));
|
||||
Assert.Contains("External ID cannot be null or empty", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreatePlaylistId_WithEmptyExternalId_ThrowsArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
var provider = "deezer";
|
||||
var externalId = "";
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<ArgumentException>(() => PlaylistIdHelper.CreatePlaylistId(provider, externalId));
|
||||
Assert.Contains("External ID cannot be null or empty", exception.Message);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Round-Trip Tests
|
||||
|
||||
[Fact]
|
||||
public void RoundTrip_CreateAndParse_ReturnsOriginalValues()
|
||||
{
|
||||
// Arrange
|
||||
var originalProvider = "deezer";
|
||||
var originalExternalId = "123456";
|
||||
|
||||
// Act
|
||||
var playlistId = PlaylistIdHelper.CreatePlaylistId(originalProvider, originalExternalId);
|
||||
var (parsedProvider, parsedExternalId) = PlaylistIdHelper.ParsePlaylistId(playlistId);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(originalProvider, parsedProvider);
|
||||
Assert.Equal(originalExternalId, parsedExternalId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RoundTrip_CreateWithUpperCaseAndParse_ReturnsLowerCaseProvider()
|
||||
{
|
||||
// Arrange
|
||||
var originalProvider = "QOBUZ";
|
||||
var originalExternalId = "789012";
|
||||
|
||||
// Act
|
||||
var playlistId = PlaylistIdHelper.CreatePlaylistId(originalProvider, originalExternalId);
|
||||
var (parsedProvider, parsedExternalId) = PlaylistIdHelper.ParsePlaylistId(playlistId);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("qobuz", parsedProvider); // Converted to lowercase
|
||||
Assert.Equal(originalExternalId, parsedExternalId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RoundTrip_WithComplexExternalId_PreservesValue()
|
||||
{
|
||||
// Arrange
|
||||
var originalProvider = "deezer";
|
||||
var originalExternalId = "abc-123-def-456";
|
||||
|
||||
// Act
|
||||
var playlistId = PlaylistIdHelper.CreatePlaylistId(originalProvider, originalExternalId);
|
||||
var (parsedProvider, parsedExternalId) = PlaylistIdHelper.ParsePlaylistId(playlistId);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(originalProvider, parsedProvider);
|
||||
Assert.Equal(originalExternalId, parsedExternalId);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -22,6 +22,7 @@ public class QobuzDownloadServiceTests : IDisposable
|
||||
private readonly Mock<IMusicMetadataService> _metadataServiceMock;
|
||||
private readonly Mock<ILogger<QobuzBundleService>> _bundleServiceLoggerMock;
|
||||
private readonly Mock<ILogger<QobuzDownloadService>> _loggerMock;
|
||||
private readonly Mock<IServiceProvider> _serviceProviderMock;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly string _testDownloadPath;
|
||||
private QobuzBundleService _bundleService;
|
||||
@@ -41,6 +42,7 @@ public class QobuzDownloadServiceTests : IDisposable
|
||||
_metadataServiceMock = new Mock<IMusicMetadataService>();
|
||||
_bundleServiceLoggerMock = new Mock<ILogger<QobuzBundleService>>();
|
||||
_loggerMock = new Mock<ILogger<QobuzDownloadService>>();
|
||||
_serviceProviderMock = new Mock<IServiceProvider>();
|
||||
|
||||
// Create a real QobuzBundleService for testing (it will use the mocked HttpClient)
|
||||
_bundleService = new QobuzBundleService(_httpClientFactoryMock.Object, _bundleServiceLoggerMock.Object);
|
||||
@@ -94,6 +96,7 @@ public class QobuzDownloadServiceTests : IDisposable
|
||||
_bundleService,
|
||||
subsonicSettings,
|
||||
qobuzSettings,
|
||||
_serviceProviderMock.Object,
|
||||
_loggerMock.Object);
|
||||
}
|
||||
|
||||
|
||||
659
octo-fiesta.Tests/QobuzMetadataServiceTests.cs
Normal file
659
octo-fiesta.Tests/QobuzMetadataServiceTests.cs
Normal file
@@ -0,0 +1,659 @@
|
||||
using octo_fiesta.Services.Qobuz;
|
||||
using octo_fiesta.Models.Domain;
|
||||
using octo_fiesta.Models.Settings;
|
||||
using octo_fiesta.Models.Subsonic;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using Moq.Protected;
|
||||
using System.Net;
|
||||
|
||||
namespace octo_fiesta.Tests;
|
||||
|
||||
public class QobuzMetadataServiceTests
|
||||
{
|
||||
private readonly Mock<IHttpClientFactory> _httpClientFactoryMock;
|
||||
private readonly Mock<HttpMessageHandler> _httpMessageHandlerMock;
|
||||
private readonly Mock<QobuzBundleService> _bundleServiceMock;
|
||||
private readonly Mock<ILogger<QobuzMetadataService>> _loggerMock;
|
||||
private readonly QobuzMetadataService _service;
|
||||
|
||||
public QobuzMetadataServiceTests()
|
||||
{
|
||||
_httpMessageHandlerMock = new Mock<HttpMessageHandler>();
|
||||
var httpClient = new HttpClient(_httpMessageHandlerMock.Object);
|
||||
|
||||
_httpClientFactoryMock = new Mock<IHttpClientFactory>();
|
||||
_httpClientFactoryMock.Setup(f => f.CreateClient(It.IsAny<string>())).Returns(httpClient);
|
||||
|
||||
var httpClientFactory = Mock.Of<IHttpClientFactory>();
|
||||
var bundleLogger = Mock.Of<ILogger<QobuzBundleService>>();
|
||||
|
||||
_bundleServiceMock = new Mock<QobuzBundleService>(httpClientFactory, bundleLogger);
|
||||
_bundleServiceMock.Setup(b => b.GetAppIdAsync()).ReturnsAsync("fake-app-id-12345");
|
||||
|
||||
_loggerMock = new Mock<ILogger<QobuzMetadataService>>();
|
||||
|
||||
var subsonicSettings = Options.Create(new SubsonicSettings());
|
||||
var qobuzSettings = Options.Create(new QobuzSettings
|
||||
{
|
||||
UserAuthToken = "fake-user-auth-token",
|
||||
UserId = "8807208"
|
||||
});
|
||||
|
||||
_service = new QobuzMetadataService(
|
||||
_httpClientFactoryMock.Object,
|
||||
subsonicSettings,
|
||||
qobuzSettings,
|
||||
_bundleServiceMock.Object,
|
||||
_loggerMock.Object);
|
||||
}
|
||||
|
||||
#region SearchPlaylistsAsync Tests
|
||||
|
||||
[Fact]
|
||||
public async Task SearchPlaylistsAsync_WithValidQuery_ReturnsPlaylists()
|
||||
{
|
||||
// Arrange
|
||||
var mockResponse = new HttpResponseMessage
|
||||
{
|
||||
StatusCode = HttpStatusCode.OK,
|
||||
Content = new StringContent(@"{
|
||||
""playlists"": {
|
||||
""items"": [
|
||||
{
|
||||
""id"": 1578664,
|
||||
""name"": ""Jazz Classics"",
|
||||
""description"": ""Best of classic jazz music"",
|
||||
""tracks_count"": 50,
|
||||
""duration"": 12000,
|
||||
""owner"": {
|
||||
""name"": ""Qobuz Editorial""
|
||||
},
|
||||
""created_at"": 1609459200,
|
||||
""images300"": [""https://example.com/cover.jpg""]
|
||||
}
|
||||
]
|
||||
}
|
||||
}")
|
||||
};
|
||||
|
||||
_httpMessageHandlerMock.Protected()
|
||||
.Setup<Task<HttpResponseMessage>>(
|
||||
"SendAsync",
|
||||
ItExpr.IsAny<HttpRequestMessage>(),
|
||||
ItExpr.IsAny<CancellationToken>())
|
||||
.ReturnsAsync(mockResponse);
|
||||
|
||||
// Act
|
||||
var result = await _service.SearchPlaylistsAsync("jazz", 20);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Single(result);
|
||||
Assert.Equal("Jazz Classics", result[0].Name);
|
||||
Assert.Equal("Best of classic jazz music", result[0].Description);
|
||||
Assert.Equal(50, result[0].TrackCount);
|
||||
Assert.Equal(12000, result[0].Duration);
|
||||
Assert.Equal("qobuz", result[0].Provider);
|
||||
Assert.Equal("1578664", result[0].ExternalId);
|
||||
Assert.Equal("pl-qobuz-1578664", result[0].Id);
|
||||
Assert.Equal("Qobuz Editorial", result[0].CuratorName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SearchPlaylistsAsync_WithEmptyResults_ReturnsEmptyList()
|
||||
{
|
||||
// Arrange
|
||||
var mockResponse = new HttpResponseMessage
|
||||
{
|
||||
StatusCode = HttpStatusCode.OK,
|
||||
Content = new StringContent(@"{
|
||||
""playlists"": {
|
||||
""items"": []
|
||||
}
|
||||
}")
|
||||
};
|
||||
|
||||
_httpMessageHandlerMock.Protected()
|
||||
.Setup<Task<HttpResponseMessage>>(
|
||||
"SendAsync",
|
||||
ItExpr.IsAny<HttpRequestMessage>(),
|
||||
ItExpr.IsAny<CancellationToken>())
|
||||
.ReturnsAsync(mockResponse);
|
||||
|
||||
// Act
|
||||
var result = await _service.SearchPlaylistsAsync("nonexistent", 20);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SearchPlaylistsAsync_WhenHttpFails_ReturnsEmptyList()
|
||||
{
|
||||
// Arrange
|
||||
var mockResponse = new HttpResponseMessage
|
||||
{
|
||||
StatusCode = HttpStatusCode.InternalServerError
|
||||
};
|
||||
|
||||
_httpMessageHandlerMock.Protected()
|
||||
.Setup<Task<HttpResponseMessage>>(
|
||||
"SendAsync",
|
||||
ItExpr.IsAny<HttpRequestMessage>(),
|
||||
ItExpr.IsAny<CancellationToken>())
|
||||
.ReturnsAsync(mockResponse);
|
||||
|
||||
// Act
|
||||
var result = await _service.SearchPlaylistsAsync("jazz", 20);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetPlaylistAsync Tests
|
||||
|
||||
[Fact]
|
||||
public async Task GetPlaylistAsync_WithValidId_ReturnsPlaylist()
|
||||
{
|
||||
// Arrange
|
||||
var mockResponse = new HttpResponseMessage
|
||||
{
|
||||
StatusCode = HttpStatusCode.OK,
|
||||
Content = new StringContent(@"{
|
||||
""id"": 1578664,
|
||||
""name"": ""Best Of Jazz"",
|
||||
""description"": ""Top jazz tracks"",
|
||||
""tracks_count"": 100,
|
||||
""duration"": 24000,
|
||||
""owner"": {
|
||||
""name"": ""Qobuz Editor""
|
||||
},
|
||||
""created_at"": 1609459200,
|
||||
""image_rectangle"": [""https://example.com/cover-large.jpg""]
|
||||
}")
|
||||
};
|
||||
|
||||
_httpMessageHandlerMock.Protected()
|
||||
.Setup<Task<HttpResponseMessage>>(
|
||||
"SendAsync",
|
||||
ItExpr.IsAny<HttpRequestMessage>(),
|
||||
ItExpr.IsAny<CancellationToken>())
|
||||
.ReturnsAsync(mockResponse);
|
||||
|
||||
// Act
|
||||
var result = await _service.GetPlaylistAsync("qobuz", "1578664");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal("Best Of Jazz", result.Name);
|
||||
Assert.Equal("Top jazz tracks", result.Description);
|
||||
Assert.Equal(100, result.TrackCount);
|
||||
Assert.Equal(24000, result.Duration);
|
||||
Assert.Equal("pl-qobuz-1578664", result.Id);
|
||||
Assert.Equal("Qobuz Editor", result.CuratorName);
|
||||
Assert.Equal("https://example.com/cover-large.jpg", result.CoverUrl);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetPlaylistAsync_WithWrongProvider_ReturnsNull()
|
||||
{
|
||||
// Act
|
||||
var result = await _service.GetPlaylistAsync("deezer", "12345");
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetPlaylistTracksAsync Tests
|
||||
|
||||
[Fact]
|
||||
public async Task GetPlaylistTracksAsync_WithValidId_ReturnsTracks()
|
||||
{
|
||||
// Arrange
|
||||
var mockResponse = new HttpResponseMessage
|
||||
{
|
||||
StatusCode = HttpStatusCode.OK,
|
||||
Content = new StringContent(@"{
|
||||
""id"": 1578664,
|
||||
""name"": ""My Jazz Playlist"",
|
||||
""tracks"": {
|
||||
""items"": [
|
||||
{
|
||||
""id"": 123456789,
|
||||
""title"": ""Take Five"",
|
||||
""duration"": 324,
|
||||
""track_number"": 1,
|
||||
""media_number"": 1,
|
||||
""performer"": {
|
||||
""id"": 111,
|
||||
""name"": ""Dave Brubeck Quartet""
|
||||
},
|
||||
""album"": {
|
||||
""id"": 222,
|
||||
""title"": ""Time Out"",
|
||||
""artist"": {
|
||||
""id"": 111,
|
||||
""name"": ""Dave Brubeck Quartet""
|
||||
},
|
||||
""image"": {
|
||||
""thumbnail"": ""https://example.com/time-out.jpg""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
""id"": 987654321,
|
||||
""title"": ""So What"",
|
||||
""duration"": 562,
|
||||
""track_number"": 2,
|
||||
""media_number"": 1,
|
||||
""performer"": {
|
||||
""id"": 333,
|
||||
""name"": ""Miles Davis""
|
||||
},
|
||||
""album"": {
|
||||
""id"": 444,
|
||||
""title"": ""Kind of Blue"",
|
||||
""artist"": {
|
||||
""id"": 333,
|
||||
""name"": ""Miles Davis""
|
||||
},
|
||||
""image"": {
|
||||
""thumbnail"": ""https://example.com/kind-of-blue.jpg""
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}")
|
||||
};
|
||||
|
||||
_httpMessageHandlerMock.Protected()
|
||||
.Setup<Task<HttpResponseMessage>>(
|
||||
"SendAsync",
|
||||
ItExpr.IsAny<HttpRequestMessage>(),
|
||||
ItExpr.IsAny<CancellationToken>())
|
||||
.ReturnsAsync(mockResponse);
|
||||
|
||||
// Act
|
||||
var result = await _service.GetPlaylistTracksAsync("qobuz", "1578664");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(2, result.Count);
|
||||
|
||||
// First track
|
||||
Assert.Equal("Take Five", result[0].Title);
|
||||
Assert.Equal("Dave Brubeck Quartet", result[0].Artist);
|
||||
Assert.Equal("My Jazz Playlist", result[0].Album); // Album should be playlist name
|
||||
Assert.Equal(1, result[0].Track); // Track index starts at 1
|
||||
Assert.Equal("ext-qobuz-song-123456789", result[0].Id);
|
||||
Assert.Equal("qobuz", result[0].ExternalProvider);
|
||||
Assert.Equal("123456789", result[0].ExternalId);
|
||||
|
||||
// Second track
|
||||
Assert.Equal("So What", result[1].Title);
|
||||
Assert.Equal("Miles Davis", result[1].Artist);
|
||||
Assert.Equal("My Jazz Playlist", result[1].Album); // Album should be playlist name
|
||||
Assert.Equal(2, result[1].Track); // Track index increments
|
||||
Assert.Equal("ext-qobuz-song-987654321", result[1].Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetPlaylistTracksAsync_WithWrongProvider_ReturnsEmptyList()
|
||||
{
|
||||
// Act
|
||||
var result = await _service.GetPlaylistTracksAsync("deezer", "12345");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetPlaylistTracksAsync_WhenHttpFails_ReturnsEmptyList()
|
||||
{
|
||||
// Arrange
|
||||
var mockResponse = new HttpResponseMessage
|
||||
{
|
||||
StatusCode = HttpStatusCode.NotFound
|
||||
};
|
||||
|
||||
_httpMessageHandlerMock.Protected()
|
||||
.Setup<Task<HttpResponseMessage>>(
|
||||
"SendAsync",
|
||||
ItExpr.IsAny<HttpRequestMessage>(),
|
||||
ItExpr.IsAny<CancellationToken>())
|
||||
.ReturnsAsync(mockResponse);
|
||||
|
||||
// Act
|
||||
var result = await _service.GetPlaylistTracksAsync("qobuz", "999999");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetPlaylistTracksAsync_WithMissingPlaylistName_UsesDefaultName()
|
||||
{
|
||||
// Arrange
|
||||
var mockResponse = new HttpResponseMessage
|
||||
{
|
||||
StatusCode = HttpStatusCode.OK,
|
||||
Content = new StringContent(@"{
|
||||
""id"": 1578664,
|
||||
""tracks"": {
|
||||
""items"": [
|
||||
{
|
||||
""id"": 123,
|
||||
""title"": ""Test Track"",
|
||||
""performer"": {
|
||||
""id"": 1,
|
||||
""name"": ""Test Artist""
|
||||
},
|
||||
""album"": {
|
||||
""id"": 2,
|
||||
""title"": ""Test Album"",
|
||||
""artist"": {
|
||||
""id"": 1,
|
||||
""name"": ""Test Artist""
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}")
|
||||
};
|
||||
|
||||
_httpMessageHandlerMock.Protected()
|
||||
.Setup<Task<HttpResponseMessage>>(
|
||||
"SendAsync",
|
||||
ItExpr.IsAny<HttpRequestMessage>(),
|
||||
ItExpr.IsAny<CancellationToken>())
|
||||
.ReturnsAsync(mockResponse);
|
||||
|
||||
// Act
|
||||
var result = await _service.GetPlaylistTracksAsync("qobuz", "1578664");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Single(result);
|
||||
Assert.Equal("Unknown Playlist", result[0].Album);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SearchSongsAsync Tests
|
||||
|
||||
[Fact]
|
||||
public async Task SearchSongsAsync_WithValidQuery_ReturnsSongs()
|
||||
{
|
||||
// Arrange
|
||||
var mockResponse = new HttpResponseMessage
|
||||
{
|
||||
StatusCode = HttpStatusCode.OK,
|
||||
Content = new StringContent(@"{
|
||||
""tracks"": {
|
||||
""items"": [
|
||||
{
|
||||
""id"": 123456789,
|
||||
""title"": ""Take Five"",
|
||||
""duration"": 324,
|
||||
""track_number"": 1,
|
||||
""performer"": {
|
||||
""id"": 111,
|
||||
""name"": ""Dave Brubeck Quartet""
|
||||
},
|
||||
""album"": {
|
||||
""id"": 222,
|
||||
""title"": ""Time Out"",
|
||||
""artist"": {
|
||||
""id"": 111,
|
||||
""name"": ""Dave Brubeck Quartet""
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}")
|
||||
};
|
||||
|
||||
_httpMessageHandlerMock.Protected()
|
||||
.Setup<Task<HttpResponseMessage>>(
|
||||
"SendAsync",
|
||||
ItExpr.IsAny<HttpRequestMessage>(),
|
||||
ItExpr.IsAny<CancellationToken>())
|
||||
.ReturnsAsync(mockResponse);
|
||||
|
||||
// Act
|
||||
var result = await _service.SearchSongsAsync("Take Five", 20);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Single(result);
|
||||
Assert.Equal("Take Five", result[0].Title);
|
||||
Assert.Equal("Dave Brubeck Quartet", result[0].Artist);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SearchAlbumsAsync Tests
|
||||
|
||||
[Fact]
|
||||
public async Task SearchAlbumsAsync_WithValidQuery_ReturnsAlbums()
|
||||
{
|
||||
// Arrange
|
||||
var mockResponse = new HttpResponseMessage
|
||||
{
|
||||
StatusCode = HttpStatusCode.OK,
|
||||
Content = new StringContent(@"{
|
||||
""albums"": {
|
||||
""items"": [
|
||||
{
|
||||
""id"": 222,
|
||||
""title"": ""Time Out"",
|
||||
""tracks_count"": 7,
|
||||
""artist"": {
|
||||
""id"": 111,
|
||||
""name"": ""Dave Brubeck Quartet""
|
||||
},
|
||||
""release_date_original"": ""1959-12-14""
|
||||
}
|
||||
]
|
||||
}
|
||||
}")
|
||||
};
|
||||
|
||||
_httpMessageHandlerMock.Protected()
|
||||
.Setup<Task<HttpResponseMessage>>(
|
||||
"SendAsync",
|
||||
ItExpr.IsAny<HttpRequestMessage>(),
|
||||
ItExpr.IsAny<CancellationToken>())
|
||||
.ReturnsAsync(mockResponse);
|
||||
|
||||
// Act
|
||||
var result = await _service.SearchAlbumsAsync("Time Out", 20);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Single(result);
|
||||
Assert.Equal("Time Out", result[0].Title);
|
||||
Assert.Equal("Dave Brubeck Quartet", result[0].Artist);
|
||||
Assert.Equal(1959, result[0].Year);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetSongAsync Tests
|
||||
|
||||
[Fact]
|
||||
public async Task GetSongAsync_WithValidId_ReturnsSong()
|
||||
{
|
||||
// Arrange
|
||||
var mockResponse = new HttpResponseMessage
|
||||
{
|
||||
StatusCode = HttpStatusCode.OK,
|
||||
Content = new StringContent(@"{
|
||||
""id"": 123456789,
|
||||
""title"": ""Take Five"",
|
||||
""duration"": 324,
|
||||
""track_number"": 1,
|
||||
""isrc"": ""USCO10300456"",
|
||||
""copyright"": ""(P) 1959 Columbia Records"",
|
||||
""performer"": {
|
||||
""id"": 111,
|
||||
""name"": ""Dave Brubeck Quartet""
|
||||
},
|
||||
""composer"": {
|
||||
""id"": 999,
|
||||
""name"": ""Paul Desmond""
|
||||
},
|
||||
""album"": {
|
||||
""id"": 222,
|
||||
""title"": ""Time Out"",
|
||||
""tracks_count"": 7,
|
||||
""release_date_original"": ""1959-12-14"",
|
||||
""artist"": {
|
||||
""id"": 111,
|
||||
""name"": ""Dave Brubeck Quartet""
|
||||
},
|
||||
""genres_list"": [""Jazz"", ""Jazz→Cool Jazz""]
|
||||
}
|
||||
}")
|
||||
};
|
||||
|
||||
_httpMessageHandlerMock.Protected()
|
||||
.Setup<Task<HttpResponseMessage>>(
|
||||
"SendAsync",
|
||||
ItExpr.IsAny<HttpRequestMessage>(),
|
||||
ItExpr.IsAny<CancellationToken>())
|
||||
.ReturnsAsync(mockResponse);
|
||||
|
||||
// Act
|
||||
var result = await _service.GetSongAsync("qobuz", "123456789");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal("Take Five", result.Title);
|
||||
Assert.Equal("Dave Brubeck Quartet", result.Artist);
|
||||
Assert.Equal("Time Out", result.Album);
|
||||
Assert.Equal("USCO10300456", result.Isrc);
|
||||
Assert.Equal("℗ 1959 Columbia Records", result.Copyright);
|
||||
Assert.Equal(1959, result.Year);
|
||||
Assert.Equal("1959-12-14", result.ReleaseDate);
|
||||
Assert.Contains("Paul Desmond", result.Contributors);
|
||||
Assert.Equal("Jazz, Cool Jazz", result.Genre);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetSongAsync_WithWrongProvider_ReturnsNull()
|
||||
{
|
||||
// Act
|
||||
var result = await _service.GetSongAsync("deezer", "123456789");
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetAlbumAsync Tests
|
||||
|
||||
[Fact]
|
||||
public async Task GetAlbumAsync_WithValidId_ReturnsAlbumWithTracks()
|
||||
{
|
||||
// Arrange
|
||||
var mockResponse = new HttpResponseMessage
|
||||
{
|
||||
StatusCode = HttpStatusCode.OK,
|
||||
Content = new StringContent(@"{
|
||||
""id"": 222,
|
||||
""title"": ""Time Out"",
|
||||
""tracks_count"": 2,
|
||||
""release_date_original"": ""1959-12-14"",
|
||||
""artist"": {
|
||||
""id"": 111,
|
||||
""name"": ""Dave Brubeck Quartet""
|
||||
},
|
||||
""genres_list"": [""Jazz""],
|
||||
""tracks"": {
|
||||
""items"": [
|
||||
{
|
||||
""id"": 1,
|
||||
""title"": ""Blue Rondo à la Turk"",
|
||||
""track_number"": 1,
|
||||
""performer"": {
|
||||
""id"": 111,
|
||||
""name"": ""Dave Brubeck Quartet""
|
||||
},
|
||||
""album"": {
|
||||
""id"": 222,
|
||||
""title"": ""Time Out"",
|
||||
""artist"": {
|
||||
""id"": 111,
|
||||
""name"": ""Dave Brubeck Quartet""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
""id"": 2,
|
||||
""title"": ""Take Five"",
|
||||
""track_number"": 2,
|
||||
""performer"": {
|
||||
""id"": 111,
|
||||
""name"": ""Dave Brubeck Quartet""
|
||||
},
|
||||
""album"": {
|
||||
""id"": 222,
|
||||
""title"": ""Time Out"",
|
||||
""artist"": {
|
||||
""id"": 111,
|
||||
""name"": ""Dave Brubeck Quartet""
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}")
|
||||
};
|
||||
|
||||
_httpMessageHandlerMock.Protected()
|
||||
.Setup<Task<HttpResponseMessage>>(
|
||||
"SendAsync",
|
||||
ItExpr.IsAny<HttpRequestMessage>(),
|
||||
ItExpr.IsAny<CancellationToken>())
|
||||
.ReturnsAsync(mockResponse);
|
||||
|
||||
// Act
|
||||
var result = await _service.GetAlbumAsync("qobuz", "222");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal("Time Out", result.Title);
|
||||
Assert.Equal("Dave Brubeck Quartet", result.Artist);
|
||||
Assert.Equal(1959, result.Year);
|
||||
Assert.Equal(2, result.Songs.Count);
|
||||
Assert.Equal("Blue Rondo à la Turk", result.Songs[0].Title);
|
||||
Assert.Equal("Take Five", result.Songs[1].Title);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetAlbumAsync_WithWrongProvider_ReturnsNull()
|
||||
{
|
||||
// Act
|
||||
var result = await _service.GetAlbumAsync("deezer", "222");
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using octo_fiesta.Models.Domain;
|
||||
using octo_fiesta.Models.Search;
|
||||
using octo_fiesta.Models.Subsonic;
|
||||
using octo_fiesta.Services.Subsonic;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
@@ -187,39 +188,12 @@ public class SubsonicModelMapperTests
|
||||
|
||||
// Act
|
||||
var (mergedSongs, mergedAlbums, mergedArtists) = _mapper.MergeSearchResults(
|
||||
localSongs, new List<object>(), new List<object>(), externalResult, true);
|
||||
localSongs, new List<object>(), new List<object>(), externalResult, new List<ExternalPlaylist>(), true);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, mergedSongs.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeSearchResults_Json_DeduplicatesArtists()
|
||||
{
|
||||
// Arrange
|
||||
var localArtists = new List<object>
|
||||
{
|
||||
new Dictionary<string, object> { ["id"] = "local1", ["name"] = "Test Artist" }
|
||||
};
|
||||
var externalResult = new SearchResult
|
||||
{
|
||||
Songs = new List<Song>(),
|
||||
Albums = new List<Album>(),
|
||||
Artists = new List<Artist>
|
||||
{
|
||||
new Artist { Id = "ext1", Name = "Test Artist" }, // Same name - should be filtered
|
||||
new Artist { Id = "ext2", Name = "Different Artist" } // Different name - should be included
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var (mergedSongs, mergedAlbums, mergedArtists) = _mapper.MergeSearchResults(
|
||||
new List<object>(), new List<object>(), localArtists, externalResult, true);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, mergedArtists.Count); // 1 local + 1 external (duplicate filtered)
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MergeSearchResults_Json_CaseInsensitiveDeduplication()
|
||||
{
|
||||
@@ -240,7 +214,7 @@ public class SubsonicModelMapperTests
|
||||
|
||||
// Act
|
||||
var (mergedSongs, mergedAlbums, mergedArtists) = _mapper.MergeSearchResults(
|
||||
new List<object>(), new List<object>(), localArtists, externalResult, true);
|
||||
new List<object>(), new List<object>(), localArtists, externalResult, new List<ExternalPlaylist>(), true);
|
||||
|
||||
// Assert
|
||||
Assert.Single(mergedArtists); // Only the local artist
|
||||
@@ -267,7 +241,7 @@ public class SubsonicModelMapperTests
|
||||
|
||||
// Act
|
||||
var (mergedSongs, mergedAlbums, mergedArtists) = _mapper.MergeSearchResults(
|
||||
localSongs, new List<object>(), new List<object>(), externalResult, false);
|
||||
localSongs, new List<object>(), new List<object>(), externalResult, new List<ExternalPlaylist>(), false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, mergedSongs.Count);
|
||||
@@ -294,7 +268,7 @@ public class SubsonicModelMapperTests
|
||||
|
||||
// Act
|
||||
var (mergedSongs, mergedAlbums, mergedArtists) = _mapper.MergeSearchResults(
|
||||
new List<object>(), new List<object>(), localArtists, externalResult, false);
|
||||
new List<object>(), new List<object>(), localArtists, externalResult, new List<ExternalPlaylist>(), false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, mergedArtists.Count); // 1 local + 1 external (duplicate filtered)
|
||||
@@ -313,7 +287,7 @@ public class SubsonicModelMapperTests
|
||||
|
||||
// Act
|
||||
var (mergedSongs, mergedAlbums, mergedArtists) = _mapper.MergeSearchResults(
|
||||
new List<object>(), new List<object>(), new List<object>(), externalResult, true);
|
||||
new List<object>(), new List<object>(), new List<object>(), externalResult, new List<ExternalPlaylist>(), true);
|
||||
|
||||
// Assert
|
||||
Assert.Single(mergedSongs);
|
||||
@@ -337,7 +311,7 @@ public class SubsonicModelMapperTests
|
||||
|
||||
// Act
|
||||
var (mergedSongs, mergedAlbums, mergedArtists) = _mapper.MergeSearchResults(
|
||||
localSongs, localAlbums, localArtists, externalResult, true);
|
||||
localSongs, localAlbums, localArtists, externalResult, new List<ExternalPlaylist>(), true);
|
||||
|
||||
// Assert
|
||||
Assert.Single(mergedSongs);
|
||||
|
||||
Reference in New Issue
Block a user