blob: 13d4d25f77259856fa37b605b1f5b5c4b9579c3e [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 "chrome/browser/signin/signin_error_notifier_ash.h"
#include <stddef.h>
#include <memory>
#include <string>
#include "base/memory/ptr_util.h"
#include "base/stl_util.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/login/users/mock_user_manager.h"
#include "chrome/browser/notifications/notification_display_service_tester.h"
#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
#include "chrome/browser/signin/signin_error_notifier_factory_ash.h"
#include "chrome/browser/supervised_user/supervised_user_service.h"
#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/testing_profile.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
#include "components/user_manager/scoped_user_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/message_center/public/cpp/notification.h"
namespace {
const char kTestEmail[] = "email@example.com";
// Notification ID corresponding to kProfileSigninNotificationId +
// kTestAccountId.
const char kPrimaryAccountErrorNotificationId[] =
"chrome://settings/signin/testing_profile";
const char kSecondaryAccountErrorNotificationId[] =
"chrome://settings/signin/testing_profile/secondary-account";
class SigninErrorNotifierTest : public BrowserWithTestWindowTest {
public:
void SetUp() override {
BrowserWithTestWindowTest::SetUp();
mock_user_manager_ = new chromeos::MockUserManager();
user_manager_enabler_ = std::make_unique<user_manager::ScopedUserManager>(
base::WrapUnique(mock_user_manager_));
SigninErrorNotifierFactory::GetForProfile(GetProfile());
display_service_ =
std::make_unique<NotificationDisplayServiceTester>(profile());
identity_test_env_profile_adaptor_ =
std::make_unique<IdentityTestEnvironmentProfileAdaptor>(GetProfile());
}
void TearDown() override {
// Need to be destroyed before the profile associated to this test, which
// will be destroyed as part of the TearDown() process.
identity_test_env_profile_adaptor_.reset();
BrowserWithTestWindowTest::TearDown();
}
TestingProfile::TestingFactories GetTestingFactories() override {
return IdentityTestEnvironmentProfileAdaptor::
GetIdentityTestEnvironmentFactories();
}
void SetAuthError(const std::string& account_id,
const GoogleServiceAuthError& error) {
signin::UpdatePersistentErrorOfRefreshTokenForAccount(
identity_test_env()->identity_manager(), account_id, error);
}
signin::IdentityTestEnvironment* identity_test_env() {
return identity_test_env_profile_adaptor_->identity_test_env();
}
protected:
std::unique_ptr<NotificationDisplayServiceTester> display_service_;
chromeos::MockUserManager* mock_user_manager_; // Not owned.
std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_;
std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
identity_test_env_profile_adaptor_;
};
TEST_F(SigninErrorNotifierTest, NoNotification) {
EXPECT_FALSE(
display_service_->GetNotification(kPrimaryAccountErrorNotificationId));
EXPECT_FALSE(
display_service_->GetNotification(kSecondaryAccountErrorNotificationId));
}
// Verify that if Supervision has just been added for the current user
// the notification isn't shown. This is because the Add Supervision
// flow itself will prompt the user to sign out, so the notification
// is unnecessary.
TEST_F(SigninErrorNotifierTest, NoNotificationAfterAddSupervisionEnabled) {
std::string account_id =
identity_test_env()->MakeAccountAvailable(kTestEmail).account_id;
identity_test_env()->SetPrimaryAccount(kTestEmail);
// Mark signout required.
SupervisedUserService* service =
SupervisedUserServiceFactory::GetForProfile(profile());
service->set_signout_required_after_supervision_enabled();
SetAuthError(
identity_test_env()->identity_manager()->GetPrimaryAccountId(),
GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
EXPECT_FALSE(
display_service_->GetNotification(kPrimaryAccountErrorNotificationId));
}
TEST_F(SigninErrorNotifierTest, ErrorResetForPrimaryAccount) {
EXPECT_FALSE(
display_service_->GetNotification(kPrimaryAccountErrorNotificationId));
std::string account_id =
identity_test_env()->MakePrimaryAccountAvailable(kTestEmail).account_id;
SetAuthError(
account_id,
GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
EXPECT_TRUE(
display_service_->GetNotification(kPrimaryAccountErrorNotificationId));
SetAuthError(account_id, GoogleServiceAuthError::AuthErrorNone());
EXPECT_FALSE(
display_service_->GetNotification(kPrimaryAccountErrorNotificationId));
}
TEST_F(SigninErrorNotifierTest, ErrorResetForSecondaryAccount) {
EXPECT_FALSE(
display_service_->GetNotification(kSecondaryAccountErrorNotificationId));
std::string account_id =
identity_test_env()->MakeAccountAvailable(kTestEmail).account_id;
SetAuthError(
account_id,
GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
// Uses the run loop from |BrowserTaskEnvironment|.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(
display_service_->GetNotification(kSecondaryAccountErrorNotificationId));
SetAuthError(account_id, GoogleServiceAuthError::AuthErrorNone());
EXPECT_FALSE(
display_service_->GetNotification(kSecondaryAccountErrorNotificationId));
}
TEST_F(SigninErrorNotifierTest, ErrorTransitionForPrimaryAccount) {
std::string account_id =
identity_test_env()->MakePrimaryAccountAvailable(kTestEmail).account_id;
SetAuthError(
account_id,
GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
base::Optional<message_center::Notification> notification =
display_service_->GetNotification(kPrimaryAccountErrorNotificationId);
ASSERT_TRUE(notification);
base::string16 message = notification->message();
EXPECT_FALSE(message.empty());
// Now set another auth error.
SetAuthError(account_id,
GoogleServiceAuthError(
GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE));
notification =
display_service_->GetNotification(kPrimaryAccountErrorNotificationId);
ASSERT_TRUE(notification);
base::string16 new_message = notification->message();
EXPECT_FALSE(new_message.empty());
ASSERT_NE(new_message, message);
}
// Verify that SigninErrorNotifier ignores certain errors.
TEST_F(SigninErrorNotifierTest, AuthStatusEnumerateAllErrors) {
typedef struct {
GoogleServiceAuthError::State error_state;
bool is_error;
} ErrorTableEntry;
ErrorTableEntry table[] = {
{GoogleServiceAuthError::NONE, false},
{GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, true},
{GoogleServiceAuthError::USER_NOT_SIGNED_UP, true},
{GoogleServiceAuthError::CONNECTION_FAILED, false},
{GoogleServiceAuthError::SERVICE_UNAVAILABLE, false},
{GoogleServiceAuthError::REQUEST_CANCELED, false},
{GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE, true},
{GoogleServiceAuthError::SERVICE_ERROR, true},
};
static_assert(
base::size(table) == GoogleServiceAuthError::NUM_STATES -
GoogleServiceAuthError::kDeprecatedStateCount,
"table size should match number of auth error types");
std::string account_id =
identity_test_env()->MakePrimaryAccountAvailable(kTestEmail).account_id;
for (size_t i = 0; i < base::size(table); ++i) {
SetAuthError(account_id, GoogleServiceAuthError(table[i].error_state));
base::Optional<message_center::Notification> notification =
display_service_->GetNotification(kPrimaryAccountErrorNotificationId);
ASSERT_EQ(table[i].is_error, !!notification) << "Failed case #" << i;
if (table[i].is_error) {
EXPECT_FALSE(notification->title().empty());
EXPECT_FALSE(notification->message().empty());
EXPECT_EQ((size_t)1, notification->buttons().size());
}
SetAuthError(account_id, GoogleServiceAuthError::AuthErrorNone());
}
}
} // namespace