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 OAuth2MintTokenFlow.
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/json/json_reader.h"
     11 #include "base/memory/scoped_ptr.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "base/values.h"
     14 #include "google_apis/gaia/google_service_auth_error.h"
     15 #include "google_apis/gaia/oauth2_mint_token_flow.h"
     16 #include "net/url_request/test_url_fetcher_factory.h"
     17 #include "net/url_request/url_request_status.h"
     18 #include "testing/gmock/include/gmock/gmock.h"
     19 #include "testing/gtest/include/gtest/gtest.h"
     20 
     21 using net::TestURLFetcher;
     22 using net::URLFetcher;
     23 using net::URLRequestStatus;
     24 using testing::_;
     25 using testing::StrictMock;
     26 
     27 namespace {
     28 
     29 static const char kValidTokenResponse[] =
     30     "{"
     31     "  \"token\": \"at1\","
     32     "  \"issueAdvice\": \"Auto\","
     33     "  \"expiresIn\": \"3600\""
     34     "}";
     35 static const char kTokenResponseNoAccessToken[] =
     36     "{"
     37     "  \"issueAdvice\": \"Auto\""
     38     "}";
     39 
     40 static const char kValidIssueAdviceResponse[] =
     41     "{"
     42     "  \"issueAdvice\": \"consent\","
     43     "  \"consent\": {"
     44     "    \"oauthClient\": {"
     45     "      \"name\": \"Test app\","
     46     "      \"iconUri\": \"\","
     47     "      \"developerEmail\": \"munjal (at) chromium.org\""
     48     "    },"
     49     "    \"scopes\": ["
     50     "      {"
     51     "        \"description\": \"Manage your calendars\","
     52     "        \"detail\": \"\nView and manage your calendars\n\""
     53     "      },"
     54     "      {"
     55     "        \"description\": \"Manage your documents\","
     56     "        \"detail\": \"\nView your documents\nUpload new documents\n\""
     57     "      }"
     58     "    ]"
     59     "  }"
     60     "}";
     61 
     62 static const char kIssueAdviceResponseNoDescription[] =
     63     "{"
     64     "  \"issueAdvice\": \"consent\","
     65     "  \"consent\": {"
     66     "    \"oauthClient\": {"
     67     "      \"name\": \"Test app\","
     68     "      \"iconUri\": \"\","
     69     "      \"developerEmail\": \"munjal (at) chromium.org\""
     70     "    },"
     71     "    \"scopes\": ["
     72     "      {"
     73     "        \"description\": \"Manage your calendars\","
     74     "        \"detail\": \"\nView and manage your calendars\n\""
     75     "      },"
     76     "      {"
     77     "        \"detail\": \"\nView your documents\nUpload new documents\n\""
     78     "      }"
     79     "    ]"
     80     "  }"
     81     "}";
     82 
     83 static const char kIssueAdviceResponseNoDetail[] =
     84     "{"
     85     "  \"issueAdvice\": \"consent\","
     86     "  \"consent\": {"
     87     "    \"oauthClient\": {"
     88     "      \"name\": \"Test app\","
     89     "      \"iconUri\": \"\","
     90     "      \"developerEmail\": \"munjal (at) chromium.org\""
     91     "    },"
     92     "    \"scopes\": ["
     93     "      {"
     94     "        \"description\": \"Manage your calendars\","
     95     "        \"detail\": \"\nView and manage your calendars\n\""
     96     "      },"
     97     "      {"
     98     "        \"description\": \"Manage your documents\""
     99     "      }"
    100     "    ]"
    101     "  }"
    102     "}";
    103 
    104 std::vector<std::string> CreateTestScopes() {
    105   std::vector<std::string> scopes;
    106   scopes.push_back("http://scope1");
    107   scopes.push_back("http://scope2");
    108   return scopes;
    109 }
    110 
    111 static IssueAdviceInfo CreateIssueAdvice() {
    112   IssueAdviceInfo ia;
    113   IssueAdviceInfoEntry e1;
    114   e1.description = base::ASCIIToUTF16("Manage your calendars");
    115   e1.details.push_back(base::ASCIIToUTF16("View and manage your calendars"));
    116   ia.push_back(e1);
    117   IssueAdviceInfoEntry e2;
    118   e2.description = base::ASCIIToUTF16("Manage your documents");
    119   e2.details.push_back(base::ASCIIToUTF16("View your documents"));
    120   e2.details.push_back(base::ASCIIToUTF16("Upload new documents"));
    121   ia.push_back(e2);
    122   return ia;
    123 }
    124 
    125 class MockDelegate : public OAuth2MintTokenFlow::Delegate {
    126  public:
    127   MockDelegate() {}
    128   ~MockDelegate() {}
    129 
    130   MOCK_METHOD2(OnMintTokenSuccess, void(const std::string& access_token,
    131                                         int time_to_live));
    132   MOCK_METHOD1(OnIssueAdviceSuccess,
    133                void (const IssueAdviceInfo& issue_advice));
    134   MOCK_METHOD1(OnMintTokenFailure,
    135                void(const GoogleServiceAuthError& error));
    136 };
    137 
    138 class MockMintTokenFlow : public OAuth2MintTokenFlow {
    139  public:
    140   explicit MockMintTokenFlow(MockDelegate* delegate,
    141     const OAuth2MintTokenFlow::Parameters& parameters )
    142       : OAuth2MintTokenFlow(NULL, delegate, parameters) {}
    143   ~MockMintTokenFlow() {}
    144 
    145   MOCK_METHOD0(CreateAccessTokenFetcher, OAuth2AccessTokenFetcher*());
    146 };
    147 
    148 }  // namespace
    149 
    150 class OAuth2MintTokenFlowTest : public testing::Test {
    151  public:
    152   OAuth2MintTokenFlowTest() {}
    153   virtual ~OAuth2MintTokenFlowTest() { }
    154 
    155  protected:
    156   void CreateFlow(OAuth2MintTokenFlow::Mode mode) {
    157     return CreateFlow(&delegate_, mode);
    158   }
    159 
    160   void CreateFlow(MockDelegate* delegate,
    161                   OAuth2MintTokenFlow::Mode mode) {
    162     std::string rt = "refresh_token";
    163     std::string ext_id = "ext1";
    164     std::string client_id = "client1";
    165     std::vector<std::string> scopes(CreateTestScopes());
    166     flow_.reset(new MockMintTokenFlow(
    167         delegate,
    168         OAuth2MintTokenFlow::Parameters(rt, ext_id, client_id, scopes, mode)));
    169   }
    170 
    171   // Helper to parse the given string to DictionaryValue.
    172   static base::DictionaryValue* ParseJson(const std::string& str) {
    173     scoped_ptr<base::Value> value(base::JSONReader::Read(str));
    174     EXPECT_TRUE(value.get());
    175     EXPECT_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
    176     return static_cast<base::DictionaryValue*>(value.release());
    177   }
    178 
    179   scoped_ptr<MockMintTokenFlow> flow_;
    180   StrictMock<MockDelegate> delegate_;
    181 };
    182 
    183 TEST_F(OAuth2MintTokenFlowTest, CreateApiCallBody) {
    184   {  // Issue advice mode.
    185     CreateFlow(OAuth2MintTokenFlow::MODE_ISSUE_ADVICE);
    186     std::string body = flow_->CreateApiCallBody();
    187     std::string expected_body(
    188           "force=false"
    189           "&response_type=none"
    190           "&scope=http://scope1+http://scope2"
    191           "&client_id=client1"
    192           "&origin=ext1");
    193     EXPECT_EQ(expected_body, body);
    194   }
    195   {  // Record grant mode.
    196     CreateFlow(OAuth2MintTokenFlow::MODE_RECORD_GRANT);
    197     std::string body = flow_->CreateApiCallBody();
    198     std::string expected_body(
    199         "force=true"
    200         "&response_type=none"
    201         "&scope=http://scope1+http://scope2"
    202         "&client_id=client1"
    203         "&origin=ext1");
    204     EXPECT_EQ(expected_body, body);
    205   }
    206   {  // Mint token no force mode.
    207     CreateFlow(OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE);
    208     std::string body = flow_->CreateApiCallBody();
    209     std::string expected_body(
    210         "force=false"
    211         "&response_type=token"
    212         "&scope=http://scope1+http://scope2"
    213         "&client_id=client1"
    214         "&origin=ext1");
    215     EXPECT_EQ(expected_body, body);
    216   }
    217   {  // Mint token force mode.
    218     CreateFlow(OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE);
    219     std::string body = flow_->CreateApiCallBody();
    220     std::string expected_body(
    221         "force=true"
    222         "&response_type=token"
    223         "&scope=http://scope1+http://scope2"
    224         "&client_id=client1"
    225         "&origin=ext1");
    226     EXPECT_EQ(expected_body, body);
    227   }
    228 }
    229 
    230 TEST_F(OAuth2MintTokenFlowTest, ParseMintTokenResponse) {
    231   {  // Access token missing.
    232     scoped_ptr<base::DictionaryValue> json(
    233         ParseJson(kTokenResponseNoAccessToken));
    234     std::string at;
    235     int ttl;
    236     EXPECT_FALSE(OAuth2MintTokenFlow::ParseMintTokenResponse(json.get(), &at,
    237                                                              &ttl));
    238     EXPECT_TRUE(at.empty());
    239   }
    240   {  // All good.
    241     scoped_ptr<base::DictionaryValue> json(ParseJson(kValidTokenResponse));
    242     std::string at;
    243     int ttl;
    244     EXPECT_TRUE(OAuth2MintTokenFlow::ParseMintTokenResponse(json.get(), &at,
    245                                                             &ttl));
    246     EXPECT_EQ("at1", at);
    247     EXPECT_EQ(3600, ttl);
    248   }
    249 }
    250 
    251 TEST_F(OAuth2MintTokenFlowTest, ParseIssueAdviceResponse) {
    252   {  // Description missing.
    253     scoped_ptr<base::DictionaryValue> json(
    254         ParseJson(kIssueAdviceResponseNoDescription));
    255     IssueAdviceInfo ia;
    256     EXPECT_FALSE(OAuth2MintTokenFlow::ParseIssueAdviceResponse(
    257         json.get(), &ia));
    258     EXPECT_TRUE(ia.empty());
    259   }
    260   {  // Detail missing.
    261     scoped_ptr<base::DictionaryValue> json(
    262         ParseJson(kIssueAdviceResponseNoDetail));
    263     IssueAdviceInfo ia;
    264     EXPECT_FALSE(OAuth2MintTokenFlow::ParseIssueAdviceResponse(
    265         json.get(), &ia));
    266     EXPECT_TRUE(ia.empty());
    267   }
    268   {  // All good.
    269     scoped_ptr<base::DictionaryValue> json(
    270         ParseJson(kValidIssueAdviceResponse));
    271     IssueAdviceInfo ia;
    272     EXPECT_TRUE(OAuth2MintTokenFlow::ParseIssueAdviceResponse(
    273         json.get(), &ia));
    274     IssueAdviceInfo ia_expected(CreateIssueAdvice());
    275     EXPECT_EQ(ia_expected, ia);
    276   }
    277 }
    278 
    279 TEST_F(OAuth2MintTokenFlowTest, ProcessApiCallSuccess) {
    280   {  // No body.
    281     TestURLFetcher url_fetcher(1, GURL("http://www.google.com"), NULL);
    282     url_fetcher.SetResponseString(std::string());
    283     CreateFlow(OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE);
    284     EXPECT_CALL(delegate_, OnMintTokenFailure(_));
    285     flow_->ProcessApiCallSuccess(&url_fetcher);
    286   }
    287   {  // Bad json.
    288     TestURLFetcher url_fetcher(1, GURL("http://www.google.com"), NULL);
    289     url_fetcher.SetResponseString("foo");
    290     CreateFlow(OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE);
    291     EXPECT_CALL(delegate_, OnMintTokenFailure(_));
    292     flow_->ProcessApiCallSuccess(&url_fetcher);
    293   }
    294   {  // Valid json: no access token.
    295     TestURLFetcher url_fetcher(1, GURL("http://www.google.com"), NULL);
    296     url_fetcher.SetResponseString(kTokenResponseNoAccessToken);
    297     CreateFlow(OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE);
    298     EXPECT_CALL(delegate_, OnMintTokenFailure(_));
    299     flow_->ProcessApiCallSuccess(&url_fetcher);
    300   }
    301   {  // Valid json: good token response.
    302     TestURLFetcher url_fetcher(1, GURL("http://www.google.com"), NULL);
    303     url_fetcher.SetResponseString(kValidTokenResponse);
    304     CreateFlow(OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE);
    305     EXPECT_CALL(delegate_, OnMintTokenSuccess("at1", 3600));
    306     flow_->ProcessApiCallSuccess(&url_fetcher);
    307   }
    308   {  // Valid json: no description.
    309     TestURLFetcher url_fetcher(1, GURL("http://www.google.com"), NULL);
    310     url_fetcher.SetResponseString(kIssueAdviceResponseNoDescription);
    311     CreateFlow(OAuth2MintTokenFlow::MODE_ISSUE_ADVICE);
    312     EXPECT_CALL(delegate_, OnMintTokenFailure(_));
    313     flow_->ProcessApiCallSuccess(&url_fetcher);
    314   }
    315   {  // Valid json: no detail.
    316     TestURLFetcher url_fetcher(1, GURL("http://www.google.com"), NULL);
    317     url_fetcher.SetResponseString(kIssueAdviceResponseNoDetail);
    318     CreateFlow(OAuth2MintTokenFlow::MODE_ISSUE_ADVICE);
    319     EXPECT_CALL(delegate_, OnMintTokenFailure(_));
    320     flow_->ProcessApiCallSuccess(&url_fetcher);
    321   }
    322   {  // Valid json: good issue advice response.
    323     TestURLFetcher url_fetcher(1, GURL("http://www.google.com"), NULL);
    324     url_fetcher.SetResponseString(kValidIssueAdviceResponse);
    325     CreateFlow(OAuth2MintTokenFlow::MODE_ISSUE_ADVICE);
    326     IssueAdviceInfo ia(CreateIssueAdvice());
    327     EXPECT_CALL(delegate_, OnIssueAdviceSuccess(ia));
    328     flow_->ProcessApiCallSuccess(&url_fetcher);
    329   }
    330 }
    331 
    332 TEST_F(OAuth2MintTokenFlowTest, ProcessApiCallFailure) {
    333   {  // Null delegate should work fine.
    334     TestURLFetcher url_fetcher(1, GURL("http://www.google.com"), NULL);
    335     url_fetcher.set_status(URLRequestStatus(URLRequestStatus::FAILED, 101));
    336     CreateFlow(NULL, OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE);
    337     flow_->ProcessApiCallFailure(&url_fetcher);
    338   }
    339 
    340   {  // Non-null delegate.
    341     TestURLFetcher url_fetcher(1, GURL("http://www.google.com"), NULL);
    342     url_fetcher.set_status(URLRequestStatus(URLRequestStatus::FAILED, 101));
    343     CreateFlow(OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE);
    344     EXPECT_CALL(delegate_, OnMintTokenFailure(_));
    345     flow_->ProcessApiCallFailure(&url_fetcher);
    346   }
    347 }
    348 
    349 TEST_F(OAuth2MintTokenFlowTest, ProcessMintAccessTokenFailure) {
    350   {  // Null delegate should work fine.
    351     GoogleServiceAuthError error(
    352         GoogleServiceAuthError::FromConnectionError(101));
    353     CreateFlow(NULL, OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE);
    354     flow_->ProcessMintAccessTokenFailure(error);
    355   }
    356 
    357   {  // Non-null delegate.
    358     GoogleServiceAuthError error(
    359         GoogleServiceAuthError::FromConnectionError(101));
    360     CreateFlow(OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE);
    361     EXPECT_CALL(delegate_, OnMintTokenFailure(error));
    362     flow_->ProcessMintAccessTokenFailure(error);
    363   }
    364 }
    365