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