Home | History | Annotate | Download | only in gaia
      1 // Copyright (c) 2010 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 // This file defines a unit test for the profile's token service.
      6 
      7 #include "chrome/browser/net/gaia/token_service_unittest.h"
      8 
      9 #include "base/command_line.h"
     10 #include "base/synchronization/waitable_event.h"
     11 #include "chrome/browser/password_manager/encryptor.h"
     12 #include "chrome/common/chrome_switches.h"
     13 #include "chrome/common/net/gaia/gaia_auth_fetcher_unittest.h"
     14 #include "chrome/common/net/gaia/gaia_constants.h"
     15 #include "chrome/common/net/test_url_fetcher_factory.h"
     16 
     17 TokenAvailableTracker::TokenAvailableTracker() {}
     18 
     19 TokenAvailableTracker::~TokenAvailableTracker() {}
     20 
     21 void TokenAvailableTracker::Observe(NotificationType type,
     22                                     const NotificationSource& source,
     23                                     const NotificationDetails& details) {
     24   TestNotificationTracker::Observe(type, source, details);
     25   if (type == NotificationType::TOKEN_AVAILABLE) {
     26     Details<const TokenService::TokenAvailableDetails> full = details;
     27     details_ = *full.ptr();
     28   }
     29 }
     30 
     31 TokenFailedTracker::TokenFailedTracker() {}
     32 
     33 TokenFailedTracker::~TokenFailedTracker() {}
     34 
     35 void TokenFailedTracker::Observe(NotificationType type,
     36                                  const NotificationSource& source,
     37                                  const NotificationDetails& details) {
     38   TestNotificationTracker::Observe(type, source, details);
     39   if (type == NotificationType::TOKEN_REQUEST_FAILED) {
     40     Details<const TokenService::TokenRequestFailedDetails> full = details;
     41     details_ = *full.ptr();
     42   }
     43 }
     44 
     45 TokenServiceTestHarness::TokenServiceTestHarness()
     46     : ui_thread_(BrowserThread::UI, &message_loop_),
     47       db_thread_(BrowserThread::DB) {
     48 }
     49 
     50 TokenServiceTestHarness::~TokenServiceTestHarness() {}
     51 
     52 void TokenServiceTestHarness::SetUp() {
     53 #if defined(OS_MACOSX)
     54   Encryptor::UseMockKeychain(true);
     55 #endif
     56   credentials_.sid = "sid";
     57   credentials_.lsid = "lsid";
     58   credentials_.token = "token";
     59   credentials_.data = "data";
     60 
     61   ASSERT_TRUE(db_thread_.Start());
     62 
     63   profile_.reset(new TestingProfile());
     64   profile_->CreateWebDataService(false);
     65   WaitForDBLoadCompletion();
     66 
     67   success_tracker_.ListenFor(NotificationType::TOKEN_AVAILABLE,
     68                              Source<TokenService>(&service_));
     69   failure_tracker_.ListenFor(NotificationType::TOKEN_REQUEST_FAILED,
     70                              Source<TokenService>(&service_));
     71 
     72   service_.Initialize("test", profile_.get());
     73 
     74   URLFetcher::set_factory(NULL);
     75 }
     76 
     77 void TokenServiceTestHarness::TearDown() {
     78   // You have to destroy the profile before the db_thread_ stops.
     79   if (profile_.get()) {
     80     profile_.reset(NULL);
     81   }
     82 
     83   db_thread_.Stop();
     84   MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask);
     85   MessageLoop::current()->Run();
     86 }
     87 
     88 void TokenServiceTestHarness::WaitForDBLoadCompletion() {
     89   // The WebDB does all work on the DB thread. This will add an event
     90   // to the end of the DB thread, so when we reach this task, all DB
     91   // operations should be complete.
     92   base::WaitableEvent done(false, false);
     93   BrowserThread::PostTask(
     94       BrowserThread::DB, FROM_HERE, new SignalingTask(&done));
     95   done.Wait();
     96 
     97   // Notifications should be returned from the DB thread onto the UI thread.
     98   message_loop_.RunAllPending();
     99 }
    100 
    101 class TokenServiceTest : public TokenServiceTestHarness {
    102  public:
    103   virtual void SetUp() {
    104     TokenServiceTestHarness::SetUp();
    105     service_.UpdateCredentials(credentials_);
    106   }
    107 };
    108 
    109 TEST_F(TokenServiceTest, SanityCheck) {
    110   EXPECT_TRUE(service_.HasLsid());
    111   EXPECT_EQ(service_.GetLsid(), "lsid");
    112   EXPECT_FALSE(service_.HasTokenForService("nonexistent service"));
    113 }
    114 
    115 TEST_F(TokenServiceTest, NoToken) {
    116   EXPECT_FALSE(service_.HasTokenForService("nonexistent service"));
    117   EXPECT_EQ(service_.GetTokenForService("nonexistent service"), std::string());
    118 }
    119 
    120 TEST_F(TokenServiceTest, NotificationSuccess) {
    121   EXPECT_EQ(0U, success_tracker_.size());
    122   EXPECT_EQ(0U, failure_tracker_.size());
    123   service_.OnIssueAuthTokenSuccess(GaiaConstants::kSyncService, "token");
    124   EXPECT_EQ(1U, success_tracker_.size());
    125   EXPECT_EQ(0U, failure_tracker_.size());
    126 
    127   TokenService::TokenAvailableDetails details = success_tracker_.details();
    128   // MSVC doesn't like this comparison as EQ.
    129   EXPECT_TRUE(details.service() == GaiaConstants::kSyncService);
    130   EXPECT_EQ(details.token(), "token");
    131 }
    132 
    133 TEST_F(TokenServiceTest, NotificationFailed) {
    134   EXPECT_EQ(0U, success_tracker_.size());
    135   EXPECT_EQ(0U, failure_tracker_.size());
    136   GoogleServiceAuthError error(GoogleServiceAuthError::REQUEST_CANCELED);
    137   service_.OnIssueAuthTokenFailure(GaiaConstants::kSyncService, error);
    138   EXPECT_EQ(0U, success_tracker_.size());
    139   EXPECT_EQ(1U, failure_tracker_.size());
    140 
    141   TokenService::TokenRequestFailedDetails details = failure_tracker_.details();
    142 
    143   // MSVC doesn't like this comparison as EQ.
    144   EXPECT_TRUE(details.service() == GaiaConstants::kSyncService);
    145   EXPECT_TRUE(details.error() == error);  // Struct has no print function.
    146 }
    147 
    148 TEST_F(TokenServiceTest, OnTokenSuccessUpdate) {
    149   service_.OnIssueAuthTokenSuccess(GaiaConstants::kSyncService, "token");
    150   EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService));
    151   EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kSyncService), "token");
    152 
    153   service_.OnIssueAuthTokenSuccess(GaiaConstants::kSyncService, "token2");
    154   EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService));
    155   EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kSyncService), "token2");
    156 
    157   service_.OnIssueAuthTokenSuccess(GaiaConstants::kSyncService, "");
    158   EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService));
    159   EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kSyncService), "");
    160 }
    161 
    162 TEST_F(TokenServiceTest, OnTokenSuccess) {
    163   // Don't "start fetching", just go ahead and issue the callback.
    164   service_.OnIssueAuthTokenSuccess(GaiaConstants::kSyncService, "token");
    165   EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService));
    166   EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kTalkService));
    167   // Gaia returns the entire result as the token so while this is a shared
    168   // result with ClientLogin, it doesn't matter, we should still get it back.
    169   EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kSyncService), "token");
    170 
    171   // Check the second service.
    172   service_.OnIssueAuthTokenSuccess(GaiaConstants::kTalkService, "token2");
    173   EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kTalkService));
    174   EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kTalkService), "token2");
    175 
    176   // It didn't change.
    177   EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kSyncService), "token");
    178 }
    179 
    180 TEST_F(TokenServiceTest, ResetSimple) {
    181   service_.OnIssueAuthTokenSuccess(GaiaConstants::kSyncService, "token");
    182   EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService));
    183   EXPECT_TRUE(service_.HasLsid());
    184 
    185   service_.ResetCredentialsInMemory();
    186 
    187   EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kSyncService));
    188   EXPECT_FALSE(service_.HasLsid());
    189 }
    190 
    191 TEST_F(TokenServiceTest, ResetComplex) {
    192   TestURLFetcherFactory factory;
    193   URLFetcher::set_factory(&factory);
    194   service_.StartFetchingTokens();
    195   // You have to call delegates by hand with the test fetcher,
    196   // Let's pretend only one returned.
    197 
    198   service_.OnIssueAuthTokenSuccess(GaiaConstants::kSyncService, "eraseme");
    199   EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService));
    200   EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kSyncService),
    201             "eraseme");
    202   EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kTalkService));
    203 
    204   service_.ResetCredentialsInMemory();
    205   EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kSyncService));
    206   EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kTalkService));
    207 
    208   // Now start using it again.
    209   service_.UpdateCredentials(credentials_);
    210   service_.StartFetchingTokens();
    211 
    212   service_.OnIssueAuthTokenSuccess(GaiaConstants::kSyncService, "token");
    213   service_.OnIssueAuthTokenSuccess(GaiaConstants::kTalkService, "token2");
    214 
    215   EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kSyncService), "token");
    216   EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kTalkService), "token2");
    217 }
    218 
    219 TEST_F(TokenServiceTest, FullIntegration) {
    220   MockFactory<MockFetcher> factory;
    221   std::string result = "SID=sid\nLSID=lsid\nAuth=auth\n";
    222   factory.set_results(result);
    223   URLFetcher::set_factory(&factory);
    224   EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kSyncService));
    225   EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kTalkService));
    226   service_.StartFetchingTokens();
    227   URLFetcher::set_factory(NULL);
    228 
    229   EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService));
    230   EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kTalkService));
    231   // Gaia returns the entire result as the token so while this is a shared
    232   // result with ClientLogin, it doesn't matter, we should still get it back.
    233   EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kSyncService), result);
    234   EXPECT_EQ(service_.GetTokenForService(GaiaConstants::kTalkService), result);
    235 
    236   service_.ResetCredentialsInMemory();
    237   EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kSyncService));
    238   EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kTalkService));
    239 }
    240 
    241 TEST_F(TokenServiceTest, LoadTokensIntoMemoryBasic) {
    242   // Validate that the method sets proper data in notifications and map.
    243   std::map<std::string, std::string> db_tokens;
    244   std::map<std::string, std::string> memory_tokens;
    245 
    246   service_.LoadTokensIntoMemory(db_tokens, &memory_tokens);
    247   EXPECT_TRUE(db_tokens.empty());
    248   EXPECT_TRUE(memory_tokens.empty());
    249   EXPECT_EQ(0U, success_tracker_.size());
    250 
    251   db_tokens[GaiaConstants::kSyncService] = "token";
    252   service_.LoadTokensIntoMemory(db_tokens, &memory_tokens);
    253   EXPECT_EQ(1U, success_tracker_.size());
    254 
    255   TokenService::TokenAvailableDetails details = success_tracker_.details();
    256   // MSVC doesn't like this comparison as EQ.
    257   EXPECT_TRUE(details.service() == GaiaConstants::kSyncService);
    258   EXPECT_EQ(details.token(), "token");
    259   EXPECT_EQ(1U, memory_tokens.count(GaiaConstants::kSyncService));
    260   EXPECT_EQ(memory_tokens[GaiaConstants::kSyncService], "token");
    261 }
    262 
    263 TEST_F(TokenServiceTest, LoadTokensIntoMemoryAdvanced) {
    264   // LoadTokensIntoMemory should avoid setting tokens already in the
    265   // token map.
    266   std::map<std::string, std::string> db_tokens;
    267   std::map<std::string, std::string> memory_tokens;
    268 
    269   db_tokens["ignore"] = "token";
    270 
    271   service_.LoadTokensIntoMemory(db_tokens, &memory_tokens);
    272   EXPECT_TRUE(memory_tokens.empty());
    273   db_tokens[GaiaConstants::kSyncService] = "pepper";
    274 
    275   service_.LoadTokensIntoMemory(db_tokens, &memory_tokens);
    276   EXPECT_EQ(1U, memory_tokens.count(GaiaConstants::kSyncService));
    277   EXPECT_EQ(memory_tokens[GaiaConstants::kSyncService], "pepper");
    278   EXPECT_EQ(1U, success_tracker_.size());
    279   success_tracker_.Reset();
    280 
    281   // SyncService token is already in memory. Pretend we got it off
    282   // the disk as well, but an older token.
    283   db_tokens[GaiaConstants::kSyncService] = "ignoreme";
    284   db_tokens[GaiaConstants::kTalkService] = "tomato";
    285   service_.LoadTokensIntoMemory(db_tokens, &memory_tokens);
    286 
    287   EXPECT_EQ(2U, memory_tokens.size());
    288   EXPECT_EQ(1U, memory_tokens.count(GaiaConstants::kTalkService));
    289   EXPECT_EQ(memory_tokens[GaiaConstants::kTalkService], "tomato");
    290   EXPECT_EQ(1U, success_tracker_.size());
    291   EXPECT_EQ(1U, memory_tokens.count(GaiaConstants::kSyncService));
    292   EXPECT_EQ(memory_tokens[GaiaConstants::kSyncService], "pepper");
    293 }
    294 
    295 TEST_F(TokenServiceTest, WebDBLoadIntegration) {
    296   service_.LoadTokensFromDB();
    297   WaitForDBLoadCompletion();
    298   EXPECT_EQ(0U, success_tracker_.size());
    299 
    300   // Should result in DB write.
    301   service_.OnIssueAuthTokenSuccess(GaiaConstants::kSyncService, "token");
    302   EXPECT_EQ(1U, success_tracker_.size());
    303 
    304   EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService));
    305   // Clean slate.
    306   service_.ResetCredentialsInMemory();
    307   success_tracker_.Reset();
    308   EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kSyncService));
    309 
    310   service_.LoadTokensFromDB();
    311   WaitForDBLoadCompletion();
    312 
    313   EXPECT_EQ(1U, success_tracker_.size());
    314   EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService));
    315   EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kTalkService));
    316   EXPECT_FALSE(service_.HasLsid());
    317 }
    318 
    319 TEST_F(TokenServiceTest, MultipleLoadResetIntegration) {
    320   // Should result in DB write.
    321   service_.OnIssueAuthTokenSuccess(GaiaConstants::kSyncService, "token");
    322   service_.ResetCredentialsInMemory();
    323   success_tracker_.Reset();
    324   EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kSyncService));
    325 
    326   service_.LoadTokensFromDB();
    327   WaitForDBLoadCompletion();
    328 
    329   service_.LoadTokensFromDB();  // Should do nothing.
    330   WaitForDBLoadCompletion();
    331 
    332   EXPECT_EQ(1U, success_tracker_.size());
    333   EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService));
    334   EXPECT_FALSE(service_.HasTokenForService(GaiaConstants::kTalkService));
    335   EXPECT_FALSE(service_.HasLsid());
    336 
    337   // Reset it one more time so there's no surprises.
    338   service_.ResetCredentialsInMemory();
    339   success_tracker_.Reset();
    340 
    341   service_.LoadTokensFromDB();
    342   WaitForDBLoadCompletion();
    343 
    344   EXPECT_EQ(1U, success_tracker_.size());
    345   EXPECT_TRUE(service_.HasTokenForService(GaiaConstants::kSyncService));
    346 }
    347 
    348 #ifndef NDEBUG
    349 class TokenServiceCommandLineTest : public TokenServiceTestHarness {
    350  public:
    351   virtual void SetUp() {
    352     CommandLine original_cl(*CommandLine::ForCurrentProcess());
    353     CommandLine::ForCurrentProcess()->AppendSwitchASCII(
    354         switches::kSetToken, "my_service:my_value");
    355     TokenServiceTestHarness::SetUp();
    356     service_.UpdateCredentials(credentials_);
    357 
    358     *CommandLine::ForCurrentProcess() = original_cl;
    359   }
    360 };
    361 
    362 TEST_F(TokenServiceCommandLineTest, TestValueOverride) {
    363   EXPECT_TRUE(service_.HasTokenForService("my_service"));
    364   EXPECT_EQ("my_value", service_.GetTokenForService("my_service"));
    365 }
    366 #endif   // ifndef NDEBUG
    367