token_service_unittest.cc revision dc0f95d653279beabeb9817299e2902918ba123e
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
17TokenAvailableTracker::TokenAvailableTracker() {}
18
19TokenAvailableTracker::~TokenAvailableTracker() {}
20
21void 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
31TokenFailedTracker::TokenFailedTracker() {}
32
33TokenFailedTracker::~TokenFailedTracker() {}
34
35void 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
45TokenServiceTestHarness::TokenServiceTestHarness()
46    : ui_thread_(BrowserThread::UI, &message_loop_),
47      db_thread_(BrowserThread::DB) {
48}
49
50TokenServiceTestHarness::~TokenServiceTestHarness() {}
51
52void 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
77void 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
88void 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
101class TokenServiceTest : public TokenServiceTestHarness {
102 public:
103  virtual void SetUp() {
104    TokenServiceTestHarness::SetUp();
105    service_.UpdateCredentials(credentials_);
106  }
107};
108
109TEST_F(TokenServiceTest, SanityCheck) {
110  EXPECT_TRUE(service_.HasLsid());
111  EXPECT_EQ(service_.GetLsid(), "lsid");
112  EXPECT_FALSE(service_.HasTokenForService("nonexistent service"));
113}
114
115TEST_F(TokenServiceTest, NoToken) {
116  EXPECT_FALSE(service_.HasTokenForService("nonexistent service"));
117  EXPECT_EQ(service_.GetTokenForService("nonexistent service"), std::string());
118}
119
120TEST_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
133TEST_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
148TEST_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
162TEST_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
180TEST_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
191TEST_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
219TEST_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
241TEST_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
263TEST_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
295TEST_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
319TEST_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
349class 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
362TEST_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