diff --git a/.gitignore b/.gitignore index de33e35..0daaf12 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# Ignore syro config +/config.json +/syro.json + ################# ## Visual Studio ################# diff --git a/src/IF.Lastfm.Core.Tests/Api/Commands/UserGetRecentTracksCommandTests.cs b/src/IF.Lastfm.Core.Tests/Api/Commands/UserGetRecentTracksCommandTests.cs index 52ef5f5..c2cbbe9 100644 --- a/src/IF.Lastfm.Core.Tests/Api/Commands/UserGetRecentTracksCommandTests.cs +++ b/src/IF.Lastfm.Core.Tests/Api/Commands/UserGetRecentTracksCommandTests.cs @@ -1,7 +1,5 @@ -using IF.Lastfm.Core.Api; -using IF.Lastfm.Core.Api.Commands.UserApi; +using IF.Lastfm.Core.Api.Commands.UserApi; using IF.Lastfm.Core.Api.Enums; -using IF.Lastfm.Core.Api.Helpers; using IF.Lastfm.Core.Objects; using IF.Lastfm.Core.Tests.Resources; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -9,7 +7,6 @@ using System.Collections.Generic; using System.Text; using System.Threading.Tasks; -using Moq; using Newtonsoft.Json.Linq; namespace IF.Lastfm.Core.Tests.Api.Commands @@ -86,67 +83,4 @@ public async Task HandleErrorResponse() Assert.IsTrue(parsed.Error == LastFmApiError.MissingParameters); } } - - [TestClass] - public class UserGetTopAlbumsCommandTests : CommandTestsBase - { - private const string USER = "test"; - private const LastStatsTimeSpan SPAN = LastStatsTimeSpan.Month; - - private GetTopAlbumsCommand _command; - private Mock _mockAuth; - - [TestInitialize] - public void TestInitialise() - { - _mockAuth = new Mock(); - _command = new GetTopAlbumsCommand(_mockAuth.Object, USER, SPAN) - { - Page = 5, - Count = 20 - }; - - _command.SetParameters(); - } - - [TestMethod] - public void CorrectParameters() - { - var expected = new Dictionary - { - {"user", USER}, - {"period", SPAN.GetApiName()}, - {"limit", "20"}, - {"page", "5"}, - {"disablecachetoken", ""} - }; - - _command.Parameters["disablecachetoken"] = ""; - - TestHelper.AssertEqual(expected, _command.Parameters); - } - - public void HandleErrorResponse() - { - - } - - [TestMethod] - public void HandleResponseEmpty() - { - - } - - [TestMethod] - public void HandleResponseSingle() - { - - } - - [TestMethod] - public void HandleResponseMultiple() - { - - } - } } diff --git a/src/IF.Lastfm.Core.Tests/Api/Commands/UserGetTopAlbumsCommandTests.cs b/src/IF.Lastfm.Core.Tests/Api/Commands/UserGetTopAlbumsCommandTests.cs new file mode 100644 index 0000000..309bdd9 --- /dev/null +++ b/src/IF.Lastfm.Core.Tests/Api/Commands/UserGetTopAlbumsCommandTests.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using IF.Lastfm.Core.Api; +using IF.Lastfm.Core.Api.Commands.UserApi; +using IF.Lastfm.Core.Api.Enums; +using IF.Lastfm.Core.Api.Helpers; +using IF.Lastfm.Core.Objects; +using IF.Lastfm.Core.Tests.Resources; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace IF.Lastfm.Core.Tests.Api.Commands +{ + [TestClass] + public class UserGetTopAlbumsCommandTests : CommandTestsBase + { + private const string USER = "test"; + private const LastStatsTimeSpan SPAN = LastStatsTimeSpan.Month; + + private GetTopAlbumsCommand _command; + private Mock _mockAuth; + + [TestInitialize] + public void TestInitialise() + { + _mockAuth = new Mock(); + _command = new GetTopAlbumsCommand(_mockAuth.Object, USER, SPAN) + { + Page = 5, + Count = 20 + }; + + _command.SetParameters(); + } + + [TestMethod] + public void CorrectParameters() + { + var expected = new Dictionary + { + {"user", USER}, + {"period", SPAN.GetApiName()}, + {"limit", "20"}, + {"page", "5"}, + {"disablecachetoken", ""} + }; + + _command.Parameters["disablecachetoken"] = ""; + + TestHelper.AssertSerialiseEqual(expected, _command.Parameters); + } + + [TestMethod] + public async Task HandleErrorResponse() + { + var http = CreateResponseMessage(Encoding.UTF8.GetString(UserApiResponses.UserGetTopAlbumsError)); + var response = await _command.HandleResponse(http); + + Assert.IsFalse(response.Success); + } + + [TestMethod] + public async Task HandleResponseEmpty() + { + var http = CreateResponseMessage(Encoding.UTF8.GetString(UserApiResponses.UserGetTopAlbumsEmpty)); + var response = await _command.HandleResponse(http); + + Assert.IsTrue(response.Success); + Assert.IsTrue(!response.Content.Any()); + Assert.AreEqual(0, response.TotalItems); + Assert.AreEqual(1, response.TotalPages); + } + + [TestMethod] + public async Task HandleResponseSingle() + { + var expectedAlbum = new LastAlbum + { + ArtistName = "Crystal Castles", + Name = "Crystal Castles", + PlayCount = 2206, + Mbid = "a432a420-f374-4556-8421-b4ea097c7fe9", + Url = new Uri("http://www.last.fm/music/Crystal+Castles/Crystal+Castles"), + ListenerCount = null, + Images = new LastImageSet( + "http://userserve-ak.last.fm/serve/34s/78606386.png", + "http://userserve-ak.last.fm/serve/64s/78606386.png", + "http://userserve-ak.last.fm/serve/126/78606386.png", + "http://userserve-ak.last.fm/serve/300x300/78606386.png"), + Tracks = Enumerable.Empty(), + ReleaseDateUtc = null, + TopTags = Enumerable.Empty() + }; + + var http = CreateResponseMessage(Encoding.UTF8.GetString(UserApiResponses.UserGetTopAlbumsSingle)); + var response = await _command.HandleResponse(http); + + Assert.IsTrue(response.Success); + Assert.AreEqual(1, response.Content.Count); + + var actualAlbum = response.Content.First(); + + TestHelper.AssertSerialiseEqual(expectedAlbum, actualAlbum); + } + + [TestMethod] + public async Task HandleResponseMultiple() + { + + } + } +} \ No newline at end of file diff --git a/src/IF.Lastfm.Core.Tests/IF.Lastfm.Core.Tests.csproj b/src/IF.Lastfm.Core.Tests/IF.Lastfm.Core.Tests.csproj index b9eb271..338cdeb 100644 --- a/src/IF.Lastfm.Core.Tests/IF.Lastfm.Core.Tests.csproj +++ b/src/IF.Lastfm.Core.Tests/IF.Lastfm.Core.Tests.csproj @@ -90,6 +90,7 @@ + @@ -165,6 +166,10 @@ + + + + diff --git a/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopAlbumsEmpty.json b/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopAlbumsEmpty.json new file mode 100644 index 0000000..bea4a48 --- /dev/null +++ b/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopAlbumsEmpty.json @@ -0,0 +1,11 @@ +{ + "topalbums": { + "#text": "\n ", + "user": "rq", + "type": "overall", + "page": "0", + "perPage": "1", + "totalPages": "0", + "total": "0" + } +} \ No newline at end of file diff --git a/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopAlbumsError.json b/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopAlbumsError.json new file mode 100644 index 0000000..17a1ac4 --- /dev/null +++ b/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopAlbumsError.json @@ -0,0 +1,5 @@ +{ + "error": 6, + "message": "Invalid parameters - Your request is missing the [user] parameter", + "links": [] +} \ No newline at end of file diff --git a/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopAlbumsMultiple.json b/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopAlbumsMultiple.json new file mode 100644 index 0000000..77fe2b4 --- /dev/null +++ b/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopAlbumsMultiple.json @@ -0,0 +1,110 @@ +{ + "topalbums": { + "album": [ + { + "name": "Crystal Castles", + "playcount": "2206", + "mbid": "a432a420-f374-4556-8421-b4ea097c7fe9", + "url": "http://www.last.fm/music/Crystal+Castles/Crystal+Castles", + "artist": { + "name": "Crystal Castles", + "mbid": "b1570544-93ab-4b2b-8398-131735394202", + "url": "http://www.last.fm/music/Crystal+Castles" + }, + "image": [ + { + "#text": "http://userserve-ak.last.fm/serve/34s/78606386.png", + "size": "small" + }, + { + "#text": "http://userserve-ak.last.fm/serve/64s/78606386.png", + "size": "medium" + }, + { + "#text": "http://userserve-ak.last.fm/serve/126/78606386.png", + "size": "large" + }, + { + "#text": "http://userserve-ak.last.fm/serve/300x300/78606386.png", + "size": "extralarge" + } + ], + "@attr": { + "rank": "1" + } + }, + { + "name": "In Ghost Colours", + "playcount": "1422", + "mbid": "32bb96fa-e91b-49e7-ba07-98c2e5edd28c", + "url": "http://www.last.fm/music/Cut+Copy/In+Ghost+Colours", + "artist": { + "name": "Cut Copy", + "mbid": "caaba574-dfbc-4681-8e56-19b5150897d2", + "url": "http://www.last.fm/music/Cut+Copy" + }, + "image": [ + { + "#text": "http://userserve-ak.last.fm/serve/34s/69495270.png", + "size": "small" + }, + { + "#text": "http://userserve-ak.last.fm/serve/64s/69495270.png", + "size": "medium" + }, + { + "#text": "http://userserve-ak.last.fm/serve/126/69495270.png", + "size": "large" + }, + { + "#text": "http://userserve-ak.last.fm/serve/300x300/69495270.png", + "size": "extralarge" + } + ], + "@attr": { + "rank": "2" + } + }, + { + "name": "The Reminder", + "playcount": "1360", + "mbid": "48d53708-53f0-3a66-8520-5d8093a0e477", + "url": "http://www.last.fm/music/Feist/The+Reminder", + "artist": { + "name": "Feist", + "mbid": "a670e05a-cea8-4b37-bce9-d82daf1a0fa4", + "url": "http://www.last.fm/music/Feist" + }, + "image": [ + { + "#text": "http://userserve-ak.last.fm/serve/34s/67147496.png", + "size": "small" + }, + { + "#text": "http://userserve-ak.last.fm/serve/64s/67147496.png", + "size": "medium" + }, + { + "#text": "http://userserve-ak.last.fm/serve/126/67147496.png", + "size": "large" + }, + { + "#text": "http://userserve-ak.last.fm/serve/300x300/67147496.png", + "size": "extralarge" + } + ], + "@attr": { + "rank": "3" + } + } + ], + "@attr": { + "user": "tehrikkit", + "type": "overall", + "page": "0", + "perPage": "3", + "totalPages": "1014", + "total": "3040" + } + } +} \ No newline at end of file diff --git a/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopAlbumsSingle.json b/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopAlbumsSingle.json new file mode 100644 index 0000000..178697f --- /dev/null +++ b/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopAlbumsSingle.json @@ -0,0 +1,44 @@ +{ + "topalbums": { + "album": { + "name": "Crystal Castles", + "playcount": "2206", + "mbid": "a432a420-f374-4556-8421-b4ea097c7fe9", + "url": "http://www.last.fm/music/Crystal+Castles/Crystal+Castles", + "artist": { + "name": "Crystal Castles", + "mbid": "b1570544-93ab-4b2b-8398-131735394202", + "url": "http://www.last.fm/music/Crystal+Castles" + }, + "image": [ + { + "#text": "http://userserve-ak.last.fm/serve/34s/78606386.png", + "size": "small" + }, + { + "#text": "http://userserve-ak.last.fm/serve/64s/78606386.png", + "size": "medium" + }, + { + "#text": "http://userserve-ak.last.fm/serve/126/78606386.png", + "size": "large" + }, + { + "#text": "http://userserve-ak.last.fm/serve/300x300/78606386.png", + "size": "extralarge" + } + ], + "@attr": { + "rank": "1" + } + }, + "@attr": { + "user": "tehrikkit", + "type": "overall", + "page": "1", + "perPage": "1", + "totalPages": "3040", + "total": "3040" + } + } +} \ No newline at end of file diff --git a/src/IF.Lastfm.Core.Tests/Resources/UserApiResponses.Designer.cs b/src/IF.Lastfm.Core.Tests/Resources/UserApiResponses.Designer.cs index f3bdb17..666f91c 100644 --- a/src/IF.Lastfm.Core.Tests/Resources/UserApiResponses.Designer.cs +++ b/src/IF.Lastfm.Core.Tests/Resources/UserApiResponses.Designer.cs @@ -129,5 +129,45 @@ internal static byte[] UserGetRecommendedArtistsSingle { return ((byte[])(obj)); } } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] UserGetTopAlbumsEmpty { + get { + object obj = ResourceManager.GetObject("UserGetTopAlbumsEmpty", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] UserGetTopAlbumsError { + get { + object obj = ResourceManager.GetObject("UserGetTopAlbumsError", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] UserGetTopAlbumsMultiple { + get { + object obj = ResourceManager.GetObject("UserGetTopAlbumsMultiple", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] UserGetTopAlbumsSingle { + get { + object obj = ResourceManager.GetObject("UserGetTopAlbumsSingle", resourceCulture); + return ((byte[])(obj)); + } + } } } diff --git a/src/IF.Lastfm.Core.Tests/Resources/UserApiResponses.resx b/src/IF.Lastfm.Core.Tests/Resources/UserApiResponses.resx index dc81a2c..798b34c 100644 --- a/src/IF.Lastfm.Core.Tests/Resources/UserApiResponses.resx +++ b/src/IF.Lastfm.Core.Tests/Resources/UserApiResponses.resx @@ -139,4 +139,16 @@ UserApi\UserGetRecommendedArtistsSingle.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + userapi\usergettopalbumsempty.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + userapi\usergettopalbumserror.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + userapi\usergettopalbumsmultiple.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + userapi\usergettopalbumssingle.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + \ No newline at end of file diff --git a/src/IF.Lastfm.Core.Tests/TestHelper.cs b/src/IF.Lastfm.Core.Tests/TestHelper.cs index c22d5dd..8710e8b 100644 --- a/src/IF.Lastfm.Core.Tests/TestHelper.cs +++ b/src/IF.Lastfm.Core.Tests/TestHelper.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using IF.Lastfm.Core.Api.Helpers; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -17,7 +18,7 @@ private static JsonSerializer GetTestSerialiser() return new JsonSerializer { DateFormatString = "yyyy-MM-dd HH:mm:ss.fff", - NullValueHandling = NullValueHandling.Ignore, + NullValueHandling = NullValueHandling.Include, ContractResolver = new OrderedContractResolver() }; } @@ -49,7 +50,7 @@ public static string TestSerialise(this T poco) return ordered.ToString(); } - public static void AssertEqual(T one, T two) + public static void AssertSerialiseEqual(T one, T two) { var ones = one.TestSerialise(); var twos = two.TestSerialise(); @@ -67,7 +68,7 @@ public static string DifferencesTo(this IEnumerable expected, IEnumerable< public static string DifferencesTo(this string first, string second) { - const string lineDiffTemplate = "{0}A: {1}\n{0}B: {2}"; + const string lineDiffTemplate = "{0}E: {1}\n{0}A: {2}"; var start = Environment.NewLine + Environment.NewLine + "Differences:" + Environment.NewLine; var sb = new StringBuilder(start); @@ -89,7 +90,7 @@ public static string DifferencesTo(this string first, string second) { if (line1 != line2) { - var line = string.Format(lineDiffTemplate, count, line1, line2); + var line = String.Format(lineDiffTemplate, count, line1, line2); sb.AppendLine(line); } } @@ -119,7 +120,10 @@ public static bool LineRead(this StringReader reader, out string line) public static IEnumerable WrapEnumerable(this T t) { - return new[] {t}; + return new[] + { + t + }; } public static DateTime RoundToNearestSecond(this DateTime dt) @@ -141,7 +145,8 @@ public static void AssertValues( { const string messageFormat = "Page response:\n{0}\n\nExpected {1} to equal {2}"; var json = pageResponse.TestSerialise(); - Func testMessage = (property, count) => string.Format(messageFormat, json, property, count); + Func testMessage = + (property, count) => string.Format(messageFormat, json, property, count); Assert.IsTrue(pageResponse.Success == success, testMessage("success", success)); Assert.IsTrue(pageResponse.TotalItems == totalItems, testMessage("totalitems", totalItems)); diff --git a/src/IF.Lastfm.Core/Api/Commands/UserApi/GetTopAlbumsCommand.cs b/src/IF.Lastfm.Core/Api/Commands/UserApi/GetTopAlbumsCommand.cs index 238b4f3..cda6fde 100644 --- a/src/IF.Lastfm.Core/Api/Commands/UserApi/GetTopAlbumsCommand.cs +++ b/src/IF.Lastfm.Core/Api/Commands/UserApi/GetTopAlbumsCommand.cs @@ -36,7 +36,7 @@ public async override Task> HandleResponse(HttpResponseM LastFmApiError error; if (LastFm.IsResponseValid(json, out error) && response.IsSuccessStatusCode) { - var jtoken = JsonConvert.DeserializeObject(json); + var jtoken = JToken.Parse(json); var itemsToken = jtoken.SelectToken("topalbums").SelectToken("album"); var pageInfoToken = jtoken.SelectToken("@attr"); diff --git a/src/IF.Lastfm.Core/Objects/LastAlbum.cs b/src/IF.Lastfm.Core/Objects/LastAlbum.cs index 1030e74..56cdf6c 100644 --- a/src/IF.Lastfm.Core/Objects/LastAlbum.cs +++ b/src/IF.Lastfm.Core/Objects/LastAlbum.cs @@ -8,15 +8,18 @@ namespace IF.Lastfm.Core.Objects public class LastAlbum : ILastfmObject { public string Id { get; set; } + public string Name { get; set; } + public IEnumerable Tracks { get; set; } public string ArtistName { get; set; } - public DateTimeOffset ReleaseDateUtc { get; set; } + public DateTimeOffset? ReleaseDateUtc { get; set; } - public int ListenerCount { get; set; } - public int PlayCount { get; set; } + public int? ListenerCount { get; set; } + + public int? PlayCount { get; set; } public string Mbid { get; set; } @@ -26,7 +29,6 @@ public class LastAlbum : ILastfmObject public LastImageSet Images { get; set; } - internal static LastAlbum ParseJToken(JToken token) { var a = new LastAlbum(); @@ -48,9 +50,13 @@ internal static LastAlbum ParseJToken(JToken token) { var trackToken = tracksToken.SelectToken("track"); if (trackToken != null) - a.Tracks = trackToken.Type == JTokenType.Array - ? trackToken.Children().Select(t => LastTrack.ParseJToken(t, a.Name)) - : new List() { LastTrack.ParseJToken(trackToken, a.Name) }; + a.Tracks = trackToken.Type == JTokenType.Array + ? trackToken.Children().Select(t => LastTrack.ParseJToken(t, a.Name)) + : new List() {LastTrack.ParseJToken(trackToken, a.Name)}; + } + else + { + a.Tracks = Enumerable.Empty(); } var tagsToken = token.SelectToken("toptags"); @@ -65,8 +71,12 @@ internal static LastAlbum ParseJToken(JToken token) : new List { LastTag.ParseJToken(tagToken) }; } } + else + { + a.TopTags = Enumerable.Empty(); + } - a.ListenerCount = token.Value("listeners"); + a.ListenerCount = token.Value("listeners"); a.Mbid = token.Value("mbid"); a.Name = token.Value("name"); @@ -98,18 +108,10 @@ internal static LastAlbum ParseJToken(JToken token) internal static string GetNameFromJToken(JToken albumToken) { - var name = albumToken.Value("title"); - - if (string.IsNullOrEmpty(name)) - { - name = albumToken.Value("#text"); - } - - if (string.IsNullOrEmpty(name)) - { - name = albumToken.Value("name"); // Used in Library track lists - } - + var name = albumToken.Value("title") + ?? albumToken.Value("#text") + ?? albumToken.Value("name"); // Used in Library track lists + return name; } }