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 <algorithm> 6 #include <vector> 7 8 #include "base/message_loop/message_loop.h" 9 #include "base/prefs/pref_registry_simple.h" 10 #include "base/prefs/testing_pref_service.h" 11 #include "base/strings/stringprintf.h" 12 #include "components/signin/core/browser/account_tracker_service.h" 13 #include "google_apis/gaia/fake_oauth2_token_service.h" 14 #include "google_apis/gaia/gaia_oauth_client.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_fetcher_delegate.h" 18 #include "net/url_request/url_request_test_util.h" 19 #include "testing/gtest/include/gtest/gtest.h" 20 21 namespace { 22 23 enum TrackingEventType { 24 UPDATED, 25 REMOVED, 26 }; 27 28 std::string AccountIdToEmail(const std::string account_id) { 29 return account_id + "@gmail.com"; 30 } 31 32 std::string AccountIdToGaiaId(const std::string account_id) { 33 return "gaia-" + account_id; 34 } 35 36 class TrackingEvent { 37 public: 38 TrackingEvent(TrackingEventType type, 39 const std::string& account_id, 40 const std::string& gaia_id) 41 : type_(type), 42 account_id_(account_id), 43 gaia_id_(gaia_id) {} 44 45 TrackingEvent(TrackingEventType type, 46 const std::string& account_id) 47 : type_(type), 48 account_id_(account_id), 49 gaia_id_(AccountIdToGaiaId(account_id)) {} 50 51 bool operator==(const TrackingEvent& event) const { 52 return type_ == event.type_ && account_id_ == event.account_id_ && 53 gaia_id_ == event.gaia_id_; 54 } 55 56 std::string ToString() const { 57 const char * typestr = "INVALID"; 58 switch (type_) { 59 case UPDATED: 60 typestr = "UPD"; 61 break; 62 case REMOVED: 63 typestr = "REM"; 64 break; 65 } 66 return base::StringPrintf("{ type: %s, account_id: %s, gaia: %s }", 67 typestr, 68 account_id_.c_str(), 69 gaia_id_.c_str()); 70 } 71 72 private: 73 friend bool CompareByUser(TrackingEvent a, TrackingEvent b); 74 75 TrackingEventType type_; 76 std::string account_id_; 77 std::string gaia_id_; 78 }; 79 80 bool CompareByUser(TrackingEvent a, TrackingEvent b) { 81 return a.account_id_ < b.account_id_; 82 } 83 84 std::string Str(const std::vector<TrackingEvent>& events) { 85 std::string str = "["; 86 bool needs_comma = false; 87 for (std::vector<TrackingEvent>::const_iterator it = 88 events.begin(); it != events.end(); ++it) { 89 if (needs_comma) 90 str += ",\n "; 91 needs_comma = true; 92 str += it->ToString(); 93 } 94 str += "]"; 95 return str; 96 } 97 98 class AccountTrackerObserver : public AccountTrackerService::Observer { 99 public: 100 AccountTrackerObserver() {} 101 virtual ~AccountTrackerObserver() {} 102 103 void Clear(); 104 void SortEventsByUser(); 105 106 testing::AssertionResult CheckEvents(); 107 testing::AssertionResult CheckEvents(const TrackingEvent& e1); 108 testing::AssertionResult CheckEvents(const TrackingEvent& e1, 109 const TrackingEvent& e2); 110 testing::AssertionResult CheckEvents(const TrackingEvent& e1, 111 const TrackingEvent& e2, 112 const TrackingEvent& e3); 113 114 private: 115 // AccountTrackerService::Observer implementation 116 virtual void OnAccountUpdated( 117 const AccountTrackerService::AccountInfo& ids) OVERRIDE; 118 virtual void OnAccountRemoved( 119 const AccountTrackerService::AccountInfo& ids) OVERRIDE; 120 121 testing::AssertionResult CheckEvents( 122 const std::vector<TrackingEvent>& events); 123 124 std::vector<TrackingEvent> events_; 125 }; 126 127 void AccountTrackerObserver::OnAccountUpdated( 128 const AccountTrackerService::AccountInfo& ids) { 129 events_.push_back(TrackingEvent(UPDATED, ids.account_id, ids.gaia)); 130 } 131 132 void AccountTrackerObserver::OnAccountRemoved( 133 const AccountTrackerService::AccountInfo& ids) { 134 events_.push_back(TrackingEvent(REMOVED, ids.account_id, ids.gaia)); 135 } 136 137 void AccountTrackerObserver::Clear() { 138 events_.clear(); 139 } 140 141 void AccountTrackerObserver::SortEventsByUser() { 142 std::stable_sort(events_.begin(), events_.end(), CompareByUser); 143 } 144 145 testing::AssertionResult AccountTrackerObserver::CheckEvents() { 146 std::vector<TrackingEvent> events; 147 return CheckEvents(events); 148 } 149 150 testing::AssertionResult AccountTrackerObserver::CheckEvents( 151 const TrackingEvent& e1) { 152 std::vector<TrackingEvent> events; 153 events.push_back(e1); 154 return CheckEvents(events); 155 } 156 157 testing::AssertionResult AccountTrackerObserver::CheckEvents( 158 const TrackingEvent& e1, 159 const TrackingEvent& e2) { 160 std::vector<TrackingEvent> events; 161 events.push_back(e1); 162 events.push_back(e2); 163 return CheckEvents(events); 164 } 165 166 testing::AssertionResult AccountTrackerObserver::CheckEvents( 167 const TrackingEvent& e1, 168 const TrackingEvent& e2, 169 const TrackingEvent& e3) { 170 std::vector<TrackingEvent> events; 171 events.push_back(e1); 172 events.push_back(e2); 173 events.push_back(e3); 174 return CheckEvents(events); 175 } 176 177 testing::AssertionResult AccountTrackerObserver::CheckEvents( 178 const std::vector<TrackingEvent>& events) { 179 std::string maybe_newline = (events.size() + events_.size()) > 2 ? "\n" : ""; 180 testing::AssertionResult result( 181 (events_ == events) 182 ? testing::AssertionSuccess() 183 : (testing::AssertionFailure() 184 << "Expected " << maybe_newline << Str(events) << ", " 185 << maybe_newline << "Got " << maybe_newline << Str(events_))); 186 events_.clear(); 187 return result; 188 } 189 190 } // namespace 191 192 class AccountTrackerServiceTest : public testing::Test { 193 public: 194 AccountTrackerServiceTest() {} 195 196 virtual ~AccountTrackerServiceTest() {} 197 198 virtual void SetUp() OVERRIDE { 199 fake_oauth2_token_service_.reset(new FakeOAuth2TokenService()); 200 201 pref_service_.registry()->RegisterListPref( 202 AccountTrackerService::kAccountInfoPref); 203 204 account_tracker_.reset(new AccountTrackerService()); 205 account_tracker_->Initialize(fake_oauth2_token_service_.get(), 206 &pref_service_, 207 new net::TestURLRequestContextGetter( 208 message_loop_.message_loop_proxy())); 209 account_tracker_->AddObserver(&observer_); 210 } 211 212 virtual void TearDown() OVERRIDE { 213 account_tracker_->RemoveObserver(&observer_); 214 account_tracker_->Shutdown(); 215 } 216 217 void SimulateTokenAvailable(const std::string& account_id) { 218 fake_oauth2_token_service_->AddAccount(account_id); 219 } 220 221 void SimulateTokenRevoked(const std::string& account_id) { 222 fake_oauth2_token_service_->RemoveAccount(account_id); 223 } 224 225 // Helpers to fake access token and user info fetching 226 void IssueAccessToken(const std::string& account_id) { 227 fake_oauth2_token_service_->IssueAllTokensForAccount( 228 account_id, "access_token-" + account_id, base::Time::Max()); 229 } 230 231 std::string GenerateValidTokenInfoResponse(const std::string& account_id) { 232 return base::StringPrintf("{\"id\": \"%s\", \"email\": \"%s\"}", 233 AccountIdToGaiaId(account_id).c_str(), 234 AccountIdToEmail(account_id).c_str()); 235 } 236 237 void ReturnOAuthUrlFetchSuccess(const std::string& account_id); 238 void ReturnOAuthUrlFetchFailure(const std::string& account_id); 239 240 base::MessageLoopForIO* message_loop() { return &message_loop_; } 241 AccountTrackerService* account_tracker() { return account_tracker_.get(); } 242 AccountTrackerObserver* observer() { return &observer_; } 243 OAuth2TokenService* token_service() { 244 return fake_oauth2_token_service_.get(); 245 } 246 TestingPrefServiceSimple* pref_service() { return &pref_service_; } 247 248 private: 249 void ReturnOAuthUrlFetchResults(int fetcher_id, 250 net::HttpStatusCode response_code, 251 const std::string& response_string); 252 253 base::MessageLoopForIO message_loop_; 254 net::TestURLFetcherFactory test_fetcher_factory_; 255 scoped_ptr<FakeOAuth2TokenService> fake_oauth2_token_service_; 256 TestingPrefServiceSimple pref_service_; 257 scoped_ptr<AccountTrackerService> account_tracker_; 258 AccountTrackerObserver observer_; 259 }; 260 261 void AccountTrackerServiceTest::ReturnOAuthUrlFetchResults( 262 int fetcher_id, 263 net::HttpStatusCode response_code, 264 const std::string& response_string) { 265 net::TestURLFetcher* fetcher = 266 test_fetcher_factory_.GetFetcherByID(fetcher_id); 267 ASSERT_TRUE(fetcher); 268 fetcher->set_response_code(response_code); 269 fetcher->SetResponseString(response_string); 270 fetcher->delegate()->OnURLFetchComplete(fetcher); 271 } 272 273 void AccountTrackerServiceTest::ReturnOAuthUrlFetchSuccess( 274 const std::string& account_id) { 275 IssueAccessToken(account_id); 276 ReturnOAuthUrlFetchResults(gaia::GaiaOAuthClient::kUrlFetcherId, 277 net::HTTP_OK, 278 GenerateValidTokenInfoResponse(account_id)); 279 } 280 281 void AccountTrackerServiceTest::ReturnOAuthUrlFetchFailure( 282 const std::string& account_id) { 283 IssueAccessToken(account_id); 284 ReturnOAuthUrlFetchResults( 285 gaia::GaiaOAuthClient::kUrlFetcherId, net::HTTP_BAD_REQUEST, ""); 286 } 287 288 TEST_F(AccountTrackerServiceTest, Basic) { 289 } 290 291 TEST_F(AccountTrackerServiceTest, TokenAvailable) { 292 SimulateTokenAvailable("alpha"); 293 ASSERT_FALSE(account_tracker()->IsAllUserInfoFetched()); 294 ASSERT_TRUE(observer()->CheckEvents()); 295 } 296 297 TEST_F(AccountTrackerServiceTest, TokenAvailable_Revoked) { 298 SimulateTokenAvailable("alpha"); 299 SimulateTokenRevoked("alpha"); 300 ASSERT_TRUE(account_tracker()->IsAllUserInfoFetched()); 301 ASSERT_TRUE(observer()->CheckEvents()); 302 } 303 304 TEST_F(AccountTrackerServiceTest, TokenAvailable_UserInfo) { 305 SimulateTokenAvailable("alpha"); 306 ReturnOAuthUrlFetchSuccess("alpha"); 307 ASSERT_TRUE(account_tracker()->IsAllUserInfoFetched()); 308 ASSERT_TRUE(observer()->CheckEvents(TrackingEvent(UPDATED, "alpha"))); 309 } 310 311 TEST_F(AccountTrackerServiceTest, TokenAvailable_UserInfo_Revoked) { 312 SimulateTokenAvailable("alpha"); 313 ReturnOAuthUrlFetchSuccess("alpha"); 314 ASSERT_TRUE(account_tracker()->IsAllUserInfoFetched()); 315 ASSERT_TRUE(observer()->CheckEvents(TrackingEvent(UPDATED, "alpha"))); 316 SimulateTokenRevoked("alpha"); 317 ASSERT_TRUE(observer()->CheckEvents(TrackingEvent(REMOVED, "alpha"))); 318 } 319 320 TEST_F(AccountTrackerServiceTest, TokenAvailable_UserInfoFailed) { 321 SimulateTokenAvailable("alpha"); 322 ReturnOAuthUrlFetchFailure("alpha"); 323 ASSERT_TRUE(account_tracker()->IsAllUserInfoFetched()); 324 ASSERT_TRUE(observer()->CheckEvents()); 325 } 326 327 TEST_F(AccountTrackerServiceTest, TokenAlreadyExists) { 328 SimulateTokenAvailable("alpha"); 329 AccountTrackerService tracker; 330 AccountTrackerObserver observer; 331 tracker.AddObserver(&observer); 332 tracker.Initialize(token_service(), 333 pref_service(), 334 new net::TestURLRequestContextGetter( 335 message_loop()->message_loop_proxy())); 336 ASSERT_FALSE(tracker.IsAllUserInfoFetched()); 337 ASSERT_TRUE(observer.CheckEvents()); 338 tracker.RemoveObserver(&observer); 339 tracker.Shutdown(); 340 } 341 342 TEST_F(AccountTrackerServiceTest, TwoTokenAvailable_TwoUserInfo) { 343 SimulateTokenAvailable("alpha"); 344 SimulateTokenAvailable("beta"); 345 ReturnOAuthUrlFetchSuccess("alpha"); 346 ReturnOAuthUrlFetchSuccess("beta"); 347 ASSERT_TRUE(account_tracker()->IsAllUserInfoFetched()); 348 ASSERT_TRUE(observer()->CheckEvents(TrackingEvent(UPDATED, "alpha"), 349 TrackingEvent(UPDATED, "beta"))); 350 } 351 352 TEST_F(AccountTrackerServiceTest, TwoTokenAvailable_OneUserInfo) { 353 SimulateTokenAvailable("alpha"); 354 SimulateTokenAvailable("beta"); 355 ReturnOAuthUrlFetchSuccess("beta"); 356 ASSERT_FALSE(account_tracker()->IsAllUserInfoFetched()); 357 ASSERT_TRUE(observer()->CheckEvents(TrackingEvent(UPDATED, "beta"))); 358 ReturnOAuthUrlFetchSuccess("alpha"); 359 ASSERT_TRUE(account_tracker()->IsAllUserInfoFetched()); 360 ASSERT_TRUE(observer()->CheckEvents(TrackingEvent(UPDATED, "alpha"))); 361 } 362 363 TEST_F(AccountTrackerServiceTest, GetAccounts) { 364 SimulateTokenAvailable("alpha"); 365 SimulateTokenAvailable("beta"); 366 SimulateTokenAvailable("gamma"); 367 ReturnOAuthUrlFetchSuccess("alpha"); 368 ReturnOAuthUrlFetchSuccess("beta"); 369 ReturnOAuthUrlFetchSuccess("gamma"); 370 371 std::vector<AccountTrackerService::AccountInfo> infos = 372 account_tracker()->GetAccounts(); 373 374 EXPECT_EQ(3u, infos.size()); 375 EXPECT_EQ("alpha", infos[0].account_id); 376 EXPECT_EQ(AccountIdToGaiaId("alpha"), infos[0].gaia); 377 EXPECT_EQ(AccountIdToEmail("alpha"), infos[0].email); 378 EXPECT_EQ("beta", infos[1].account_id); 379 EXPECT_EQ(AccountIdToGaiaId("beta"), infos[1].gaia); 380 EXPECT_EQ(AccountIdToEmail("beta"), infos[1].email); 381 EXPECT_EQ("gamma", infos[2].account_id); 382 EXPECT_EQ(AccountIdToGaiaId("gamma"), infos[2].gaia); 383 EXPECT_EQ(AccountIdToEmail("gamma"), infos[2].email); 384 } 385 386 TEST_F(AccountTrackerServiceTest, GetAccountInfo_Empty) { 387 AccountTrackerService::AccountInfo info = 388 account_tracker()->GetAccountInfo("alpha"); 389 ASSERT_EQ("", info.account_id); 390 } 391 392 TEST_F(AccountTrackerServiceTest, GetAccountInfo_TokenAvailable) { 393 SimulateTokenAvailable("alpha"); 394 AccountTrackerService::AccountInfo info = 395 account_tracker()->GetAccountInfo("alpha"); 396 ASSERT_EQ("alpha", info.account_id); 397 ASSERT_EQ("", info.gaia); 398 ASSERT_EQ("", info.email); 399 } 400 401 TEST_F(AccountTrackerServiceTest, GetAccountInfo_TokenAvailable_UserInfo) { 402 SimulateTokenAvailable("alpha"); 403 ReturnOAuthUrlFetchSuccess("alpha"); 404 AccountTrackerService::AccountInfo info = 405 account_tracker()->GetAccountInfo("alpha"); 406 ASSERT_EQ("alpha", info.account_id); 407 ASSERT_EQ(AccountIdToGaiaId("alpha"), info.gaia); 408 ASSERT_EQ(AccountIdToEmail("alpha"), info.email); 409 } 410 411 TEST_F(AccountTrackerServiceTest, FindAccountInfoByGaiaId) { 412 SimulateTokenAvailable("alpha"); 413 ReturnOAuthUrlFetchSuccess("alpha"); 414 415 std::string gaia_id = AccountIdToGaiaId("alpha"); 416 AccountTrackerService::AccountInfo info = 417 account_tracker()->FindAccountInfoByGaiaId(gaia_id); 418 ASSERT_EQ("alpha", info.account_id); 419 ASSERT_EQ(gaia_id, info.gaia); 420 421 gaia_id = AccountIdToGaiaId("beta"); 422 info = account_tracker()->FindAccountInfoByGaiaId(gaia_id); 423 ASSERT_EQ("", info.account_id); 424 } 425 426 TEST_F(AccountTrackerServiceTest, FindAccountInfoByEmail) { 427 SimulateTokenAvailable("alpha"); 428 ReturnOAuthUrlFetchSuccess("alpha"); 429 430 std::string email = AccountIdToEmail("alpha"); 431 AccountTrackerService::AccountInfo info = 432 account_tracker()->FindAccountInfoByEmail(email); 433 ASSERT_EQ("alpha", info.account_id); 434 ASSERT_EQ(email, info.email); 435 436 // Should also work with "canonically-equal" email addresses. 437 info = account_tracker()->FindAccountInfoByEmail("Alpha (at) Gmail.COM"); 438 ASSERT_EQ("alpha", info.account_id); 439 ASSERT_EQ(email, info.email); 440 info = account_tracker()->FindAccountInfoByEmail("al.pha (at) gmail.com"); 441 ASSERT_EQ("alpha", info.account_id); 442 ASSERT_EQ(email, info.email); 443 444 email = AccountIdToEmail("beta"); 445 info = account_tracker()->FindAccountInfoByEmail(email); 446 ASSERT_EQ("", info.account_id); 447 } 448 449 TEST_F(AccountTrackerServiceTest, Persistence) { 450 // Create a tracker and add two accounts. This should cause the accounts 451 // to be saved to persistence. 452 { 453 AccountTrackerService tracker; 454 tracker.Initialize(token_service(), 455 pref_service(), 456 new net::TestURLRequestContextGetter( 457 message_loop()->message_loop_proxy())); 458 SimulateTokenAvailable("alpha"); 459 ReturnOAuthUrlFetchSuccess("alpha"); 460 SimulateTokenAvailable("beta"); 461 ReturnOAuthUrlFetchSuccess("beta"); 462 tracker.Shutdown(); 463 } 464 465 // Create a new tracker and make sure it loads the accounts corectly from 466 // persistence. 467 { 468 AccountTrackerService tracker; 469 tracker.AddObserver(observer()); 470 tracker.Initialize(token_service(), 471 pref_service(), 472 new net::TestURLRequestContextGetter( 473 message_loop()->message_loop_proxy())); 474 ASSERT_TRUE(observer()->CheckEvents(TrackingEvent(UPDATED, "alpha"), 475 TrackingEvent(UPDATED, "beta"))); 476 477 std::vector<AccountTrackerService::AccountInfo> infos = 478 tracker.GetAccounts(); 479 ASSERT_EQ(2u, infos.size()); 480 EXPECT_EQ(AccountIdToGaiaId("alpha"), infos[0].gaia); 481 EXPECT_EQ(AccountIdToEmail("alpha"), infos[0].email); 482 EXPECT_EQ("beta", infos[1].account_id); 483 EXPECT_EQ(AccountIdToGaiaId("beta"), infos[1].gaia); 484 EXPECT_EQ(AccountIdToEmail("beta"), infos[1].email); 485 486 // Remove account. 487 SimulateTokenRevoked("alpha"); 488 tracker.RemoveObserver(observer()); 489 tracker.Shutdown(); 490 } 491 492 // Create a new tracker and make sure it loads the single account from 493 // persistence. 494 { 495 AccountTrackerService tracker; 496 tracker.Initialize(token_service(), 497 pref_service(), 498 new net::TestURLRequestContextGetter( 499 message_loop()->message_loop_proxy())); 500 501 std::vector<AccountTrackerService::AccountInfo> infos = 502 tracker.GetAccounts(); 503 ASSERT_EQ(1u, infos.size()); 504 EXPECT_EQ("beta", infos[0].account_id); 505 EXPECT_EQ(AccountIdToGaiaId("beta"), infos[0].gaia); 506 EXPECT_EQ(AccountIdToEmail("beta"), infos[0].email); 507 tracker.Shutdown(); 508 } 509 } 510