diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d6c2b968bb..c1f87134f9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -68,6 +68,7 @@ jobs: shell: bash run: | vstest.console /TestAdapterPath:test /Settings:test/test.runsettings \ + test/GitHub.Api.UnitTests/bin/${{ env.config }}/net46/GitHub.Api.UnitTests.dll \ test/GitHub.App.UnitTests/bin/${{ env.config }}/net46/GitHub.App.UnitTests.dll \ test/GitHub.Exports.Reactive.UnitTests/bin/${{ env.config }}/net46/GitHub.Exports.Reactive.UnitTests.dll \ test/GitHub.Exports.UnitTests/bin/${{ env.config }}/net46/GitHub.Exports.UnitTests.dll \ diff --git a/src/GitHub.Api/LoginManager.cs b/src/GitHub.Api/LoginManager.cs index 228daa65ef..bdfe84c2c9 100644 --- a/src/GitHub.Api/LoginManager.cs +++ b/src/GitHub.Api/LoginManager.cs @@ -346,9 +346,14 @@ async Task GetUserAndCheckScopes(IGitHubClient client) var response = await client.Connection.Get( UserEndpoint, null, null).ConfigureAwait(false); - if (response.HttpResponse.Headers.ContainsKey(ScopesHeader)) + var scopes = response.HttpResponse.Headers + .Where(h => string.Equals(h.Key, ScopesHeader, StringComparison.OrdinalIgnoreCase)) + .Select(h => h.Value) + .FirstOrDefault(); + + if (scopes != null) { - var returnedScopes = new ScopesCollection(response.HttpResponse.Headers[ScopesHeader] + var returnedScopes = new ScopesCollection(scopes .Split(',') .Select(x => x.Trim()) .ToArray()); diff --git a/src/GitHub.Exports/ExceptionExtensions.cs b/src/GitHub.Exports/ExceptionExtensions.cs index a9f54c6eff..4afbb3011a 100644 --- a/src/GitHub.Exports/ExceptionExtensions.cs +++ b/src/GitHub.Exports/ExceptionExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using Octokit; namespace GitHub.Extensions @@ -9,8 +10,7 @@ public static class ApiExceptionExtensions public static bool IsGitHubApiException(this Exception ex) { var apiex = ex as ApiException; - return apiex?.HttpResponse?.Headers.ContainsKey(GithubHeader) ?? false; + return apiex?.HttpResponse?.Headers.Keys.Contains(GithubHeader, StringComparer.OrdinalIgnoreCase) ?? false; } } - } diff --git a/submodules/octokit.net b/submodules/octokit.net index a51818a7e0..a25a399ab7 160000 --- a/submodules/octokit.net +++ b/submodules/octokit.net @@ -1 +1 @@ -Subproject commit a51818a7e0879ae21aacbbfd48e62dcb172d68b4 +Subproject commit a25a399ab7994ff1cc230dac0cc07e31f3fafd1f diff --git a/test/GitHub.Api.UnitTests/LoginManagerTests.cs b/test/GitHub.Api.UnitTests/LoginManagerTests.cs index 6e7ca4c31c..2a2fc732be 100644 --- a/test/GitHub.Api.UnitTests/LoginManagerTests.cs +++ b/test/GitHub.Api.UnitTests/LoginManagerTests.cs @@ -298,13 +298,30 @@ public void InvalidResponseScopesCauseException() Assert.ThrowsAsync(() => target.Login(host, client, "foo", "bar")); } - IGitHubClient CreateClient(User user = null, string[] responseScopes = null) + [TestCase("X-OAuth-Scopes")] + [TestCase("x-oauth-scopes")] + public void ValidResponseScopesDoesNotThrow(string scopesHeader) + { + var client = CreateClient(responseScopes: scopes, scopesHeader: scopesHeader); + client.Authorization.GetOrCreateApplicationAuthentication("id", "secret", Arg.Any()) + .Returns(CreateApplicationAuthorization("123abc")); + + var keychain = Substitute.For(); + var tfa = new Lazy(() => Substitute.For()); + var oauthListener = Substitute.For(); + + var target = new LoginManager(keychain, tfa, oauthListener, "id", "secret", scopes, scopes); + + Assert.DoesNotThrowAsync(() => target.Login(host, client, "foo", "bar")); + } + + IGitHubClient CreateClient(User user = null, string[] responseScopes = null, string scopesHeader = "X-OAuth-Scopes") { var result = Substitute.For(); var userResponse = Substitute.For>(); userResponse.HttpResponse.Headers.Returns(new Dictionary { - { "X-OAuth-Scopes", string.Join(",", responseScopes ?? scopes) } + { scopesHeader, string.Join(",", responseScopes ?? scopes) } }); userResponse.Body.Returns(user ?? new User()); result.Connection.Get(new Uri("user", UriKind.Relative), null, null).Returns(userResponse); diff --git a/test/GitHub.Exports.UnitTests/ApiExceptionExtensionsTests.cs b/test/GitHub.Exports.UnitTests/ApiExceptionExtensionsTests.cs new file mode 100644 index 0000000000..90133a3673 --- /dev/null +++ b/test/GitHub.Exports.UnitTests/ApiExceptionExtensionsTests.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using Octokit; +using NSubstitute; +using NUnit.Framework; +using GitHub.Extensions; + +public class ApiExceptionExtensionsTests +{ + public class TheIsGitHubApiExceptionMethod + { + [TestCase("Not-GitHub-Request-Id", false)] + [TestCase("X-GitHub-Request-Id", true)] + [TestCase("x-github-request-id", true)] + public void NoGitHubRequestId(string key, bool expect) + { + var ex = CreateApiException(new Dictionary { { key, "ANYTHING" } }); + + var result = ApiExceptionExtensions.IsGitHubApiException(ex); + + Assert.That(result, Is.EqualTo(expect)); + } + + [Test] + public void NoResponse() + { + var ex = new ApiException(); + + var result = ApiExceptionExtensions.IsGitHubApiException(ex); + + Assert.That(result, Is.EqualTo(false)); + } + + static ApiException CreateApiException(Dictionary headers) + { + var response = Substitute.For(); + response.Headers.Returns(headers.ToImmutableDictionary()); + var ex = new ApiException(response); + return ex; + } + } +}