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 int kTestExpiresIn = 3920; 134 135 const std::string kDummyGetTokensResult = 136 "{\"access_token\":\"" + kTestAccessToken + "\"," 137 "\"expires_in\":" + base::IntToString(kTestExpiresIn) + "," 138 "\"refresh_token\":\"" + kTestRefreshToken + "\"}"; 139 140 const std::string kDummyRefreshTokenResult = 141 "{\"access_token\":\"" + kTestAccessToken + "\"," 142 "\"expires_in\":" + base::IntToString(kTestExpiresIn) + "}"; 143 144 const std::string kDummyUserInfoResult = 145 "{\"email\":\"" + kTestUserEmail + "\"}"; 146 147 const std::string kDummyTokenInfoResult = 148 "{\"issued_to\": \"1234567890.apps.googleusercontent.com\"," 149 "\"audience\": \"1234567890.apps.googleusercontent.com\"," 150 "\"scope\": \"https://googleapis.com/oauth2/v2/tokeninfo\"," 151 "\"expires_in\":" + base::IntToString(kTestExpiresIn) + "}"; 152 } 153 154 namespace gaia { 155 156 class GaiaOAuthClientTest : public testing::Test { 157 protected: 158 virtual void SetUp() OVERRIDE { 159 client_info_.client_id = "test_client_id"; 160 client_info_.client_secret = "test_client_secret"; 161 client_info_.redirect_uri = "test_redirect_uri"; 162 }; 163 164 protected: 165 net::TestURLRequestContextGetter* GetRequestContext() { 166 if (!request_context_getter_) { 167 request_context_getter_ = new net::TestURLRequestContextGetter( 168 message_loop_.message_loop_proxy()); 169 } 170 return request_context_getter_; 171 } 172 173 base::MessageLoop message_loop_; 174 scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_; 175 OAuthClientInfo client_info_; 176 }; 177 178 class MockGaiaOAuthClientDelegate : public gaia::GaiaOAuthClient::Delegate { 179 public: 180 MockGaiaOAuthClientDelegate() {} 181 ~MockGaiaOAuthClientDelegate() {} 182 183 MOCK_METHOD3(OnGetTokensResponse, void(const std::string& refresh_token, 184 const std::string& access_token, 185 int expires_in_seconds)); 186 MOCK_METHOD2(OnRefreshTokenResponse, void(const std::string& access_token, 187 int expires_in_seconds)); 188 MOCK_METHOD1(OnGetUserEmailResponse, void(const std::string& user_email)); 189 MOCK_METHOD0(OnOAuthError, void()); 190 MOCK_METHOD1(OnNetworkError, void(int response_code)); 191 192 // gMock doesn't like methods that take or return scoped_ptr. A 193 // work-around is to create a mock method that takes a raw ptr, and 194 // override the problematic method to call through to it. 195 // https://groups.google.com/a/chromium.org/d/msg/chromium-dev/01sDxsJ1OYw/I_S0xCBRF2oJ 196 MOCK_METHOD1(OnGetTokenInfoResponsePtr, 197 void(const DictionaryValue* token_info)); 198 virtual void OnGetTokenInfoResponse(scoped_ptr<DictionaryValue> token_info) 199 OVERRIDE { 200 token_info_.reset(token_info.release()); 201 OnGetTokenInfoResponsePtr(token_info_.get()); 202 } 203 204 private: 205 scoped_ptr<DictionaryValue> token_info_; 206 DISALLOW_COPY_AND_ASSIGN(MockGaiaOAuthClientDelegate); 207 }; 208 209 TEST_F(GaiaOAuthClientTest, NetworkFailure) { 210 int response_code = net::HTTP_INTERNAL_SERVER_ERROR; 211 212 MockGaiaOAuthClientDelegate delegate; 213 EXPECT_CALL(delegate, OnNetworkError(response_code)) 214 .Times(1); 215 216 MockOAuthFetcherFactory factory; 217 factory.set_response_code(response_code); 218 factory.set_max_failure_count(4); 219 220 GaiaOAuthClient auth(GetRequestContext()); 221 auth.GetTokensFromAuthCode(client_info_, "auth_code", 2, &delegate); 222 } 223 224 TEST_F(GaiaOAuthClientTest, NetworkFailureRecover) { 225 int response_code = net::HTTP_INTERNAL_SERVER_ERROR; 226 227 MockGaiaOAuthClientDelegate delegate; 228 EXPECT_CALL(delegate, OnGetTokensResponse(kTestRefreshToken, kTestAccessToken, 229 kTestExpiresIn)).Times(1); 230 231 MockOAuthFetcherFactory factory; 232 factory.set_response_code(response_code); 233 factory.set_max_failure_count(4); 234 factory.set_results(kDummyGetTokensResult); 235 236 GaiaOAuthClient auth(GetRequestContext()); 237 auth.GetTokensFromAuthCode(client_info_, "auth_code", -1, &delegate); 238 } 239 240 TEST_F(GaiaOAuthClientTest, OAuthFailure) { 241 int response_code = net::HTTP_BAD_REQUEST; 242 243 MockGaiaOAuthClientDelegate delegate; 244 EXPECT_CALL(delegate, OnOAuthError()).Times(1); 245 246 MockOAuthFetcherFactory factory; 247 factory.set_response_code(response_code); 248 factory.set_max_failure_count(-1); 249 factory.set_results(kDummyGetTokensResult); 250 251 GaiaOAuthClient auth(GetRequestContext()); 252 auth.GetTokensFromAuthCode(client_info_, "auth_code", -1, &delegate); 253 } 254 255 256 TEST_F(GaiaOAuthClientTest, GetTokensSuccess) { 257 MockGaiaOAuthClientDelegate delegate; 258 EXPECT_CALL(delegate, OnGetTokensResponse(kTestRefreshToken, kTestAccessToken, 259 kTestExpiresIn)).Times(1); 260 261 MockOAuthFetcherFactory factory; 262 factory.set_results(kDummyGetTokensResult); 263 264 GaiaOAuthClient auth(GetRequestContext()); 265 auth.GetTokensFromAuthCode(client_info_, "auth_code", -1, &delegate); 266 } 267 268 TEST_F(GaiaOAuthClientTest, RefreshTokenSuccess) { 269 MockGaiaOAuthClientDelegate delegate; 270 EXPECT_CALL(delegate, OnRefreshTokenResponse(kTestAccessToken, 271 kTestExpiresIn)).Times(1); 272 273 MockOAuthFetcherFactory factory; 274 factory.set_results(kDummyRefreshTokenResult); 275 factory.set_complete_immediately(false); 276 277 GaiaOAuthClient auth(GetRequestContext()); 278 auth.RefreshToken(client_info_, "refresh_token", std::vector<std::string>(), 279 -1, &delegate); 280 EXPECT_THAT(factory.get_url_fetcher()->upload_data(), 281 Not(HasSubstr("scope"))); 282 factory.get_url_fetcher()->Finish(); 283 } 284 285 TEST_F(GaiaOAuthClientTest, RefreshTokenDownscopingSuccess) { 286 MockGaiaOAuthClientDelegate delegate; 287 EXPECT_CALL(delegate, OnRefreshTokenResponse(kTestAccessToken, 288 kTestExpiresIn)).Times(1); 289 290 MockOAuthFetcherFactory factory; 291 factory.set_results(kDummyRefreshTokenResult); 292 factory.set_complete_immediately(false); 293 294 GaiaOAuthClient auth(GetRequestContext()); 295 auth.RefreshToken(client_info_, "refresh_token", 296 std::vector<std::string>(1, "scope4test"), -1, &delegate); 297 EXPECT_THAT(factory.get_url_fetcher()->upload_data(), 298 HasSubstr("&scope=scope4test")); 299 factory.get_url_fetcher()->Finish(); 300 } 301 302 303 TEST_F(GaiaOAuthClientTest, GetUserEmail) { 304 MockGaiaOAuthClientDelegate delegate; 305 EXPECT_CALL(delegate, OnGetUserEmailResponse(kTestUserEmail)).Times(1); 306 307 MockOAuthFetcherFactory factory; 308 factory.set_results(kDummyUserInfoResult); 309 310 GaiaOAuthClient auth(GetRequestContext()); 311 auth.GetUserEmail("access_token", 1, &delegate); 312 } 313 314 TEST_F(GaiaOAuthClientTest, GetTokenInfo) { 315 const DictionaryValue* captured_result; 316 317 MockGaiaOAuthClientDelegate delegate; 318 EXPECT_CALL(delegate, OnGetTokenInfoResponsePtr(_)) 319 .WillOnce(SaveArg<0>(&captured_result)); 320 321 MockOAuthFetcherFactory factory; 322 factory.set_results(kDummyTokenInfoResult); 323 324 GaiaOAuthClient auth(GetRequestContext()); 325 auth.GetTokenInfo("access_token", 1, &delegate); 326 327 std::string issued_to; 328 ASSERT_TRUE(captured_result->GetString("issued_to", &issued_to)); 329 ASSERT_EQ("1234567890.apps.googleusercontent.com", issued_to); 330 } 331 332 } // namespace gaia 333