Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(793)

Unified Diff: sync/internal_api/attachments/attachment_uploader_impl_unittest.cc

Issue 304253010: Add authentication support to AttachmentUploaderImpl. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Apply more CR feedback from pavely Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: sync/internal_api/attachments/attachment_uploader_impl_unittest.cc
diff --git a/sync/internal_api/attachments/attachment_uploader_impl_unittest.cc b/sync/internal_api/attachments/attachment_uploader_impl_unittest.cc
index 99c2247f972b494fa704e4cf1e93455ab13ea9b4..f2ad7ce96fe4aa91393242e5e788aa8c3ef16bbc 100644
--- a/sync/internal_api/attachments/attachment_uploader_impl_unittest.cc
+++ b/sync/internal_api/attachments/attachment_uploader_impl_unittest.cc
@@ -11,19 +11,28 @@
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
+#include "base/synchronization/lock.h"
+#include "base/thread_task_runner_handle.h"
#include "base/threading/non_thread_safe.h"
#include "base/threading/thread.h"
+#include "google_apis/gaia/fake_oauth2_token_service.h"
+#include "google_apis/gaia/gaia_constants.h"
+#include "google_apis/gaia/oauth2_token_service_request.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/url_request/url_request_test_util.h"
#include "sync/api/attachments/attachment.h"
#include "sync/protocol/sync.pb.h"
+#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
const char kAttachmentData[] = "some data";
+const char kAccountId[] = "some-account-id";
+const char kAccessToken[] = "some-access-token";
+const char kAuthorization[] = "Authorization";
} // namespace
@@ -35,10 +44,131 @@ using net::test_server::HttpResponse;
class RequestHandler;
+// A mock implementation of an OAuth2TokenService.
+//
+// Use |SetResponse| to vary the response to token requests.
+//
+// Use |num_invalidate_token| and |last_token_invalidated| to check the number
+// of invalidate token operations performed and the last token invalidated.
+class MockOAuth2TokenService : public FakeOAuth2TokenService {
+ public:
+ MockOAuth2TokenService();
+ virtual ~MockOAuth2TokenService();
+
+ void SetResponse(const GoogleServiceAuthError& error,
+ const std::string& access_token,
+ const base::Time& expiration);
+
+ int num_invalidate_token() const { return num_invalidate_token_; }
+
+ const std::string& last_token_invalidated() const {
+ return last_token_invalidated_;
+ }
+
+ protected:
+ virtual void FetchOAuth2Token(RequestImpl* request,
+ const std::string& account_id,
+ net::URLRequestContextGetter* getter,
+ const std::string& client_id,
+ const std::string& client_secret,
+ const ScopeSet& scopes) OVERRIDE;
+
+ virtual void InvalidateOAuth2Token(const std::string& account_id,
+ const std::string& client_id,
+ const ScopeSet& scopes,
+ const std::string& access_token) OVERRIDE;
+
+ private:
+ GoogleServiceAuthError response_error_;
+ std::string response_access_token_;
+ base::Time response_expiration_;
+ int num_invalidate_token_;
+ std::string last_token_invalidated_;
+};
+
+MockOAuth2TokenService::MockOAuth2TokenService()
+ : response_error_(GoogleServiceAuthError::AuthErrorNone()),
+ response_access_token_(kAccessToken),
+ response_expiration_(base::Time::Max()),
+ num_invalidate_token_(0) {
+}
+
+MockOAuth2TokenService::~MockOAuth2TokenService() {
+}
+
+void MockOAuth2TokenService::SetResponse(const GoogleServiceAuthError& error,
+ const std::string& access_token,
+ const base::Time& expiration) {
+ response_error_ = error;
+ response_access_token_ = access_token;
+ response_expiration_ = expiration;
+}
+
+void MockOAuth2TokenService::FetchOAuth2Token(
+ RequestImpl* request,
+ const std::string& account_id,
+ net::URLRequestContextGetter* getter,
+ const std::string& client_id,
+ const std::string& client_secret,
+ const ScopeSet& scopes) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&OAuth2TokenService::RequestImpl::InformConsumer,
+ request->AsWeakPtr(),
+ response_error_,
+ response_access_token_,
+ response_expiration_));
+}
+
+void MockOAuth2TokenService::InvalidateOAuth2Token(
+ const std::string& account_id,
+ const std::string& client_id,
+ const ScopeSet& scopes,
+ const std::string& access_token) {
+ ++num_invalidate_token_;
+ last_token_invalidated_ = access_token;
+}
+
+class TokenServiceProvider
+ : public OAuth2TokenServiceRequest::TokenServiceProvider,
+ base::NonThreadSafe {
+ public:
+ TokenServiceProvider(OAuth2TokenService* token_service);
+ virtual ~TokenServiceProvider();
+
+ // OAuth2TokenService::TokenServiceProvider implementation.
+ virtual scoped_refptr<base::SingleThreadTaskRunner>
+ GetTokenServiceTaskRunner() OVERRIDE;
+ virtual OAuth2TokenService* GetTokenService() OVERRIDE;
+
+ private:
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ OAuth2TokenService* token_service_;
+};
+
+TokenServiceProvider::TokenServiceProvider(OAuth2TokenService* token_service)
+ : task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ token_service_(token_service) {
+ DCHECK(token_service_);
+}
+
+TokenServiceProvider::~TokenServiceProvider() {
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+TokenServiceProvider::GetTokenServiceTaskRunner() {
+ return task_runner_;
+}
+
+OAuth2TokenService* TokenServiceProvider::GetTokenService() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ return token_service_;
+}
+
// Text fixture for AttachmentUploaderImpl test.
//
-// This fixture provides an embedded HTTP server for interacting with
-// AttachmentUploaderImpl.
+// This fixture provides an embedded HTTP server and a mock OAuth2 token service
+// for interacting with AttachmentUploaderImpl
class AttachmentUploaderImplTest : public testing::Test,
public base::NonThreadSafe {
public:
@@ -47,6 +177,7 @@ class AttachmentUploaderImplTest : public testing::Test,
protected:
AttachmentUploaderImplTest();
virtual void SetUp();
+ virtual void TearDown();
// Run the message loop until UploadDone has been invoked |num_uploads| times.
void RunAndWaitFor(int num_uploads);
@@ -56,6 +187,9 @@ class AttachmentUploaderImplTest : public testing::Test,
std::vector<HttpRequest>& http_requests_received();
std::vector<AttachmentUploader::UploadResult>& upload_results();
std::vector<AttachmentId>& updated_attachment_ids();
+ MockOAuth2TokenService& token_service();
+ base::MessageLoopForIO& message_loop();
+ RequestHandler& request_handler();
private:
// An UploadCallback invoked by AttachmentUploaderImpl.
@@ -68,18 +202,20 @@ class AttachmentUploaderImplTest : public testing::Test,
scoped_ptr<AttachmentUploader> uploader_;
AttachmentUploader::UploadCallback upload_callback_;
net::test_server::EmbeddedTestServer server_;
-
// A closure that signals an upload has finished.
base::Closure signal_upload_done_;
std::vector<HttpRequest> http_requests_received_;
std::vector<AttachmentUploader::UploadResult> upload_results_;
std::vector<AttachmentId> updated_attachment_ids_;
+ scoped_ptr<MockOAuth2TokenService> token_service_;
// Must be last data member.
base::WeakPtrFactory<AttachmentUploaderImplTest> weak_ptr_factory_;
};
// Handles HTTP requests received by the EmbeddedTestServer.
+//
+// Responds with HTTP_OK by default. See |SetStatusCode|.
class RequestHandler : public base::NonThreadSafe {
public:
// Construct a RequestHandler that will PostTask to |test| using
@@ -92,7 +228,17 @@ class RequestHandler : public base::NonThreadSafe {
scoped_ptr<HttpResponse> HandleRequest(const HttpRequest& request);
+ // Set the HTTP status code to respond with.
+ void SetStatusCode(const net::HttpStatusCode& status_code);
+
+ // Returns the HTTP status code that will be used in responses.
+ net::HttpStatusCode GetStatusCode() const;
+
private:
+ // Protects status_code_.
+ mutable base::Lock mutex_;
+ net::HttpStatusCode status_code_;
+
scoped_refptr<base::SingleThreadTaskRunner> test_task_runner_;
base::WeakPtr<AttachmentUploaderImplTest> test_;
};
@@ -121,12 +267,26 @@ void AttachmentUploaderImplTest::SetUp() {
std::string url_prefix(
base::StringPrintf("http://localhost:%d/uploads/", server_.port()));
- uploader().reset(
- new AttachmentUploaderImpl(url_prefix, url_request_context_getter_));
+ token_service_.reset(new MockOAuth2TokenService);
+ scoped_ptr<OAuth2TokenServiceRequest::TokenServiceProvider>
+ token_service_provider(new TokenServiceProvider(token_service_.get()));
+
+ OAuth2TokenService::ScopeSet scopes;
+ scopes.insert(GaiaConstants::kChromeSyncOAuth2Scope);
+ uploader().reset(new AttachmentUploaderImpl(url_prefix,
+ url_request_context_getter_,
+ kAccountId,
+ scopes,
+ token_service_provider.Pass()));
+
upload_callback_ = base::Bind(&AttachmentUploaderImplTest::UploadDone,
base::Unretained(this));
}
+void AttachmentUploaderImplTest::TearDown() {
+ base::RunLoop().RunUntilIdle();
+}
+
void AttachmentUploaderImplTest::RunAndWaitFor(int num_uploads) {
for (int i = 0; i < num_uploads; ++i) {
// Run the loop until one upload completes.
@@ -159,6 +319,18 @@ AttachmentUploaderImplTest::updated_attachment_ids() {
return updated_attachment_ids_;
}
+MockOAuth2TokenService& AttachmentUploaderImplTest::token_service() {
+ return *token_service_;
+}
+
+base::MessageLoopForIO& AttachmentUploaderImplTest::message_loop() {
+ return message_loop_;
+}
+
+RequestHandler& AttachmentUploaderImplTest::request_handler() {
+ return *request_handler_;
+}
+
void AttachmentUploaderImplTest::UploadDone(
const AttachmentUploader::UploadResult& result,
const AttachmentId& updated_attachment_id) {
@@ -171,7 +343,9 @@ void AttachmentUploaderImplTest::UploadDone(
RequestHandler::RequestHandler(
const scoped_refptr<base::SingleThreadTaskRunner>& test_task_runner,
const base::WeakPtr<AttachmentUploaderImplTest>& test)
- : test_task_runner_(test_task_runner), test_(test) {
+ : status_code_(net::HTTP_OK),
+ test_task_runner_(test_task_runner),
+ test_(test) {
DetachFromThread();
}
@@ -186,23 +360,46 @@ scoped_ptr<HttpResponse> RequestHandler::HandleRequest(
FROM_HERE,
base::Bind(
&AttachmentUploaderImplTest::OnRequestReceived, test_, request));
- scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse);
- http_response->set_code(net::HTTP_OK);
- http_response->set_content("hello");
- http_response->set_content_type("text/plain");
- return http_response.PassAs<HttpResponse>();
+ scoped_ptr<BasicHttpResponse> response(new BasicHttpResponse);
+ response->set_code(GetStatusCode());
+ response->set_content_type("text/plain");
+ return response.PassAs<HttpResponse>();
+}
+
+void RequestHandler::SetStatusCode(const net::HttpStatusCode& status_code) {
+ base::AutoLock lock(mutex_);
+ status_code_ = status_code;
+}
+
+net::HttpStatusCode RequestHandler::GetStatusCode() const {
+ base::AutoLock lock(mutex_);
+ return status_code_;
}
// Verify the "happy case" of uploading an attachment.
+//
+// Token is requested, token is returned, HTTP request is made, attachment is
+// received by server.
TEST_F(AttachmentUploaderImplTest, UploadAttachment_HappyCase) {
+ token_service().AddAccount(kAccountId);
+ request_handler().SetStatusCode(net::HTTP_OK);
+
scoped_refptr<base::RefCountedString> some_data(new base::RefCountedString);
some_data->data() = kAttachmentData;
Attachment attachment = Attachment::Create(some_data);
uploader()->UploadAttachment(attachment, upload_callback());
+
+ // Run until the done callback is invoked.
RunAndWaitFor(1);
+ // See that the done callback was invoked with the right arguments.
+ ASSERT_EQ(1U, upload_results().size());
+ EXPECT_EQ(AttachmentUploader::UPLOAD_SUCCESS, upload_results()[0]);
+ ASSERT_EQ(1U, updated_attachment_ids().size());
+ EXPECT_EQ(attachment.GetId(), updated_attachment_ids()[0]);
+
// See that the HTTP server received one request.
- EXPECT_EQ(1U, http_requests_received().size());
+ ASSERT_EQ(1U, http_requests_received().size());
const HttpRequest& http_request = http_requests_received().front();
EXPECT_EQ(net::test_server::METHOD_POST, http_request.method);
std::string expected_relative_url("/uploads/" +
@@ -210,12 +407,11 @@ TEST_F(AttachmentUploaderImplTest, UploadAttachment_HappyCase) {
EXPECT_EQ(expected_relative_url, http_request.relative_url);
EXPECT_TRUE(http_request.has_content);
EXPECT_EQ(kAttachmentData, http_request.content);
+ const std::string header_name(kAuthorization);
+ const std::string header_value(std::string("Bearer ") + kAccessToken);
+ EXPECT_THAT(http_request.headers,
+ testing::Contains(testing::Pair(header_name, header_value)));
- // See that the UploadCallback received a result and updated AttachmentId.
- EXPECT_EQ(1U, upload_results().size());
- EXPECT_EQ(1U, updated_attachment_ids().size());
- EXPECT_EQ(AttachmentUploader::UPLOAD_SUCCESS, upload_results().front());
- EXPECT_EQ(attachment.GetId(), updated_attachment_ids().front());
// TODO(maniscalco): Once AttachmentUploaderImpl is capable of updating the
// AttachmentId with server address information about the attachment, add some
// checks here to verify it works properly (bug 371522).
@@ -224,12 +420,17 @@ TEST_F(AttachmentUploaderImplTest, UploadAttachment_HappyCase) {
// Verify two overlapping calls to upload the same attachment result in only one
// HTTP request.
TEST_F(AttachmentUploaderImplTest, UploadAttachment_Collapse) {
+ token_service().AddAccount(kAccountId);
+ request_handler().SetStatusCode(net::HTTP_OK);
+
scoped_refptr<base::RefCountedString> some_data(new base::RefCountedString);
some_data->data() = kAttachmentData;
Attachment attachment1 = Attachment::Create(some_data);
Attachment attachment2 = attachment1;
uploader()->UploadAttachment(attachment1, upload_callback());
uploader()->UploadAttachment(attachment2, upload_callback());
+ base::RunLoop().RunUntilIdle();
+
// Wait for upload_callback() to be invoked twice.
RunAndWaitFor(2);
// See there was only one request.
@@ -240,18 +441,127 @@ TEST_F(AttachmentUploaderImplTest, UploadAttachment_Collapse) {
// uplaod finishes. We do this by issuing two non-overlapping uploads for the
// same attachment and see that it results in two HTTP requests.
TEST_F(AttachmentUploaderImplTest, UploadAttachment_CleanUpAfterUpload) {
+ token_service().AddAccount(kAccountId);
+ request_handler().SetStatusCode(net::HTTP_OK);
+
scoped_refptr<base::RefCountedString> some_data(new base::RefCountedString);
some_data->data() = kAttachmentData;
Attachment attachment1 = Attachment::Create(some_data);
Attachment attachment2 = attachment1;
uploader()->UploadAttachment(attachment1, upload_callback());
+ base::RunLoop().RunUntilIdle();
+
// Wait for upload_callback() to be invoked before starting the second upload.
RunAndWaitFor(1);
uploader()->UploadAttachment(attachment2, upload_callback());
+ base::RunLoop().RunUntilIdle();
+
// Wait for upload_callback() to be invoked a second time.
RunAndWaitFor(1);
// See there were two requests.
- EXPECT_EQ(2U, http_requests_received().size());
+ ASSERT_EQ(2U, http_requests_received().size());
+}
+
+// Verify that we do not issue an HTTP request when we fail to receive an access
+// token.
+//
+// Token is requested, no token is returned, no HTTP request is made
+TEST_F(AttachmentUploaderImplTest, UploadAttachment_FailToGetToken) {
+ // Note, we won't receive a token because we did not add kAccountId to the
+ // token service.
+ scoped_refptr<base::RefCountedString> some_data(new base::RefCountedString);
+ some_data->data() = kAttachmentData;
+ Attachment attachment = Attachment::Create(some_data);
+ uploader()->UploadAttachment(attachment, upload_callback());
+
+ RunAndWaitFor(1);
+
+ // See that the done callback was invoked.
+ ASSERT_EQ(1U, upload_results().size());
+ EXPECT_EQ(AttachmentUploader::UPLOAD_UNSPECIFIED_ERROR, upload_results()[0]);
+ ASSERT_EQ(1U, updated_attachment_ids().size());
+ EXPECT_EQ(attachment.GetId(), updated_attachment_ids()[0]);
+
+ // See that no HTTP request was received.
+ ASSERT_EQ(0U, http_requests_received().size());
+}
+
+// Verify behavior when the server returns "503 Service Unavailable".
+TEST_F(AttachmentUploaderImplTest, UploadAttachment_ServiceUnavilable) {
+ token_service().AddAccount(kAccountId);
+ request_handler().SetStatusCode(net::HTTP_SERVICE_UNAVAILABLE);
+
+ scoped_refptr<base::RefCountedString> some_data(new base::RefCountedString);
+ some_data->data() = kAttachmentData;
+ Attachment attachment = Attachment::Create(some_data);
+ uploader()->UploadAttachment(attachment, upload_callback());
+
+ RunAndWaitFor(1);
+
+ // See that the done callback was invoked.
+ ASSERT_EQ(1U, upload_results().size());
+ EXPECT_EQ(AttachmentUploader::UPLOAD_UNSPECIFIED_ERROR, upload_results()[0]);
+ ASSERT_EQ(1U, updated_attachment_ids().size());
+ EXPECT_EQ(attachment.GetId(), updated_attachment_ids()[0]);
+
+ // See that the HTTP server received one request.
+ ASSERT_EQ(1U, http_requests_received().size());
+ const HttpRequest& http_request = http_requests_received().front();
+ EXPECT_EQ(net::test_server::METHOD_POST, http_request.method);
+ std::string expected_relative_url("/uploads/" +
+ attachment.GetId().GetProto().unique_id());
+ EXPECT_EQ(expected_relative_url, http_request.relative_url);
+ EXPECT_TRUE(http_request.has_content);
+ EXPECT_EQ(kAttachmentData, http_request.content);
+ std::string expected_header(kAuthorization);
+ const std::string header_name(kAuthorization);
+ const std::string header_value(std::string("Bearer ") + kAccessToken);
+ EXPECT_THAT(http_request.headers,
+ testing::Contains(testing::Pair(header_name, header_value)));
+
+ // See that we did not invalidate the token.
+ ASSERT_EQ(0, token_service().num_invalidate_token());
}
+// Verify that when we receive an "401 Unauthorized" we invalidate the access
+// token.
+TEST_F(AttachmentUploaderImplTest, UploadAttachment_BadToken) {
+ token_service().AddAccount(kAccountId);
+ request_handler().SetStatusCode(net::HTTP_UNAUTHORIZED);
+
+ scoped_refptr<base::RefCountedString> some_data(new base::RefCountedString);
+ some_data->data() = kAttachmentData;
+ Attachment attachment = Attachment::Create(some_data);
+ uploader()->UploadAttachment(attachment, upload_callback());
+
+ RunAndWaitFor(1);
+
+ // See that the done callback was invoked.
+ ASSERT_EQ(1U, upload_results().size());
+ EXPECT_EQ(AttachmentUploader::UPLOAD_UNSPECIFIED_ERROR, upload_results()[0]);
+ ASSERT_EQ(1U, updated_attachment_ids().size());
+ EXPECT_EQ(attachment.GetId(), updated_attachment_ids()[0]);
+
+ // See that the HTTP server received one request.
+ ASSERT_EQ(1U, http_requests_received().size());
+ const HttpRequest& http_request = http_requests_received().front();
+ EXPECT_EQ(net::test_server::METHOD_POST, http_request.method);
+ std::string expected_relative_url("/uploads/" +
+ attachment.GetId().GetProto().unique_id());
+ EXPECT_EQ(expected_relative_url, http_request.relative_url);
+ EXPECT_TRUE(http_request.has_content);
+ EXPECT_EQ(kAttachmentData, http_request.content);
+ std::string expected_header(kAuthorization);
+ const std::string header_name(kAuthorization);
+ const std::string header_value(std::string("Bearer ") + kAccessToken);
+ EXPECT_THAT(http_request.headers,
+ testing::Contains(testing::Pair(header_name, header_value)));
+
+ // See that we invalidated the token.
+ ASSERT_EQ(1, token_service().num_invalidate_token());
+}
+
+// TODO(maniscalco): Add test case for when we are uploading an attachment that
+// already exists. 409 Conflict? (bug 379825)
+
} // namespace syncer

Powered by Google App Engine
This is Rietveld 408576698