diff options
author | blundell@chromium.org <blundell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-31 11:41:20 +0000 |
---|---|---|
committer | blundell@chromium.org <blundell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-31 11:41:20 +0000 |
commit | 7fa4af9b89a7d7357db7b1ad12fba8a24f4ad12f (patch) | |
tree | 2a9458179a9e4d93b505294b70609240db75043f /components/signin | |
parent | e20bc8f3849b2738d158ceb3f56c135128248ab7 (diff) | |
download | chromium_src-7fa4af9b89a7d7357db7b1ad12fba8a24f4ad12f.zip chromium_src-7fa4af9b89a7d7357db7b1ad12fba8a24f4ad12f.tar.gz chromium_src-7fa4af9b89a7d7357db7b1ad12fba8a24f4ad12f.tar.bz2 |
Componentize MutableProfileOAuth2TokenService unittest.
This entails:
- Creating TestSigninClient for the test to use instead of ChromeSigninClient
- Eliminating dependencies on TestingProfile
BUG=358001,358000
Review URL: https://codereview.chromium.org/217423003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@260525 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components/signin')
4 files changed, 433 insertions, 0 deletions
diff --git a/components/signin/core/browser/mutable_profile_oauth2_token_service.h b/components/signin/core/browser/mutable_profile_oauth2_token_service.h index 4f10555..a5a100d 100644 --- a/components/signin/core/browser/mutable_profile_oauth2_token_service.h +++ b/components/signin/core/browser/mutable_profile_oauth2_token_service.h @@ -68,6 +68,7 @@ class MutableProfileOAuth2TokenService : public ProfileOAuth2TokenService, typedef std::map<std::string, linked_ptr<AccountInfo> > AccountInfoMap; friend class ProfileOAuth2TokenServiceFactory; + friend class MutableProfileOAuth2TokenServiceTest; MutableProfileOAuth2TokenService(); virtual ~MutableProfileOAuth2TokenService(); diff --git a/components/signin/core/browser/mutable_profile_oauth2_token_service_unittest.cc b/components/signin/core/browser/mutable_profile_oauth2_token_service_unittest.cc new file mode 100644 index 0000000..153e0b4 --- /dev/null +++ b/components/signin/core/browser/mutable_profile_oauth2_token_service_unittest.cc @@ -0,0 +1,336 @@ +// 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/mutable_profile_oauth2_token_service.h" + +#include "base/run_loop.h" +#include "components/signin/core/browser/profile_oauth2_token_service.h" +#include "components/signin/core/browser/signin_error_controller.h" +#include "components/signin/core/browser/test_signin_client.h" +#include "components/signin/core/browser/webdata/token_web_data.h" +#include "google_apis/gaia/gaia_constants.h" +#include "google_apis/gaia/gaia_urls.h" +#include "google_apis/gaia/oauth2_token_service_test_util.h" +#include "net/http/http_status_code.h" +#include "net/url_request/test_url_fetcher_factory.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if defined(OS_MACOSX) +#include "components/os_crypt/os_crypt.h" +#endif + +// Defining constant here to handle backward compatiblity tests, but this +// constant is no longer used in current versions of chrome. +static const char kLSOService[] = "lso"; +static const char kEmail[] = "user@gmail.com"; + +class MutableProfileOAuth2TokenServiceTest + : public testing::Test, + public OAuth2TokenService::Observer { + public: + MutableProfileOAuth2TokenServiceTest() + : factory_(NULL), + token_available_count_(0), + token_revoked_count_(0), + tokens_loaded_count_(0) {} + + virtual void SetUp() OVERRIDE { +#if defined(OS_MACOSX) + OSCrypt::UseMockKeychain(true); +#endif + + factory_.SetFakeResponse(GaiaUrls::GetInstance()->oauth2_revoke_url(), + "", + net::HTTP_OK, + net::URLRequestStatus::SUCCESS); + oauth2_service_.Initialize(&client_); + // Make sure PO2TS has a chance to load itself before continuing. + base::RunLoop().RunUntilIdle(); + oauth2_service_.AddObserver(this); + } + + virtual void TearDown() OVERRIDE { + oauth2_service_.RemoveObserver(this); + oauth2_service_.Shutdown(); + } + + void AddAuthTokenManually(const std::string& service, + const std::string& value) { + scoped_refptr<TokenWebData> token_web_data = client_.GetDatabase(); + if (token_web_data.get()) + token_web_data->SetTokenForService(service, value); + } + + // OAuth2TokenService::Observer implementation. + virtual void OnRefreshTokenAvailable(const std::string& account_id) OVERRIDE { + ++token_available_count_; + } + virtual void OnRefreshTokenRevoked(const std::string& account_id) OVERRIDE { + ++token_revoked_count_; + } + virtual void OnRefreshTokensLoaded() OVERRIDE { ++tokens_loaded_count_; } + + void ResetObserverCounts() { + token_available_count_ = 0; + token_revoked_count_ = 0; + tokens_loaded_count_ = 0; + } + + void ExpectNoNotifications() { + EXPECT_EQ(0, token_available_count_); + EXPECT_EQ(0, token_revoked_count_); + EXPECT_EQ(0, tokens_loaded_count_); + ResetObserverCounts(); + } + + void ExpectOneTokenAvailableNotification() { + EXPECT_EQ(1, token_available_count_); + EXPECT_EQ(0, token_revoked_count_); + EXPECT_EQ(0, tokens_loaded_count_); + ResetObserverCounts(); + } + + void ExpectOneTokenRevokedNotification() { + EXPECT_EQ(0, token_available_count_); + EXPECT_EQ(1, token_revoked_count_); + EXPECT_EQ(0, tokens_loaded_count_); + ResetObserverCounts(); + } + + void ExpectOneTokensLoadedNotification() { + EXPECT_EQ(0, token_available_count_); + EXPECT_EQ(0, token_revoked_count_); + EXPECT_EQ(1, tokens_loaded_count_); + ResetObserverCounts(); + } + + protected: + base::MessageLoop message_loop_; + net::FakeURLFetcherFactory factory_; + TestSigninClient client_; + MutableProfileOAuth2TokenService oauth2_service_; + TestingOAuth2TokenServiceConsumer consumer_; + int token_available_count_; + int token_revoked_count_; + int tokens_loaded_count_; +}; + +TEST_F(MutableProfileOAuth2TokenServiceTest, PersistenceDBUpgrade) { + std::string main_account_id(kEmail); + std::string main_refresh_token("old_refresh_token"); + + // Populate DB with legacy tokens. + AddAuthTokenManually(GaiaConstants::kSyncService, "syncServiceToken"); + AddAuthTokenManually(kLSOService, "lsoToken"); + AddAuthTokenManually(GaiaConstants::kGaiaOAuth2LoginRefreshToken, + main_refresh_token); + + // Force LoadCredentials. + oauth2_service_.LoadCredentials(main_account_id); + base::RunLoop().RunUntilIdle(); + + // Legacy tokens get discarded, but the old refresh token is kept. + EXPECT_EQ(1, tokens_loaded_count_); + EXPECT_EQ(1, token_available_count_); + EXPECT_TRUE(oauth2_service_.RefreshTokenIsAvailable(main_account_id)); + EXPECT_EQ(1U, oauth2_service_.refresh_tokens().size()); + EXPECT_EQ(main_refresh_token, + oauth2_service_.refresh_tokens()[main_account_id]->refresh_token()); + + // Add an old legacy token to the DB, to ensure it will not overwrite existing + // credentials for main account. + AddAuthTokenManually(GaiaConstants::kGaiaOAuth2LoginRefreshToken, + "secondOldRefreshToken"); + // Add some other legacy token. (Expected to get discarded). + AddAuthTokenManually(kLSOService, "lsoToken"); + // Also add a token using PO2TS.UpdateCredentials and make sure upgrade does + // not wipe it. + std::string other_account_id("other_account_id"); + std::string other_refresh_token("other_refresh_token"); + oauth2_service_.UpdateCredentials(other_account_id, other_refresh_token); + ResetObserverCounts(); + + // Force LoadCredentials. + oauth2_service_.LoadCredentials(main_account_id); + base::RunLoop().RunUntilIdle(); + + // Again legacy tokens get discarded, but since the main porfile account + // token is present it is not overwritten. + EXPECT_EQ(2, token_available_count_); + EXPECT_EQ(1, tokens_loaded_count_); + EXPECT_TRUE(oauth2_service_.RefreshTokenIsAvailable(main_account_id)); + // TODO(fgorski): cover both using RefreshTokenIsAvailable() and then get the + // tokens using GetRefreshToken() + EXPECT_EQ(2U, oauth2_service_.refresh_tokens().size()); + EXPECT_EQ(main_refresh_token, + oauth2_service_.refresh_tokens()[main_account_id]->refresh_token()); + EXPECT_EQ( + other_refresh_token, + oauth2_service_.refresh_tokens()[other_account_id]->refresh_token()); + + oauth2_service_.RevokeAllCredentials(); +} + +TEST_F(MutableProfileOAuth2TokenServiceTest, PersistenceRevokeCredentials) { + std::string account_id_1 = "account_id_1"; + std::string refresh_token_1 = "refresh_token_1"; + std::string account_id_2 = "account_id_2"; + std::string refresh_token_2 = "refresh_token_2"; + + // TODO(fgorski): Enable below when implemented: + // EXPECT_FALSE(oauth2_servive_->RefreshTokenIsAvailable(account_id_1)); + // EXPECT_FALSE(oauth2_servive_->RefreshTokenIsAvailable(account_id_2)); + + oauth2_service_.UpdateCredentials(account_id_1, refresh_token_1); + oauth2_service_.UpdateCredentials(account_id_2, refresh_token_2); + + // TODO(fgorski): Enable below when implemented: + // EXPECT_TRUE(oauth2_servive_->RefreshTokenIsAvailable(account_id_1)); + // EXPECT_TRUE(oauth2_service_.RefreshTokenIsAvailable(account_id_2)); + + ResetObserverCounts(); + oauth2_service_.RevokeCredentials(account_id_1); + ExpectOneTokenRevokedNotification(); + + // TODO(fgorski): Enable below when implemented: + // EXPECT_FALSE(oauth2_servive_->RefreshTokenIsAvailable(account_id_1)); + // EXPECT_TRUE(oauth2_service_.RefreshTokenIsAvailable(account_id_2)); + + oauth2_service_.RevokeAllCredentials(); + EXPECT_EQ(0, token_available_count_); + EXPECT_EQ(1, token_revoked_count_); + EXPECT_EQ(0, tokens_loaded_count_); + ResetObserverCounts(); +} + +TEST_F(MutableProfileOAuth2TokenServiceTest, PersistenceLoadCredentials) { + // Ensure DB is clean. + oauth2_service_.RevokeAllCredentials(); + ResetObserverCounts(); + // Perform a load from an empty DB. + oauth2_service_.LoadCredentials("account_id"); + base::RunLoop().RunUntilIdle(); + ExpectOneTokensLoadedNotification(); + // LoadCredentials() guarantees that the account given to it as argument + // is in the refresh_token map. + EXPECT_EQ(1U, oauth2_service_.refresh_tokens().size()); + EXPECT_TRUE( + oauth2_service_.refresh_tokens()["account_id"]->refresh_token().empty()); + // Setup a DB with tokens that don't require upgrade and clear memory. + oauth2_service_.UpdateCredentials("account_id", "refresh_token"); + oauth2_service_.UpdateCredentials("account_id2", "refresh_token2"); + oauth2_service_.refresh_tokens().clear(); + ResetObserverCounts(); + + oauth2_service_.LoadCredentials("account_id"); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(2, token_available_count_); + EXPECT_EQ(0, token_revoked_count_); + EXPECT_EQ(1, tokens_loaded_count_); + ResetObserverCounts(); + + // TODO(fgorski): Enable below when implemented: + // EXPECT_TRUE(oauth2_servive_->RefreshTokenIsAvailable("account_id")); + // EXPECT_TRUE(oauth2_service_.RefreshTokenIsAvailable("account_id2")); + + oauth2_service_.RevokeAllCredentials(); + EXPECT_EQ(0, token_available_count_); + EXPECT_EQ(2, token_revoked_count_); + EXPECT_EQ(0, tokens_loaded_count_); + ResetObserverCounts(); +} + +TEST_F(MutableProfileOAuth2TokenServiceTest, PersistanceNotifications) { + EXPECT_EQ(0, oauth2_service_.cache_size_for_testing()); + oauth2_service_.UpdateCredentials("account_id", "refresh_token"); + ExpectOneTokenAvailableNotification(); + + oauth2_service_.UpdateCredentials("account_id", "refresh_token"); + ExpectNoNotifications(); + + oauth2_service_.UpdateCredentials("account_id", "refresh_token2"); + ExpectOneTokenAvailableNotification(); + + oauth2_service_.RevokeCredentials("account_id"); + ExpectOneTokenRevokedNotification(); + + oauth2_service_.UpdateCredentials("account_id", "refresh_token2"); + ExpectOneTokenAvailableNotification(); + + oauth2_service_.RevokeAllCredentials(); + ResetObserverCounts(); +} + +TEST_F(MutableProfileOAuth2TokenServiceTest, GetAccounts) { + EXPECT_TRUE(oauth2_service_.GetAccounts().empty()); + oauth2_service_.UpdateCredentials("account_id1", "refresh_token1"); + oauth2_service_.UpdateCredentials("account_id2", "refresh_token2"); + std::vector<std::string> accounts = oauth2_service_.GetAccounts(); + EXPECT_EQ(2u, accounts.size()); + EXPECT_EQ(1, count(accounts.begin(), accounts.end(), "account_id1")); + EXPECT_EQ(1, count(accounts.begin(), accounts.end(), "account_id2")); + oauth2_service_.RevokeCredentials("account_id2"); + accounts = oauth2_service_.GetAccounts(); + EXPECT_EQ(1u, oauth2_service_.GetAccounts().size()); + EXPECT_EQ(1, count(accounts.begin(), accounts.end(), "account_id1")); +} + +TEST_F(MutableProfileOAuth2TokenServiceTest, TokenServiceUpdateClearsCache) { + EXPECT_EQ(0, oauth2_service_.cache_size_for_testing()); + std::set<std::string> scope_list; + scope_list.insert("scope"); + oauth2_service_.UpdateCredentials(kEmail, "refreshToken"); + ExpectOneTokenAvailableNotification(); + factory_.SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(), + GetValidTokenResponse("token", 3600), + net::HTTP_OK, + net::URLRequestStatus::SUCCESS); + + scoped_ptr<OAuth2TokenService::Request> request( + oauth2_service_.StartRequest(kEmail, scope_list, &consumer_)); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + EXPECT_EQ("token", consumer_.last_token_); + EXPECT_EQ(1, oauth2_service_.cache_size_for_testing()); + + // Signs out and signs in + oauth2_service_.RevokeCredentials(kEmail); + ExpectOneTokenRevokedNotification(); + + EXPECT_EQ(0, oauth2_service_.cache_size_for_testing()); + oauth2_service_.UpdateCredentials(kEmail, "refreshToken"); + ExpectOneTokenAvailableNotification(); + factory_.SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(), + GetValidTokenResponse("another token", 3600), + net::HTTP_OK, + net::URLRequestStatus::SUCCESS); + + request = oauth2_service_.StartRequest(kEmail, scope_list, &consumer_); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(2, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + EXPECT_EQ("another token", consumer_.last_token_); + EXPECT_EQ(1, oauth2_service_.cache_size_for_testing()); +} + +TEST_F(MutableProfileOAuth2TokenServiceTest, FetchTransientError) { + factory_.SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(), + "", + net::HTTP_FORBIDDEN, + net::URLRequestStatus::FAILED); + + EXPECT_EQ(0, oauth2_service_.cache_size_for_testing()); + std::set<std::string> scope_list; + scope_list.insert("scope"); + oauth2_service_.set_max_authorization_token_fetch_retries_for_testing(0); + oauth2_service_.UpdateCredentials(kEmail, "refreshToken"); + ExpectOneTokenAvailableNotification(); + + scoped_ptr<OAuth2TokenService::Request> request( + oauth2_service_.StartRequest(kEmail, scope_list, &consumer_)); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(GoogleServiceAuthError::AuthErrorNone(), + oauth2_service_.signin_error_controller()->auth_error()); +} diff --git a/components/signin/core/browser/test_signin_client.cc b/components/signin/core/browser/test_signin_client.cc new file mode 100644 index 0000000..dd75e19 --- /dev/null +++ b/components/signin/core/browser/test_signin_client.cc @@ -0,0 +1,46 @@ +// 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 "base/logging.h" +#include "components/signin/core/browser/test_signin_client.h" +#include "components/signin/core/browser/webdata/token_service_table.h" +#include "components/webdata/common/web_data_service_base.h" +#include "components/webdata/common/web_database_service.h" +#include "testing/gtest/include/gtest/gtest.h" + +TestSigninClient::TestSigninClient() + : request_context_(new net::TestURLRequestContextGetter( + base::MessageLoopProxy::current())) { + LoadDatabase(); +} + +TestSigninClient::~TestSigninClient() {} + +PrefService* TestSigninClient::GetPrefs() { return NULL; } + +scoped_refptr<TokenWebData> TestSigninClient::GetDatabase() { + return database_; +} + +bool TestSigninClient::CanRevokeCredentials() { return true; } + +net::URLRequestContextGetter* TestSigninClient::GetURLRequestContext() { + return request_context_; +} + +void TestSigninClient::LoadDatabase() { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + base::FilePath path = temp_dir_.path().AppendASCII("TestWebDB"); + scoped_refptr<WebDatabaseService> web_database = + new WebDatabaseService(path, + base::MessageLoopProxy::current(), + base::MessageLoopProxy::current()); + web_database->AddTable(scoped_ptr<WebDatabaseTable>(new TokenServiceTable())); + web_database->LoadDatabase(); + database_ = new TokenWebData(web_database, + base::MessageLoopProxy::current(), + base::MessageLoopProxy::current(), + WebDataServiceBase::ProfileErrorCallback()); + database_->Init(); +} diff --git a/components/signin/core/browser/test_signin_client.h b/components/signin/core/browser/test_signin_client.h new file mode 100644 index 0000000..d1d2fac --- /dev/null +++ b/components/signin/core/browser/test_signin_client.h @@ -0,0 +1,50 @@ +// 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. + +#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_TEST_SIGNIN_CLIENT_H_ +#define COMPONENTS_SIGNIN_CORE_BROWSER_TEST_SIGNIN_CLIENT_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/files/scoped_temp_dir.h" +#include "base/memory/ref_counted.h" +#include "components/signin/core/browser/signin_client.h" +#include "net/url_request/url_request_test_util.h" + +// An implementation of SigninClient for use in unittests. Instantiates test +// versions of the various objects that SigninClient is required to provide as +// part of its interface. +class TestSigninClient : public SigninClient { + public: + TestSigninClient(); + virtual ~TestSigninClient(); + + // SigninClient implementation that is specialized for unit tests. + + // Returns NULL. + // NOTE: This should be changed to return a properly-initalized PrefService + // once there is a unit test that requires it. + virtual PrefService* GetPrefs() OVERRIDE; + + // Returns a pointer to a loaded database. + virtual scoped_refptr<TokenWebData> GetDatabase() OVERRIDE; + + // Returns true. + virtual bool CanRevokeCredentials() OVERRIDE; + + // Returns a TestURLRequestContextGetter. + virtual net::URLRequestContextGetter* GetURLRequestContext() OVERRIDE; + + private: + // Loads the token database. + void LoadDatabase(); + + base::ScopedTempDir temp_dir_; + scoped_refptr<net::TestURLRequestContextGetter> request_context_; + scoped_refptr<TokenWebData> database_; + + DISALLOW_COPY_AND_ASSIGN(TestSigninClient); +}; + +#endif // COMPONENTS_SIGNIN_CORE_BROWSER_TEST_SIGNIN_CLIENT_H_ |