1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/services/gcm/gcm_account_tracker.h" 6 7 #include <map> 8 #include <string> 9 10 #include "base/memory/scoped_ptr.h" 11 #include "base/message_loop/message_loop.h" 12 #include "google_apis/gaia/fake_identity_provider.h" 13 #include "google_apis/gaia/fake_oauth2_token_service.h" 14 #include "google_apis/gaia/google_service_auth_error.h" 15 #include "net/http/http_status_code.h" 16 #include "net/url_request/test_url_fetcher_factory.h" 17 #include "net/url_request/url_request_test_util.h" 18 #include "testing/gtest/include/gtest/gtest.h" 19 20 namespace gcm { 21 22 namespace { 23 24 const char kAccountId1[] = "account_1"; 25 const char kAccountId2[] = "account_2"; 26 27 std::string AccountKeyToObfuscatedId(const std::string email) { 28 return "obfid-" + email; 29 } 30 31 std::string GetValidTokenInfoResponse(const std::string account_key) { 32 return std::string("{ \"id\": \"") + AccountKeyToObfuscatedId(account_key) + 33 "\" }"; 34 } 35 36 std::string MakeAccessToken(const std::string& account_key) { 37 return "access_token-" + account_key; 38 } 39 40 } // namespace 41 42 class GCMAccountTrackerTest : public testing::Test { 43 public: 44 GCMAccountTrackerTest(); 45 virtual ~GCMAccountTrackerTest(); 46 47 // Callback for the account tracker. 48 void UpdateAccounts(const std::map<std::string, std::string>& accounts); 49 50 // Helpers to pass fake events to the tracker. Tests should have either a pair 51 // of Start/FinishAccountSignIn or SignInAccount per account. Don't mix. 52 // Call to SignOutAccount is not mandatory. 53 void StartAccountSignIn(const std::string& account_key); 54 void FinishAccountSignIn(const std::string& account_key); 55 void SignInAccount(const std::string& account_key); 56 void SignOutAccount(const std::string& account_key); 57 58 // Helpers for dealing with OAuth2 access token requests. 59 void IssueAccessToken(const std::string& account_key); 60 void IssueError(const std::string& account_key); 61 62 // Test results and helpers. 63 void ResetResults(); 64 bool update_accounts_called() const { return update_accounts_called_; } 65 const std::map<std::string, std::string>& accounts() const { 66 return accounts_; 67 } 68 69 // Accessor to account tracker. 70 GCMAccountTracker* tracker() { return tracker_.get(); } 71 72 private: 73 std::map<std::string, std::string> accounts_; 74 bool update_accounts_called_; 75 76 base::MessageLoop message_loop_; 77 net::TestURLFetcherFactory test_fetcher_factory_; 78 scoped_ptr<FakeOAuth2TokenService> fake_token_service_; 79 scoped_ptr<FakeIdentityProvider> fake_identity_provider_; 80 scoped_ptr<GCMAccountTracker> tracker_; 81 }; 82 83 GCMAccountTrackerTest::GCMAccountTrackerTest() 84 : update_accounts_called_(false) { 85 fake_token_service_.reset(new FakeOAuth2TokenService()); 86 87 fake_identity_provider_.reset( 88 new FakeIdentityProvider(fake_token_service_.get())); 89 90 scoped_ptr<gaia::AccountTracker> gaia_account_tracker( 91 new gaia::AccountTracker(fake_identity_provider_.get(), 92 new net::TestURLRequestContextGetter( 93 message_loop_.message_loop_proxy()))); 94 95 tracker_.reset(new GCMAccountTracker( 96 gaia_account_tracker.Pass(), 97 base::Bind(&GCMAccountTrackerTest::UpdateAccounts, 98 base::Unretained(this)))); 99 } 100 101 GCMAccountTrackerTest::~GCMAccountTrackerTest() { 102 if (tracker_) 103 tracker_->Shutdown(); 104 } 105 106 void GCMAccountTrackerTest::UpdateAccounts( 107 const std::map<std::string, std::string>& accounts) { 108 update_accounts_called_ = true; 109 accounts_ = accounts; 110 } 111 112 void GCMAccountTrackerTest::ResetResults() { 113 accounts_.clear(); 114 update_accounts_called_ = false; 115 } 116 117 void GCMAccountTrackerTest::StartAccountSignIn(const std::string& account_key) { 118 fake_identity_provider_->LogIn(account_key); 119 fake_token_service_->AddAccount(account_key); 120 } 121 122 void GCMAccountTrackerTest::FinishAccountSignIn( 123 const std::string& account_key) { 124 IssueAccessToken(account_key); 125 126 net::TestURLFetcher* fetcher = test_fetcher_factory_.GetFetcherByID( 127 gaia::GaiaOAuthClient::kUrlFetcherId); 128 ASSERT_TRUE(fetcher); 129 fetcher->set_response_code(net::HTTP_OK); 130 fetcher->SetResponseString(GetValidTokenInfoResponse(account_key)); 131 fetcher->delegate()->OnURLFetchComplete(fetcher); 132 } 133 134 void GCMAccountTrackerTest::SignInAccount(const std::string& account_key) { 135 StartAccountSignIn(account_key); 136 FinishAccountSignIn(account_key); 137 } 138 139 void GCMAccountTrackerTest::SignOutAccount(const std::string& account_key) { 140 fake_token_service_->RemoveAccount(account_key); 141 } 142 143 void GCMAccountTrackerTest::IssueAccessToken(const std::string& account_key) { 144 fake_token_service_->IssueAllTokensForAccount( 145 account_key, MakeAccessToken(account_key), base::Time::Max()); 146 } 147 148 void GCMAccountTrackerTest::IssueError(const std::string& account_key) { 149 fake_token_service_->IssueErrorForAllPendingRequestsForAccount( 150 account_key, 151 GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE)); 152 } 153 154 TEST_F(GCMAccountTrackerTest, NoAccounts) { 155 EXPECT_FALSE(update_accounts_called()); 156 tracker()->Start(); 157 // Callback should not be called if there where no accounts provided. 158 EXPECT_FALSE(update_accounts_called()); 159 EXPECT_TRUE(accounts().empty()); 160 tracker()->Stop(); 161 } 162 163 // Verifies that callback is called after a token is issued for a single account 164 // with a specific scope. In this scenario, the underlying account tracker is 165 // still working when the CompleteCollectingTokens is called for the first time. 166 TEST_F(GCMAccountTrackerTest, SingleAccount) { 167 StartAccountSignIn(kAccountId1); 168 169 tracker()->Start(); 170 // We don't have any accounts to report, but given the inner account tracker 171 // is still working we don't make a call with empty accounts list. 172 EXPECT_FALSE(update_accounts_called()); 173 174 // This concludes the work of inner account tracker. 175 FinishAccountSignIn(kAccountId1); 176 IssueAccessToken(kAccountId1); 177 178 EXPECT_TRUE(update_accounts_called()); 179 180 std::map<std::string, std::string> expected_accounts; 181 expected_accounts[kAccountId1] = MakeAccessToken(kAccountId1); 182 EXPECT_EQ(expected_accounts, accounts()); 183 tracker()->Stop(); 184 } 185 186 TEST_F(GCMAccountTrackerTest, MultipleAccounts) { 187 StartAccountSignIn(kAccountId1); 188 StartAccountSignIn(kAccountId2); 189 190 tracker()->Start(); 191 EXPECT_FALSE(update_accounts_called()); 192 193 FinishAccountSignIn(kAccountId1); 194 IssueAccessToken(kAccountId1); 195 EXPECT_FALSE(update_accounts_called()); 196 197 FinishAccountSignIn(kAccountId2); 198 IssueAccessToken(kAccountId2); 199 EXPECT_TRUE(update_accounts_called()); 200 201 std::map<std::string, std::string> expected_accounts; 202 expected_accounts[kAccountId1] = MakeAccessToken(kAccountId1); 203 expected_accounts[kAccountId2] = MakeAccessToken(kAccountId2); 204 EXPECT_EQ(expected_accounts, accounts()); 205 206 tracker()->Stop(); 207 } 208 209 TEST_F(GCMAccountTrackerTest, AccountAdded) { 210 tracker()->Start(); 211 ResetResults(); 212 213 SignInAccount(kAccountId1); 214 EXPECT_FALSE(update_accounts_called()); 215 216 IssueAccessToken(kAccountId1); 217 EXPECT_TRUE(update_accounts_called()); 218 219 std::map<std::string, std::string> expected_accounts; 220 expected_accounts[kAccountId1] = MakeAccessToken(kAccountId1); 221 EXPECT_EQ(expected_accounts, accounts()); 222 223 tracker()->Stop(); 224 } 225 226 TEST_F(GCMAccountTrackerTest, AccountRemoved) { 227 SignInAccount(kAccountId1); 228 SignInAccount(kAccountId2); 229 230 tracker()->Start(); 231 IssueAccessToken(kAccountId1); 232 IssueAccessToken(kAccountId2); 233 EXPECT_TRUE(update_accounts_called()); 234 235 ResetResults(); 236 EXPECT_FALSE(update_accounts_called()); 237 238 SignOutAccount(kAccountId2); 239 EXPECT_TRUE(update_accounts_called()); 240 241 std::map<std::string, std::string> expected_accounts; 242 expected_accounts[kAccountId1] = MakeAccessToken(kAccountId1); 243 EXPECT_EQ(expected_accounts, accounts()); 244 245 tracker()->Stop(); 246 } 247 248 TEST_F(GCMAccountTrackerTest, GetTokenFailed) { 249 SignInAccount(kAccountId1); 250 SignInAccount(kAccountId2); 251 252 tracker()->Start(); 253 IssueAccessToken(kAccountId1); 254 EXPECT_FALSE(update_accounts_called()); 255 256 IssueError(kAccountId2); 257 EXPECT_TRUE(update_accounts_called()); 258 259 std::map<std::string, std::string> expected_accounts; 260 expected_accounts[kAccountId1] = MakeAccessToken(kAccountId1); 261 EXPECT_EQ(expected_accounts, accounts()); 262 263 tracker()->Stop(); 264 } 265 266 TEST_F(GCMAccountTrackerTest, GetTokenFailedAccountRemoved) { 267 SignInAccount(kAccountId1); 268 SignInAccount(kAccountId2); 269 270 tracker()->Start(); 271 IssueAccessToken(kAccountId1); 272 IssueError(kAccountId2); 273 274 ResetResults(); 275 SignOutAccount(kAccountId2); 276 EXPECT_TRUE(update_accounts_called()); 277 278 std::map<std::string, std::string> expected_accounts; 279 expected_accounts[kAccountId1] = MakeAccessToken(kAccountId1); 280 EXPECT_EQ(expected_accounts, accounts()); 281 282 tracker()->Stop(); 283 } 284 285 TEST_F(GCMAccountTrackerTest, AccountRemovedWhileRequestsPending) { 286 SignInAccount(kAccountId1); 287 SignInAccount(kAccountId2); 288 289 tracker()->Start(); 290 IssueAccessToken(kAccountId1); 291 EXPECT_FALSE(update_accounts_called()); 292 293 SignOutAccount(kAccountId2); 294 IssueAccessToken(kAccountId2); 295 EXPECT_TRUE(update_accounts_called()); 296 297 std::map<std::string, std::string> expected_accounts; 298 expected_accounts[kAccountId1] = MakeAccessToken(kAccountId1); 299 EXPECT_EQ(expected_accounts, accounts()); 300 301 tracker()->Stop(); 302 } 303 304 // TODO(fgorski): Add test for adding account after removal >> make sure it does 305 // not mark removal. 306 307 } // namespace gcm 308