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.
5 // A complete set of unit tests for GaiaOAuthClient.
10 #include "base/json/json_reader.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/values.h"
13 #include "google_apis/gaia/gaia_oauth_client.h"
14 #include "net/base/net_errors.h"
15 #include "net/http/http_status_code.h"
16 #include "net/url_request/test_url_fetcher_factory.h"
17 #include "net/url_request/url_fetcher_delegate.h"
18 #include "net/url_request/url_request_status.h"
19 #include "net/url_request/url_request_test_util.h"
20 #include "testing/gmock/include/gmock/gmock.h"
21 #include "testing/gtest/include/gtest/gtest.h"
26 using ::testing::HasSubstr
;
27 using ::testing::Pointee
;
28 using ::testing::SaveArg
;
32 // Responds as though OAuth returned from the server.
33 class MockOAuthFetcher
: public net::TestURLFetcher
{
35 MockOAuthFetcher(int response_code
,
36 int max_failure_count
,
37 bool complete_immediately
,
39 const std::string
& results
,
40 net::URLFetcher::RequestType request_type
,
41 net::URLFetcherDelegate
* d
)
42 : net::TestURLFetcher(0, url
, d
),
43 max_failure_count_(max_failure_count
),
44 current_failure_count_(0),
45 complete_immediately_(complete_immediately
) {
47 set_response_code(response_code
);
48 SetResponseString(results
);
51 ~MockOAuthFetcher() override
{}
53 void Start() override
{
54 if ((GetResponseCode() != net::HTTP_OK
) && (max_failure_count_
!= -1) &&
55 (current_failure_count_
== max_failure_count_
)) {
56 set_response_code(net::HTTP_OK
);
59 net::URLRequestStatus::Status code
= net::URLRequestStatus::SUCCESS
;
60 if (GetResponseCode() != net::HTTP_OK
) {
61 code
= net::URLRequestStatus::FAILED
;
62 current_failure_count_
++;
64 set_status(net::URLRequestStatus(code
, 0));
66 if (complete_immediately_
)
67 delegate()->OnURLFetchComplete(this);
71 ASSERT_FALSE(complete_immediately_
);
72 delegate()->OnURLFetchComplete(this);
76 int max_failure_count_
;
77 int current_failure_count_
;
78 bool complete_immediately_
;
79 DISALLOW_COPY_AND_ASSIGN(MockOAuthFetcher
);
82 class MockOAuthFetcherFactory
: public net::URLFetcherFactory
,
83 public net::ScopedURLFetcherFactory
{
85 MockOAuthFetcherFactory()
86 : net::ScopedURLFetcherFactory(this),
87 response_code_(net::HTTP_OK
),
88 complete_immediately_(true) {
90 ~MockOAuthFetcherFactory() override
{}
91 scoped_ptr
<net::URLFetcher
> CreateURLFetcher(
94 net::URLFetcher::RequestType request_type
,
95 net::URLFetcherDelegate
* d
) override
{
96 url_fetcher_
= new MockOAuthFetcher(
99 complete_immediately_
,
104 return scoped_ptr
<net::URLFetcher
>(url_fetcher_
);
106 void set_response_code(int response_code
) {
107 response_code_
= response_code
;
109 void set_max_failure_count(int count
) {
110 max_failure_count_
= count
;
112 void set_results(const std::string
& results
) {
115 MockOAuthFetcher
* get_url_fetcher() {
118 void set_complete_immediately(bool complete_immediately
) {
119 complete_immediately_
= complete_immediately
;
122 MockOAuthFetcher
* url_fetcher_
;
124 bool complete_immediately_
;
125 int max_failure_count_
;
126 std::string results_
;
127 DISALLOW_COPY_AND_ASSIGN(MockOAuthFetcherFactory
);
130 const std::string kTestAccessToken
= "1/fFAGRNJru1FTz70BzhT3Zg";
131 const std::string kTestAccessTokenHandle
= "1/kjhH87dfgkj87Hhj5KJkjZ";
132 const std::string kTestRefreshToken
=
133 "1/6BMfW9j53gdGImsixUH6kU5RsR4zwI9lUVX-tqf8JXQ";
134 const std::string kTestUserEmail
= "a_user@gmail.com";
135 const std::string kTestUserId
= "8675309";
136 const int kTestExpiresIn
= 3920;
138 const std::string kDummyGetTokensResult
=
139 "{\"access_token\":\"" + kTestAccessToken
+ "\","
140 "\"expires_in\":" + base::IntToString(kTestExpiresIn
) + ","
141 "\"refresh_token\":\"" + kTestRefreshToken
+ "\"}";
143 const std::string kDummyRefreshTokenResult
=
144 "{\"access_token\":\"" + kTestAccessToken
+ "\","
145 "\"expires_in\":" + base::IntToString(kTestExpiresIn
) + "}";
147 const std::string kDummyUserInfoResult
=
148 "{\"email\":\"" + kTestUserEmail
+ "\"}";
150 const std::string kDummyUserIdResult
=
151 "{\"id\":\"" + kTestUserId
+ "\"}";
153 const std::string kDummyFullUserInfoResult
=
155 "\"family_name\": \"Bar\", "
156 "\"name\": \"Foo Bar\", "
157 "\"picture\": \"https://lh4.googleusercontent.com/hash/photo.jpg\", "
158 "\"locale\": \"en\", "
159 "\"gender\": \"male\", "
160 "\"link\": \"https://plus.google.com/+FooBar\", "
161 "\"given_name\": \"Foo\", "
162 "\"id\": \"12345678901234567890\""
165 const std::string kDummyTokenInfoResult
=
166 "{\"issued_to\": \"1234567890.apps.googleusercontent.com\","
167 "\"audience\": \"1234567890.apps.googleusercontent.com\","
168 "\"scope\": \"https://googleapis.com/oauth2/v2/tokeninfo\","
169 "\"expires_in\":" + base::IntToString(kTestExpiresIn
) + "}";
171 const std::string kDummyTokenHandleInfoResult
=
172 "{\"audience\": \"1234567890.apps.googleusercontent.com\","
173 "\"expires_in\":" + base::IntToString(kTestExpiresIn
) + "}";
179 class GaiaOAuthClientTest
: public testing::Test
{
181 void SetUp() override
{
182 client_info_
.client_id
= "test_client_id";
183 client_info_
.client_secret
= "test_client_secret";
184 client_info_
.redirect_uri
= "test_redirect_uri";
188 net::TestURLRequestContextGetter
* GetRequestContext() {
189 if (!request_context_getter_
.get()) {
190 request_context_getter_
= new net::TestURLRequestContextGetter(
191 message_loop_
.message_loop_proxy());
193 return request_context_getter_
.get();
196 base::MessageLoop message_loop_
;
197 scoped_refptr
<net::TestURLRequestContextGetter
> request_context_getter_
;
198 OAuthClientInfo client_info_
;
201 class MockGaiaOAuthClientDelegate
: public gaia::GaiaOAuthClient::Delegate
{
203 MockGaiaOAuthClientDelegate() {}
204 ~MockGaiaOAuthClientDelegate() {}
206 MOCK_METHOD3(OnGetTokensResponse
, void(const std::string
& refresh_token
,
207 const std::string
& access_token
,
208 int expires_in_seconds
));
209 MOCK_METHOD2(OnRefreshTokenResponse
, void(const std::string
& access_token
,
210 int expires_in_seconds
));
211 MOCK_METHOD1(OnGetUserEmailResponse
, void(const std::string
& user_email
));
212 MOCK_METHOD1(OnGetUserIdResponse
, void(const std::string
& user_id
));
213 MOCK_METHOD0(OnOAuthError
, void());
214 MOCK_METHOD1(OnNetworkError
, void(int response_code
));
216 // gMock doesn't like methods that take or return scoped_ptr. A
217 // work-around is to create a mock method that takes a raw ptr, and
218 // override the problematic method to call through to it.
219 // https://groups.google.com/a/chromium.org/d/msg/chromium-dev/01sDxsJ1OYw/I_S0xCBRF2oJ
220 MOCK_METHOD1(OnGetUserInfoResponsePtr
,
221 void(const base::DictionaryValue
* user_info
));
222 void OnGetUserInfoResponse(
223 scoped_ptr
<base::DictionaryValue
> user_info
) override
{
224 user_info_
.reset(user_info
.release());
225 OnGetUserInfoResponsePtr(user_info_
.get());
227 MOCK_METHOD1(OnGetTokenInfoResponsePtr
,
228 void(const base::DictionaryValue
* token_info
));
229 void OnGetTokenInfoResponse(
230 scoped_ptr
<base::DictionaryValue
> token_info
) override
{
231 token_info_
.reset(token_info
.release());
232 OnGetTokenInfoResponsePtr(token_info_
.get());
236 scoped_ptr
<base::DictionaryValue
> user_info_
;
237 scoped_ptr
<base::DictionaryValue
> token_info_
;
238 DISALLOW_COPY_AND_ASSIGN(MockGaiaOAuthClientDelegate
);
241 TEST_F(GaiaOAuthClientTest
, NetworkFailure
) {
242 int response_code
= net::HTTP_INTERNAL_SERVER_ERROR
;
244 MockGaiaOAuthClientDelegate delegate
;
245 EXPECT_CALL(delegate
, OnNetworkError(response_code
))
248 MockOAuthFetcherFactory factory
;
249 factory
.set_response_code(response_code
);
250 factory
.set_max_failure_count(4);
252 GaiaOAuthClient
auth(GetRequestContext());
253 auth
.GetTokensFromAuthCode(client_info_
, "auth_code", 2, &delegate
);
256 TEST_F(GaiaOAuthClientTest
, NetworkFailureRecover
) {
257 int response_code
= net::HTTP_INTERNAL_SERVER_ERROR
;
259 MockGaiaOAuthClientDelegate delegate
;
260 EXPECT_CALL(delegate
, OnGetTokensResponse(kTestRefreshToken
, kTestAccessToken
,
261 kTestExpiresIn
)).Times(1);
263 MockOAuthFetcherFactory factory
;
264 factory
.set_response_code(response_code
);
265 factory
.set_max_failure_count(4);
266 factory
.set_results(kDummyGetTokensResult
);
268 GaiaOAuthClient
auth(GetRequestContext());
269 auth
.GetTokensFromAuthCode(client_info_
, "auth_code", -1, &delegate
);
272 TEST_F(GaiaOAuthClientTest
, OAuthFailure
) {
273 int response_code
= net::HTTP_BAD_REQUEST
;
275 MockGaiaOAuthClientDelegate delegate
;
276 EXPECT_CALL(delegate
, OnOAuthError()).Times(1);
278 MockOAuthFetcherFactory factory
;
279 factory
.set_response_code(response_code
);
280 factory
.set_max_failure_count(-1);
281 factory
.set_results(kDummyGetTokensResult
);
283 GaiaOAuthClient
auth(GetRequestContext());
284 auth
.GetTokensFromAuthCode(client_info_
, "auth_code", -1, &delegate
);
288 TEST_F(GaiaOAuthClientTest
, GetTokensSuccess
) {
289 MockGaiaOAuthClientDelegate delegate
;
290 EXPECT_CALL(delegate
, OnGetTokensResponse(kTestRefreshToken
, kTestAccessToken
,
291 kTestExpiresIn
)).Times(1);
293 MockOAuthFetcherFactory factory
;
294 factory
.set_results(kDummyGetTokensResult
);
296 GaiaOAuthClient
auth(GetRequestContext());
297 auth
.GetTokensFromAuthCode(client_info_
, "auth_code", -1, &delegate
);
300 TEST_F(GaiaOAuthClientTest
, RefreshTokenSuccess
) {
301 MockGaiaOAuthClientDelegate delegate
;
302 EXPECT_CALL(delegate
, OnRefreshTokenResponse(kTestAccessToken
,
303 kTestExpiresIn
)).Times(1);
305 MockOAuthFetcherFactory factory
;
306 factory
.set_results(kDummyRefreshTokenResult
);
307 factory
.set_complete_immediately(false);
309 GaiaOAuthClient
auth(GetRequestContext());
310 auth
.RefreshToken(client_info_
, "refresh_token", std::vector
<std::string
>(),
312 EXPECT_THAT(factory
.get_url_fetcher()->upload_data(),
313 Not(HasSubstr("scope")));
314 factory
.get_url_fetcher()->Finish();
317 TEST_F(GaiaOAuthClientTest
, RefreshTokenDownscopingSuccess
) {
318 MockGaiaOAuthClientDelegate delegate
;
319 EXPECT_CALL(delegate
, OnRefreshTokenResponse(kTestAccessToken
,
320 kTestExpiresIn
)).Times(1);
322 MockOAuthFetcherFactory factory
;
323 factory
.set_results(kDummyRefreshTokenResult
);
324 factory
.set_complete_immediately(false);
326 GaiaOAuthClient
auth(GetRequestContext());
327 auth
.RefreshToken(client_info_
, "refresh_token",
328 std::vector
<std::string
>(1, "scope4test"), -1, &delegate
);
329 EXPECT_THAT(factory
.get_url_fetcher()->upload_data(),
330 HasSubstr("&scope=scope4test"));
331 factory
.get_url_fetcher()->Finish();
335 TEST_F(GaiaOAuthClientTest
, GetUserEmail
) {
336 MockGaiaOAuthClientDelegate delegate
;
337 EXPECT_CALL(delegate
, OnGetUserEmailResponse(kTestUserEmail
)).Times(1);
339 MockOAuthFetcherFactory factory
;
340 factory
.set_results(kDummyUserInfoResult
);
342 GaiaOAuthClient
auth(GetRequestContext());
343 auth
.GetUserEmail("access_token", 1, &delegate
);
346 TEST_F(GaiaOAuthClientTest
, GetUserId
) {
347 MockGaiaOAuthClientDelegate delegate
;
348 EXPECT_CALL(delegate
, OnGetUserIdResponse(kTestUserId
)).Times(1);
350 MockOAuthFetcherFactory factory
;
351 factory
.set_results(kDummyUserIdResult
);
353 GaiaOAuthClient
auth(GetRequestContext());
354 auth
.GetUserId("access_token", 1, &delegate
);
357 TEST_F(GaiaOAuthClientTest
, GetUserInfo
) {
358 const base::DictionaryValue
* captured_result
;
360 MockGaiaOAuthClientDelegate delegate
;
361 EXPECT_CALL(delegate
, OnGetUserInfoResponsePtr(_
))
362 .WillOnce(SaveArg
<0>(&captured_result
));
364 MockOAuthFetcherFactory factory
;
365 factory
.set_results(kDummyFullUserInfoResult
);
367 GaiaOAuthClient
auth(GetRequestContext());
368 auth
.GetUserInfo("access_token", 1, &delegate
);
370 scoped_ptr
<base::Value
> value(
371 base::JSONReader::Read(kDummyFullUserInfoResult
));
373 ASSERT_TRUE(value
->IsType(base::Value::TYPE_DICTIONARY
));
374 base::DictionaryValue
* expected_result
;
375 value
->GetAsDictionary(&expected_result
);
377 ASSERT_TRUE(expected_result
->Equals(captured_result
));
380 TEST_F(GaiaOAuthClientTest
, GetTokenInfo
) {
381 const base::DictionaryValue
* captured_result
;
383 MockGaiaOAuthClientDelegate delegate
;
384 EXPECT_CALL(delegate
, OnGetTokenInfoResponsePtr(_
))
385 .WillOnce(SaveArg
<0>(&captured_result
));
387 MockOAuthFetcherFactory factory
;
388 factory
.set_results(kDummyTokenInfoResult
);
390 GaiaOAuthClient
auth(GetRequestContext());
391 auth
.GetTokenInfo("some_token", 1, &delegate
);
393 std::string issued_to
;
394 ASSERT_TRUE(captured_result
->GetString("issued_to", &issued_to
));
395 ASSERT_EQ("1234567890.apps.googleusercontent.com", issued_to
);
398 TEST_F(GaiaOAuthClientTest
, GetTokenHandleInfo
) {
399 const base::DictionaryValue
* captured_result
;
401 MockGaiaOAuthClientDelegate delegate
;
402 EXPECT_CALL(delegate
, OnGetTokenInfoResponsePtr(_
))
403 .WillOnce(SaveArg
<0>(&captured_result
));
405 MockOAuthFetcherFactory factory
;
406 factory
.set_results(kDummyTokenHandleInfoResult
);
408 GaiaOAuthClient
auth(GetRequestContext());
409 auth
.GetTokenHandleInfo("some_handle", 1, &delegate
);
411 std::string audience
;
412 ASSERT_TRUE(captured_result
->GetString("audience", &audience
));
413 ASSERT_EQ("1234567890.apps.googleusercontent.com", audience
);