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