// 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/gaia_cookie_manager_service.h" #include #include #include #include #include "base/bind.h" #include "base/location.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/strings/stringprintf.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/mock_callback.h" #include "base/test/scoped_task_environment.h" #include "base/test/test_mock_time_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/testing_pref_service.h" #include "components/signin/core/browser/account_tracker_service.h" #include "components/signin/core/browser/signin_pref_names.h" #include "components/signin/core/browser/test_signin_client.h" #include "google_apis/gaia/fake_oauth2_token_service.h" #include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/gaia_urls.h" #include "services/network/test/test_url_loader_factory.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace { using MockAddAccountToCookieCompletedCallback = base::MockCallback< GaiaCookieManagerService::AddAccountToCookieCompletedCallback>; class MockObserver : public GaiaCookieManagerService::Observer { public: explicit MockObserver(GaiaCookieManagerService* helper) : helper_(helper) { helper_->AddObserver(this); } ~MockObserver() override { helper_->RemoveObserver(this); } MOCK_METHOD3(OnGaiaAccountsInCookieUpdated, void(const std::vector&, const std::vector&, const GoogleServiceAuthError&)); private: GaiaCookieManagerService* helper_; DISALLOW_COPY_AND_ASSIGN(MockObserver); }; // Counts number of InstrumentedGaiaCookieManagerService created. // We can EXPECT_* to be zero at the end of our unit tests // to make sure everything is properly deleted. int total = 0; bool AreAccountListsEqual(const std::vector& left, const std::vector& right) { if (left.size() != right.size()) return false; for (size_t i = 0u; i < left.size(); ++i) { const gaia::ListedAccount& left_account = left[i]; const gaia::ListedAccount& actual_account = right[i]; // If both accounts have an ID, use it for the comparison. if (!left_account.id.empty() && !actual_account.id.empty()) { if (left_account.id != actual_account.id) return false; } else if (left_account.email != actual_account.email || left_account.gaia_id != actual_account.gaia_id || left_account.raw_email != actual_account.raw_email || left_account.valid != actual_account.valid || left_account.signed_out != actual_account.signed_out || left_account.verified != actual_account.verified) { return false; } } return true; } // Custom matcher for ListedAccounts. MATCHER_P(ListedAccountEquals, expected, "") { return AreAccountListsEqual(expected, arg); } class InstrumentedGaiaCookieManagerService : public GaiaCookieManagerService { public: InstrumentedGaiaCookieManagerService(OAuth2TokenService* token_service, SigninClient* signin_client) : GaiaCookieManagerService( token_service, signin_client, base::BindRepeating(&SigninClient::GetURLLoaderFactory, base::Unretained(signin_client))) { total++; } ~InstrumentedGaiaCookieManagerService() override { total--; } MOCK_METHOD0(StartFetchingUbertoken, void()); MOCK_METHOD0(StartFetchingListAccounts, void()); MOCK_METHOD0(StartFetchingLogOut, void()); MOCK_METHOD0(StartFetchingMergeSession, void()); MOCK_METHOD1(StartFetchingAccessTokenForMultilogin, void(const std::string& account_id)); MOCK_METHOD0(SetAccountsInCookieWithTokens, void()); MOCK_METHOD1(OnSetAccountsFinished, void(const GoogleServiceAuthError& error)); private: DISALLOW_COPY_AND_ASSIGN(InstrumentedGaiaCookieManagerService); }; class GaiaCookieManagerServiceTest : public testing::Test { public: GaiaCookieManagerServiceTest() : no_error_(GoogleServiceAuthError::NONE), error_(GoogleServiceAuthError::SERVICE_ERROR), canceled_(GoogleServiceAuthError::REQUEST_CANCELED) {} class RequestMockImpl : public OAuth2TokenService::Request { public: RequestMockImpl(std::string account_id) { account_id_ = account_id; } std::string GetAccountId() const override { return account_id_; } private: std::string account_id_; }; void SetUp() override { AccountTrackerService::RegisterPrefs(pref_service_.registry()); signin_client_.reset(new TestSigninClient(&pref_service_)); } OAuth2TokenService* token_service() { return &token_service_; } TestSigninClient* signin_client() { return signin_client_.get(); } void SimulateUbertokenSuccess(GaiaCookieManagerService* gcms, const std::string& uber_token) { gcms->OnUbertokenFetchComplete( GoogleServiceAuthError(GoogleServiceAuthError::NONE), uber_token); } void SimulateUbertokenFailure(GaiaCookieManagerService* gcms, const GoogleServiceAuthError& error) { gcms->OnUbertokenFetchComplete(error, /*uber_token=*/std::string()); } void SimulateAccessTokenFailure(OAuth2TokenService::Consumer* consumer, OAuth2TokenService::Request* request, const GoogleServiceAuthError& error) { consumer->OnGetTokenFailure(request, error); } void SimulateAccessTokenSuccess(OAuth2TokenService::Consumer* consumer, OAuth2TokenService::Request* request) { OAuth2AccessTokenConsumer::TokenResponse token_response = OAuth2AccessTokenConsumer::TokenResponse("AccessToken", base::Time(), "Idtoken"); consumer->OnGetTokenSuccess(request, token_response); } void SimulateMergeSessionSuccess(GaiaAuthConsumer* consumer, const std::string& data) { consumer->OnMergeSessionSuccess(data); } void SimulateMergeSessionFailure(GaiaAuthConsumer* consumer, const GoogleServiceAuthError& error) { consumer->OnMergeSessionFailure(error); } void SimulateMultiloginFinished(GaiaAuthConsumer* consumer, const OAuthMultiloginResult& result) { consumer->OnOAuthMultiloginFinished(result); } void SimulateListAccountsSuccess(GaiaAuthConsumer* consumer, const std::string& data) { consumer->OnListAccountsSuccess(data); } void SimulateLogOutSuccess(GaiaAuthConsumer* consumer) { consumer->OnLogOutSuccess(); } void SimulateLogOutFailure(GaiaAuthConsumer* consumer, const GoogleServiceAuthError& error) { consumer->OnLogOutFailure(error); } void SimulateGetCheckConnectionInfoSuccess(const std::string& data) { signin_client_->test_url_loader_factory()->AddResponse( GaiaUrls::GetInstance() ->GetCheckConnectionInfoURLWithSource(GaiaConstants::kChromeSource) .spec(), data); base::RunLoop().RunUntilIdle(); } void SimulateGetCheckConnectionInfoResult(const std::string& url, const std::string& result) { signin_client_->test_url_loader_factory()->AddResponse(url, result); base::RunLoop().RunUntilIdle(); } void Advance(scoped_refptr test_task_runner, base::TimeDelta advance_by) { test_task_runner->FastForwardBy(advance_by + base::TimeDelta::FromMilliseconds(1)); test_task_runner->RunUntilIdle(); } bool IsLoadPending(const std::string& url) { return signin_client_->test_url_loader_factory()->IsPending( GURL(url).spec()); } bool IsLoadPending() { return signin_client_->test_url_loader_factory()->NumPending() > 0; } const GoogleServiceAuthError& no_error() { return no_error_; } const GoogleServiceAuthError& error() { return error_; } const GoogleServiceAuthError& canceled() { return canceled_; } scoped_refptr factory() const { return signin_client_->GetURLLoaderFactory(); } private: base::test::ScopedTaskEnvironment task_environment_; FakeOAuth2TokenService token_service_; GoogleServiceAuthError no_error_; GoogleServiceAuthError error_; GoogleServiceAuthError canceled_; TestingPrefServiceSimple pref_service_; std::unique_ptr signin_client_; }; } // namespace using ::testing::_; TEST_F(GaiaCookieManagerServiceTest, Success) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); EXPECT_CALL(helper, StartFetchingUbertoken()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; EXPECT_CALL(add_account_to_cookie_completed, Run("acc1@gmail.com", no_error())); helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); SimulateMergeSessionSuccess(&helper, "token"); } TEST_F(GaiaCookieManagerServiceTest, FailedMergeSession) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); base::HistogramTester histograms; EXPECT_CALL(helper, StartFetchingUbertoken()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; EXPECT_CALL(add_account_to_cookie_completed, Run("acc1@gmail.com", error())); helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); SimulateMergeSessionFailure(&helper, error()); // Persistent error incurs no further retries. DCHECK(!helper.is_running()); histograms.ExpectUniqueSample("OAuth2Login.MergeSessionFailure", GoogleServiceAuthError::SERVICE_ERROR, 1); } TEST_F(GaiaCookieManagerServiceTest, AddAccountCookiesDisabled) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); signin_client()->set_are_signin_cookies_allowed(false); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; EXPECT_CALL(add_account_to_cookie_completed, Run("acc1@gmail.com", canceled())); helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); } TEST_F(GaiaCookieManagerServiceTest, MergeSessionRetried) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); auto test_task_runner = base::MakeRefCounted(); base::ScopedClosureRunner task_runner_ = base::ThreadTaskRunnerHandle::OverrideForTesting(test_task_runner); EXPECT_CALL(helper, StartFetchingUbertoken()); EXPECT_CALL(helper, StartFetchingMergeSession()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; EXPECT_CALL(add_account_to_cookie_completed, Run("acc1@gmail.com", no_error())); helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); SimulateMergeSessionFailure(&helper, canceled()); DCHECK(helper.is_running()); Advance(test_task_runner, helper.GetBackoffEntry()->GetTimeUntilRelease()); SimulateMergeSessionSuccess(&helper, "token"); DCHECK(!helper.is_running()); } TEST_F(GaiaCookieManagerServiceTest, MergeSessionRetriedTwice) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); base::HistogramTester histograms; auto test_task_runner = base::MakeRefCounted(); base::ScopedClosureRunner task_runner_ = base::ThreadTaskRunnerHandle::OverrideForTesting(test_task_runner); EXPECT_CALL(helper, StartFetchingUbertoken()); EXPECT_CALL(helper, StartFetchingMergeSession()).Times(2); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; EXPECT_CALL(add_account_to_cookie_completed, Run("acc1@gmail.com", no_error())); helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); SimulateMergeSessionFailure(&helper, canceled()); DCHECK(helper.is_running()); Advance(test_task_runner, helper.GetBackoffEntry()->GetTimeUntilRelease()); SimulateMergeSessionFailure(&helper, canceled()); DCHECK(helper.is_running()); Advance(test_task_runner, helper.GetBackoffEntry()->GetTimeUntilRelease()); SimulateMergeSessionSuccess(&helper, "token"); DCHECK(!helper.is_running()); histograms.ExpectUniqueSample("OAuth2Login.MergeSessionRetry", GoogleServiceAuthError::REQUEST_CANCELED, 2); } TEST_F(GaiaCookieManagerServiceTest, FailedUbertoken) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); EXPECT_CALL(helper, StartFetchingUbertoken()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; EXPECT_CALL(add_account_to_cookie_completed, Run("acc1@gmail.com", error())); helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); SimulateUbertokenFailure(&helper, error()); } TEST_F(GaiaCookieManagerServiceTest, AccessTokenSuccess) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); auto test_task_runner = base::MakeRefCounted(); base::ScopedClosureRunner task_runner_ = base::ThreadTaskRunnerHandle::OverrideForTesting(test_task_runner); const std::string account_id1 = "12345"; const std::string account_id2 = "23456"; testing::InSequence mock_sequence; EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) .Times(1); EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2)) .Times(1); EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) .Times(1); EXPECT_CALL(helper, SetAccountsInCookieWithTokens()); const std::vector account_ids = {account_id1, account_id2}; helper.SetAccountsInCookie( account_ids, gaia::GaiaSource::kChrome, GaiaCookieManagerService::SetAccountsInCookieCompletedCallback()); RequestMockImpl request1(account_id1); RequestMockImpl request2(account_id2); SimulateAccessTokenSuccess(&helper, &request2); GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_UNAVAILABLE); // Transient error, retry. SimulateAccessTokenFailure(&helper, &request1, error); DCHECK(helper.is_running()); Advance(test_task_runner, helper.GetBackoffEntry()->GetTimeUntilRelease()); SimulateAccessTokenSuccess(&helper, &request1); } TEST_F(GaiaCookieManagerServiceTest, AccessTokenFailureTransientErrorMaxRetriesReached) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); auto test_task_runner = base::MakeRefCounted(); base::ScopedClosureRunner task_runner_ = base::ThreadTaskRunnerHandle::OverrideForTesting(test_task_runner); const std::string account_id1 = "12345"; const std::string account_id2 = "23456"; GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_UNAVAILABLE); testing::InSequence mock_sequence; EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) .Times(1); EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2)) .Times(1); EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) .Times(signin::kMaxFetcherRetries - 1); EXPECT_CALL(helper, OnSetAccountsFinished(error)).Times(1); EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(0); const std::vector account_ids = {account_id1, account_id2}; helper.SetAccountsInCookie( account_ids, gaia::GaiaSource::kChrome, GaiaCookieManagerService::SetAccountsInCookieCompletedCallback()); RequestMockImpl request1(account_id1); RequestMockImpl request2(account_id2); EXPECT_LT(helper.GetBackoffEntry()->GetTimeUntilRelease(), base::TimeDelta::FromMilliseconds(1100)); // Transient error, retry, fail when maximum number of retries is reached. for (int i = 0; i < signin::kMaxFetcherRetries - 1; ++i) { SimulateAccessTokenFailure(&helper, &request1, error); Advance(test_task_runner, helper.GetBackoffEntry()->GetTimeUntilRelease()); } SimulateAccessTokenFailure(&helper, &request1, error); // Check that no Multilogin is triggered. SimulateAccessTokenSuccess(&helper, &request2); } TEST_F(GaiaCookieManagerServiceTest, AccessTokenFailurePersistentError) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); const std::string account_id1 = "12345"; const std::string account_id2 = "23456"; GoogleServiceAuthError error( GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); testing::InSequence mock_sequence; EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) .Times(1); EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2)) .Times(1); EXPECT_CALL(helper, OnSetAccountsFinished(error)).Times(1); EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(0); const std::vector account_ids = {account_id1, account_id2}; helper.SetAccountsInCookie( account_ids, gaia::GaiaSource::kChrome, GaiaCookieManagerService::SetAccountsInCookieCompletedCallback()); RequestMockImpl request1(account_id1); RequestMockImpl request2(account_id2); SimulateAccessTokenFailure(&helper, &request1, error); // Check that no Multilogin is triggered. SimulateAccessTokenSuccess(&helper, &request2); } TEST_F(GaiaCookieManagerServiceTest, FetcherRetriesZeroedBetweenCalls) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); auto test_task_runner = base::MakeRefCounted(); base::ScopedClosureRunner task_runner_ = base::ThreadTaskRunnerHandle::OverrideForTesting(test_task_runner); const std::string account_id1 = "12345"; const std::string account_id2 = "23456"; GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_UNAVAILABLE); std::string data = R"()]}' { "status": "OK", "cookies":[ { "name":"SID", "value":"vAlUe1", "domain":".google.ru", "path":"/", "isSecure":true, "isHttpOnly":false, "priority":"HIGH", "maxAge":63070000 } ] } )"; OAuthMultiloginResult result(data); ASSERT_EQ(result.error().state(), GoogleServiceAuthError::State::NONE); testing::InSequence mock_sequence; EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) .Times(1); EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2)) .Times(1); // retry call EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) .Times(signin::kMaxFetcherRetries - 1); // retry call EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(1); EXPECT_CALL(helper, OnSetAccountsFinished(GoogleServiceAuthError::AuthErrorNone())) .Times(1); const std::vector account_ids = {account_id1, account_id2}; helper.SetAccountsInCookie( account_ids, gaia::GaiaSource::kChrome, GaiaCookieManagerService::SetAccountsInCookieCompletedCallback()); RequestMockImpl request1(account_id1); RequestMockImpl request2(account_id2); // Transient error, retry. // Succeed when only one retry is left. Simulate Multilogin failure. Check // that it retries. for (int i = 0; i < signin::kMaxFetcherRetries - 1; ++i) { SimulateAccessTokenFailure(&helper, &request1, error); Advance(test_task_runner, helper.GetBackoffEntry()->GetTimeUntilRelease()); } SimulateAccessTokenSuccess(&helper, &request1); SimulateAccessTokenSuccess(&helper, &request2); std::vector accounts = std::vector(); accounts.emplace_back(account_id1, "AccessToken"); accounts.emplace_back(account_id2, "AccessToken"); helper.StartFetchingMultiLogin(accounts); SimulateMultiloginFinished(&helper, OAuthMultiloginResult(error)); SimulateMultiloginFinished(&helper, result); } TEST_F(GaiaCookieManagerServiceTest, MultiloginSuccessAndCookiesSet) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); auto test_task_runner = base::MakeRefCounted(); base::ScopedClosureRunner task_runner_ = base::ThreadTaskRunnerHandle::OverrideForTesting(test_task_runner); const std::string account_id1 = "12345"; const std::string account_id2 = "23456"; const std::vector account_ids = {account_id1, account_id2}; std::vector accounts = std::vector(); accounts.emplace_back(account_id1, "AccessToken"); accounts.emplace_back(account_id2, "AccessToken"); GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_UNAVAILABLE); std::string data = R"()]}' { "status": "OK", "cookies":[ { "name":"SID", "value":"vAlUe1", "domain":".google.ru", "path":"/", "isSecure":true, "isHttpOnly":false, "priority":"HIGH", "maxAge":63070000 }, { "name":"SID", "value":"vAlUe1", "domain":".google.ru", "path":"/", "isSecure":true, "isHttpOnly":false, "priority":"HIGH", "maxAge":63070000 }, { "name":"HSID", "value":"vAlUe4", "host":"google.fr", "path":"/", "isSecure":true, "isHttpOnly":false, "priority":"HIGH", "maxAge":0 } ] } )"; OAuthMultiloginResult result(data); ASSERT_EQ(result.error().state(), GoogleServiceAuthError::State::NONE); testing::InSequence mock_sequence; EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) .Times(1); EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2)) .Times(1); EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(1); EXPECT_CALL(helper, OnSetAccountsFinished(GoogleServiceAuthError::AuthErrorNone())) .Times(1); // Needed to insert request in the queue. helper.SetAccountsInCookie( account_ids, gaia::GaiaSource::kChrome, GaiaCookieManagerService::SetAccountsInCookieCompletedCallback()); helper.StartFetchingMultiLogin(accounts); SimulateMultiloginFinished(&helper, OAuthMultiloginResult(error)); DCHECK(helper.is_running()); Advance(test_task_runner, helper.GetBackoffEntry()->GetTimeUntilRelease()); SimulateMultiloginFinished(&helper, result); } TEST_F(GaiaCookieManagerServiceTest, MultiloginFailurePersistentError) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); const std::string account_id1 = "12345"; const std::string account_id2 = "23456"; const std::vector account_ids = {account_id1, account_id2}; std::vector accounts = std::vector(); accounts.emplace_back(account_id1, "AccessToken"); accounts.emplace_back(account_id2, "AccessToken"); GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_ERROR); testing::InSequence mock_sequence; EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) .Times(1); EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2)) .Times(1); EXPECT_CALL(helper, OnSetAccountsFinished(error)).Times(1); // Needed to insert request in the queue. helper.SetAccountsInCookie( account_ids, gaia::GaiaSource::kChrome, GaiaCookieManagerService::SetAccountsInCookieCompletedCallback()); helper.StartFetchingMultiLogin(accounts); SimulateMultiloginFinished(&helper, OAuthMultiloginResult(error)); } TEST_F(GaiaCookieManagerServiceTest, MultiloginFailureMaxRetriesReached) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); auto test_task_runner = base::MakeRefCounted(); base::ScopedClosureRunner task_runner_ = base::ThreadTaskRunnerHandle::OverrideForTesting(test_task_runner); const std::string account_id1 = "12345"; const std::string account_id2 = "23456"; const std::vector account_ids = {account_id1, account_id2}; std::vector accounts = std::vector(); accounts.emplace_back(account_id1, "AccessToken"); accounts.emplace_back(account_id2, "AccessToken"); GoogleServiceAuthError error(GoogleServiceAuthError::SERVICE_UNAVAILABLE); testing::InSequence mock_sequence; EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) .Times(1); EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2)) .Times(1); // This is the retry call, the first call is skipped as we call // StartFetchingMultiLogim explicitly instead. EXPECT_CALL(helper, SetAccountsInCookieWithTokens()) .Times(signin::kMaxFetcherRetries - 1); EXPECT_CALL(helper, OnSetAccountsFinished(error)).Times(1); // Needed to insert request in the queue. helper.SetAccountsInCookie( account_ids, gaia::GaiaSource::kChrome, GaiaCookieManagerService::SetAccountsInCookieCompletedCallback()); helper.StartFetchingMultiLogin(accounts); // Transient error, retry, fail when maximum number of retries is reached. for (int i = 0; i < signin::kMaxFetcherRetries - 1; ++i) { SimulateMultiloginFinished(&helper, OAuthMultiloginResult(error)); Advance(test_task_runner, helper.GetBackoffEntry()->GetTimeUntilRelease()); } SimulateMultiloginFinished(&helper, OAuthMultiloginResult(error)); } TEST_F(GaiaCookieManagerServiceTest, MultiloginFailureInvalidGaiaCredentialsMobile) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); const std::string account_id1 = "12345"; const std::string account_id2 = "23456"; const std::vector account_ids = {account_id1, account_id2}; std::vector accounts = std::vector(); accounts.emplace_back(account_id1, "AccessToken"); accounts.emplace_back(account_id2, "AccessToken"); RequestMockImpl request1(account_id1); RequestMockImpl request2(account_id2); GoogleServiceAuthError error( GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); std::string data_ok = R"()]}' { "status": "OK", "cookies":[ { "name":"SID", "value":"vAlUe1", "domain":".google.ru", "path":"/", "isSecure":true, "isHttpOnly":false, "priority":"HIGH", "maxAge":63070000 } ] } )"; OAuthMultiloginResult result_ok(data_ok); ASSERT_EQ(result_ok.error().state(), GoogleServiceAuthError::State::NONE); std::string data_failed = R"()]}' { "status": "INVALID_TOKENS", "failed_accounts": [ { "obfuscated_id": "12345", "status": "RECOVERABLE" }, { "obfuscated_id": "23456", "status": "OK" } ] } )"; OAuthMultiloginResult result_failed(data_failed); ASSERT_EQ(result_failed.error().state(), GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS); testing::InSequence mock_sequence; EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) .Times(1); EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2)) .Times(1); EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(1); // On mobile token_service is expected to retry getting access tokens first. EXPECT_CALL(helper, OnSetAccountsFinished(error)).Times(0); // Expect to try to refetch failed account one more time. EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) .Times(1); // This time access tokens are supposed to be correct. EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(1); EXPECT_CALL(helper, OnSetAccountsFinished(GoogleServiceAuthError::AuthErrorNone())) .Times(1); // Needed to insert request in the queue. helper.SetAccountsInCookie( account_ids, gaia::GaiaSource::kChrome, GaiaCookieManagerService::SetAccountsInCookieCompletedCallback()); // Both requests for access tokens are successful but they could be returned // from cache and be stale. SimulateAccessTokenSuccess(&helper, &request1); SimulateAccessTokenSuccess(&helper, &request2); // Both tokens are inserted in the map. EXPECT_EQ(2u, helper.access_tokens_.size()); helper.StartFetchingMultiLogin(accounts); // Access tokens were stale, Multilogin failed. SimulateMultiloginFinished(&helper, result_failed); // GaiaCookieManagerService should retry fetching access token again, // it should be removed from the token map. EXPECT_EQ(1u, helper.access_tokens_.size()); EXPECT_EQ(helper.access_tokens_.begin()->first, account_id2); // This time access token is fresh. SimulateAccessTokenSuccess(&helper, &request1); EXPECT_EQ(2u, helper.access_tokens_.size()); // And Multilogin is successful. SimulateMultiloginFinished(&helper, result_ok); // The end. } TEST_F(GaiaCookieManagerServiceTest, MultiloginFailureInvalidGaiaCredentialsDesktop) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); const std::string account_id1 = "12345"; const std::string account_id2 = "23456"; const std::vector account_ids = {account_id1, account_id2}; std::vector accounts = std::vector(); accounts.emplace_back(account_id1, "AccessToken"); accounts.emplace_back(account_id2, "AccessToken"); RequestMockImpl request1(account_id1); RequestMockImpl request2(account_id2); GoogleServiceAuthError error( GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); std::string data_failed = R"()]}' { "status": "INVALID_TOKENS", "failed_accounts": [ { "obfuscated_id": "12345", "status": "RECOVERABLE" }, { "obfuscated_id": "23456", "status": "OK" } ] } )"; OAuthMultiloginResult result_failed(data_failed); ASSERT_EQ(result_failed.error().state(), GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS); testing::InSequence mock_sequence; EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) .Times(1); EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id2)) .Times(1); EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(1); // Expect to try to refetch failed account one more time. EXPECT_CALL(helper, StartFetchingAccessTokenForMultilogin(account_id1)) .Times(1); // And fail right away. EXPECT_CALL(helper, SetAccountsInCookieWithTokens()).Times(0); EXPECT_CALL(helper, OnSetAccountsFinished(error)).Times(1); // Needed to insert request in the queue. helper.SetAccountsInCookie( account_ids, gaia::GaiaSource::kChrome, GaiaCookieManagerService::SetAccountsInCookieCompletedCallback()); // Both requests for access tokens are successful but they could be returned // from cache and be stale. SimulateAccessTokenSuccess(&helper, &request1); SimulateAccessTokenSuccess(&helper, &request2); // Both tokens are inserted in the map. EXPECT_EQ(2u, helper.access_tokens_.size()); helper.StartFetchingMultiLogin(accounts); // On desktop refresh tokens were used and failed. Token service will retry // fetching access token but refresh token is supposed to be set in error and // request will return with immediate error. SimulateMultiloginFinished(&helper, result_failed); SimulateAccessTokenFailure(&helper, &request1, error); } TEST_F(GaiaCookieManagerServiceTest, ContinueAfterSuccess) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); EXPECT_CALL(helper, StartFetchingUbertoken()).Times(2); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed1, add_account_to_cookie_completed2; EXPECT_CALL(add_account_to_cookie_completed1, Run("acc1@gmail.com", no_error())); EXPECT_CALL(add_account_to_cookie_completed2, Run("acc2@gmail.com", no_error())); helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed1.Get()); helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed2.Get()); SimulateMergeSessionSuccess(&helper, "token1"); SimulateMergeSessionSuccess(&helper, "token2"); } TEST_F(GaiaCookieManagerServiceTest, ContinueAfterFailure1) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); EXPECT_CALL(helper, StartFetchingUbertoken()).Times(2); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed1, add_account_to_cookie_completed2; EXPECT_CALL(add_account_to_cookie_completed1, Run("acc1@gmail.com", error())); EXPECT_CALL(add_account_to_cookie_completed2, Run("acc2@gmail.com", no_error())); helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed1.Get()); helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed2.Get()); SimulateMergeSessionFailure(&helper, error()); SimulateMergeSessionSuccess(&helper, "token2"); } TEST_F(GaiaCookieManagerServiceTest, ContinueAfterFailure2) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); EXPECT_CALL(helper, StartFetchingUbertoken()).Times(2); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed1, add_account_to_cookie_completed2; EXPECT_CALL(add_account_to_cookie_completed1, Run("acc1@gmail.com", error())); EXPECT_CALL(add_account_to_cookie_completed2, Run("acc2@gmail.com", no_error())); helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed1.Get()); helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed2.Get()); SimulateUbertokenFailure(&helper, error()); SimulateMergeSessionSuccess(&helper, "token2"); } TEST_F(GaiaCookieManagerServiceTest, AllRequestsInMultipleGoes) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); EXPECT_CALL(helper, StartFetchingUbertoken()).Times(4); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; EXPECT_CALL(add_account_to_cookie_completed, Run(_, no_error())).Times(4); helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); SimulateMergeSessionSuccess(&helper, "token1"); helper.AddAccountToCookie("acc3@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); SimulateMergeSessionSuccess(&helper, "token2"); SimulateMergeSessionSuccess(&helper, "token3"); helper.AddAccountToCookie("acc4@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); SimulateMergeSessionSuccess(&helper, "token4"); } TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsNoQueue) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); EXPECT_CALL(helper, StartFetchingUbertoken()); EXPECT_CALL(helper, StartFetchingLogOut()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; EXPECT_CALL(add_account_to_cookie_completed, Run("acc2@gmail.com", no_error())); helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); SimulateMergeSessionSuccess(&helper, "token1"); helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); SimulateLogOutSuccess(&helper); ASSERT_FALSE(helper.is_running()); } TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsFails) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); EXPECT_CALL(helper, StartFetchingUbertoken()); EXPECT_CALL(helper, StartFetchingLogOut()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; EXPECT_CALL(add_account_to_cookie_completed, Run("acc2@gmail.com", no_error())); helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); SimulateMergeSessionSuccess(&helper, "token1"); helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); SimulateLogOutFailure(&helper, error()); // CookieManagerService is still running; it is retrying the failed logout. ASSERT_TRUE(helper.is_running()); } TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsAfterOneAddInQueue) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); EXPECT_CALL(helper, StartFetchingUbertoken()); EXPECT_CALL(helper, StartFetchingLogOut()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; EXPECT_CALL(add_account_to_cookie_completed, Run("acc2@gmail.com", no_error())); helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); SimulateMergeSessionSuccess(&helper, "token1"); SimulateLogOutSuccess(&helper); } TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsAfterTwoAddsInQueue) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); EXPECT_CALL(helper, StartFetchingUbertoken()); EXPECT_CALL(helper, StartFetchingLogOut()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed1, add_account_to_cookie_completed2; EXPECT_CALL(add_account_to_cookie_completed1, Run("acc1@gmail.com", no_error())); EXPECT_CALL(add_account_to_cookie_completed2, Run("acc2@gmail.com", canceled())); helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed1.Get()); // The Log Out should prevent this AddAccount from being fetched. helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed2.Get()); helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); SimulateMergeSessionSuccess(&helper, "token1"); SimulateLogOutSuccess(&helper); } TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsTwice) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); EXPECT_CALL(helper, StartFetchingUbertoken()); EXPECT_CALL(helper, StartFetchingLogOut()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed; EXPECT_CALL(add_account_to_cookie_completed, Run("acc2@gmail.com", no_error())); helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed.Get()); SimulateMergeSessionSuccess(&helper, "token1"); helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); // Only one LogOut will be fetched. helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); SimulateLogOutSuccess(&helper); } TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsBeforeAdd) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); EXPECT_CALL(helper, StartFetchingUbertoken()).Times(2); EXPECT_CALL(helper, StartFetchingLogOut()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed2, add_account_to_cookie_completed3; EXPECT_CALL(add_account_to_cookie_completed2, Run("acc2@gmail.com", no_error())); EXPECT_CALL(add_account_to_cookie_completed3, Run("acc3@gmail.com", no_error())); helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed2.Get()); SimulateMergeSessionSuccess(&helper, "token1"); helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); helper.AddAccountToCookie("acc3@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed3.Get()); SimulateLogOutSuccess(&helper); // After LogOut the MergeSession should be fetched. SimulateMergeSessionSuccess(&helper, "token2"); } TEST_F(GaiaCookieManagerServiceTest, LogOutAllAccountsBeforeLogoutAndAdd) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); EXPECT_CALL(helper, StartFetchingUbertoken()).Times(2); EXPECT_CALL(helper, StartFetchingLogOut()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed2, add_account_to_cookie_completed3; EXPECT_CALL(add_account_to_cookie_completed2, Run("acc2@gmail.com", no_error())); EXPECT_CALL(add_account_to_cookie_completed3, Run("acc3@gmail.com", no_error())); helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed2.Get()); SimulateMergeSessionSuccess(&helper, "token1"); helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); // Second LogOut will never be fetched. helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); helper.AddAccountToCookie("acc3@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed3.Get()); SimulateLogOutSuccess(&helper); // After LogOut the MergeSession should be fetched. SimulateMergeSessionSuccess(&helper, "token2"); } TEST_F(GaiaCookieManagerServiceTest, PendingSigninThenSignout) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); // From the first Signin. MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed1; EXPECT_CALL(add_account_to_cookie_completed1, Run("acc1@gmail.com", no_error())); // From the sign out and then re-sign in. EXPECT_CALL(helper, StartFetchingLogOut()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed3; EXPECT_CALL(add_account_to_cookie_completed3, Run("acc3@gmail.com", no_error())); // Total sign in 2 times, not enforcing ordered sequences. EXPECT_CALL(helper, StartFetchingUbertoken()).Times(2); helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed1.Get()); helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); SimulateMergeSessionSuccess(&helper, "token1"); SimulateLogOutSuccess(&helper); helper.AddAccountToCookie("acc3@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed3.Get()); SimulateMergeSessionSuccess(&helper, "token3"); } TEST_F(GaiaCookieManagerServiceTest, CancelSignIn) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); EXPECT_CALL(helper, StartFetchingUbertoken()); MockAddAccountToCookieCompletedCallback add_account_to_cookie_completed1, add_account_to_cookie_completed2; EXPECT_CALL(add_account_to_cookie_completed1, Run("acc1@gmail.com", no_error())); EXPECT_CALL(add_account_to_cookie_completed2, Run("acc2@gmail.com", canceled())); EXPECT_CALL(helper, StartFetchingLogOut()); helper.AddAccountToCookie("acc1@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed1.Get()); helper.AddAccountToCookie("acc2@gmail.com", gaia::GaiaSource::kChrome, add_account_to_cookie_completed2.Get()); helper.LogOutAllAccounts(gaia::GaiaSource::kChrome); SimulateMergeSessionSuccess(&helper, "token1"); SimulateLogOutSuccess(&helper); } TEST_F(GaiaCookieManagerServiceTest, ListAccountsFirstReturnsEmpty) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); std::vector list_accounts; std::vector signed_out_accounts; EXPECT_CALL(helper, StartFetchingListAccounts()); ASSERT_FALSE(helper.ListAccounts(&list_accounts, &signed_out_accounts)); ASSERT_TRUE(list_accounts.empty()); ASSERT_TRUE(signed_out_accounts.empty()); } TEST_F(GaiaCookieManagerServiceTest, ListAccountsFindsOneAccount) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); std::vector list_accounts; std::vector expected_accounts; gaia::ListedAccount listed_account; listed_account.email = "a@b.com"; listed_account.raw_email = "a@b.com"; listed_account.gaia_id = "8"; expected_accounts.push_back(listed_account); std::vector signed_out_accounts; std::vector expected_signed_out_accounts; EXPECT_CALL(helper, StartFetchingListAccounts()); EXPECT_CALL(observer, OnGaiaAccountsInCookieUpdated( ListedAccountEquals(expected_accounts), ListedAccountEquals(expected_signed_out_accounts), no_error())); ASSERT_FALSE(helper.ListAccounts(&list_accounts, &signed_out_accounts)); SimulateListAccountsSuccess( &helper, "[\"f\", [[\"b\", 0, \"n\", \"a@b.com\", \"p\", 0, 0, 0, 0, 1, \"8\"]]]"); } TEST_F(GaiaCookieManagerServiceTest, ListAccountsFindsSignedOutAccounts) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); std::vector list_accounts; std::vector expected_accounts; gaia::ListedAccount listed_account; listed_account.email = "a@b.com"; listed_account.raw_email = "a@b.com"; listed_account.gaia_id = "8"; expected_accounts.push_back(listed_account); std::vector signed_out_accounts; std::vector expected_signed_out_accounts; gaia::ListedAccount signed_out_account; signed_out_account.email = "c@d.com"; signed_out_account.raw_email = "c@d.com"; signed_out_account.gaia_id = "9"; signed_out_account.signed_out = true; expected_signed_out_accounts.push_back(signed_out_account); EXPECT_CALL(helper, StartFetchingListAccounts()); EXPECT_CALL(observer, OnGaiaAccountsInCookieUpdated( ListedAccountEquals(expected_accounts), ListedAccountEquals(expected_signed_out_accounts), no_error())); ASSERT_FALSE(helper.ListAccounts(&list_accounts, &signed_out_accounts)); SimulateListAccountsSuccess( &helper, "[\"f\"," "[[\"b\", 0, \"n\", \"a@b.com\", \"p\", 0, 0, 0, 0, 1, \"8\"]," " [\"b\", 0, \"n\", \"c@d.com\", \"p\", 0, 0, 0, 0, 1, \"9\"," "null,null,null,1]]]"); } TEST_F(GaiaCookieManagerServiceTest, ListAccountsAcceptsNull) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); ASSERT_FALSE(helper.ListAccounts(nullptr, nullptr)); SimulateListAccountsSuccess( &helper, "[\"f\"," "[[\"b\", 0, \"n\", \"a@b.com\", \"p\", 0, 0, 0, 0, 1, \"8\"]," " [\"b\", 0, \"n\", \"c@d.com\", \"p\", 0, 0, 0, 0, 1, \"9\"," "null,null,null,1]]]"); std::vector signed_out_accounts; ASSERT_TRUE(helper.ListAccounts(nullptr, &signed_out_accounts)); ASSERT_EQ(1u, signed_out_accounts.size()); std::vector accounts; ASSERT_TRUE(helper.ListAccounts(&accounts, nullptr)); ASSERT_EQ(1u, accounts.size()); } TEST_F(GaiaCookieManagerServiceTest, ListAccountsAfterOnCookieChange) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); MockObserver observer(&helper); std::vector list_accounts; std::vector empty_list_accounts; std::vector signed_out_accounts; std::vector empty_signed_out_accounts; std::vector nonempty_list_accounts; gaia::ListedAccount listed_account; listed_account.email = "a@b.com"; listed_account.raw_email = "a@b.com"; listed_account.gaia_id = "8"; nonempty_list_accounts.push_back(listed_account); // Add a single account. EXPECT_CALL(helper, StartFetchingListAccounts()); EXPECT_CALL(observer, OnGaiaAccountsInCookieUpdated( ListedAccountEquals(nonempty_list_accounts), ListedAccountEquals(empty_signed_out_accounts), no_error())); ASSERT_FALSE(helper.ListAccounts(&list_accounts, &signed_out_accounts)); ASSERT_TRUE(list_accounts.empty()); ASSERT_TRUE(signed_out_accounts.empty()); SimulateListAccountsSuccess( &helper, "[\"f\", [[\"b\", 0, \"n\", \"a@b.com\", \"p\", 0, 0, 0, 0, 1, \"8\"]]]"); // Sanity-check that ListAccounts returns the cached data. ASSERT_TRUE(helper.ListAccounts(&list_accounts, &signed_out_accounts)); ASSERT_TRUE(AreAccountListsEqual(nonempty_list_accounts, list_accounts)); ASSERT_TRUE(signed_out_accounts.empty()); EXPECT_CALL(helper, StartFetchingListAccounts()); EXPECT_CALL(observer, OnGaiaAccountsInCookieUpdated( ListedAccountEquals(empty_list_accounts), ListedAccountEquals(empty_signed_out_accounts), no_error())); helper.ForceOnCookieChangeProcessing(); // OnCookieChange should invalidate the cached data. // Clear the list before calling |ListAccounts()| to make sure that // GaiaCookieManagerService repopulates it with the stale cached information. list_accounts.clear(); ASSERT_FALSE(helper.ListAccounts(&list_accounts, &signed_out_accounts)); ASSERT_TRUE(AreAccountListsEqual(nonempty_list_accounts, list_accounts)); ASSERT_TRUE(signed_out_accounts.empty()); SimulateListAccountsSuccess(&helper, "[\"f\",[]]"); } TEST_F(GaiaCookieManagerServiceTest, ExternalCcResultFetcher) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); GaiaCookieManagerService::ExternalCcResultFetcher result_fetcher(&helper); EXPECT_CALL(helper, StartFetchingMergeSession()); result_fetcher.Start(base::BindOnce( &InstrumentedGaiaCookieManagerService::StartFetchingMergeSession, base::Unretained(&helper))); // Simulate a successful completion of GetCheckConnctionInfo. SimulateGetCheckConnectionInfoSuccess( "[{\"carryBackToken\": \"yt\", \"url\": \"http://www.yt.com\"}," " {\"carryBackToken\": \"bl\", \"url\": \"http://www.bl.com\"}]"); // Simulate responses for the two connection URLs. GaiaCookieManagerService::ExternalCcResultFetcher::LoaderToToken loaders = result_fetcher.get_loader_map_for_testing(); ASSERT_EQ(2u, loaders.size()); ASSERT_TRUE(IsLoadPending("http://www.yt.com")); ASSERT_TRUE(IsLoadPending("http://www.bl.com")); ASSERT_EQ("bl:null,yt:null", result_fetcher.GetExternalCcResult()); SimulateGetCheckConnectionInfoResult("http://www.yt.com", "yt_result"); ASSERT_EQ("bl:null,yt:yt_result", result_fetcher.GetExternalCcResult()); SimulateGetCheckConnectionInfoResult("http://www.bl.com", "bl_result"); ASSERT_EQ("bl:bl_result,yt:yt_result", result_fetcher.GetExternalCcResult()); } TEST_F(GaiaCookieManagerServiceTest, ExternalCcResultFetcherTimeout) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); GaiaCookieManagerService::ExternalCcResultFetcher result_fetcher(&helper); EXPECT_CALL(helper, StartFetchingMergeSession()); result_fetcher.Start(base::BindOnce( &InstrumentedGaiaCookieManagerService::StartFetchingMergeSession, base::Unretained(&helper))); // Simulate a successful completion of GetCheckConnctionInfo. SimulateGetCheckConnectionInfoSuccess( "[{\"carryBackToken\": \"yt\", \"url\": \"http://www.yt.com\"}," " {\"carryBackToken\": \"bl\", \"url\": \"http://www.bl.com\"}]"); GaiaCookieManagerService::ExternalCcResultFetcher::LoaderToToken loaders = result_fetcher.get_loader_map_for_testing(); ASSERT_EQ(2u, loaders.size()); ASSERT_TRUE(IsLoadPending("http://www.yt.com")); ASSERT_TRUE(IsLoadPending("http://www.bl.com")); // Simulate response only for "yt". ASSERT_EQ("bl:null,yt:null", result_fetcher.GetExternalCcResult()); SimulateGetCheckConnectionInfoResult("http://www.yt.com", "yt_result"); ASSERT_EQ("bl:null,yt:yt_result", result_fetcher.GetExternalCcResult()); // Now timeout. result_fetcher.TimeoutForTests(); ASSERT_EQ("bl:null,yt:yt_result", result_fetcher.GetExternalCcResult()); loaders = result_fetcher.get_loader_map_for_testing(); ASSERT_EQ(0u, loaders.size()); } TEST_F(GaiaCookieManagerServiceTest, ExternalCcResultFetcherTruncate) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); GaiaCookieManagerService::ExternalCcResultFetcher result_fetcher(&helper); EXPECT_CALL(helper, StartFetchingMergeSession()); result_fetcher.Start(base::BindOnce( &InstrumentedGaiaCookieManagerService::StartFetchingMergeSession, base::Unretained(&helper))); // Simulate a successful completion of GetCheckConnctionInfo. SimulateGetCheckConnectionInfoSuccess( "[{\"carryBackToken\": \"yt\", \"url\": \"http://www.yt.com\"}]"); GaiaCookieManagerService::ExternalCcResultFetcher::LoaderToToken loaders = result_fetcher.get_loader_map_for_testing(); ASSERT_EQ(1u, loaders.size()); ASSERT_TRUE(IsLoadPending("http://www.yt.com")); // Simulate response for "yt" with a string that is too long. SimulateGetCheckConnectionInfoResult("http://www.yt.com", "1234567890123456trunc"); ASSERT_EQ("yt:1234567890123456", result_fetcher.GetExternalCcResult()); } TEST_F(GaiaCookieManagerServiceTest, UbertokenSuccessFetchesExternalCC) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); EXPECT_CALL(helper, StartFetchingUbertoken()); helper.AddAccountToCookie( "acc1@gmail.com", gaia::GaiaSource::kChrome, GaiaCookieManagerService::AddAccountToCookieCompletedCallback()); ASSERT_FALSE(IsLoadPending()); SimulateUbertokenSuccess(&helper, "token"); // Check there is now a fetcher that belongs to the ExternalCCResultFetcher. SimulateGetCheckConnectionInfoSuccess( "[{\"carryBackToken\": \"bl\", \"url\": \"http://www.bl.com\"}]"); GaiaCookieManagerService::ExternalCcResultFetcher* result_fetcher = helper.external_cc_result_fetcher_for_testing(); GaiaCookieManagerService::ExternalCcResultFetcher::LoaderToToken loaders = result_fetcher->get_loader_map_for_testing(); ASSERT_EQ(1u, loaders.size()); ASSERT_TRUE(IsLoadPending("http://www.bl.com")); } TEST_F(GaiaCookieManagerServiceTest, UbertokenSuccessFetchesExternalCCOnce) { InstrumentedGaiaCookieManagerService helper(token_service(), signin_client()); helper.external_cc_result_fetcher_for_testing()->Start(base::BindOnce( &InstrumentedGaiaCookieManagerService::StartFetchingMergeSession, base::Unretained(&helper))); EXPECT_CALL(helper, StartFetchingUbertoken()); helper.AddAccountToCookie( "acc2@gmail.com", gaia::GaiaSource::kChrome, GaiaCookieManagerService::AddAccountToCookieCompletedCallback()); // There is already a ExternalCCResultFetch underway. This will trigger // StartFetchingMergeSession. EXPECT_CALL(helper, StartFetchingMergeSession()); SimulateUbertokenSuccess(&helper, "token3"); }