From e5d417f0f499ae4d81a5516fff7b974d47e29b66 Mon Sep 17 00:00:00 2001 From: skuill Date: Mon, 11 Dec 2023 00:49:58 +0400 Subject: [PATCH] #14 #15 Add the ability to search only for a specific provider. Add status codes and an additional message to the search result. --- LyricsScraperNET/Common/Constants.cs | 22 ++ .../ExternalProviderOptionsExtensions.cs | 2 +- .../ExternalProviderTypeExtensions.cs | 10 + .../Extensions/SearchRequestExtensions.cs | 21 ++ .../Extensions/SearchResultExtensions.cs | 33 +++ LyricsScraperNET/LyricsScraperClient.cs | 158 ++++++++++++--- .../Requests/ArtistAndSongSearchRequest.cs | 25 ++- .../Models/Requests/UriSearchRequest.cs | 24 ++- .../Models/Responses/ResponseStatusCode.cs | 33 +++ .../Models/Responses/SearchResult.cs | 36 +++- .../AZLyrics/AZLyricsProviderTest.cs | 3 + .../Providers/Genius/GeniusProviderTest.cs | 3 + .../LyricFind/LyricFindProviderTest.cs | 3 + .../Musixmatch/MusixmatchProviderTest.cs | 3 + .../SongLyrics/SongLyricsProviderTest.cs | 3 + .../LyricsScraperClientTests.cs | 190 +++++++++++++++++- .../AZLyrics/AZLyricsProviderTest.cs | 3 + .../Providers/Genius/GeniusProviderTest.cs | 3 + .../LyricFind/LyricFindProviderTest.cs | 3 + .../SongLyrics/SongLyricsProviderTest.cs | 3 + 20 files changed, 544 insertions(+), 37 deletions(-) create mode 100644 LyricsScraperNET/Common/Constants.cs create mode 100644 LyricsScraperNET/Extensions/ExternalProviderTypeExtensions.cs create mode 100644 LyricsScraperNET/Extensions/SearchRequestExtensions.cs create mode 100644 LyricsScraperNET/Extensions/SearchResultExtensions.cs create mode 100644 LyricsScraperNET/Models/Responses/ResponseStatusCode.cs diff --git a/LyricsScraperNET/Common/Constants.cs b/LyricsScraperNET/Common/Constants.cs new file mode 100644 index 0000000..ec8103b --- /dev/null +++ b/LyricsScraperNET/Common/Constants.cs @@ -0,0 +1,22 @@ +namespace LyricsScraperNET.Common +{ + internal static class Constants + { + internal static class ResponseMessages + { + internal static readonly string ExternalProvidersListIsEmpty = "Empty external providers list! Please set any external provider first"; + + internal static readonly string ExternalProvidersAreDisabled = "All external providers are disabled. Searching lyrics is disabled"; + + internal static readonly string NotFoundLyric = "Can't find lyric for the search request"; + + internal static readonly string ExternalProviderForRequestNotSpecified = "The provider specified in the request is disabled or has not been configured for the client"; + + internal static readonly string SearchRequestIsEmpty = "Search request is empty"; + + internal static readonly string ArtistAndSongSearchRequestFieldsAreEmpty = "The ArtistAndSongSearchRequest not valid and contains one or more empty fields"; + + internal static readonly string UriSearchRequestFieldsAreEmpty = "The UriSearchRequest not valid and contains one or more empty fields"; + } + } +} diff --git a/LyricsScraperNET/Extensions/ExternalProviderOptionsExtensions.cs b/LyricsScraperNET/Extensions/ExternalProviderOptionsExtensions.cs index 58dd5ee..a03e89e 100644 --- a/LyricsScraperNET/Extensions/ExternalProviderOptionsExtensions.cs +++ b/LyricsScraperNET/Extensions/ExternalProviderOptionsExtensions.cs @@ -2,7 +2,7 @@ namespace LyricsScraperNET.Extensions { - public static class ExternalProviderOptionsExtensions + internal static class ExternalProviderOptionsExtensions { public static bool TryGetApiKeyFromOptions(this IExternalProviderOptions options, out string apiKey) { diff --git a/LyricsScraperNET/Extensions/ExternalProviderTypeExtensions.cs b/LyricsScraperNET/Extensions/ExternalProviderTypeExtensions.cs new file mode 100644 index 0000000..b12e897 --- /dev/null +++ b/LyricsScraperNET/Extensions/ExternalProviderTypeExtensions.cs @@ -0,0 +1,10 @@ +using LyricsScraperNET.Providers.Models; + +namespace LyricsScraperNET.Extensions +{ + internal static class ExternalProviderTypeExtensions + { + public static bool IsNoneProviderType(this ExternalProviderType providerType) + => providerType == ExternalProviderType.None; + } +} diff --git a/LyricsScraperNET/Extensions/SearchRequestExtensions.cs b/LyricsScraperNET/Extensions/SearchRequestExtensions.cs new file mode 100644 index 0000000..9a6c95a --- /dev/null +++ b/LyricsScraperNET/Extensions/SearchRequestExtensions.cs @@ -0,0 +1,21 @@ +using LyricsScraperNET.Models.Requests; +using LyricsScraperNET.Providers.Models; + +namespace LyricsScraperNET.Extensions +{ + internal static class SearchRequestExtensions + { + public static ExternalProviderType GetProviderTypeFromRequest(this SearchRequest searchRequest) + { + switch (searchRequest) + { + case ArtistAndSongSearchRequest artistAndSongSearchRequest: + return artistAndSongSearchRequest.Provider; + case UriSearchRequest uriSearchRequest: + return uriSearchRequest.Provider; + default: + return ExternalProviderType.None; + } + } + } +} diff --git a/LyricsScraperNET/Extensions/SearchResultExtensions.cs b/LyricsScraperNET/Extensions/SearchResultExtensions.cs new file mode 100644 index 0000000..b82bb9e --- /dev/null +++ b/LyricsScraperNET/Extensions/SearchResultExtensions.cs @@ -0,0 +1,33 @@ +using LyricsScraperNET.Models.Responses; + +namespace LyricsScraperNET.Extensions +{ + internal static class SearchResultExtensions + { + internal static SearchResult AddErrorMessage(this SearchResult searchResult, string responseMessage) + { + searchResult.ResponseStatusCode = ResponseStatusCode.Error; + return searchResult.AppendResponseMessage(responseMessage); + } + + internal static SearchResult AddNoDataFoundMessage(this SearchResult searchResult, string responseMessage) + { + searchResult.ResponseStatusCode = ResponseStatusCode.NoDataFound; + return searchResult.AppendResponseMessage(responseMessage); + } + + internal static SearchResult AddBadRequestMessage(this SearchResult searchResult, string responseMessage) + { + searchResult.ResponseStatusCode = ResponseStatusCode.BadRequest; + return searchResult.AppendResponseMessage(responseMessage); + } + + internal static SearchResult AppendResponseMessage(this SearchResult searchResult, string responseMessage) + { + searchResult.ResponseMessage = !string.IsNullOrWhiteSpace(searchResult.ResponseMessage) + ? string.Join("; ", new[] { searchResult.ResponseMessage, responseMessage }) + : responseMessage; + return searchResult; + } + } +} diff --git a/LyricsScraperNET/LyricsScraperClient.cs b/LyricsScraperNET/LyricsScraperClient.cs index 4a7e9e0..dc5e33f 100644 --- a/LyricsScraperNET/LyricsScraperClient.cs +++ b/LyricsScraperNET/LyricsScraperClient.cs @@ -1,10 +1,13 @@ -using LyricsScraperNET.Configuration; +using LyricsScraperNET.Common; +using LyricsScraperNET.Configuration; +using LyricsScraperNET.Extensions; using LyricsScraperNET.Helpers; using LyricsScraperNET.Models.Requests; using LyricsScraperNET.Models.Responses; using LyricsScraperNET.Providers.Abstract; using LyricsScraperNET.Providers.Models; using Microsoft.Extensions.Logging; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -23,8 +26,8 @@ public sealed class LyricsScraperClient : ILyricsScraperClient public IExternalProvider this[ExternalProviderType providerType] { - get => !IsEmptyProviders() - ? _externalProviders.FirstOrDefault(p => p.Options.ExternalProviderType == providerType) + get => IsProviderAvailable(providerType) + ? _externalProviders.First(p => p.Options.ExternalProviderType == providerType) : null; } @@ -50,66 +53,151 @@ public LyricsScraperClient(ILogger logger, public SearchResult SearchLyric(SearchRequest searchRequest) { - if (!ValidateRequest()) - return new SearchResult(); + var searchResult = new SearchResult(); - foreach (var externalProvider in _externalProviders.OrderByDescending(x => x.SearchPriority)) + if (!ValidSearchRequest(searchRequest, out var badRequestErrorMessage)) { - var searchResult = externalProvider.SearchLyric(searchRequest); - if (!searchResult.IsEmpty()) + searchResult.AddBadRequestMessage(badRequestErrorMessage); + return searchResult; + } + + if (!ValidClientConfiguration(out var errorMessage)) + { + searchResult.AddNoDataFoundMessage(errorMessage); + return searchResult; + } + + foreach (var externalProvider in GetAvailableProvidersForSearchRequest(searchRequest)) + { + var providerSearchResult = externalProvider.SearchLyric(searchRequest); + if (!providerSearchResult.IsEmpty()) { - return searchResult; + return providerSearchResult; } _logger?.LogWarning($"Can't find lyric by provider: {externalProvider}."); } + + searchResult.AddNoDataFoundMessage(Constants.ResponseMessages.NotFoundLyric); _logger?.LogError($"Can't find lyrics for searchRequest: {searchRequest}."); - return new SearchResult(); + + return searchResult; } public async Task SearchLyricAsync(SearchRequest searchRequest) { - if (!ValidateRequest()) - return new SearchResult(); + var searchResult = new SearchResult(); + + if (!ValidSearchRequest(searchRequest, out var badRequestErrorMessage)) + { + searchResult.AddBadRequestMessage(badRequestErrorMessage); + return searchResult; + } + + if (!ValidClientConfiguration(out var errorMessage)) + { + searchResult.AddNoDataFoundMessage(errorMessage); + return searchResult; + } - foreach (var externalProvider in _externalProviders.OrderByDescending(x => x.SearchPriority)) + foreach (var externalProvider in GetAvailableProvidersForSearchRequest(searchRequest)) { - var searchResult = await externalProvider.SearchLyricAsync(searchRequest); - if (!searchResult.IsEmpty()) + var providerSearchResult = await externalProvider.SearchLyricAsync(searchRequest); + if (!providerSearchResult.IsEmpty()) { - return searchResult; + return providerSearchResult; } _logger?.LogWarning($"Can't find lyric by provider: {externalProvider}."); } + + searchResult.AddNoDataFoundMessage(Constants.ResponseMessages.NotFoundLyric); _logger?.LogError($"Can't find lyrics for searchRequest: {searchRequest}."); - return new SearchResult(); + + return searchResult; + } + + private IEnumerable GetAvailableProvidersForSearchRequest(SearchRequest searchRequest) + { + var searchRequestExternalProvider = searchRequest.GetProviderTypeFromRequest(); + + if (searchRequestExternalProvider.IsNoneProviderType()) + return _externalProviders.Where(p => p.IsEnabled).OrderByDescending(p => p.SearchPriority); + + var availableProviders = _externalProviders.Where(p => p.IsEnabled && p.Options.ExternalProviderType == searchRequestExternalProvider); + + if (availableProviders.Any()) + return availableProviders.OrderByDescending(p => p.SearchPriority); + + return Array.Empty(); } - private bool ValidateRequest() + private bool ValidClientConfiguration(out string errorMessage) { - string error = string.Empty; + errorMessage = string.Empty; LogLevel logLevel = LogLevel.Error; - if (IsEmptyProviders()) + if (IsEmptyProvidersList()) { - error = "Empty providers list! Please set any external provider first."; + errorMessage = Constants.ResponseMessages.ExternalProvidersListIsEmpty; } else if (!IsEnabled) { - error = "All external providers is disabled. Searching lyrics is disabled."; + errorMessage = Constants.ResponseMessages.ExternalProvidersAreDisabled; logLevel = LogLevel.Debug; } - if (!string.IsNullOrWhiteSpace(error)) + if (!string.IsNullOrWhiteSpace(errorMessage)) + { + _logger?.Log(logLevel, errorMessage); + return false; + } + return true; + } + + private bool ValidSearchRequest(SearchRequest searchRequest, out string errorMessage) + { + errorMessage = string.Empty; + LogLevel logLevel = LogLevel.Error; + + if (searchRequest == null) + { + errorMessage = Constants.ResponseMessages.SearchRequestIsEmpty; + _logger?.Log(logLevel, errorMessage); + return false; + } + + switch (searchRequest) { - _logger?.Log(logLevel, error); + case ArtistAndSongSearchRequest artistAndSongSearchRequest: + errorMessage = string.IsNullOrEmpty(artistAndSongSearchRequest.Artist) || string.IsNullOrEmpty(artistAndSongSearchRequest.Song) + ? Constants.ResponseMessages.ArtistAndSongSearchRequestFieldsAreEmpty + : string.Empty; + break; + case UriSearchRequest uriSearchRequest: + errorMessage = uriSearchRequest.Uri == null + ? Constants.ResponseMessages.UriSearchRequestFieldsAreEmpty + : string.Empty; + break; + } + + var searchRequestExternalProvider = searchRequest.GetProviderTypeFromRequest(); + + if (!searchRequestExternalProvider.IsNoneProviderType() && !IsProviderEnabled(searchRequestExternalProvider)) + { + errorMessage = Constants.ResponseMessages.ExternalProviderForRequestNotSpecified; + } + + if (!string.IsNullOrWhiteSpace(errorMessage)) + { + _logger?.Log(logLevel, errorMessage); return false; } + return true; } public void AddProvider(IExternalProvider provider) { - if (IsEmptyProviders()) + if (IsEmptyProvidersList()) _externalProviders = new List(); if (!_externalProviders.Contains(provider)) _externalProviders.Add(provider); @@ -117,11 +205,9 @@ public void AddProvider(IExternalProvider provider) _logger?.LogWarning($"External provider {provider} already added"); } - private bool IsEmptyProviders() => _externalProviders == null || !_externalProviders.Any(); - public void RemoveProvider(ExternalProviderType providerType) { - if (IsEmptyProviders()) + if (providerType.IsNoneProviderType() || IsEmptyProvidersList()) return; _externalProviders.RemoveAll(x => x.Options.ExternalProviderType == providerType); @@ -129,7 +215,7 @@ public void RemoveProvider(ExternalProviderType providerType) public void Enable() { - if (IsEmptyProviders()) + if (IsEmptyProvidersList()) return; foreach (var provider in _externalProviders) @@ -140,7 +226,7 @@ public void Enable() public void Disable() { - if (IsEmptyProviders()) + if (IsEmptyProvidersList()) return; foreach (var provider in _externalProviders) @@ -148,5 +234,17 @@ public void Disable() provider.Disable(); } } + + private bool IsEmptyProvidersList() => _externalProviders == null || !_externalProviders.Any(); + + private bool IsProviderAvailable(ExternalProviderType providerType) + => !providerType.IsNoneProviderType() + && !IsEmptyProvidersList() + && _externalProviders.Any(p => p.Options.ExternalProviderType == providerType); + + private bool IsProviderEnabled(ExternalProviderType providerType) + => !providerType.IsNoneProviderType() + && IsProviderAvailable(providerType) + && this[providerType].IsEnabled; } } \ No newline at end of file diff --git a/LyricsScraperNET/Models/Requests/ArtistAndSongSearchRequest.cs b/LyricsScraperNET/Models/Requests/ArtistAndSongSearchRequest.cs index 01cf4cd..d0e4b5e 100644 --- a/LyricsScraperNET/Models/Requests/ArtistAndSongSearchRequest.cs +++ b/LyricsScraperNET/Models/Requests/ArtistAndSongSearchRequest.cs @@ -1,15 +1,38 @@ -namespace LyricsScraperNET.Models.Requests +using LyricsScraperNET.Providers.Models; + +namespace LyricsScraperNET.Models.Requests { + /// + /// Query model for searching lyrics by artist/band name and song/track title. + /// public sealed class ArtistAndSongSearchRequest : SearchRequest { + /// + /// Artist or band name. + /// public string Artist { get; } + /// + /// Song or track title. + /// public string Song { get; } + /// + /// The type of external provider for which lyrics will be searched. + /// By default, it is set to - the search will be performed across all available client providers. + /// + public ExternalProviderType Provider { get; } = ExternalProviderType.None; + public ArtistAndSongSearchRequest(string artist, string song) { Artist = artist; Song = song; } + + public ArtistAndSongSearchRequest(string artist, string song, ExternalProviderType provider) + : this(artist, song) + { + Provider = provider; + } } } diff --git a/LyricsScraperNET/Models/Requests/UriSearchRequest.cs b/LyricsScraperNET/Models/Requests/UriSearchRequest.cs index a100205..6e8f27a 100644 --- a/LyricsScraperNET/Models/Requests/UriSearchRequest.cs +++ b/LyricsScraperNET/Models/Requests/UriSearchRequest.cs @@ -1,17 +1,39 @@ -using System; +using LyricsScraperNET.Providers.Models; +using System; namespace LyricsScraperNET.Models.Requests { + /// + /// Query model for searching lyrics by the web address where the lyrics of one of the supported providers are located. + /// public sealed class UriSearchRequest : SearchRequest { + /// + /// The web address where the lyrics of one of the supported providers are located. + /// public Uri Uri { get; } + /// + /// The type of external provider for which lyrics will be searched. + /// By default, it is set to - the search will be performed across all available client providers. + /// + public ExternalProviderType Provider { get; } = ExternalProviderType.None; + public UriSearchRequest(Uri uri) { Uri = uri; } + public UriSearchRequest(Uri uri, ExternalProviderType provider) + : this(uri) + { + Provider = provider; + } + public UriSearchRequest(string uri) : this(new Uri(uri)) { } + + public UriSearchRequest(string uri, ExternalProviderType provider) : this(new Uri(uri), provider) + { } } } diff --git a/LyricsScraperNET/Models/Responses/ResponseStatusCode.cs b/LyricsScraperNET/Models/Responses/ResponseStatusCode.cs new file mode 100644 index 0000000..53c1a67 --- /dev/null +++ b/LyricsScraperNET/Models/Responses/ResponseStatusCode.cs @@ -0,0 +1,33 @@ +namespace LyricsScraperNET.Models.Responses +{ + /// + /// Search response status code list. + /// + public enum ResponseStatusCode + { + /// + /// Not specified status code. + /// + None = 0, + + /// + /// An error occurred during the search. + /// + Error = 1, + + /// + /// The lyrics was found in one of the available providers. + /// + Success = 2, + + /// + /// The lyrics could not be found in one of the available providers. + /// + NoDataFound = 3, + + /// + /// The search request is incorrect or contains malformed data. + /// + BadRequest = 4 + } +} diff --git a/LyricsScraperNET/Models/Responses/SearchResult.cs b/LyricsScraperNET/Models/Responses/SearchResult.cs index 57c7a53..b27cf73 100644 --- a/LyricsScraperNET/Models/Responses/SearchResult.cs +++ b/LyricsScraperNET/Models/Responses/SearchResult.cs @@ -2,24 +2,54 @@ namespace LyricsScraperNET.Models.Responses { + /// + /// Lyrics search result model. + /// public class SearchResult { - public SearchResult() + internal SearchResult() { LyricText = string.Empty; ExternalProviderType = ExternalProviderType.None; + ResponseStatusCode = ResponseStatusCode.NoDataFound; } - public SearchResult(string lyricText, ExternalProviderType externalProviderType) + internal SearchResult(ExternalProviderType externalProviderType) + : this() { - LyricText = lyricText; ExternalProviderType = externalProviderType; } + internal SearchResult(string lyricText, ExternalProviderType externalProviderType) + : this(externalProviderType) + { + LyricText = lyricText; + ResponseStatusCode = ResponseStatusCode.Success; + } + + /// + /// The text of the found lyrics. If the lyrics could not be found, an empty value is returned. + /// public string LyricText { get; } + /// + /// The type of external provider for which the lyrics were found. + /// public ExternalProviderType ExternalProviderType { get; } + /// + /// Search result status code. + /// + public ResponseStatusCode ResponseStatusCode { get; internal set; } = ResponseStatusCode.Success; + + /// + /// A message that may contain additional information in case of problems with the search. + /// + public string ResponseMessage { get; internal set; } = string.Empty; + + /// + /// Returns true if the field is empty. + /// public bool IsEmpty() => string.IsNullOrWhiteSpace(LyricText); } } diff --git a/Tests/LyricsScraperNET.IntegrationTest/Providers/AZLyrics/AZLyricsProviderTest.cs b/Tests/LyricsScraperNET.IntegrationTest/Providers/AZLyrics/AZLyricsProviderTest.cs index 6897c39..ea8b49a 100644 --- a/Tests/LyricsScraperNET.IntegrationTest/Providers/AZLyrics/AZLyricsProviderTest.cs +++ b/Tests/LyricsScraperNET.IntegrationTest/Providers/AZLyrics/AZLyricsProviderTest.cs @@ -1,4 +1,5 @@ using LyricsScraperNET.Models.Requests; +using LyricsScraperNET.Models.Responses; using LyricsScraperNET.Providers.AZLyrics; using LyricsScraperNET.Providers.Models; using LyricsScraperNET.TestShared.Providers; @@ -23,6 +24,8 @@ public void SearchLyric_IntegrationDynamicData_AreEqual(LyricsTestData testData) // Assert Assert.NotNull(searchResult); Assert.False(searchResult.IsEmpty()); + Assert.Equal(ResponseStatusCode.Success, searchResult.ResponseStatusCode); + Assert.True(string.IsNullOrEmpty(searchResult.ResponseMessage)); Assert.Equal(ExternalProviderType.AZLyrics, searchResult.ExternalProviderType); Assert.Equal(testData.LyricResultData.Replace("\r\n", "\n"), searchResult.LyricText.Replace("\r\n", "\n")); } diff --git a/Tests/LyricsScraperNET.IntegrationTest/Providers/Genius/GeniusProviderTest.cs b/Tests/LyricsScraperNET.IntegrationTest/Providers/Genius/GeniusProviderTest.cs index b3ef931..fce24f2 100644 --- a/Tests/LyricsScraperNET.IntegrationTest/Providers/Genius/GeniusProviderTest.cs +++ b/Tests/LyricsScraperNET.IntegrationTest/Providers/Genius/GeniusProviderTest.cs @@ -1,4 +1,5 @@ using LyricsScraperNET.Models.Requests; +using LyricsScraperNET.Models.Responses; using LyricsScraperNET.Providers.Genius; using LyricsScraperNET.Providers.Models; using LyricsScraperNET.TestShared.Providers; @@ -23,6 +24,8 @@ public void SearchLyric_IntegrationDynamicData_AreEqual(LyricsTestData testData) // Assert Assert.NotNull(searchResult); Assert.False(searchResult.IsEmpty()); + Assert.Equal(ResponseStatusCode.Success, searchResult.ResponseStatusCode); + Assert.True(string.IsNullOrEmpty(searchResult.ResponseMessage)); Assert.Equal(ExternalProviderType.Genius, searchResult.ExternalProviderType); Assert.Equal(testData.LyricResultData.Replace("\r\n", "\n"), searchResult.LyricText.Replace("\r\n", "\n")); } diff --git a/Tests/LyricsScraperNET.IntegrationTest/Providers/LyricFind/LyricFindProviderTest.cs b/Tests/LyricsScraperNET.IntegrationTest/Providers/LyricFind/LyricFindProviderTest.cs index 3d741d3..e0fcb4e 100644 --- a/Tests/LyricsScraperNET.IntegrationTest/Providers/LyricFind/LyricFindProviderTest.cs +++ b/Tests/LyricsScraperNET.IntegrationTest/Providers/LyricFind/LyricFindProviderTest.cs @@ -1,4 +1,5 @@ using LyricsScraperNET.Models.Requests; +using LyricsScraperNET.Models.Responses; using LyricsScraperNET.Providers.LyricFind; using LyricsScraperNET.Providers.Models; using LyricsScraperNET.TestShared.Providers; @@ -23,6 +24,8 @@ public void SearchLyric_IntegrationDynamicData_AreEqual(LyricsTestData testData) // Assert Assert.NotNull(searchResult); Assert.False(searchResult.IsEmpty()); + Assert.Equal(ResponseStatusCode.Success, searchResult.ResponseStatusCode); + Assert.True(string.IsNullOrEmpty(searchResult.ResponseMessage)); Assert.Equal(ExternalProviderType.LyricFind, searchResult.ExternalProviderType); Assert.Equal(testData.LyricResultData.Replace("\r\n", "\n"), searchResult.LyricText.Replace("\r\n", "\n")); } diff --git a/Tests/LyricsScraperNET.IntegrationTest/Providers/Musixmatch/MusixmatchProviderTest.cs b/Tests/LyricsScraperNET.IntegrationTest/Providers/Musixmatch/MusixmatchProviderTest.cs index 65b2e88..28b0e12 100644 --- a/Tests/LyricsScraperNET.IntegrationTest/Providers/Musixmatch/MusixmatchProviderTest.cs +++ b/Tests/LyricsScraperNET.IntegrationTest/Providers/Musixmatch/MusixmatchProviderTest.cs @@ -1,4 +1,5 @@ using LyricsScraperNET.Models.Requests; +using LyricsScraperNET.Models.Responses; using LyricsScraperNET.Providers.Models; using LyricsScraperNET.Providers.Musixmatch; using LyricsScraperNET.TestShared.Providers; @@ -23,6 +24,8 @@ public void SearchLyric_IntegrationDynamicData_AreEqual(LyricsTestData testData) // Assert Assert.NotNull(searchResult); Assert.False(searchResult.IsEmpty()); + Assert.Equal(ResponseStatusCode.Success, searchResult.ResponseStatusCode); + Assert.True(string.IsNullOrEmpty(searchResult.ResponseMessage)); Assert.Equal(ExternalProviderType.Musixmatch, searchResult.ExternalProviderType); Assert.Equal(testData.LyricResultData.Replace("\r\n", "\n"), searchResult.LyricText); } diff --git a/Tests/LyricsScraperNET.IntegrationTest/Providers/SongLyrics/SongLyricsProviderTest.cs b/Tests/LyricsScraperNET.IntegrationTest/Providers/SongLyrics/SongLyricsProviderTest.cs index 30e74ab..791c10b 100644 --- a/Tests/LyricsScraperNET.IntegrationTest/Providers/SongLyrics/SongLyricsProviderTest.cs +++ b/Tests/LyricsScraperNET.IntegrationTest/Providers/SongLyrics/SongLyricsProviderTest.cs @@ -1,4 +1,5 @@ using LyricsScraperNET.Models.Requests; +using LyricsScraperNET.Models.Responses; using LyricsScraperNET.Providers.Models; using LyricsScraperNET.Providers.SongLyrics; using LyricsScraperNET.TestShared.Providers; @@ -23,6 +24,8 @@ public void SearchLyric_IntegrationDynamicData_AreEqual(LyricsTestData testData) // Assert Assert.NotNull(searchResult); Assert.False(searchResult.IsEmpty()); + Assert.Equal(ResponseStatusCode.Success, searchResult.ResponseStatusCode); + Assert.True(string.IsNullOrEmpty(searchResult.ResponseMessage)); Assert.Equal(ExternalProviderType.SongLyrics, searchResult.ExternalProviderType); Assert.Equal(testData.LyricResultData.Replace("\r\n", "\n"), searchResult.LyricText.Replace("\r\n", "\n")); } diff --git a/Tests/LyricsScraperNET.UnitTest/LyricsScraperClientTests.cs b/Tests/LyricsScraperNET.UnitTest/LyricsScraperClientTests.cs index 25291cd..0b128a4 100644 --- a/Tests/LyricsScraperNET.UnitTest/LyricsScraperClientTests.cs +++ b/Tests/LyricsScraperNET.UnitTest/LyricsScraperClientTests.cs @@ -1,6 +1,10 @@ -using LyricsScraperNET.Models.Requests; +using LyricsScraperNET.Common; +using LyricsScraperNET.Models.Requests; +using LyricsScraperNET.Models.Responses; +using LyricsScraperNET.Providers.Abstract; using LyricsScraperNET.Providers.Models; using Moq; +using System; using Xunit; namespace LyricsScraperNET.UnitTest @@ -28,8 +32,12 @@ public async void SearchLyric_WithDisabledClient_ShouldReturnEmptySearchResult() } Assert.NotNull(searchResult); Assert.True(searchResult.IsEmpty()); + Assert.Equal(ResponseStatusCode.NoDataFound, searchResult.ResponseStatusCode); + Assert.Equal(Constants.ResponseMessages.ExternalProvidersAreDisabled, searchResult.ResponseMessage); Assert.NotNull(searchResultAsync); Assert.True(searchResultAsync.IsEmpty()); + Assert.Equal(ResponseStatusCode.NoDataFound, searchResultAsync.ResponseStatusCode); + Assert.Equal(Constants.ResponseMessages.ExternalProvidersAreDisabled, searchResultAsync.ResponseMessage); } [Fact] @@ -47,8 +55,139 @@ public async void SearchLyric_DefaultClient_ShouldReturnEmptySearchResult() Assert.False(lyricsScraperClient.IsEnabled); Assert.NotNull(searchResult); Assert.True(searchResult.IsEmpty()); + Assert.Equal(ResponseStatusCode.NoDataFound, searchResult.ResponseStatusCode); + Assert.Equal(Constants.ResponseMessages.ExternalProvidersListIsEmpty, searchResult.ResponseMessage); Assert.NotNull(searchResultAsync); Assert.True(searchResultAsync.IsEmpty()); + Assert.Equal(ResponseStatusCode.NoDataFound, searchResultAsync.ResponseStatusCode); + Assert.Equal(Constants.ResponseMessages.ExternalProvidersListIsEmpty, searchResultAsync.ResponseMessage); + } + + [Theory] + [InlineData("", "")] + [InlineData(null, null)] + [InlineData("Muse", null)] + [InlineData(null, "Hysteria")] + public async void SearchLyric_MalformedArtistAndSongSearchRequest_ShouldReturnBadRequestStatus(string artist, string song) + { + // Arrange + var lyricsScraperClient = new LyricsScraperClient(); + var searchRequest = new ArtistAndSongSearchRequest(artist, song); + + // Act + var searchResult = lyricsScraperClient.SearchLyric(searchRequest); + var searchResultAsync = await lyricsScraperClient.SearchLyricAsync(searchRequest); + + // Assert + Assert.False(lyricsScraperClient.IsEnabled); + Assert.NotNull(searchResult); + Assert.True(searchResult.IsEmpty()); + Assert.Equal(ExternalProviderType.None, searchResult.ExternalProviderType); + Assert.Equal(ResponseStatusCode.BadRequest, searchResult.ResponseStatusCode); + Assert.Equal(Constants.ResponseMessages.ArtistAndSongSearchRequestFieldsAreEmpty, searchResult.ResponseMessage); + Assert.NotNull(searchResultAsync); + Assert.True(searchResultAsync.IsEmpty()); + Assert.Equal(ExternalProviderType.None, searchResultAsync.ExternalProviderType); + Assert.Equal(ResponseStatusCode.BadRequest, searchResultAsync.ResponseStatusCode); + Assert.Equal(Constants.ResponseMessages.ArtistAndSongSearchRequestFieldsAreEmpty, searchResultAsync.ResponseMessage); + } + + [Theory] + [InlineData(null)] + public async void SearchLyric_MalformedUriSearchRequest_ShouldReturnBadRequestStatus(Uri uri) + { + // Arrange + var lyricsScraperClient = new LyricsScraperClient(); + var searchRequest = new UriSearchRequest(uri); + + // Act + var searchResult = lyricsScraperClient.SearchLyric(searchRequest); + var searchResultAsync = await lyricsScraperClient.SearchLyricAsync(searchRequest); + + // Assert + Assert.False(lyricsScraperClient.IsEnabled); + Assert.NotNull(searchResult); + Assert.True(searchResult.IsEmpty()); + Assert.Equal(ExternalProviderType.None, searchResult.ExternalProviderType); + Assert.Equal(ResponseStatusCode.BadRequest, searchResult.ResponseStatusCode); + Assert.Equal(Constants.ResponseMessages.UriSearchRequestFieldsAreEmpty, searchResult.ResponseMessage); + Assert.NotNull(searchResultAsync); + Assert.True(searchResultAsync.IsEmpty()); + Assert.Equal(ExternalProviderType.None, searchResultAsync.ExternalProviderType); + Assert.Equal(ResponseStatusCode.BadRequest, searchResultAsync.ResponseStatusCode); + Assert.Equal(Constants.ResponseMessages.UriSearchRequestFieldsAreEmpty, searchResultAsync.ResponseMessage); + } + + [Fact] + public async void SearchLyric_EmptySearchRequest_ShouldReturnBadRequestStatus() + { + // Arrange + var lyricsScraperClient = new LyricsScraperClient(); + + // Act + var searchResult = lyricsScraperClient.SearchLyric(null); + var searchResultAsync = await lyricsScraperClient.SearchLyricAsync(null); + + // Assert + Assert.False(lyricsScraperClient.IsEnabled); + Assert.NotNull(searchResult); + Assert.True(searchResult.IsEmpty()); + Assert.Equal(ExternalProviderType.None, searchResult.ExternalProviderType); + Assert.Equal(ResponseStatusCode.BadRequest, searchResult.ResponseStatusCode); + Assert.Equal(Constants.ResponseMessages.SearchRequestIsEmpty, searchResult.ResponseMessage); + Assert.NotNull(searchResultAsync); + Assert.True(searchResultAsync.IsEmpty()); + Assert.Equal(ExternalProviderType.None, searchResultAsync.ExternalProviderType); + Assert.Equal(ResponseStatusCode.BadRequest, searchResultAsync.ResponseStatusCode); + Assert.Equal(Constants.ResponseMessages.SearchRequestIsEmpty, searchResultAsync.ResponseMessage); + } + + [Fact] + public async void SearchLyric_ProviderWithEmptyResult_ShouldReturnNotFoundStatus() + { + // Arrange + var lyricsScraperClient = GetLyricsScraperClientWithMockedProvider(); + var searchRequestMock = new Mock(); + + // Act + var searchResult = lyricsScraperClient.SearchLyric(searchRequestMock.Object); + var searchResultAsync = await lyricsScraperClient.SearchLyricAsync(searchRequestMock.Object); + + // Assert + Assert.NotNull(searchResult); + Assert.True(searchResult.IsEmpty()); + Assert.Equal(ExternalProviderType.None, searchResult.ExternalProviderType); + Assert.Equal(ResponseStatusCode.NoDataFound, searchResult.ResponseStatusCode); + Assert.Equal(Constants.ResponseMessages.NotFoundLyric, searchResult.ResponseMessage); + Assert.NotNull(searchResultAsync); + Assert.True(searchResultAsync.IsEmpty()); + Assert.Equal(ExternalProviderType.None, searchResultAsync.ExternalProviderType); + Assert.Equal(ResponseStatusCode.NoDataFound, searchResultAsync.ResponseStatusCode); + Assert.Equal(Constants.ResponseMessages.NotFoundLyric, searchResultAsync.ResponseMessage); + } + + [Fact] + public async void SearchLyric_ProviderNotSpecifiedForRequest_ShouldReturnNotFoundStatus() + { + // Arrange + var lyricsScraperClient = GetLyricsScraperClientWithMockedProvider(); + var searchRequest = new ArtistAndSongSearchRequest("test", "test", ExternalProviderType.Genius); + + // Act + var searchResult = lyricsScraperClient.SearchLyric(searchRequest); + var searchResultAsync = await lyricsScraperClient.SearchLyricAsync(searchRequest); + + // Assert + Assert.NotNull(searchResult); + Assert.True(searchResult.IsEmpty()); + Assert.Equal(ExternalProviderType.None, searchResult.ExternalProviderType); + Assert.Equal(ResponseStatusCode.BadRequest, searchResult.ResponseStatusCode); + Assert.Equal(Constants.ResponseMessages.ExternalProviderForRequestNotSpecified, searchResult.ResponseMessage); + Assert.NotNull(searchResultAsync); + Assert.True(searchResultAsync.IsEmpty()); + Assert.Equal(ExternalProviderType.None, searchResultAsync.ExternalProviderType); + Assert.Equal(ResponseStatusCode.BadRequest, searchResultAsync.ResponseStatusCode); + Assert.Equal(Constants.ResponseMessages.ExternalProviderForRequestNotSpecified, searchResultAsync.ResponseMessage); } [Fact] @@ -87,6 +226,8 @@ public async void Enable_WithDisabledClient_ShouldBeEnabled() // Act lyricsScraperClient.Disable(); + Assert.False(lyricsScraperClient.IsEnabled); + lyricsScraperClient.Enable(); // Assert @@ -97,6 +238,31 @@ public async void Enable_WithDisabledClient_ShouldBeEnabled() } } + [Fact] + public void AddAndRemoveExternalProvider_ShouldBeSwitched() + { + // Arrange + var lyricsScraperClient = new LyricsScraperClient(); + var externalProviderType = ExternalProviderType.AZLyrics; + var externalProvider = GetExternalProviderMock(externalProviderType); + + // Assert 0 + Assert.Null(lyricsScraperClient[externalProviderType]); + + // Act 1 + lyricsScraperClient.AddProvider(externalProvider.Object); + var externalProviderActual = lyricsScraperClient[externalProviderType]; + + // Assert 1 + Assert.NotNull(externalProviderActual); + + // Act 2 + lyricsScraperClient.RemoveProvider(externalProviderType); + + // Assert 2 + Assert.Null(lyricsScraperClient[externalProviderType]); + } + private ExternalProviderType[] GetExternalProviderTypes() { return new[] { ExternalProviderType.AZLyrics, ExternalProviderType.SongLyrics }; @@ -108,5 +274,27 @@ private ILyricsScraperClient GetLyricsScraperClient() .WithAZLyrics() .WithSongLyrics(); } + + private ILyricsScraperClient GetLyricsScraperClientWithMockedProvider() + { + + var client = new LyricsScraperClient(); + var externalProvider = GetExternalProviderMock(ExternalProviderType.AZLyrics); + client.AddProvider(externalProvider.Object); + + return client; + } + + private Mock GetExternalProviderMock(ExternalProviderType externalProviderType) + { + var externalProviderMock = new Mock(); + + externalProviderMock.Setup(p => p.IsEnabled).Returns(true); + externalProviderMock.Setup(p => p.SearchLyric(It.IsAny())).Returns(new SearchResult()); + externalProviderMock.Setup(p => p.SearchLyricAsync(It.IsAny())).ReturnsAsync(new SearchResult()); + externalProviderMock.Setup(p => p.Options.ExternalProviderType).Returns(externalProviderType); + + return externalProviderMock; + } } } diff --git a/Tests/LyricsScraperNET.UnitTest/Providers/AZLyrics/AZLyricsProviderTest.cs b/Tests/LyricsScraperNET.UnitTest/Providers/AZLyrics/AZLyricsProviderTest.cs index 7093e79..19e76e4 100644 --- a/Tests/LyricsScraperNET.UnitTest/Providers/AZLyrics/AZLyricsProviderTest.cs +++ b/Tests/LyricsScraperNET.UnitTest/Providers/AZLyrics/AZLyricsProviderTest.cs @@ -1,4 +1,5 @@ using LyricsScraperNET.Models.Requests; +using LyricsScraperNET.Models.Responses; using LyricsScraperNET.Providers.AZLyrics; using LyricsScraperNET.Providers.Models; using LyricsScraperNET.TestShared.Extensions; @@ -25,6 +26,8 @@ public void SearchLyric_UnitDynamicData_AreEqual(LyricsTestData testData) // Assert Assert.NotNull(searchResult); + Assert.Equal(ResponseStatusCode.Success, searchResult.ResponseStatusCode); + Assert.True(string.IsNullOrEmpty(searchResult.ResponseMessage)); Assert.Equal(ExternalProviderType.AZLyrics, searchResult.ExternalProviderType); Assert.Equal(testData.LyricResultData.Replace("\r\n", "\n"), searchResult.LyricText.Replace("\r\n", "\n")); } diff --git a/Tests/LyricsScraperNET.UnitTest/Providers/Genius/GeniusProviderTest.cs b/Tests/LyricsScraperNET.UnitTest/Providers/Genius/GeniusProviderTest.cs index 15b033b..535d053 100644 --- a/Tests/LyricsScraperNET.UnitTest/Providers/Genius/GeniusProviderTest.cs +++ b/Tests/LyricsScraperNET.UnitTest/Providers/Genius/GeniusProviderTest.cs @@ -1,4 +1,5 @@ using LyricsScraperNET.Models.Requests; +using LyricsScraperNET.Models.Responses; using LyricsScraperNET.Providers.Genius; using LyricsScraperNET.Providers.Models; using LyricsScraperNET.TestShared.Extensions; @@ -25,6 +26,8 @@ public void SearchLyric_UnitDynamicData_AreEqual(LyricsTestData testData) // Assert Assert.NotNull(searchResult); + Assert.Equal(ResponseStatusCode.Success, searchResult.ResponseStatusCode); + Assert.True(string.IsNullOrEmpty(searchResult.ResponseMessage)); Assert.Equal(ExternalProviderType.Genius, searchResult.ExternalProviderType); Assert.Equal(testData.LyricResultData.Replace("\r\n", "\n"), searchResult.LyricText.Replace("\r\n", "\n")); } diff --git a/Tests/LyricsScraperNET.UnitTest/Providers/LyricFind/LyricFindProviderTest.cs b/Tests/LyricsScraperNET.UnitTest/Providers/LyricFind/LyricFindProviderTest.cs index 4ab77f7..ec8746c 100644 --- a/Tests/LyricsScraperNET.UnitTest/Providers/LyricFind/LyricFindProviderTest.cs +++ b/Tests/LyricsScraperNET.UnitTest/Providers/LyricFind/LyricFindProviderTest.cs @@ -1,4 +1,5 @@ using LyricsScraperNET.Models.Requests; +using LyricsScraperNET.Models.Responses; using LyricsScraperNET.Providers.LyricFind; using LyricsScraperNET.Providers.Models; using LyricsScraperNET.TestShared.Extensions; @@ -25,6 +26,8 @@ public void SearchLyric_UnitDynamicData_AreEqual(LyricsTestData testData) // Assert Assert.NotNull(searchResult); + Assert.Equal(ResponseStatusCode.Success, searchResult.ResponseStatusCode); + Assert.True(string.IsNullOrEmpty(searchResult.ResponseMessage)); Assert.Equal(ExternalProviderType.LyricFind, searchResult.ExternalProviderType); Assert.Equal(testData.LyricResultData.Replace("\r\n", "\n"), searchResult.LyricText.Replace("\r\n", "\n")); } diff --git a/Tests/LyricsScraperNET.UnitTest/Providers/SongLyrics/SongLyricsProviderTest.cs b/Tests/LyricsScraperNET.UnitTest/Providers/SongLyrics/SongLyricsProviderTest.cs index cef566f..b22a8aa 100644 --- a/Tests/LyricsScraperNET.UnitTest/Providers/SongLyrics/SongLyricsProviderTest.cs +++ b/Tests/LyricsScraperNET.UnitTest/Providers/SongLyrics/SongLyricsProviderTest.cs @@ -1,4 +1,5 @@ using LyricsScraperNET.Models.Requests; +using LyricsScraperNET.Models.Responses; using LyricsScraperNET.Providers.Models; using LyricsScraperNET.Providers.SongLyrics; using LyricsScraperNET.TestShared.Extensions; @@ -25,6 +26,8 @@ public void SearchLyric_UnitDynamicData_AreEqual(LyricsTestData testData) // Assert Assert.NotNull(searchResult); + Assert.Equal(ResponseStatusCode.Success, searchResult.ResponseStatusCode); + Assert.True(string.IsNullOrEmpty(searchResult.ResponseMessage)); Assert.Equal(ExternalProviderType.SongLyrics, searchResult.ExternalProviderType); Assert.Equal(testData.LyricResultData.Replace("\r\n", "\n"), searchResult.LyricText.Replace("\r\n", "\n")); }