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