-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
422 additions
and
13 deletions.
There are no files selected for viewing
144 changes: 144 additions & 0 deletions
144
...ackup/GithubBackup.Cli.Tests/Commands/Github/Auth/Pipeline/DeviceFlowAuthPipelineTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
using FluentAssertions; | ||
using GithubBackup.Cli.Commands.Github.Auth; | ||
using GithubBackup.Cli.Commands.Github.Auth.Pipeline; | ||
using GithubBackup.Cli.Commands.Github.Login; | ||
using GithubBackup.Cli.Commands.Global; | ||
using GithubBackup.Core.Github.Authentication; | ||
using GithubBackup.Core.Github.Credentials; | ||
using GithubBackup.Core.Github.Users; | ||
using GithubBackup.TestUtils.Logging; | ||
using Microsoft.Extensions.Logging; | ||
using NSubstitute; | ||
using NSubstitute.ExceptionExtensions; | ||
using Spectre.Console.Testing; | ||
|
||
namespace GithubBackup.Cli.Tests.Commands.Github.Auth.Pipeline; | ||
|
||
public class DeviceFlowAuthPipelineTests | ||
{ | ||
private readonly DeviceFlowAuthPipeline _sut; | ||
|
||
private readonly ILoginPipeline _next; | ||
|
||
private readonly ILogger<DeviceFlowAuthPipeline> _logger; | ||
private readonly IGithubTokenStore _githubTokenStore; | ||
private readonly IPersistentCredentialStore _persistentCredentialStore; | ||
private readonly IUserService _userService; | ||
private readonly IAuthenticationService _authenticationService; | ||
private readonly TestConsole _ansiConsole; | ||
|
||
public DeviceFlowAuthPipelineTests() | ||
{ | ||
_logger = Substitute.For<ILogger<DeviceFlowAuthPipeline>>(); | ||
_githubTokenStore = Substitute.For<IGithubTokenStore>(); | ||
_persistentCredentialStore = Substitute.For<IPersistentCredentialStore>(); | ||
_userService = Substitute.For<IUserService>(); | ||
_authenticationService = Substitute.For<IAuthenticationService>(); | ||
_ansiConsole = new TestConsole(); | ||
|
||
_sut = new DeviceFlowAuthPipeline( | ||
_logger, | ||
_githubTokenStore, | ||
_persistentCredentialStore, | ||
_userService, | ||
_authenticationService, | ||
_ansiConsole | ||
); | ||
|
||
_next = Substitute.For<ILoginPipeline>(); | ||
_sut.Next = _next; | ||
} | ||
|
||
[Fact] | ||
public async Task LoginAsync_NotResponsible_CallNext() | ||
{ | ||
var ct = CancellationToken.None; | ||
var globalArgs = new GlobalArgs(LogLevel.Debug, false, new FileInfo("Test")); | ||
var loginArgs = new LoginArgs(null, false); | ||
|
||
await _sut.LoginAsync(globalArgs, loginArgs, true, ct); | ||
|
||
await _next.Received(1).LoginAsync(globalArgs, loginArgs, true, ct); | ||
|
||
_logger.VerifyLogs(); | ||
} | ||
|
||
[Fact] | ||
public async Task LoginAsync_ValidAndPersist_PersistTokenAndReturnUser() | ||
{ | ||
const string token = "token"; | ||
var ct = CancellationToken.None; | ||
var globalArgs = new GlobalArgs(LogLevel.Debug, false, new FileInfo("Test")); | ||
var loginArgs = new LoginArgs(null, true); | ||
var user = new User("test", "test"); | ||
|
||
var deviceAndUserCodes = new DeviceAndUserCodes("device", "user", "url", 1, 1); | ||
_authenticationService.RequestDeviceAndUserCodesAsync(ct).Returns(deviceAndUserCodes); | ||
var accessToken = new AccessToken(token, "type", "scope"); | ||
_authenticationService.PollForAccessTokenAsync(deviceAndUserCodes.DeviceCode, deviceAndUserCodes.Interval, ct) | ||
.Returns(accessToken); | ||
_userService.WhoAmIAsync(ct).Returns(user); | ||
|
||
await _sut.LoginAsync(globalArgs, loginArgs, true, ct); | ||
|
||
await _githubTokenStore.Received(1).SetAsync(token); | ||
await _persistentCredentialStore.Received(1).StoreTokenAsync(token, ct); | ||
await _next.Received(0).LoginAsync(Arg.Any<GlobalArgs>(), Arg.Any<LoginArgs>(), Arg.Any<bool>(), Arg.Any<CancellationToken>()); | ||
|
||
_logger.VerifyLogs(new LogEntry(LogLevel.Information, "Using device flow authentication")); | ||
} | ||
|
||
[Fact] | ||
public async Task LoginAsync_ValidAndNotPersist_DoNotPersistTokenAndReturnUser() | ||
{ | ||
const string token = "token"; | ||
var ct = CancellationToken.None; | ||
var globalArgs = new GlobalArgs(LogLevel.Debug, false, new FileInfo("Test")); | ||
var loginArgs = new LoginArgs(null, true); | ||
var user = new User("test", "test"); | ||
|
||
var deviceAndUserCodes = new DeviceAndUserCodes("device", "user", "url", 1, 1); | ||
_authenticationService.RequestDeviceAndUserCodesAsync(ct).Returns(deviceAndUserCodes); | ||
var accessToken = new AccessToken(token, "type", "scope"); | ||
_authenticationService.PollForAccessTokenAsync(deviceAndUserCodes.DeviceCode, deviceAndUserCodes.Interval, ct) | ||
.Returns(accessToken); | ||
_userService.WhoAmIAsync(ct).Returns(user); | ||
|
||
await _sut.LoginAsync(globalArgs, loginArgs, false, ct); | ||
|
||
await _githubTokenStore.Received(1).SetAsync(token); | ||
await _persistentCredentialStore.Received(0).StoreTokenAsync(Arg.Any<string>(), Arg.Any<CancellationToken>()); | ||
await _next.Received(0).LoginAsync(Arg.Any<GlobalArgs>(), Arg.Any<LoginArgs>(), Arg.Any<bool>(), Arg.Any<CancellationToken>()); | ||
|
||
_logger.VerifyLogs(new LogEntry(LogLevel.Information, "Using device flow authentication")); | ||
} | ||
|
||
|
||
[Fact] | ||
public async Task LoginAsync_Invalid_ThrowException() | ||
{ | ||
const string token = "token"; | ||
var ct = CancellationToken.None; | ||
var globalArgs = new GlobalArgs(LogLevel.Debug, false, new FileInfo("Test")); | ||
var loginArgs = new LoginArgs(null, true); | ||
|
||
var deviceAndUserCodes = new DeviceAndUserCodes("device", "user", "url", 1, 1); | ||
_authenticationService.RequestDeviceAndUserCodesAsync(ct).Returns(deviceAndUserCodes); | ||
var accessToken = new AccessToken(token, "type", "scope"); | ||
_authenticationService.PollForAccessTokenAsync(deviceAndUserCodes.DeviceCode, deviceAndUserCodes.Interval, ct) | ||
.Returns(accessToken); | ||
_userService.WhoAmIAsync(ct).ThrowsAsync<Exception>(); | ||
|
||
var action = () => _sut.LoginAsync(globalArgs, loginArgs, true, ct); | ||
|
||
await action.Should().ThrowAsync<Exception>(); | ||
|
||
await _githubTokenStore.Received(0).SetAsync(token); | ||
await _persistentCredentialStore.Received(0).StoreTokenAsync(Arg.Any<string>(), Arg.Any<CancellationToken>()); | ||
|
||
_logger.VerifyLogs( | ||
new LogEntry(LogLevel.Information, "Using device flow authentication"), | ||
new LogEntry(LogLevel.Error, """Token \(null\) is invalid""") | ||
); | ||
} | ||
} |
125 changes: 125 additions & 0 deletions
125
GithubBackup/GithubBackup.Cli.Tests/Commands/Github/Auth/Pipeline/PersistedPipelineTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
using FluentAssertions; | ||
using GithubBackup.Cli.Commands.Github.Auth; | ||
using GithubBackup.Cli.Commands.Github.Auth.Pipeline; | ||
using GithubBackup.Cli.Commands.Github.Login; | ||
using GithubBackup.Cli.Commands.Global; | ||
using GithubBackup.Core.Github.Credentials; | ||
using GithubBackup.Core.Github.Users; | ||
using GithubBackup.TestUtils.Logging; | ||
using Microsoft.Extensions.Logging; | ||
using NSubstitute; | ||
using NSubstitute.ExceptionExtensions; | ||
|
||
namespace GithubBackup.Cli.Tests.Commands.Github.Auth.Pipeline; | ||
|
||
public class PersistedPipelineTests | ||
{ | ||
private readonly PersistedPipeline _sut; | ||
|
||
private readonly ILogger<PersistedPipeline> _logger; | ||
private readonly IGithubTokenStore _githubTokenStore; | ||
private readonly IPersistentCredentialStore _persistentCredentialStore; | ||
private readonly IUserService _userService; | ||
|
||
private readonly ILoginPipeline _next; | ||
|
||
public PersistedPipelineTests() | ||
{ | ||
_logger = Substitute.For<ILogger<PersistedPipeline>>(); | ||
_githubTokenStore = Substitute.For<IGithubTokenStore>(); | ||
_persistentCredentialStore = Substitute.For<IPersistentCredentialStore>(); | ||
_userService = Substitute.For<IUserService>(); | ||
|
||
_sut = new PersistedPipeline( | ||
_logger, | ||
_persistentCredentialStore, | ||
_githubTokenStore, | ||
_userService | ||
); | ||
|
||
_next = Substitute.For<ILoginPipeline>(); | ||
_sut.Next = _next; | ||
} | ||
|
||
[Fact] | ||
public async Task LoginAsync_NotResponsible_CallNext() | ||
{ | ||
var ct = CancellationToken.None; | ||
var globalArgs = new GlobalArgs(LogLevel.Debug, false, new FileInfo("Test")); | ||
var loginArgs = new LoginArgs("token", true); | ||
|
||
await _sut.LoginAsync(globalArgs, loginArgs, true, ct); | ||
|
||
await _next.Received(1).LoginAsync(globalArgs, loginArgs, true, ct); | ||
|
||
_logger.VerifyLogs(); | ||
} | ||
|
||
[Fact] | ||
public async Task LoginAsync_NoToken_CallNext() | ||
{ | ||
const string? token = null; | ||
var ct = CancellationToken.None; | ||
var globalArgs = new GlobalArgs(LogLevel.Debug, false, new FileInfo("Test")); | ||
var loginArgs = new LoginArgs(null, false); | ||
|
||
_persistentCredentialStore.LoadTokenAsync(ct).Returns(token); | ||
|
||
await _sut.LoginAsync(globalArgs, loginArgs, true, ct); | ||
|
||
await _githubTokenStore.Received(0).SetAsync(Arg.Any<string>()); | ||
await _persistentCredentialStore.Received(0).StoreTokenAsync(Arg.Any<string>(), Arg.Any<CancellationToken>()); | ||
|
||
await _next.Received(1).LoginAsync(globalArgs, loginArgs, true, ct); | ||
|
||
_logger.VerifyLogs( | ||
new LogEntry(LogLevel.Information, "Using token from persistent store"), | ||
new LogEntry(LogLevel.Information, "Persistent token not found") | ||
); | ||
} | ||
|
||
[Fact] | ||
public async Task LoginAsync_TokenNotValid_CallNext() | ||
{ | ||
const string token = "token"; | ||
var ct = CancellationToken.None; | ||
var globalArgs = new GlobalArgs(LogLevel.Debug, false, new FileInfo("Test")); | ||
var loginArgs = new LoginArgs(null, false); | ||
|
||
_persistentCredentialStore.LoadTokenAsync(ct).Returns(token); | ||
_userService.WhoAmIAsync(ct).ThrowsAsync(new Exception("test")); | ||
|
||
await _sut.LoginAsync(globalArgs, loginArgs, true, ct); | ||
|
||
await _githubTokenStore.Received(0).SetAsync(Arg.Any<string>()); | ||
await _persistentCredentialStore.Received(0).StoreTokenAsync(Arg.Any<string>(), Arg.Any<CancellationToken>()); | ||
|
||
await _next.Received(1).LoginAsync(globalArgs, loginArgs, true, ct); | ||
|
||
_logger.VerifyLogs( | ||
new LogEntry(LogLevel.Information, "Using token from persistent store"), | ||
new LogEntry(LogLevel.Information, "Persistent token is invalid: test") | ||
); | ||
} | ||
|
||
[Fact] | ||
public async Task LoginAsync_ValidToken_ReturnUser() | ||
{ | ||
const string token = "token"; | ||
var ct = CancellationToken.None; | ||
var globalArgs = new GlobalArgs(LogLevel.Debug, false, new FileInfo("Test")); | ||
var loginArgs = new LoginArgs(null, false); | ||
var user = new User("test", "test"); | ||
|
||
_userService.WhoAmIAsync(ct).Returns(user); | ||
_persistentCredentialStore.LoadTokenAsync(ct).Returns(token); | ||
|
||
await _sut.LoginAsync(globalArgs, loginArgs, true, ct); | ||
|
||
await _githubTokenStore.Received(1).SetAsync(token); | ||
await _persistentCredentialStore.Received(0).StoreTokenAsync(Arg.Any<string>(), Arg.Any<CancellationToken>()); | ||
await _next.Received(0).LoginAsync(Arg.Any<GlobalArgs>(), Arg.Any<LoginArgs>(), Arg.Any<bool>(), Arg.Any<CancellationToken>()); | ||
|
||
_logger.VerifyLogs(new LogEntry(LogLevel.Information, "Using token from persistent store")); | ||
} | ||
} |
Oops, something went wrong.