oauth2_mint_token_flow_unittest.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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
21using net::TestURLFetcher;
22using net::URLFetcher;
23using net::URLRequestStatus;
24using testing::_;
25using testing::StrictMock;
26
27namespace {
28
29static const char kValidTokenResponse[] =
30    "{"
31    "  \"token\": \"at1\","
32    "  \"issueAdvice\": \"Auto\","
33    "  \"expiresIn\": \"3600\""
34    "}";
35static const char kTokenResponseNoAccessToken[] =
36    "{"
37    "  \"issueAdvice\": \"Auto\""
38    "}";
39
40static const char kValidIssueAdviceResponse[] =
41    "{"
42    "  \"issueAdvice\": \"consent\","
43    "  \"consent\": {"
44    "    \"oauthClient\": {"
45    "      \"name\": \"Test app\","
46    "      \"iconUri\": \"\","
47    "      \"developerEmail\": \"munjal@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
62static const char kIssueAdviceResponseNoDescription[] =
63    "{"
64    "  \"issueAdvice\": \"consent\","
65    "  \"consent\": {"
66    "    \"oauthClient\": {"
67    "      \"name\": \"Test app\","
68    "      \"iconUri\": \"\","
69    "      \"developerEmail\": \"munjal@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
83static const char kIssueAdviceResponseNoDetail[] =
84    "{"
85    "  \"issueAdvice\": \"consent\","
86    "  \"consent\": {"
87    "    \"oauthClient\": {"
88    "      \"name\": \"Test app\","
89    "      \"iconUri\": \"\","
90    "      \"developerEmail\": \"munjal@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
104std::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
111static 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
125class 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
138class 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
150class 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
183TEST_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
230TEST_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
251TEST_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
279TEST_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
332TEST_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
349TEST_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