From 5de379162678abf30dd5e67acd2fdd6d15732b87 Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Sun, 9 Feb 2020 22:13:44 -0400 Subject: [PATCH] ensure datetime offset is encoded correctly for search APIs (#2091) * add failing integration test for timestamp * make the test pass by encoding the value * generalize this pattern for now * update tests to reflect change in date formatting --- .../Clients/SearchClientTests.cs | 15 +++- Octokit.Tests/Clients/SearchClientTests.cs | 86 +++++++++---------- .../Request/SearchRepositoriesRequest.cs | 15 ++-- 3 files changed, 67 insertions(+), 49 deletions(-) diff --git a/Octokit.Tests.Integration/Clients/SearchClientTests.cs b/Octokit.Tests.Integration/Clients/SearchClientTests.cs index 616b04ae06..9d1e90d184 100644 --- a/Octokit.Tests.Integration/Clients/SearchClientTests.cs +++ b/Octokit.Tests.Integration/Clients/SearchClientTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Octokit; @@ -23,6 +24,18 @@ public async Task SearchForCSharpRepositories() Assert.NotEmpty(repos.Items); } + [IntegrationTest] + public async Task SearchForCSharpRepositoriesUpdatedIn2020() + { + var request = new SearchRepositoriesRequest("csharp") + { + Updated = DateRange.GreaterThan(new DateTimeOffset(2020, 1, 1, 0, 0, 0, TimeSpan.Zero)) + }; + var repos = await _gitHubClient.Search.SearchRepo(request); + + Assert.NotEmpty(repos.Items); + } + [IntegrationTest] public async Task SearchForForkedRepositories() { diff --git a/Octokit.Tests/Clients/SearchClientTests.cs b/Octokit.Tests/Clients/SearchClientTests.cs index c2ebdabf5a..fc6eafe156 100644 --- a/Octokit.Tests/Clients/SearchClientTests.cs +++ b/Octokit.Tests/Clients/SearchClientTests.cs @@ -219,7 +219,7 @@ public void TestingTheCreatedQualifier_GreaterThan() client.SearchUsers(request); connection.Received().Get( Arg.Is(u => u.ToString() == "search/users"), - Arg.Is>(d => d["q"] == "github+created:>2014-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "github+created:>2014-01-01T00:00:00%2B00:00")); } [Fact] @@ -232,7 +232,7 @@ public void TestingTheCreatedQualifier_GreaterThanOrEqualTo() client.SearchUsers(request); connection.Received().Get( Arg.Is(u => u.ToString() == "search/users"), - Arg.Is>(d => d["q"] == "github+created:>=2014-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "github+created:>=2014-01-01T00:00:00%2B00:00")); } [Fact] @@ -245,7 +245,7 @@ public void TestingTheCreatedQualifier_LessThanOrEqualTo() client.SearchUsers(request); connection.Received().Get( Arg.Is(u => u.ToString() == "search/users"), - Arg.Is>(d => d["q"] == "github+created:<=2014-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "github+created:<=2014-01-01T00:00:00%2B00:00")); } [Fact] @@ -258,7 +258,7 @@ public void TestingTheCreatedQualifier_LessThan() client.SearchUsers(request); connection.Received().Get( Arg.Is(u => u.ToString() == "search/users"), - Arg.Is>(d => d["q"] == "github+created:<2014-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "github+created:<2014-01-01T00:00:00%2B00:00")); } [Fact] @@ -275,7 +275,7 @@ public void TestingTheCreatedQualifier_Between() connection.Received().Get( Arg.Is(u => u.ToString() == "search/users"), - Arg.Is>(d => d["q"] == "github+created:2014-01-01T00:00:00+00:00..2014-02-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "github+created:2014-01-01T00:00:00%2B00:00..2014-02-01T00:00:00%2B00:00")); } [Fact] @@ -532,7 +532,7 @@ public void TestingTheCreatedQualifier() request.Created = DateRange.GreaterThan(new DateTimeOffset(new DateTime(2011, 1, 1), TimeSpan.Zero)); client.SearchRepo(request); connection.Received().Get(Arg.Is(u => u.ToString() == "search/repositories"), - Arg.Is>(d => d["q"] == "github+created:>2011-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "github+created:>2011-01-01T00:00:00%2B00:00")); } [Fact] @@ -545,7 +545,7 @@ public void TestingTheCreatedQualifier_GreaterThanOrEquals() request.Created = DateRange.GreaterThanOrEquals(new DateTimeOffset(new DateTime(2011, 1, 1), TimeSpan.Zero)); client.SearchRepo(request); connection.Received().Get(Arg.Is(u => u.ToString() == "search/repositories"), - Arg.Is>(d => d["q"] == "github+created:>=2011-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "github+created:>=2011-01-01T00:00:00%2B00:00")); } [Fact] @@ -558,7 +558,7 @@ public void TestingTheCreatedQualifier_LessThan() request.Created = DateRange.LessThan(new DateTimeOffset(new DateTime(2011, 1, 1), TimeSpan.Zero)); client.SearchRepo(request); connection.Received().Get(Arg.Is(u => u.ToString() == "search/repositories"), - Arg.Is>(d => d["q"] == "github+created:<2011-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "github+created:<2011-01-01T00:00:00%2B00:00")); } @@ -572,7 +572,7 @@ public void TestingTheCreatedQualifier_LessThanOrEquals() request.Created = DateRange.LessThanOrEquals(new DateTimeOffset(new DateTime(2011, 1, 1), TimeSpan.Zero)); client.SearchRepo(request); connection.Received().Get(Arg.Is(u => u.ToString() == "search/repositories"), - Arg.Is>(d => d["q"] == "github+created:<=2011-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "github+created:<=2011-01-01T00:00:00%2B00:00")); } [Fact] @@ -588,7 +588,7 @@ public void TestingTheCreatedQualifier_Between() client.SearchRepo(request); connection.Received().Get(Arg.Is(u => u.ToString() == "search/repositories"), - Arg.Is>(d => d["q"] == "github+created:2011-01-01T00:00:00+00:00..2012-11-11T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "github+created:2011-01-01T00:00:00%2B00:00..2012-11-11T00:00:00%2B00:00")); } [Fact] @@ -601,7 +601,7 @@ public void TestingTheUpdatedQualifier() request.Updated = DateRange.GreaterThan(new DateTimeOffset(new DateTime(2013, 1, 1), TimeSpan.Zero)); client.SearchRepo(request); connection.Received().Get(Arg.Is(u => u.ToString() == "search/repositories"), - Arg.Is>(d => d["q"] == "github+pushed:>2013-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "github+pushed:>2013-01-01T00:00:00%2B00:00")); } [Fact] @@ -614,7 +614,7 @@ public void TestingTheUpdatedQualifier_GreaterThanOrEquals() request.Updated = DateRange.GreaterThanOrEquals(new DateTimeOffset(new DateTime(2013, 1, 1), TimeSpan.Zero)); client.SearchRepo(request); connection.Received().Get(Arg.Is(u => u.ToString() == "search/repositories"), - Arg.Is>(d => d["q"] == "github+pushed:>=2013-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "github+pushed:>=2013-01-01T00:00:00%2B00:00")); } [Fact] @@ -627,7 +627,7 @@ public void TestingTheUpdatedQualifier_LessThan() request.Updated = DateRange.LessThan(new DateTimeOffset(new DateTime(2013, 1, 1), TimeSpan.Zero)); client.SearchRepo(request); connection.Received().Get(Arg.Is(u => u.ToString() == "search/repositories"), - Arg.Is>(d => d["q"] == "github+pushed:<2013-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "github+pushed:<2013-01-01T00:00:00%2B00:00")); } [Fact] @@ -640,7 +640,7 @@ public void TestingTheUpdatedQualifier_LessThanOrEquals() request.Updated = DateRange.LessThanOrEquals(new DateTimeOffset(new DateTime(2013, 1, 1), TimeSpan.Zero)); client.SearchRepo(request); connection.Received().Get(Arg.Is(u => u.ToString() == "search/repositories"), - Arg.Is>(d => d["q"] == "github+pushed:<=2013-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "github+pushed:<=2013-01-01T00:00:00%2B00:00")); } [Fact] @@ -656,7 +656,7 @@ public void TestingTheUpdatedQualifier_Between() client.SearchRepo(request); connection.Received().Get(Arg.Is(u => u.ToString() == "search/repositories"), - Arg.Is>(d => d["q"] == "github+pushed:2012-04-30T00:00:00+00:00..2012-07-04T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "github+pushed:2012-04-30T00:00:00%2B00:00..2012-07-04T00:00:00%2B00:00")); } [Fact] @@ -1007,7 +1007,7 @@ public void TestingTheCreatedQualifier_GreaterThan() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+created:>2014-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "something+created:>2014-01-01T00:00:00%2B00:00")); } [Fact] @@ -1022,7 +1022,7 @@ public void TestingTheCreatedQualifier_GreaterThanOrEquals() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+created:>=2014-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "something+created:>=2014-01-01T00:00:00%2B00:00")); } [Fact] @@ -1037,7 +1037,7 @@ public void TestingTheCreatedQualifier_LessThan() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+created:<2014-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "something+created:<2014-01-01T00:00:00%2B00:00")); } [Fact] @@ -1052,7 +1052,7 @@ public void TestingTheCreatedQualifier_LessThanOrEquals() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+created:<=2014-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "something+created:<=2014-01-01T00:00:00%2B00:00")); } [Fact] @@ -1069,7 +1069,7 @@ public void TestingTheCreatedQualifier_Between() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+created:2014-01-01T00:00:00+00:00..2014-02-02T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "something+created:2014-01-01T00:00:00%2B00:00..2014-02-02T00:00:00%2B00:00")); } [Fact] @@ -1084,7 +1084,7 @@ public void TestingTheMergedQualifier_GreaterThan() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+merged:>2014-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "something+merged:>2014-01-01T00:00:00%2B00:00")); } [Fact] @@ -1099,7 +1099,7 @@ public void TestingTheMergedQualifier_GreaterThanOrEquals() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+merged:>=2014-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "something+merged:>=2014-01-01T00:00:00%2B00:00")); } [Fact] @@ -1114,7 +1114,7 @@ public void TestingTheMergedQualifier_LessThan() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+merged:<2014-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "something+merged:<2014-01-01T00:00:00%2B00:00")); } [Fact] @@ -1129,7 +1129,7 @@ public void TestingTheMergedQualifier_LessThanOrEquals() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+merged:<=2014-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "something+merged:<=2014-01-01T00:00:00%2B00:00")); } [Fact] @@ -1146,7 +1146,7 @@ public void TestingTheMergedQualifier_Between() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+merged:2014-01-01T00:00:00+00:00..2014-02-02T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "something+merged:2014-01-01T00:00:00%2B00:00..2014-02-02T00:00:00%2B00:00")); } [Fact] @@ -1161,7 +1161,7 @@ public void TestingTheUpdatedQualifier_GreaterThan() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+updated:>2014-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "something+updated:>2014-01-01T00:00:00%2B00:00")); } [Fact] @@ -1176,7 +1176,7 @@ public void TestingTheUpdatedQualifier_GreaterThanOrEquals() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+updated:>=2014-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "something+updated:>=2014-01-01T00:00:00%2B00:00")); } [Fact] @@ -1191,7 +1191,7 @@ public void TestingTheUpdatedQualifier_LessThan() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+updated:<2014-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "something+updated:<2014-01-01T00:00:00%2B00:00")); } [Fact] @@ -1206,7 +1206,7 @@ public void TestingTheUpdatedQualifier_LessThanOrEquals() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+updated:<=2014-01-01T00:00:00+00:00")); + Arg.Is>(d => d["q"] == "something+updated:<=2014-01-01T00:00:00%2B00:00")); } [Fact] @@ -1221,7 +1221,7 @@ public void TestingTheCreatedQualifier_GreaterThanDateTime() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+created:>2014-01-01T02:04:06+10:00")); + Arg.Is>(d => d["q"] == "something+created:>2014-01-01T02:04:06%2B10:00")); } [Fact] @@ -1236,7 +1236,7 @@ public void TestingTheCreatedQualifier_GreaterThanOrEqualsDateTime() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+created:>=2014-01-01T02:04:06+10:00")); + Arg.Is>(d => d["q"] == "something+created:>=2014-01-01T02:04:06%2B10:00")); } [Fact] @@ -1250,7 +1250,7 @@ public void TestingTheCreatedQualifier_LessThanDateTime() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+created:<2014-01-01T02:04:06+10:00")); + Arg.Is>(d => d["q"] == "something+created:<2014-01-01T02:04:06%2B10:00")); } [Fact] @@ -1265,7 +1265,7 @@ public void TestingTheCreatedQualifier_LessThanOrEqualsDateTime() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+created:<=2014-01-01T02:04:06+10:00")); + Arg.Is>(d => d["q"] == "something+created:<=2014-01-01T02:04:06%2B10:00")); } [Fact] @@ -1282,7 +1282,7 @@ public void TestingTheCreatedQualifier_BetweenDateTime() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+created:2014-01-01T02:04:06+10:00..2014-02-02T02:04:06+10:00")); + Arg.Is>(d => d["q"] == "something+created:2014-01-01T02:04:06%2B10:00..2014-02-02T02:04:06%2B10:00")); } [Fact] @@ -1297,7 +1297,7 @@ public void TestingTheMergedQualifier_GreaterThanDateTime() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+merged:>2014-01-01T02:04:06+10:00")); + Arg.Is>(d => d["q"] == "something+merged:>2014-01-01T02:04:06%2B10:00")); } [Fact] @@ -1312,7 +1312,7 @@ public void TestingTheMergedQualifier_GreaterThanOrEqualsDateTime() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+merged:>=2014-01-01T02:04:06+10:00")); + Arg.Is>(d => d["q"] == "something+merged:>=2014-01-01T02:04:06%2B10:00")); } [Fact] @@ -1327,7 +1327,7 @@ public void TestingTheMergedQualifier_LessThanDateTime() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+merged:<2014-01-01T02:04:06+10:00")); + Arg.Is>(d => d["q"] == "something+merged:<2014-01-01T02:04:06%2B10:00")); } [Fact] @@ -1342,7 +1342,7 @@ public void TestingTheMergedQualifier_LessThanOrEqualsDateTime() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+merged:<=2014-01-01T02:04:06+10:00")); + Arg.Is>(d => d["q"] == "something+merged:<=2014-01-01T02:04:06%2B10:00")); } [Fact] @@ -1359,7 +1359,7 @@ public void TestingTheMergedQualifier_BetweenDateTime() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+merged:2014-01-01T02:04:06+10:00..2014-02-02T02:04:06+10:00")); + Arg.Is>(d => d["q"] == "something+merged:2014-01-01T02:04:06%2B10:00..2014-02-02T02:04:06%2B10:00")); } [Fact] @@ -1374,7 +1374,7 @@ public void TestingTheUpdatedQualifier_GreaterThanDateTime() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+updated:>2014-01-01T02:04:06+10:00")); + Arg.Is>(d => d["q"] == "something+updated:>2014-01-01T02:04:06%2B10:00")); } [Fact] @@ -1389,7 +1389,7 @@ public void TestingTheUpdatedQualifier_GreaterThanOrEqualsDateTime() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+updated:>=2014-01-01T02:04:06+10:00")); + Arg.Is>(d => d["q"] == "something+updated:>=2014-01-01T02:04:06%2B10:00")); } [Fact] @@ -1404,7 +1404,7 @@ public void TestingTheUpdatedQualifier_LessThanDateTime() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+updated:<2014-01-01T02:04:06+10:00")); + Arg.Is>(d => d["q"] == "something+updated:<2014-01-01T02:04:06%2B10:00")); } [Fact] @@ -1419,7 +1419,7 @@ public void TestingTheUpdatedQualifier_LessThanOrEqualsDateTime() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+updated:<=2014-01-01T02:04:06+10:00")); + Arg.Is>(d => d["q"] == "something+updated:<=2014-01-01T02:04:06%2B10:00")); } [Fact] diff --git a/Octokit/Models/Request/SearchRepositoriesRequest.cs b/Octokit/Models/Request/SearchRepositoriesRequest.cs index 48d0964535..f674fa9168 100644 --- a/Octokit/Models/Request/SearchRepositoriesRequest.cs +++ b/Octokit/Models/Request/SearchRepositoriesRequest.cs @@ -341,7 +341,7 @@ public DateRange(DateTime from, DateTime to) /// public DateRange(DateTimeOffset from, DateTimeOffset to) { - query = $"{from.ToString(DateTimePattern, CultureInfo.InvariantCulture)}..{to.ToString(DateTimePattern, CultureInfo.InvariantCulture)}"; + query = EncodeOffset($"{from.ToString(DateTimePattern, CultureInfo.InvariantCulture)}..{to.ToString(DateTimePattern, CultureInfo.InvariantCulture)}"); } /// @@ -353,20 +353,25 @@ public DateRange(DateTimeOffset dateTime, SearchQualifierOperator op) switch (op) { case SearchQualifierOperator.GreaterThan: - query = dateTime.ToString($">{DateTimePattern}", CultureInfo.InvariantCulture); + query = EncodeOffset(dateTime.ToString($">{DateTimePattern}", CultureInfo.InvariantCulture)); break; case SearchQualifierOperator.LessThan: - query = dateTime.ToString($"<{DateTimePattern}", CultureInfo.InvariantCulture); + query = EncodeOffset(dateTime.ToString($"<{DateTimePattern}", CultureInfo.InvariantCulture)); break; case SearchQualifierOperator.LessThanOrEqualTo: - query = dateTime.ToString($"<={DateTimePattern}", CultureInfo.InvariantCulture); + query = EncodeOffset(dateTime.ToString($"<={DateTimePattern}", CultureInfo.InvariantCulture)); break; case SearchQualifierOperator.GreaterThanOrEqualTo: - query = dateTime.ToString($">={DateTimePattern}", CultureInfo.InvariantCulture); + query = EncodeOffset(dateTime.ToString($">={DateTimePattern}", CultureInfo.InvariantCulture)); break; } } + private string EncodeOffset(string dateTimeOffset) + { + return dateTimeOffset.Replace("+", "%2B"); + } + internal string DebuggerDisplay { get { return string.Format(CultureInfo.InvariantCulture, "Query: {0}", query); }