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 23 using ::testing::_; 24 using ::testing::Eq; 25 using ::testing::HasSubstr; 26 using ::testing::Pointee; 27 using ::testing::SaveArg; 28 29 namespace { 30 31 // Responds as though OAuth returned from the server. 32 class 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 81 class 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 129 const std::string kTestAccessToken = "1/fFAGRNJru1FTz70BzhT3Zg"; 130 const std::string kTestRefreshToken = 131 "1/6BMfW9j53gdGImsixUH6kU5RsR4zwI9lUVX-tqf8JXQ"; 132 const std::string kTestUserEmail = "a_user (at) gmail.com"; 133 const std::string kTestUserId = "8675309"; 134 const int kTestExpiresIn = 3920; 135 136 const std::string kDummyGetTokensResult = 137 "{\"access_token\":\"" + kTestAccessToken + "\"," 138 "\"expires_in\":" + base::IntToString(kTestExpiresIn) + "," 139 "\"refresh_token\":\"" + kTestRefreshToken + "\"}"; 140 141 const std::string kDummyRefreshTokenResult = 142 "{\"access_token\":\"" + kTestAccessToken + "\"," 143 "\"expires_in\":" + base::IntToString(kTestExpiresIn) + "}"; 144 145 const std::string kDummyUserInfoResult = 146 "{\"email\":\"" + kTestUserEmail + "\"}"; 147 148 const std::string kDummyUserIdResult = 149 "{\"id\":\"" + kTestUserId + "\"}"; 150 151 const 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 158 namespace gaia { 159 160 class 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 182 class 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 214 TEST_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 229 TEST_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 245 TEST_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 261 TEST_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 273 TEST_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 290 TEST_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 308 TEST_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 319 TEST_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 330 TEST_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