blob: 93c532f3cb111f4d41d726131d50b036e10ece44 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "google_apis/drive/request_sender.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "google_apis/drive/base_requests.h"
#include "google_apis/drive/dummy_auth_service.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace google_apis {
namespace {
const char kTestRefreshToken[] = "valid-refresh-token";
const char kTestAccessToken[] = "valid-access-token";
// Enum for indicating the reason why a request is finished.
enum FinishReason {
NONE,
SUCCESS,
CANCEL,
AUTH_FAILURE,
};
// AuthService for testing purpose. It accepts kTestRefreshToken and returns
// kTestAccessToken + {"1", "2", "3", ...}.
class TestAuthService : public DummyAuthService {
public:
TestAuthService() : auth_try_count_(0) {}
void StartAuthentication(const AuthStatusCallback& callback) override {
// RequestSender should clear the rejected access token before starting
// to request another one.
EXPECT_FALSE(HasAccessToken());
++auth_try_count_;
if (refresh_token() == kTestRefreshToken) {
const std::string token =
kTestAccessToken + base::IntToString(auth_try_count_);
set_access_token(token);
callback.Run(HTTP_SUCCESS, token);
} else {
set_access_token("");
callback.Run(HTTP_UNAUTHORIZED, "");
}
}
private:
int auth_try_count_;
};
// The main test fixture class.
class RequestSenderTest : public testing::Test {
protected:
RequestSenderTest()
: auth_service_(new TestAuthService),
request_sender_(auth_service_,
NULL,
NULL,
"dummy-user-agent",
TRAFFIC_ANNOTATION_FOR_TESTS) {
auth_service_->set_refresh_token(kTestRefreshToken);
auth_service_->set_access_token(kTestAccessToken);
}
TestAuthService* auth_service_; // Owned by |request_sender_|.
RequestSender request_sender_;
};
// Minimal implementation for AuthenticatedRequestInterface that can interact
// with RequestSender correctly.
class TestRequest : public AuthenticatedRequestInterface {
public:
TestRequest(RequestSender* sender,
bool* start_called,
FinishReason* finish_reason)
: sender_(sender),
start_called_(start_called),
finish_reason_(finish_reason),
weak_ptr_factory_(this) {
}
// Test the situation that the request has finished.
void FinishRequestWithSuccess() {
*finish_reason_ = SUCCESS;
sender_->RequestFinished(this);
}
const std::string& passed_access_token() const {
return passed_access_token_;
}
const ReAuthenticateCallback& passed_reauth_callback() const {
return passed_reauth_callback_;
}
void Start(const std::string& access_token,
const std::string& custom_user_agent,
const ReAuthenticateCallback& callback) override {
*start_called_ = true;
passed_access_token_ = access_token;
passed_reauth_callback_ = callback;
// This request class itself does not return any response at this point.
// Each test case should respond properly by using the above methods.
}
void Cancel() override {
EXPECT_TRUE(*start_called_);
*finish_reason_ = CANCEL;
sender_->RequestFinished(this);
}
void OnAuthFailed(DriveApiErrorCode code) override {
*finish_reason_ = AUTH_FAILURE;
sender_->RequestFinished(this);
}
base::WeakPtr<AuthenticatedRequestInterface> GetWeakPtr() override {
return weak_ptr_factory_.GetWeakPtr();
}
private:
RequestSender* sender_;
bool* start_called_;
FinishReason* finish_reason_;
std::string passed_access_token_;
ReAuthenticateCallback passed_reauth_callback_;
base::WeakPtrFactory<TestRequest> weak_ptr_factory_;
};
} // namespace
TEST_F(RequestSenderTest, StartAndFinishRequest) {
bool start_called = false;
FinishReason finish_reason = NONE;
std::unique_ptr<TestRequest> request = std::make_unique<TestRequest>(
&request_sender_, &start_called, &finish_reason);
TestRequest* request_ptr = request.get();
base::WeakPtr<AuthenticatedRequestInterface> weak_ptr =
request_ptr->GetWeakPtr();
base::Closure cancel_closure =
request_sender_.StartRequestWithAuthRetry(std::move(request));
EXPECT_TRUE(!cancel_closure.is_null());
// Start is called with the specified access token. Let it succeed.
EXPECT_TRUE(start_called);
EXPECT_EQ(kTestAccessToken, request_ptr->passed_access_token());
request_ptr->FinishRequestWithSuccess();
EXPECT_FALSE(weak_ptr); // The request object is deleted.
// It is safe to run the cancel closure even after the request is finished.
// It is just no-op. The TestRequest::Cancel method is not called.
cancel_closure.Run();
EXPECT_EQ(SUCCESS, finish_reason);
}
TEST_F(RequestSenderTest, StartAndCancelRequest) {
bool start_called = false;
FinishReason finish_reason = NONE;
std::unique_ptr<TestRequest> request = std::make_unique<TestRequest>(
&request_sender_, &start_called, &finish_reason);
base::WeakPtr<AuthenticatedRequestInterface> weak_ptr = request->GetWeakPtr();
base::Closure cancel_closure =
request_sender_.StartRequestWithAuthRetry(std::move(request));
EXPECT_TRUE(!cancel_closure.is_null());
EXPECT_TRUE(start_called);
cancel_closure.Run();
EXPECT_EQ(CANCEL, finish_reason);
EXPECT_FALSE(weak_ptr); // The request object is deleted.
}
TEST_F(RequestSenderTest, NoRefreshToken) {
auth_service_->ClearRefreshToken();
auth_service_->ClearAccessToken();
bool start_called = false;
FinishReason finish_reason = NONE;
std::unique_ptr<TestRequest> request = std::make_unique<TestRequest>(
&request_sender_, &start_called, &finish_reason);
base::WeakPtr<AuthenticatedRequestInterface> weak_ptr = request->GetWeakPtr();
base::Closure cancel_closure =
request_sender_.StartRequestWithAuthRetry(std::move(request));
EXPECT_TRUE(!cancel_closure.is_null());
// The request is not started at all because no access token is obtained.
EXPECT_FALSE(start_called);
EXPECT_EQ(AUTH_FAILURE, finish_reason);
EXPECT_FALSE(weak_ptr); // The request object is deleted.
}
TEST_F(RequestSenderTest, ValidRefreshTokenAndNoAccessToken) {
auth_service_->ClearAccessToken();
bool start_called = false;
FinishReason finish_reason = NONE;
std::unique_ptr<TestRequest> request = std::make_unique<TestRequest>(
&request_sender_, &start_called, &finish_reason);
TestRequest* request_ptr = request.get();
base::WeakPtr<AuthenticatedRequestInterface> weak_ptr =
request_ptr->GetWeakPtr();
base::Closure cancel_closure =
request_sender_.StartRequestWithAuthRetry(std::move(request));
EXPECT_TRUE(!cancel_closure.is_null());
// Access token should indicate that this is the first retry.
EXPECT_TRUE(start_called);
EXPECT_EQ(kTestAccessToken + std::string("1"),
request_ptr->passed_access_token());
request_ptr->FinishRequestWithSuccess();
EXPECT_EQ(SUCCESS, finish_reason);
EXPECT_FALSE(weak_ptr); // The request object is deleted.
}
TEST_F(RequestSenderTest, AccessTokenRejectedSeveralTimes) {
bool start_called = false;
FinishReason finish_reason = NONE;
std::unique_ptr<TestRequest> request = std::make_unique<TestRequest>(
&request_sender_, &start_called, &finish_reason);
TestRequest* request_ptr = request.get();
base::WeakPtr<AuthenticatedRequestInterface> weak_ptr =
request_ptr->GetWeakPtr();
base::Closure cancel_closure =
request_sender_.StartRequestWithAuthRetry(std::move(request));
EXPECT_TRUE(!cancel_closure.is_null());
EXPECT_TRUE(start_called);
EXPECT_EQ(kTestAccessToken, request_ptr->passed_access_token());
// Emulate the case that the access token was rejected by the remote service.
request_ptr->passed_reauth_callback().Run(request_ptr);
// New access token is fetched. Let it fail once again.
EXPECT_EQ(kTestAccessToken + std::string("1"),
request_ptr->passed_access_token());
request_ptr->passed_reauth_callback().Run(request_ptr);
// Once more.
EXPECT_EQ(kTestAccessToken + std::string("2"),
request_ptr->passed_access_token());
request_ptr->passed_reauth_callback().Run(request_ptr);
// Currently, limit for the retry is controlled in each request object, not
// by the RequestSender. So with this TestRequest, RequestSender retries
// infinitely. Let it succeed/
EXPECT_EQ(kTestAccessToken + std::string("3"),
request_ptr->passed_access_token());
request_ptr->FinishRequestWithSuccess();
EXPECT_EQ(SUCCESS, finish_reason);
EXPECT_FALSE(weak_ptr);
}
} // namespace google_apis