gaia_oauth_client_unittest.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
1// Copyright (c) 2012 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// A complete set of unit tests for GaiaOAuthClient.
6
7#include <string>
8#include <vector>
9
10#include "base/strings/string_number_conversions.h"
11#include "base/values.h"
12#include "google_apis/gaia/gaia_oauth_client.h"
13#include "net/base/net_errors.h"
14#include "net/http/http_status_code.h"
15#include "net/url_request/test_url_fetcher_factory.h"
16#include "net/url_request/url_fetcher_delegate.h"
17#include "net/url_request/url_request_status.h"
18#include "net/url_request/url_request_test_util.h"
19#include "testing/gmock/include/gmock/gmock.h"
20#include "testing/gtest/include/gtest/gtest.h"
21#include "url/gurl.h"
22
23using ::testing::_;
24using ::testing::Eq;
25using ::testing::HasSubstr;
26using ::testing::Pointee;
27using ::testing::SaveArg;
28
29namespace {
30
31// Responds as though OAuth returned from the server.
32class MockOAuthFetcher : public net::TestURLFetcher {
33 public:
34  MockOAuthFetcher(int response_code,
35                   int max_failure_count,
36                   bool complete_immediately,
37                   const GURL& url,
38                   const std::string& results,
39                   net::URLFetcher::RequestType request_type,
40                   net::URLFetcherDelegate* d)
41      : net::TestURLFetcher(0, url, d),
42        max_failure_count_(max_failure_count),
43        current_failure_count_(0),
44        complete_immediately_(complete_immediately) {
45    set_url(url);
46    set_response_code(response_code);
47    SetResponseString(results);
48  }
49
50  virtual ~MockOAuthFetcher() { }
51
52  virtual void Start() OVERRIDE {
53    if ((GetResponseCode() != net::HTTP_OK) && (max_failure_count_ != -1) &&
54        (current_failure_count_ == max_failure_count_)) {
55      set_response_code(net::HTTP_OK);
56    }
57
58    net::URLRequestStatus::Status code = net::URLRequestStatus::SUCCESS;
59    if (GetResponseCode() != net::HTTP_OK) {
60      code = net::URLRequestStatus::FAILED;
61      current_failure_count_++;
62    }
63    set_status(net::URLRequestStatus(code, 0));
64
65    if (complete_immediately_)
66      delegate()->OnURLFetchComplete(this);
67  }
68
69  void Finish() {
70    ASSERT_FALSE(complete_immediately_);
71    delegate()->OnURLFetchComplete(this);
72  }
73
74 private:
75  int max_failure_count_;
76  int current_failure_count_;
77  bool complete_immediately_;
78  DISALLOW_COPY_AND_ASSIGN(MockOAuthFetcher);
79};
80
81class MockOAuthFetcherFactory : public net::URLFetcherFactory,
82                                public net::ScopedURLFetcherFactory {
83 public:
84  MockOAuthFetcherFactory()
85      : net::ScopedURLFetcherFactory(this),
86        response_code_(net::HTTP_OK),
87        complete_immediately_(true) {
88  }
89  virtual ~MockOAuthFetcherFactory() {}
90  virtual net::URLFetcher* CreateURLFetcher(
91      int id,
92      const GURL& url,
93      net::URLFetcher::RequestType request_type,
94      net::URLFetcherDelegate* d) OVERRIDE {
95    url_fetcher_ = new MockOAuthFetcher(
96        response_code_,
97        max_failure_count_,
98        complete_immediately_,
99        url,
100        results_,
101        request_type,
102        d);
103    return url_fetcher_;
104  }
105  void set_response_code(int response_code) {
106    response_code_ = response_code;
107  }
108  void set_max_failure_count(int count) {
109    max_failure_count_ = count;
110  }
111  void set_results(const std::string& results) {
112    results_ = results;
113  }
114  MockOAuthFetcher* get_url_fetcher() {
115    return url_fetcher_;
116  }
117  void set_complete_immediately(bool complete_immediately) {
118    complete_immediately_ = complete_immediately;
119  }
120 private:
121  MockOAuthFetcher* url_fetcher_;
122  int response_code_;
123  bool complete_immediately_;
124  int max_failure_count_;
125  std::string results_;
126  DISALLOW_COPY_AND_ASSIGN(MockOAuthFetcherFactory);
127};
128
129const std::string kTestAccessToken = "1/fFAGRNJru1FTz70BzhT3Zg";
130const std::string kTestRefreshToken =
131    "1/6BMfW9j53gdGImsixUH6kU5RsR4zwI9lUVX-tqf8JXQ";
132const std::string kTestUserEmail = "a_user@gmail.com";
133const std::string kTestUserId = "8675309";
134const int kTestExpiresIn = 3920;
135
136const std::string kDummyGetTokensResult =
137  "{\"access_token\":\"" + kTestAccessToken + "\","
138  "\"expires_in\":" + base::IntToString(kTestExpiresIn) + ","
139  "\"refresh_token\":\"" + kTestRefreshToken + "\"}";
140
141const std::string kDummyRefreshTokenResult =
142  "{\"access_token\":\"" + kTestAccessToken + "\","
143  "\"expires_in\":" + base::IntToString(kTestExpiresIn) + "}";
144
145const std::string kDummyUserInfoResult =
146  "{\"email\":\"" + kTestUserEmail + "\"}";
147
148const std::string kDummyUserIdResult =
149  "{\"id\":\"" + kTestUserId + "\"}";
150
151const std::string kDummyTokenInfoResult =
152  "{\"issued_to\": \"1234567890.apps.googleusercontent.com\","
153  "\"audience\": \"1234567890.apps.googleusercontent.com\","
154  "\"scope\": \"https://googleapis.com/oauth2/v2/tokeninfo\","
155  "\"expires_in\":" + base::IntToString(kTestExpiresIn) + "}";
156}
157
158namespace gaia {
159
160class GaiaOAuthClientTest : public testing::Test {
161 protected:
162  virtual void SetUp() OVERRIDE {
163    client_info_.client_id = "test_client_id";
164    client_info_.client_secret = "test_client_secret";
165    client_info_.redirect_uri = "test_redirect_uri";
166  };
167
168 protected:
169  net::TestURLRequestContextGetter* GetRequestContext() {
170    if (!request_context_getter_) {
171      request_context_getter_ = new net::TestURLRequestContextGetter(
172          message_loop_.message_loop_proxy());
173    }
174    return request_context_getter_;
175  }
176
177  base::MessageLoop message_loop_;
178  scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
179  OAuthClientInfo client_info_;
180};
181
182class MockGaiaOAuthClientDelegate : public gaia::GaiaOAuthClient::Delegate {
183 public:
184  MockGaiaOAuthClientDelegate() {}
185  ~MockGaiaOAuthClientDelegate() {}
186
187  MOCK_METHOD3(OnGetTokensResponse, void(const std::string& refresh_token,
188                                         const std::string& access_token,
189                                         int expires_in_seconds));
190  MOCK_METHOD2(OnRefreshTokenResponse, void(const std::string& access_token,
191                                            int expires_in_seconds));
192  MOCK_METHOD1(OnGetUserEmailResponse, void(const std::string& user_email));
193  MOCK_METHOD1(OnGetUserIdResponse, void(const std::string& user_id));
194  MOCK_METHOD0(OnOAuthError, void());
195  MOCK_METHOD1(OnNetworkError, void(int response_code));
196
197  // gMock doesn't like methods that take or return scoped_ptr.  A
198  // work-around is to create a mock method that takes a raw ptr, and
199  // override the problematic method to call through to it.
200  // https://groups.google.com/a/chromium.org/d/msg/chromium-dev/01sDxsJ1OYw/I_S0xCBRF2oJ
201  MOCK_METHOD1(OnGetTokenInfoResponsePtr,
202               void(const DictionaryValue* token_info));
203  virtual void OnGetTokenInfoResponse(scoped_ptr<DictionaryValue> token_info)
204      OVERRIDE {
205    token_info_.reset(token_info.release());
206    OnGetTokenInfoResponsePtr(token_info_.get());
207  }
208
209 private:
210  scoped_ptr<DictionaryValue> token_info_;
211  DISALLOW_COPY_AND_ASSIGN(MockGaiaOAuthClientDelegate);
212};
213
214TEST_F(GaiaOAuthClientTest, NetworkFailure) {
215  int response_code = net::HTTP_INTERNAL_SERVER_ERROR;
216
217  MockGaiaOAuthClientDelegate delegate;
218  EXPECT_CALL(delegate, OnNetworkError(response_code))
219      .Times(1);
220
221  MockOAuthFetcherFactory factory;
222  factory.set_response_code(response_code);
223  factory.set_max_failure_count(4);
224
225  GaiaOAuthClient auth(GetRequestContext());
226  auth.GetTokensFromAuthCode(client_info_, "auth_code", 2, &delegate);
227}
228
229TEST_F(GaiaOAuthClientTest, NetworkFailureRecover) {
230  int response_code = net::HTTP_INTERNAL_SERVER_ERROR;
231
232  MockGaiaOAuthClientDelegate delegate;
233  EXPECT_CALL(delegate, OnGetTokensResponse(kTestRefreshToken, kTestAccessToken,
234      kTestExpiresIn)).Times(1);
235
236  MockOAuthFetcherFactory factory;
237  factory.set_response_code(response_code);
238  factory.set_max_failure_count(4);
239  factory.set_results(kDummyGetTokensResult);
240
241  GaiaOAuthClient auth(GetRequestContext());
242  auth.GetTokensFromAuthCode(client_info_, "auth_code", -1, &delegate);
243}
244
245TEST_F(GaiaOAuthClientTest, OAuthFailure) {
246  int response_code = net::HTTP_BAD_REQUEST;
247
248  MockGaiaOAuthClientDelegate delegate;
249  EXPECT_CALL(delegate, OnOAuthError()).Times(1);
250
251  MockOAuthFetcherFactory factory;
252  factory.set_response_code(response_code);
253  factory.set_max_failure_count(-1);
254  factory.set_results(kDummyGetTokensResult);
255
256  GaiaOAuthClient auth(GetRequestContext());
257  auth.GetTokensFromAuthCode(client_info_, "auth_code", -1, &delegate);
258}
259
260
261TEST_F(GaiaOAuthClientTest, GetTokensSuccess) {
262  MockGaiaOAuthClientDelegate delegate;
263  EXPECT_CALL(delegate, OnGetTokensResponse(kTestRefreshToken, kTestAccessToken,
264      kTestExpiresIn)).Times(1);
265
266  MockOAuthFetcherFactory factory;
267  factory.set_results(kDummyGetTokensResult);
268
269  GaiaOAuthClient auth(GetRequestContext());
270  auth.GetTokensFromAuthCode(client_info_, "auth_code", -1, &delegate);
271}
272
273TEST_F(GaiaOAuthClientTest, RefreshTokenSuccess) {
274  MockGaiaOAuthClientDelegate delegate;
275  EXPECT_CALL(delegate, OnRefreshTokenResponse(kTestAccessToken,
276      kTestExpiresIn)).Times(1);
277
278  MockOAuthFetcherFactory factory;
279  factory.set_results(kDummyRefreshTokenResult);
280  factory.set_complete_immediately(false);
281
282  GaiaOAuthClient auth(GetRequestContext());
283  auth.RefreshToken(client_info_, "refresh_token", std::vector<std::string>(),
284                    -1, &delegate);
285  EXPECT_THAT(factory.get_url_fetcher()->upload_data(),
286              Not(HasSubstr("scope")));
287  factory.get_url_fetcher()->Finish();
288}
289
290TEST_F(GaiaOAuthClientTest, RefreshTokenDownscopingSuccess) {
291  MockGaiaOAuthClientDelegate delegate;
292  EXPECT_CALL(delegate, OnRefreshTokenResponse(kTestAccessToken,
293      kTestExpiresIn)).Times(1);
294
295  MockOAuthFetcherFactory factory;
296  factory.set_results(kDummyRefreshTokenResult);
297  factory.set_complete_immediately(false);
298
299  GaiaOAuthClient auth(GetRequestContext());
300  auth.RefreshToken(client_info_, "refresh_token",
301                    std::vector<std::string>(1, "scope4test"), -1, &delegate);
302  EXPECT_THAT(factory.get_url_fetcher()->upload_data(),
303              HasSubstr("&scope=scope4test"));
304  factory.get_url_fetcher()->Finish();
305}
306
307
308TEST_F(GaiaOAuthClientTest, GetUserEmail) {
309  MockGaiaOAuthClientDelegate delegate;
310  EXPECT_CALL(delegate, OnGetUserEmailResponse(kTestUserEmail)).Times(1);
311
312  MockOAuthFetcherFactory factory;
313  factory.set_results(kDummyUserInfoResult);
314
315  GaiaOAuthClient auth(GetRequestContext());
316  auth.GetUserEmail("access_token", 1, &delegate);
317}
318
319TEST_F(GaiaOAuthClientTest, GetUserId) {
320  MockGaiaOAuthClientDelegate delegate;
321  EXPECT_CALL(delegate, OnGetUserIdResponse(kTestUserId)).Times(1);
322
323  MockOAuthFetcherFactory factory;
324  factory.set_results(kDummyUserIdResult);
325
326  GaiaOAuthClient auth(GetRequestContext());
327  auth.GetUserId("access_token", 1, &delegate);
328}
329
330TEST_F(GaiaOAuthClientTest, GetTokenInfo) {
331  const DictionaryValue* captured_result;
332
333  MockGaiaOAuthClientDelegate delegate;
334  EXPECT_CALL(delegate, OnGetTokenInfoResponsePtr(_))
335      .WillOnce(SaveArg<0>(&captured_result));
336
337  MockOAuthFetcherFactory factory;
338  factory.set_results(kDummyTokenInfoResult);
339
340  GaiaOAuthClient auth(GetRequestContext());
341  auth.GetTokenInfo("access_token", 1, &delegate);
342
343  std::string issued_to;
344  ASSERT_TRUE(captured_result->GetString("issued_to",  &issued_to));
345  ASSERT_EQ("1234567890.apps.googleusercontent.com", issued_to);
346}
347
348}  // namespace gaia
349