blob: 2b88b04cf3988517dfc5815e6bf36116f419d41f [file] [log] [blame]
// Copyright 2014 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/signin_error_controller.h"
#include <stddef.h>
#include <functional>
#include <memory>
#include "base/scoped_observer.h"
#include "base/stl_util.h"
#include "base/test/scoped_task_environment.h"
#include "services/identity/public/cpp/identity_test_environment.h"
#include "services/identity/public/cpp/primary_account_mutator.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
static const char kTestEmail[] = "me@test.com";
static const char kOtherTestEmail[] = "you@test.com";
class MockSigninErrorControllerObserver
: public SigninErrorController::Observer {
public:
MOCK_METHOD0(OnErrorChanged, void());
};
} // namespace
TEST(SigninErrorControllerTest, SingleAccount) {
MockSigninErrorControllerObserver observer;
EXPECT_CALL(observer, OnErrorChanged()).Times(0);
base::test::ScopedTaskEnvironment task_environment;
identity::IdentityTestEnvironment identity_test_env;
SigninErrorController error_controller(
SigninErrorController::AccountMode::ANY_ACCOUNT,
identity_test_env.identity_manager());
ScopedObserver<SigninErrorController, SigninErrorController::Observer>
scoped_observer(&observer);
scoped_observer.Add(&error_controller);
ASSERT_FALSE(error_controller.HasError());
::testing::Mock::VerifyAndClearExpectations(&observer);
// IdentityTestEnvironment does not call OnEndBatchChanges() as part of
// MakeAccountAvailable(), and thus the signin error controller is not
// updated.
EXPECT_CALL(observer, OnErrorChanged()).Times(0);
std::string test_account_id =
identity_test_env.MakeAccountAvailable(kTestEmail).account_id;
::testing::Mock::VerifyAndClearExpectations(&observer);
GoogleServiceAuthError error1 =
GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
EXPECT_CALL(observer, OnErrorChanged()).Times(1);
identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
test_account_id, error1);
EXPECT_TRUE(error_controller.HasError());
EXPECT_EQ(error1, error_controller.auth_error());
::testing::Mock::VerifyAndClearExpectations(&observer);
GoogleServiceAuthError error2 =
GoogleServiceAuthError(GoogleServiceAuthError::ACCOUNT_DISABLED);
EXPECT_CALL(observer, OnErrorChanged()).Times(1);
identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
test_account_id, error2);
EXPECT_TRUE(error_controller.HasError());
EXPECT_EQ(error2, error_controller.auth_error());
::testing::Mock::VerifyAndClearExpectations(&observer);
EXPECT_CALL(observer, OnErrorChanged()).Times(1);
identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
test_account_id, GoogleServiceAuthError::AuthErrorNone());
EXPECT_FALSE(error_controller.HasError());
EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(),
error_controller.auth_error());
::testing::Mock::VerifyAndClearExpectations(&observer);
}
TEST(SigninErrorControllerTest, AccountTransitionAnyAccount) {
base::test::ScopedTaskEnvironment task_environment;
identity::IdentityTestEnvironment identity_test_env;
std::string test_account_id =
identity_test_env.MakeAccountAvailable(kTestEmail).account_id;
std::string other_test_account_id =
identity_test_env.MakeAccountAvailable(kOtherTestEmail).account_id;
SigninErrorController error_controller(
SigninErrorController::AccountMode::ANY_ACCOUNT,
identity_test_env.identity_manager());
ASSERT_FALSE(error_controller.HasError());
identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
test_account_id,
GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
other_test_account_id,
GoogleServiceAuthError(GoogleServiceAuthError::NONE));
ASSERT_TRUE(error_controller.HasError());
ASSERT_EQ(test_account_id, error_controller.error_account_id());
// Now resolve the auth errors - the menu item should go away.
identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
test_account_id, GoogleServiceAuthError::AuthErrorNone());
ASSERT_FALSE(error_controller.HasError());
}
// This test exercises behavior on signin/signout, which is not relevant on
// ChromeOS.
#if !defined(OS_CHROMEOS)
TEST(SigninErrorControllerTest, AccountTransitionPrimaryAccount) {
base::test::ScopedTaskEnvironment task_environment;
identity::IdentityTestEnvironment identity_test_env;
identity::PrimaryAccountMutator* primary_account_mutator =
identity_test_env.identity_manager()->GetPrimaryAccountMutator();
std::string test_account_id =
identity_test_env.MakeAccountAvailable(kTestEmail).account_id;
std::string other_test_account_id =
identity_test_env.MakeAccountAvailable(kOtherTestEmail).account_id;
SigninErrorController error_controller(
SigninErrorController::AccountMode::PRIMARY_ACCOUNT,
identity_test_env.identity_manager());
ASSERT_FALSE(error_controller.HasError());
identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
test_account_id,
GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
other_test_account_id,
GoogleServiceAuthError(GoogleServiceAuthError::NONE));
ASSERT_FALSE(error_controller.HasError()); // No primary account.
// Set the primary account.
identity_test_env.SetPrimaryAccount(kOtherTestEmail);
ASSERT_FALSE(error_controller.HasError()); // Error is on secondary.
// Change the primary account to the account with an error and check that the
// error controller updates its error status accordingly.
primary_account_mutator->ClearPrimaryAccount(
identity::PrimaryAccountMutator::ClearAccountsAction::kKeepAll,
signin_metrics::FORCE_SIGNOUT_ALWAYS_ALLOWED_FOR_TEST,
signin_metrics::SignoutDelete::IGNORE_METRIC);
identity_test_env.SetPrimaryAccount(kTestEmail);
ASSERT_TRUE(error_controller.HasError());
ASSERT_EQ(test_account_id, error_controller.error_account_id());
identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
other_test_account_id,
GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
ASSERT_TRUE(error_controller.HasError());
ASSERT_EQ(test_account_id, error_controller.error_account_id());
// Change the primary account again and check that the error controller
// updates its error status accordingly.
primary_account_mutator->ClearPrimaryAccount(
identity::PrimaryAccountMutator::ClearAccountsAction::kKeepAll,
signin_metrics::FORCE_SIGNOUT_ALWAYS_ALLOWED_FOR_TEST,
signin_metrics::SignoutDelete::IGNORE_METRIC);
identity_test_env.SetPrimaryAccount(kOtherTestEmail);
ASSERT_TRUE(error_controller.HasError());
ASSERT_EQ(other_test_account_id, error_controller.error_account_id());
// Sign out and check that that the error controller updates its error status
// accordingly.
primary_account_mutator->ClearPrimaryAccount(
identity::PrimaryAccountMutator::ClearAccountsAction::kKeepAll,
signin_metrics::FORCE_SIGNOUT_ALWAYS_ALLOWED_FOR_TEST,
signin_metrics::SignoutDelete::IGNORE_METRIC);
ASSERT_FALSE(error_controller.HasError());
}
#endif
// Verify that SigninErrorController handles errors properly.
TEST(SigninErrorControllerTest, AuthStatusEnumerateAllErrors) {
base::test::ScopedTaskEnvironment task_environment;
identity::IdentityTestEnvironment identity_test_env;
std::string test_account_id =
identity_test_env.MakeAccountAvailable(kTestEmail).account_id;
SigninErrorController error_controller(
SigninErrorController::AccountMode::ANY_ACCOUNT,
identity_test_env.identity_manager());
GoogleServiceAuthError::State table[] = {
GoogleServiceAuthError::NONE,
GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS,
GoogleServiceAuthError::USER_NOT_SIGNED_UP,
GoogleServiceAuthError::CONNECTION_FAILED,
GoogleServiceAuthError::CAPTCHA_REQUIRED,
GoogleServiceAuthError::ACCOUNT_DELETED,
GoogleServiceAuthError::ACCOUNT_DISABLED,
GoogleServiceAuthError::SERVICE_UNAVAILABLE,
GoogleServiceAuthError::TWO_FACTOR,
GoogleServiceAuthError::REQUEST_CANCELED,
GoogleServiceAuthError::HOSTED_NOT_ALLOWED_DEPRECATED,
GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE,
GoogleServiceAuthError::SERVICE_ERROR,
GoogleServiceAuthError::WEB_LOGIN_REQUIRED};
static_assert(base::size(table) == GoogleServiceAuthError::NUM_STATES,
"table array does not match the number of auth error types");
for (GoogleServiceAuthError::State state : table) {
if (GoogleServiceAuthError::IsDeprecated(state))
continue;
GoogleServiceAuthError error(state);
if (error.IsTransientError())
continue; // Only persistent errors or non-errors are reported.
identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
test_account_id, error);
EXPECT_EQ(error_controller.HasError(), error.IsPersistentError());
if (error.IsPersistentError()) {
EXPECT_EQ(state, error_controller.auth_error().state());
EXPECT_EQ(test_account_id, error_controller.error_account_id());
} else {
EXPECT_EQ(GoogleServiceAuthError::NONE,
error_controller.auth_error().state());
EXPECT_EQ("", error_controller.error_account_id());
}
}
}
// Verify that existing error is not replaced by new error.
TEST(SigninErrorControllerTest, AuthStatusChange) {
base::test::ScopedTaskEnvironment task_environment;
identity::IdentityTestEnvironment identity_test_env;
std::string test_account_id =
identity_test_env.MakeAccountAvailable(kTestEmail).account_id;
std::string other_test_account_id =
identity_test_env.MakeAccountAvailable(kOtherTestEmail).account_id;
SigninErrorController error_controller(
SigninErrorController::AccountMode::ANY_ACCOUNT,
identity_test_env.identity_manager());
ASSERT_FALSE(error_controller.HasError());
// Set an error for other_test_account_id.
identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
test_account_id, GoogleServiceAuthError(GoogleServiceAuthError::NONE));
identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
other_test_account_id,
GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
ASSERT_EQ(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS,
error_controller.auth_error().state());
ASSERT_EQ(other_test_account_id, error_controller.error_account_id());
// Change the error for other_test_account_id.
identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
other_test_account_id,
GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_ERROR));
ASSERT_EQ(GoogleServiceAuthError::SERVICE_ERROR,
error_controller.auth_error().state());
ASSERT_EQ(other_test_account_id, error_controller.error_account_id());
// Set the error for test_account_id -- nothing should change.
identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
test_account_id,
GoogleServiceAuthError(
GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE));
ASSERT_EQ(GoogleServiceAuthError::SERVICE_ERROR,
error_controller.auth_error().state());
ASSERT_EQ(other_test_account_id, error_controller.error_account_id());
// Clear the error for other_test_account_id, so the test_account_id's error
// is used.
identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
other_test_account_id,
GoogleServiceAuthError(GoogleServiceAuthError::NONE));
ASSERT_EQ(GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE,
error_controller.auth_error().state());
ASSERT_EQ(test_account_id, error_controller.error_account_id());
// Clear the remaining error.
identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
test_account_id, GoogleServiceAuthError(GoogleServiceAuthError::NONE));
ASSERT_FALSE(error_controller.HasError());
}