1// Copyright 2013 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#include "google_apis/drive/request_sender.h"
6
7#include "base/sequenced_task_runner.h"
8#include "base/strings/string_number_conversions.h"
9#include "google_apis/drive/base_requests.h"
10#include "google_apis/drive/dummy_auth_service.h"
11#include "testing/gtest/include/gtest/gtest.h"
12
13namespace google_apis {
14
15namespace {
16
17const char kTestRefreshToken[] = "valid-refresh-token";
18const char kTestAccessToken[] = "valid-access-token";
19
20// Enum for indicating the reason why a request is finished.
21enum FinishReason {
22  NONE,
23  SUCCESS,
24  CANCEL,
25  AUTH_FAILURE,
26};
27
28// AuthService for testing purpose. It accepts kTestRefreshToken and returns
29// kTestAccessToken + {"1", "2", "3", ...}.
30class TestAuthService : public DummyAuthService {
31 public:
32  TestAuthService() : auth_try_count_(0) {}
33
34  virtual void StartAuthentication(
35      const AuthStatusCallback& callback) OVERRIDE {
36    // RequestSender should clear the rejected access token before starting
37    // to request another one.
38    EXPECT_FALSE(HasAccessToken());
39
40    ++auth_try_count_;
41
42    if (refresh_token() == kTestRefreshToken) {
43      const std::string token =
44          kTestAccessToken + base::IntToString(auth_try_count_);
45      set_access_token(token);
46      callback.Run(HTTP_SUCCESS, token);
47    } else {
48      set_access_token("");
49      callback.Run(HTTP_UNAUTHORIZED, "");
50    }
51  }
52
53 private:
54  int auth_try_count_;
55};
56
57// The main test fixture class.
58class RequestSenderTest : public testing::Test {
59 protected:
60  RequestSenderTest()
61     : auth_service_(new TestAuthService),
62       request_sender_(auth_service_, NULL, NULL, "dummy-user-agent") {
63    auth_service_->set_refresh_token(kTestRefreshToken);
64    auth_service_->set_access_token(kTestAccessToken);
65  }
66
67  TestAuthService* auth_service_;  // Owned by |request_sender_|.
68  RequestSender request_sender_;
69};
70
71// Minimal implementation for AuthenticatedRequestInterface that can interact
72// with RequestSender correctly.
73class TestRequest : public AuthenticatedRequestInterface {
74 public:
75  TestRequest(RequestSender* sender,
76              bool* start_called,
77              FinishReason* finish_reason)
78      : sender_(sender),
79        start_called_(start_called),
80        finish_reason_(finish_reason),
81        weak_ptr_factory_(this) {
82  }
83
84  // Test the situation that the request has finished.
85  void FinishRequestWithSuccess() {
86    *finish_reason_ = SUCCESS;
87    sender_->RequestFinished(this);
88  }
89
90  const std::string& passed_access_token() const {
91    return passed_access_token_;
92  }
93
94  const ReAuthenticateCallback& passed_reauth_callback() const {
95    return passed_reauth_callback_;
96  }
97
98  virtual void Start(const std::string& access_token,
99                     const std::string& custom_user_agent,
100                     const ReAuthenticateCallback& callback) OVERRIDE {
101    *start_called_ = true;
102    passed_access_token_ = access_token;
103    passed_reauth_callback_ = callback;
104
105    // This request class itself does not return any response at this point.
106    // Each test case should respond properly by using the above methods.
107  }
108
109  virtual void Cancel() OVERRIDE {
110    EXPECT_TRUE(*start_called_);
111    *finish_reason_ = CANCEL;
112    sender_->RequestFinished(this);
113  }
114
115  virtual void OnAuthFailed(GDataErrorCode code) OVERRIDE {
116    *finish_reason_ = AUTH_FAILURE;
117    sender_->RequestFinished(this);
118  }
119
120  virtual base::WeakPtr<AuthenticatedRequestInterface> GetWeakPtr() OVERRIDE {
121    return weak_ptr_factory_.GetWeakPtr();
122  }
123
124 private:
125  RequestSender* sender_;
126  bool* start_called_;
127  FinishReason* finish_reason_;
128  std::string passed_access_token_;
129  ReAuthenticateCallback passed_reauth_callback_;
130  base::WeakPtrFactory<TestRequest> weak_ptr_factory_;
131};
132
133}  // namespace
134
135TEST_F(RequestSenderTest, StartAndFinishRequest) {
136  bool start_called  = false;
137  FinishReason finish_reason = NONE;
138  TestRequest* request = new TestRequest(&request_sender_,
139                                         &start_called,
140                                         &finish_reason);
141  base::WeakPtr<AuthenticatedRequestInterface> weak_ptr = request->GetWeakPtr();
142
143  base::Closure cancel_closure = request_sender_.StartRequestWithRetry(request);
144  EXPECT_TRUE(!cancel_closure.is_null());
145
146  // Start is called with the specified access token. Let it succeed.
147  EXPECT_TRUE(start_called);
148  EXPECT_EQ(kTestAccessToken, request->passed_access_token());
149  request->FinishRequestWithSuccess();
150  EXPECT_FALSE(weak_ptr);  // The request object is deleted.
151
152  // It is safe to run the cancel closure even after the request is finished.
153  // It is just no-op. The TestRequest::Cancel method is not called.
154  cancel_closure.Run();
155  EXPECT_EQ(SUCCESS, finish_reason);
156}
157
158TEST_F(RequestSenderTest, StartAndCancelRequest) {
159  bool start_called  = false;
160  FinishReason finish_reason = NONE;
161  TestRequest* request = new TestRequest(&request_sender_,
162                                         &start_called,
163                                         &finish_reason);
164  base::WeakPtr<AuthenticatedRequestInterface> weak_ptr = request->GetWeakPtr();
165
166  base::Closure cancel_closure = request_sender_.StartRequestWithRetry(request);
167  EXPECT_TRUE(!cancel_closure.is_null());
168  EXPECT_TRUE(start_called);
169
170  cancel_closure.Run();
171  EXPECT_EQ(CANCEL, finish_reason);
172  EXPECT_FALSE(weak_ptr);  // The request object is deleted.
173}
174
175TEST_F(RequestSenderTest, NoRefreshToken) {
176  auth_service_->ClearRefreshToken();
177  auth_service_->ClearAccessToken();
178
179  bool start_called  = false;
180  FinishReason finish_reason = NONE;
181  TestRequest* request = new TestRequest(&request_sender_,
182                                         &start_called,
183                                         &finish_reason);
184  base::WeakPtr<AuthenticatedRequestInterface> weak_ptr = request->GetWeakPtr();
185
186  base::Closure cancel_closure = request_sender_.StartRequestWithRetry(request);
187  EXPECT_TRUE(!cancel_closure.is_null());
188
189  // The request is not started at all because no access token is obtained.
190  EXPECT_FALSE(start_called);
191  EXPECT_EQ(AUTH_FAILURE, finish_reason);
192  EXPECT_FALSE(weak_ptr);  // The request object is deleted.
193}
194
195TEST_F(RequestSenderTest, ValidRefreshTokenAndNoAccessToken) {
196  auth_service_->ClearAccessToken();
197
198  bool start_called  = false;
199  FinishReason finish_reason = NONE;
200  TestRequest* request = new TestRequest(&request_sender_,
201                                         &start_called,
202                                         &finish_reason);
203  base::WeakPtr<AuthenticatedRequestInterface> weak_ptr = request->GetWeakPtr();
204
205  base::Closure cancel_closure = request_sender_.StartRequestWithRetry(request);
206  EXPECT_TRUE(!cancel_closure.is_null());
207
208  // Access token should indicate that this is the first retry.
209  EXPECT_TRUE(start_called);
210  EXPECT_EQ(kTestAccessToken + std::string("1"),
211            request->passed_access_token());
212  request->FinishRequestWithSuccess();
213  EXPECT_EQ(SUCCESS, finish_reason);
214  EXPECT_FALSE(weak_ptr);  // The request object is deleted.
215}
216
217TEST_F(RequestSenderTest, AccessTokenRejectedSeveralTimes) {
218  bool start_called  = false;
219  FinishReason finish_reason = NONE;
220  TestRequest* request = new TestRequest(&request_sender_,
221                                         &start_called,
222                                         &finish_reason);
223  base::WeakPtr<AuthenticatedRequestInterface> weak_ptr = request->GetWeakPtr();
224
225  base::Closure cancel_closure = request_sender_.StartRequestWithRetry(request);
226  EXPECT_TRUE(!cancel_closure.is_null());
227
228  EXPECT_TRUE(start_called);
229  EXPECT_EQ(kTestAccessToken, request->passed_access_token());
230  // Emulate the case that the access token was rejected by the remote service.
231  request->passed_reauth_callback().Run(request);
232  // New access token is fetched. Let it fail once again.
233  EXPECT_EQ(kTestAccessToken + std::string("1"),
234            request->passed_access_token());
235  request->passed_reauth_callback().Run(request);
236  // Once more.
237  EXPECT_EQ(kTestAccessToken + std::string("2"),
238            request->passed_access_token());
239  request->passed_reauth_callback().Run(request);
240
241  // Currently, limit for the retry is controlled in each request object, not
242  // by the RequestSender. So with this TestRequest, RequestSender retries
243  // infinitely. Let it succeed/
244  EXPECT_EQ(kTestAccessToken + std::string("3"),
245            request->passed_access_token());
246  request->FinishRequestWithSuccess();
247  EXPECT_EQ(SUCCESS, finish_reason);
248  EXPECT_FALSE(weak_ptr);
249}
250
251}  // namespace google_apis
252