| Index: components/signin/core/browser/access_token_fetcher_unittest.cc
|
| diff --git a/components/signin/core/browser/access_token_fetcher_unittest.cc b/components/signin/core/browser/access_token_fetcher_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..5bb9c06c9629316b796df40d4f16ff6b0d701957
|
| --- /dev/null
|
| +++ b/components/signin/core/browser/access_token_fetcher_unittest.cc
|
| @@ -0,0 +1,317 @@
|
| +// Copyright 2017 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "components/signin/core/browser/access_token_fetcher.h"
|
| +
|
| +#include <memory>
|
| +#include <utility>
|
| +
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/test/mock_callback.h"
|
| +#include "components/prefs/testing_pref_service.h"
|
| +#include "components/signin/core/browser/account_tracker_service.h"
|
| +#include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
|
| +#include "components/signin/core/browser/fake_signin_manager.h"
|
| +#include "components/signin/core/browser/test_signin_client.h"
|
| +#include "components/sync_preferences/testing_pref_service_syncable.h"
|
| +#include "testing/gmock/include/gmock/gmock.h"
|
| +#include "testing/gmock_mutant.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +using base::MockCallback;
|
| +using sync_preferences::TestingPrefServiceSyncable;
|
| +using testing::_;
|
| +using testing::CallbackToFunctor;
|
| +using testing::InvokeWithoutArgs;
|
| +using testing::StrictMock;
|
| +
|
| +#if defined(OS_CHROMEOS)
|
| +// ChromeOS doesn't have SigninManager.
|
| +using SigninManagerForTest = FakeSigninManagerBase;
|
| +#else
|
| +using SigninManagerForTest = FakeSigninManager;
|
| +#endif // OS_CHROMEOS
|
| +
|
| +class AccessTokenFetcherTest : public testing::Test {
|
| + public:
|
| + using TestTokenCallback =
|
| + StrictMock<MockCallback<AccessTokenFetcher::TokenCallback>>;
|
| +
|
| + AccessTokenFetcherTest()
|
| + : signin_client_(&pref_service_) {
|
| + AccountTrackerService::RegisterPrefs(pref_service_.registry());
|
| +#if defined(OS_CHROMEOS)
|
| + SigninManagerBase::RegisterProfilePrefs(pref_service_.registry());
|
| + SigninManagerBase::RegisterPrefs(pref_service_.registry());
|
| +#else
|
| + SigninManager::RegisterProfilePrefs(pref_service_.registry());
|
| + SigninManager::RegisterPrefs(pref_service_.registry());
|
| +#endif // OS_CHROMEOS
|
| +
|
| + account_tracker_ = base::MakeUnique<AccountTrackerService>();
|
| + account_tracker_->Initialize(&signin_client_);
|
| +
|
| +#if defined(OS_CHROMEOS)
|
| + signin_manager_ = base::MakeUnique<FakeSigninManagerBase>(
|
| + &signin_client_, account_tracker_.get());
|
| +#else
|
| + signin_manager_ = base::MakeUnique<FakeSigninManager>(
|
| + &signin_client_, &token_service_, account_tracker_.get(),
|
| + /*cookie_manager_service=*/nullptr);
|
| +#endif // OS_CHROMEOS
|
| + }
|
| +
|
| + ~AccessTokenFetcherTest() override {}
|
| +
|
| + std::unique_ptr<AccessTokenFetcher> CreateFetcher(
|
| + AccessTokenFetcher::TokenCallback callback) {
|
| + std::set<std::string> scopes{"scope"};
|
| + return base::MakeUnique<AccessTokenFetcher>(
|
| + "test_consumer", signin_manager_.get(), &token_service_, scopes,
|
| + std::move(callback));
|
| + }
|
| +
|
| + FakeProfileOAuth2TokenService* token_service() { return &token_service_; }
|
| + SigninManagerForTest* signin_manager() { return signin_manager_.get(); }
|
| +
|
| + void SignIn(const std::string& account) {
|
| +#if defined(OS_CHROMEOS)
|
| + signin_manager_->SignIn(account);
|
| +#else
|
| + signin_manager_->SignIn(account, "user", "pass");
|
| +#endif // OS_CHROMEOS
|
| + }
|
| +
|
| + private:
|
| + TestingPrefServiceSyncable pref_service_;
|
| + TestSigninClient signin_client_;
|
| + FakeProfileOAuth2TokenService token_service_;
|
| +
|
| + std::unique_ptr<AccountTrackerService> account_tracker_;
|
| + std::unique_ptr<SigninManagerForTest> signin_manager_;
|
| +};
|
| +
|
| +TEST_F(AccessTokenFetcherTest, ShouldReturnAccessToken) {
|
| + TestTokenCallback callback;
|
| +
|
| + SignIn("account");
|
| + token_service()->GetDelegate()->UpdateCredentials("account", "refresh token");
|
| +
|
| + // Signed in and refresh token already exists, so this should result in a
|
| + // request for an access token.
|
| + auto fetcher = CreateFetcher(callback.Get());
|
| +
|
| + // Once the access token request is fulfilled, we should get called back with
|
| + // the access token.
|
| + EXPECT_CALL(callback, Run("access token"));
|
| + token_service()->IssueAllTokensForAccount(
|
| + "account", "access token",
|
| + base::Time::Now() + base::TimeDelta::FromHours(1));
|
| +}
|
| +
|
| +TEST_F(AccessTokenFetcherTest, ShouldNotReplyIfDestroyed) {
|
| + TestTokenCallback callback;
|
| +
|
| + SignIn("account");
|
| + token_service()->GetDelegate()->UpdateCredentials("account", "refresh token");
|
| +
|
| + // Signed in and refresh token already exists, so this should result in a
|
| + // request for an access token.
|
| + auto fetcher = CreateFetcher(callback.Get());
|
| +
|
| + // Destroy the fetcher before the access token request is fulfilled.
|
| + fetcher.reset();
|
| +
|
| + // Now fulfilling the access token request should have no effect.
|
| + token_service()->IssueAllTokensForAccount(
|
| + "account", "access token",
|
| + base::Time::Now() + base::TimeDelta::FromHours(1));
|
| +}
|
| +
|
| +TEST_F(AccessTokenFetcherTest, ShouldNotReturnWhenSignedOut) {
|
| + TestTokenCallback callback;
|
| +
|
| + // Signed out -> the fetcher should wait for a sign-in which never happens
|
| + // in this test, so we shouldn't get called back.
|
| + auto fetcher = CreateFetcher(callback.Get());
|
| +}
|
| +
|
| +TEST_F(AccessTokenFetcherTest, ShouldWaitForSignIn) {
|
| + TestTokenCallback callback;
|
| +
|
| + // Not signed in, so this should wait for a sign-in to complete.
|
| + auto fetcher = CreateFetcher(callback.Get());
|
| +
|
| + SignIn("account");
|
| + token_service()->GetDelegate()->UpdateCredentials("account", "refresh token");
|
| +
|
| + // Once the access token request is fulfilled, we should get called back with
|
| + // the access token.
|
| + EXPECT_CALL(callback, Run("access token"));
|
| + token_service()->IssueAllTokensForAccount(
|
| + "account", "access token",
|
| + base::Time::Now() + base::TimeDelta::FromHours(1));
|
| +}
|
| +
|
| +// Tests related to auth-in-progress don't apply on ChromeOS (it doesn't have
|
| +// that concept).
|
| +#if !defined(OS_CHROMEOS)
|
| +
|
| +TEST_F(AccessTokenFetcherTest, ShouldWaitForSignInInProgress) {
|
| + TestTokenCallback callback;
|
| +
|
| + signin_manager()->set_auth_in_progress("account");
|
| +
|
| + // A sign-in is currently in progress, so this should wait for the sign-in to
|
| + // complete.
|
| + auto fetcher = CreateFetcher(callback.Get());
|
| +
|
| + SignIn("account");
|
| + token_service()->GetDelegate()->UpdateCredentials("account", "refresh token");
|
| +
|
| + // Once the access token request is fulfilled, we should get called back with
|
| + // the access token.
|
| + EXPECT_CALL(callback, Run("access token"));
|
| + token_service()->IssueAllTokensForAccount(
|
| + "account", "access token",
|
| + base::Time::Now() + base::TimeDelta::FromHours(1));
|
| +}
|
| +
|
| +TEST_F(AccessTokenFetcherTest, ShouldWaitForFailedSignIn) {
|
| + TestTokenCallback callback;
|
| +
|
| + signin_manager()->set_auth_in_progress("account");
|
| +
|
| + // A sign-in is currently in progress, so this should wait for the sign-in to
|
| + // complete.
|
| + auto fetcher = CreateFetcher(callback.Get());
|
| +
|
| + // The fetcher should detect the failed sign-in and call us with an empty
|
| + // access token.
|
| + EXPECT_CALL(callback, Run(std::string()));
|
| +
|
| + signin_manager()->FailSignin(GoogleServiceAuthError(
|
| + GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS));
|
| +}
|
| +
|
| +#endif // !OS_CHROMEOS
|
| +
|
| +TEST_F(AccessTokenFetcherTest, ShouldWaitForRefreshToken) {
|
| + TestTokenCallback callback;
|
| +
|
| + SignIn("account");
|
| +
|
| + // Signed in, but there is no refresh token -> we should not get called back
|
| + // (yet).
|
| + auto fetcher = CreateFetcher(callback.Get());
|
| +
|
| + // Getting a refresh token should result in a request for an access token.
|
| + token_service()->GetDelegate()->UpdateCredentials("account", "refresh token");
|
| +
|
| + // Once the access token request is fulfilled, we should get called back with
|
| + // the access token.
|
| + EXPECT_CALL(callback, Run("access token"));
|
| + token_service()->IssueAllTokensForAccount(
|
| + "account", "access token",
|
| + base::Time::Now() + base::TimeDelta::FromHours(1));
|
| +}
|
| +
|
| +TEST_F(AccessTokenFetcherTest, ShouldIgnoreRefreshTokensForOtherAccounts) {
|
| + TestTokenCallback callback;
|
| +
|
| + // Signed-in to "account", but there's only a refresh token for a different
|
| + // account.
|
| + SignIn("account");
|
| + token_service()->GetDelegate()->UpdateCredentials("account 2", "refresh");
|
| +
|
| + // The fetcher should wait for the correct refresh token.
|
| + auto fetcher = CreateFetcher(callback.Get());
|
| +
|
| + // A refresh token for yet another account shouldn't matter either.
|
| + token_service()->GetDelegate()->UpdateCredentials("account 3", "refresh");
|
| +}
|
| +
|
| +TEST_F(AccessTokenFetcherTest, ShouldReturnWhenNoRefreshTokenAvailable) {
|
| + TestTokenCallback callback;
|
| +
|
| + SignIn("account");
|
| +
|
| + // Signed in, but there is no refresh token -> we should not get called back
|
| + // (yet).
|
| + auto fetcher = CreateFetcher(callback.Get());
|
| +
|
| + // Getting a refresh token for some other account should have no effect.
|
| + token_service()->GetDelegate()->UpdateCredentials("different account",
|
| + "refresh token");
|
| +
|
| + // When all refresh tokens have been loaded by the token service, but the one
|
| + // for our account wasn't among them, we should get called back with an empty
|
| + // access token
|
| + EXPECT_CALL(callback, Run(std::string()));
|
| + token_service()->GetDelegate()->LoadCredentials("account doesn't matter");
|
| +}
|
| +
|
| +TEST_F(AccessTokenFetcherTest, ShouldRetryCanceledAccessTokenRequest) {
|
| + TestTokenCallback callback;
|
| +
|
| + SignIn("account");
|
| + token_service()->GetDelegate()->UpdateCredentials("account", "refresh token");
|
| +
|
| + // Signed in and refresh token already exists, so this should result in a
|
| + // request for an access token.
|
| + auto fetcher = CreateFetcher(callback.Get());
|
| +
|
| + // A canceled access token request should get retried once.
|
| + token_service()->IssueErrorForAllPendingRequestsForAccount(
|
| + "account",
|
| + GoogleServiceAuthError(GoogleServiceAuthError::State::REQUEST_CANCELED));
|
| +
|
| + // Once the access token request is fulfilled, we should get called back with
|
| + // the access token.
|
| + EXPECT_CALL(callback, Run("access token"));
|
| + token_service()->IssueAllTokensForAccount(
|
| + "account", "access token",
|
| + base::Time::Now() + base::TimeDelta::FromHours(1));
|
| +}
|
| +
|
| +TEST_F(AccessTokenFetcherTest, ShouldRetryCanceledAccessTokenRequestOnlyOnce) {
|
| + TestTokenCallback callback;
|
| +
|
| + SignIn("account");
|
| + token_service()->GetDelegate()->UpdateCredentials("account", "refresh token");
|
| +
|
| + // Signed in and refresh token already exists, so this should result in a
|
| + // request for an access token.
|
| + auto fetcher = CreateFetcher(callback.Get());
|
| +
|
| + // A canceled access token request should get retried once.
|
| + token_service()->IssueErrorForAllPendingRequestsForAccount(
|
| + "account",
|
| + GoogleServiceAuthError(GoogleServiceAuthError::State::REQUEST_CANCELED));
|
| +
|
| + // On the second failure, we should get called back with an empty access
|
| + // token.
|
| + EXPECT_CALL(callback, Run(std::string()));
|
| + token_service()->IssueErrorForAllPendingRequestsForAccount(
|
| + "account",
|
| + GoogleServiceAuthError(GoogleServiceAuthError::State::REQUEST_CANCELED));
|
| +}
|
| +
|
| +TEST_F(AccessTokenFetcherTest, ShouldNotRetryFailedAccessTokenRequest) {
|
| + TestTokenCallback callback;
|
| +
|
| + SignIn("account");
|
| + token_service()->GetDelegate()->UpdateCredentials("account", "refresh token");
|
| +
|
| + // Signed in and refresh token already exists, so this should result in a
|
| + // request for an access token.
|
| + auto fetcher = CreateFetcher(callback.Get());
|
| +
|
| + // An access token failure other than "canceled" should not be retried; we
|
| + // should immediately get called back with an empty access token.
|
| + EXPECT_CALL(callback, Run(std::string()));
|
| + token_service()->IssueErrorForAllPendingRequestsForAccount(
|
| + "account", GoogleServiceAuthError(
|
| + GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS));
|
| +}
|
|
|