blob: 912bb2978ffe8b88d3b95deb38a1db4054de9acf [file] [log] [blame]
// Copyright 2018 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 "chromeos/account_manager/account_manager.h"
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
namespace chromeos {
namespace {
constexpr char kGaiaToken[] = "gaia_token";
constexpr char kNewGaiaToken[] = "new_gaia_token";
constexpr char kRawUserEmail[] = "user@example.com";
bool IsAccountKeyPresent(const std::vector<AccountManager::Account>& accounts,
const AccountManager::AccountKey& account_key) {
for (const auto& account : accounts) {
if (account.key == account_key) {
return true;
}
}
return false;
}
} // namespace
class AccountManagerSpy : public AccountManager {
public:
AccountManagerSpy() = default;
~AccountManagerSpy() override = default;
MOCK_METHOD1(RevokeGaiaTokenOnServer, void(const std::string& refresh_token));
private:
DISALLOW_COPY_AND_ASSIGN(AccountManagerSpy);
};
class AccountManagerTest : public testing::Test {
public:
AccountManagerTest() {}
~AccountManagerTest() override {}
protected:
void SetUp() override {
ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir());
ResetAndInitializeAccountManager();
}
// Gets the list of accounts stored in |account_manager_|.
std::vector<AccountManager::Account> GetAccountsBlocking() {
std::vector<AccountManager::Account> accounts;
base::RunLoop run_loop;
account_manager_->GetAccounts(base::BindLambdaForTesting(
[&accounts, &run_loop](
const std::vector<AccountManager::Account>& stored_accounts) {
accounts = stored_accounts;
run_loop.Quit();
}));
run_loop.Run();
return accounts;
}
// Gets the raw email for |account_key|.
std::string GetAccountEmailBlocking(
const AccountManager::AccountKey& account_key) {
std::string raw_email;
base::RunLoop run_loop;
account_manager_->GetAccountEmail(
account_key,
base::BindLambdaForTesting(
[&raw_email, &run_loop](const std::string& stored_raw_email) {
raw_email = stored_raw_email;
run_loop.Quit();
}));
run_loop.Run();
return raw_email;
}
// Helper method to reset and initialize |account_manager_| with default
// parameters.
void ResetAndInitializeAccountManager() {
account_manager_ = std::make_unique<AccountManagerSpy>();
account_manager_->Initialize(
tmp_dir_.GetPath(), test_url_loader_factory_.GetSafeWeakWrapper(),
immediate_callback_runner_, base::SequencedTaskRunnerHandle::Get());
}
// Check base/test/scoped_task_environment.h. This must be the first member /
// declared before any member that cares about tasks.
base::test::ScopedTaskEnvironment scoped_task_environment_;
base::ScopedTempDir tmp_dir_;
network::TestURLLoaderFactory test_url_loader_factory_;
std::unique_ptr<AccountManagerSpy> account_manager_;
const AccountManager::AccountKey kGaiaAccountKey_{
"gaia_id", account_manager::AccountType::ACCOUNT_TYPE_GAIA};
const AccountManager::AccountKey kActiveDirectoryAccountKey_{
"object_guid",
account_manager::AccountType::ACCOUNT_TYPE_ACTIVE_DIRECTORY};
AccountManager::DelayNetworkCallRunner immediate_callback_runner_ =
base::BindRepeating(
[](base::OnceClosure closure) -> void { std::move(closure).Run(); });
private:
DISALLOW_COPY_AND_ASSIGN(AccountManagerTest);
};
class AccountManagerObserver : public AccountManager::Observer {
public:
AccountManagerObserver() = default;
~AccountManagerObserver() override = default;
void OnTokenUpserted(const AccountManager::Account& account) override {
is_token_upserted_callback_called_ = true;
accounts_.insert(account.key);
last_upserted_account_key_ = account.key;
last_upserted_account_email_ = account.raw_email;
}
void OnAccountRemoved(const AccountManager::Account& account) override {
is_account_removed_callback_called_ = true;
accounts_.erase(account.key);
last_removed_account_key_ = account.key;
last_removed_account_email_ = account.raw_email;
}
bool is_token_upserted_callback_called_ = false;
bool is_account_removed_callback_called_ = false;
AccountManager::AccountKey last_upserted_account_key_;
std::string last_upserted_account_email_;
AccountManager::AccountKey last_removed_account_key_;
std::string last_removed_account_email_;
std::set<AccountManager::AccountKey> accounts_;
private:
DISALLOW_COPY_AND_ASSIGN(AccountManagerObserver);
};
TEST(AccountManagerKeyTest, TestValidity) {
AccountManager::AccountKey key1{
std::string(), account_manager::AccountType::ACCOUNT_TYPE_GAIA};
EXPECT_FALSE(key1.IsValid());
AccountManager::AccountKey key2{
"abc", account_manager::AccountType::ACCOUNT_TYPE_UNSPECIFIED};
EXPECT_FALSE(key2.IsValid());
AccountManager::AccountKey key3{
"abc", account_manager::AccountType::ACCOUNT_TYPE_GAIA};
EXPECT_TRUE(key3.IsValid());
}
TEST_F(AccountManagerTest, TestInitialization) {
AccountManager account_manager;
EXPECT_EQ(account_manager.init_state_,
AccountManager::InitializationState::kNotStarted);
account_manager.Initialize(
tmp_dir_.GetPath(), test_url_loader_factory_.GetSafeWeakWrapper(),
immediate_callback_runner_, base::SequencedTaskRunnerHandle::Get());
scoped_task_environment_.RunUntilIdle();
EXPECT_EQ(account_manager.init_state_,
AccountManager::InitializationState::kInitialized);
}
TEST_F(AccountManagerTest, TestUpsert) {
account_manager_->UpsertAccount(kGaiaAccountKey_, kRawUserEmail, kGaiaToken);
std::vector<AccountManager::Account> accounts = GetAccountsBlocking();
EXPECT_EQ(1UL, accounts.size());
EXPECT_EQ(kGaiaAccountKey_, accounts[0].key);
EXPECT_EQ(kRawUserEmail, accounts[0].raw_email);
}
TEST_F(AccountManagerTest, TestTokenPersistence) {
account_manager_->UpsertAccount(kGaiaAccountKey_, kRawUserEmail, kGaiaToken);
scoped_task_environment_.RunUntilIdle();
ResetAndInitializeAccountManager();
std::vector<AccountManager::Account> accounts = GetAccountsBlocking();
EXPECT_EQ(1UL, accounts.size());
EXPECT_EQ(kGaiaAccountKey_, accounts[0].key);
EXPECT_EQ(kRawUserEmail, accounts[0].raw_email);
EXPECT_EQ(kGaiaToken, account_manager_->accounts_[kGaiaAccountKey_].token);
}
TEST_F(AccountManagerTest, TestAccountEmailPersistence) {
account_manager_->UpsertAccount(kGaiaAccountKey_, kRawUserEmail, kGaiaToken);
scoped_task_environment_.RunUntilIdle();
ResetAndInitializeAccountManager();
const std::string raw_email = GetAccountEmailBlocking(kGaiaAccountKey_);
EXPECT_EQ(kRawUserEmail, raw_email);
}
TEST_F(AccountManagerTest, UpdatingAccountEmailShouldNotOverwriteTokens) {
const std::string new_email = "new-email@example.org";
account_manager_->UpsertAccount(kGaiaAccountKey_, kRawUserEmail, kGaiaToken);
account_manager_->UpdateEmail(kGaiaAccountKey_, new_email);
scoped_task_environment_.RunUntilIdle();
ResetAndInitializeAccountManager();
const std::string raw_email = GetAccountEmailBlocking(kGaiaAccountKey_);
EXPECT_EQ(new_email, raw_email);
EXPECT_EQ(kGaiaToken, account_manager_->accounts_[kGaiaAccountKey_].token);
}
TEST_F(AccountManagerTest, UpsertAccountCanUpdateEmail) {
const std::string new_email = "new-email@example.org";
account_manager_->UpsertAccount(kGaiaAccountKey_, kRawUserEmail, kGaiaToken);
account_manager_->UpsertAccount(kGaiaAccountKey_, new_email, kGaiaToken);
scoped_task_environment_.RunUntilIdle();
ResetAndInitializeAccountManager();
const std::string raw_email = GetAccountEmailBlocking(kGaiaAccountKey_);
EXPECT_EQ(new_email, raw_email);
}
TEST_F(AccountManagerTest, UpdatingTokensShouldNotOverwriteAccountEmail) {
account_manager_->UpsertAccount(kGaiaAccountKey_, kRawUserEmail, kGaiaToken);
account_manager_->UpdateToken(kGaiaAccountKey_, kNewGaiaToken);
scoped_task_environment_.RunUntilIdle();
ResetAndInitializeAccountManager();
const std::string raw_email = GetAccountEmailBlocking(kGaiaAccountKey_);
EXPECT_EQ(kRawUserEmail, raw_email);
EXPECT_EQ(kNewGaiaToken, account_manager_->accounts_[kGaiaAccountKey_].token);
}
TEST_F(AccountManagerTest, ObserversAreNotifiedOnTokenInsertion) {
auto observer = std::make_unique<AccountManagerObserver>();
EXPECT_FALSE(observer->is_token_upserted_callback_called_);
account_manager_->AddObserver(observer.get());
account_manager_->UpsertAccount(kGaiaAccountKey_, kRawUserEmail, kGaiaToken);
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(observer->is_token_upserted_callback_called_);
EXPECT_EQ(1UL, observer->accounts_.size());
EXPECT_EQ(kGaiaAccountKey_, *observer->accounts_.begin());
EXPECT_EQ(kGaiaAccountKey_, observer->last_upserted_account_key_);
EXPECT_EQ(kRawUserEmail, observer->last_upserted_account_email_);
account_manager_->RemoveObserver(observer.get());
}
TEST_F(AccountManagerTest, ObserversAreNotifiedOnTokenUpdate) {
auto observer = std::make_unique<AccountManagerObserver>();
EXPECT_FALSE(observer->is_token_upserted_callback_called_);
account_manager_->AddObserver(observer.get());
account_manager_->UpsertAccount(kGaiaAccountKey_, kRawUserEmail, kGaiaToken);
scoped_task_environment_.RunUntilIdle();
// Observers should be called when token is updated.
observer->is_token_upserted_callback_called_ = false;
account_manager_->UpdateToken(kGaiaAccountKey_, kNewGaiaToken);
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(observer->is_token_upserted_callback_called_);
EXPECT_EQ(1UL, observer->accounts_.size());
EXPECT_EQ(kGaiaAccountKey_, *observer->accounts_.begin());
EXPECT_EQ(kGaiaAccountKey_, observer->last_upserted_account_key_);
EXPECT_EQ(kRawUserEmail, observer->last_upserted_account_email_);
account_manager_->RemoveObserver(observer.get());
}
TEST_F(AccountManagerTest, ObserversAreNotNotifiedIfTokenIsNotUpdated) {
auto observer = std::make_unique<AccountManagerObserver>();
EXPECT_FALSE(observer->is_token_upserted_callback_called_);
account_manager_->AddObserver(observer.get());
account_manager_->UpsertAccount(kGaiaAccountKey_, kRawUserEmail, kGaiaToken);
scoped_task_environment_.RunUntilIdle();
// Observers should not be called when token is not updated.
observer->is_token_upserted_callback_called_ = false;
account_manager_->UpdateToken(kGaiaAccountKey_, kGaiaToken);
scoped_task_environment_.RunUntilIdle();
EXPECT_FALSE(observer->is_token_upserted_callback_called_);
account_manager_->RemoveObserver(observer.get());
}
TEST_F(AccountManagerTest, RemovedAccountsAreImmediatelyUnavailable) {
account_manager_->UpsertAccount(kGaiaAccountKey_, kRawUserEmail, kGaiaToken);
account_manager_->RemoveAccount(kGaiaAccountKey_);
std::vector<AccountManager::Account> accounts = GetAccountsBlocking();
EXPECT_TRUE(accounts.empty());
}
TEST_F(AccountManagerTest, AccountRemovalIsPersistedToDisk) {
account_manager_->UpsertAccount(kGaiaAccountKey_, kRawUserEmail, kGaiaToken);
account_manager_->RemoveAccount(kGaiaAccountKey_);
scoped_task_environment_.RunUntilIdle();
ResetAndInitializeAccountManager();
std::vector<AccountManager::Account> accounts = GetAccountsBlocking();
EXPECT_TRUE(accounts.empty());
}
TEST_F(AccountManagerTest, ObserversAreNotifiedOnAccountRemoval) {
auto observer = std::make_unique<AccountManagerObserver>();
account_manager_->AddObserver(observer.get());
account_manager_->UpsertAccount(kGaiaAccountKey_, kRawUserEmail, kGaiaToken);
scoped_task_environment_.RunUntilIdle();
EXPECT_FALSE(observer->is_account_removed_callback_called_);
account_manager_->RemoveAccount(kGaiaAccountKey_);
EXPECT_TRUE(observer->is_account_removed_callback_called_);
EXPECT_TRUE(observer->accounts_.empty());
EXPECT_EQ(kGaiaAccountKey_, observer->last_removed_account_key_);
EXPECT_EQ(kRawUserEmail, observer->last_removed_account_email_);
account_manager_->RemoveObserver(observer.get());
}
TEST_F(AccountManagerTest, TokenRevocationIsAttemptedForGaiaAccountRemovals) {
ResetAndInitializeAccountManager();
EXPECT_CALL(*account_manager_.get(), RevokeGaiaTokenOnServer(kGaiaToken));
account_manager_->UpsertAccount(kGaiaAccountKey_, kRawUserEmail, kGaiaToken);
scoped_task_environment_.RunUntilIdle();
account_manager_->RemoveAccount(kGaiaAccountKey_);
}
TEST_F(AccountManagerTest,
TokenRevocationIsNotAttemptedForNonGaiaAccountRemovals) {
ResetAndInitializeAccountManager();
EXPECT_CALL(*account_manager_.get(), RevokeGaiaTokenOnServer(_)).Times(0);
account_manager_->UpsertAccount(kActiveDirectoryAccountKey_, kRawUserEmail,
AccountManager::kActiveDirectoryDummyToken);
scoped_task_environment_.RunUntilIdle();
account_manager_->RemoveAccount(kActiveDirectoryAccountKey_);
}
TEST_F(AccountManagerTest,
TokenRevocationIsNotAttemptedForInvalidTokenRemovals) {
ResetAndInitializeAccountManager();
EXPECT_CALL(*account_manager_.get(), RevokeGaiaTokenOnServer(_)).Times(0);
account_manager_->UpsertAccount(kGaiaAccountKey_, kRawUserEmail,
AccountManager::kInvalidToken);
scoped_task_environment_.RunUntilIdle();
account_manager_->RemoveAccount(kGaiaAccountKey_);
}
TEST_F(AccountManagerTest, OldTokenIsRevokedOnTokenUpdate) {
ResetAndInitializeAccountManager();
// Only 1 token should be revoked.
EXPECT_CALL(*account_manager_.get(), RevokeGaiaTokenOnServer(kGaiaToken))
.Times(1);
account_manager_->UpsertAccount(kGaiaAccountKey_, kRawUserEmail, kGaiaToken);
// Update the token.
account_manager_->UpdateToken(kGaiaAccountKey_, kNewGaiaToken);
scoped_task_environment_.RunUntilIdle();
}
TEST_F(AccountManagerTest, IsTokenAvailableReturnsTrueForValidGaiaAccounts) {
EXPECT_FALSE(account_manager_->IsTokenAvailable(kGaiaAccountKey_));
account_manager_->UpsertAccount(kGaiaAccountKey_, kRawUserEmail, kGaiaToken);
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(account_manager_->IsTokenAvailable(kGaiaAccountKey_));
}
TEST_F(AccountManagerTest,
IsTokenAvailableReturnsFalseForActiveDirectoryAccounts) {
EXPECT_FALSE(account_manager_->IsTokenAvailable(kActiveDirectoryAccountKey_));
account_manager_->UpsertAccount(kActiveDirectoryAccountKey_, kRawUserEmail,
AccountManager::kActiveDirectoryDummyToken);
scoped_task_environment_.RunUntilIdle();
EXPECT_FALSE(account_manager_->IsTokenAvailable(kActiveDirectoryAccountKey_));
std::vector<AccountManager::Account> accounts = GetAccountsBlocking();
EXPECT_TRUE(IsAccountKeyPresent(accounts, kActiveDirectoryAccountKey_));
}
TEST_F(AccountManagerTest, IsTokenAvailableReturnsTrueForInvalidTokens) {
EXPECT_FALSE(account_manager_->IsTokenAvailable(kGaiaAccountKey_));
account_manager_->UpsertAccount(kGaiaAccountKey_, kRawUserEmail,
AccountManager::kInvalidToken);
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(account_manager_->IsTokenAvailable(kGaiaAccountKey_));
std::vector<AccountManager::Account> accounts = GetAccountsBlocking();
EXPECT_TRUE(IsAccountKeyPresent(accounts, kGaiaAccountKey_));
}
} // namespace chromeos