1 // Copyright 2014 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.
5 #include "sync/internal_api/public/attachments/attachment_uploader_impl.h"
7 #include "base/base64.h"
9 #include "base/callback.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/memory/ref_counted_memory.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/run_loop.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/synchronization/lock.h"
17 #include "base/test/histogram_tester.h"
18 #include "base/thread_task_runner_handle.h"
19 #include "base/threading/non_thread_safe.h"
20 #include "base/threading/thread.h"
21 #include "google_apis/gaia/fake_oauth2_token_service.h"
22 #include "google_apis/gaia/gaia_constants.h"
23 #include "google_apis/gaia/oauth2_token_service_request.h"
24 #include "net/http/http_status_code.h"
25 #include "net/test/embedded_test_server/embedded_test_server.h"
26 #include "net/test/embedded_test_server/http_request.h"
27 #include "net/test/embedded_test_server/http_response.h"
28 #include "net/url_request/url_request_test_util.h"
29 #include "sync/api/attachments/attachment.h"
30 #include "sync/internal_api/public/attachments/attachment_util.h"
31 #include "sync/internal_api/public/base/model_type.h"
32 #include "sync/protocol/sync.pb.h"
33 #include "testing/gmock/include/gmock/gmock-matchers.h"
34 #include "testing/gtest/include/gtest/gtest.h"
38 const char kAttachmentData
[] = "some data";
39 const char kAccountId
[] = "some-account-id";
40 const char kAccessToken
[] = "some-access-token";
41 const char kContentTypeValue
[] = "application/octet-stream";
42 const char kXGoogHash
[] = "X-Goog-Hash";
43 const char kAttachments
[] = "/attachments/";
44 const char kStoreBirthday
[] =
45 "\x46\xFF\xDD\xE0\x74\x3A\x48\xFD\x9D\x06\x93\x3C\x61\x8E\xA5\x70\x09\x59"
46 "\x99\x05\x61\x46\x21\x61\x1B\x03\xD3\x02\x60\xCD\x41\x55\xFE\x26\x15\xD7"
48 const char kBase64URLSafeStoreBirthday
[] =
49 "Rv_d4HQ6SP2dBpM8YY6lcAlZmQVhRiFhGwPTAmDNQVX-JhXXDA";
50 const char kSyncStoreBirthdayHeader
[] = "X-Sync-Store-Birthday";
51 const char kSyncDataTypeIdHeader
[] = "X-Sync-Data-Type-Id";
52 const syncer::ModelType kModelType
= syncer::ModelType::ARTICLES
;
58 using net::test_server::BasicHttpResponse
;
59 using net::test_server::HttpRequest
;
60 using net::test_server::HttpResponse
;
64 // A mock implementation of an OAuth2TokenService.
66 // Use |SetResponse| to vary the response to token requests.
68 // Use |num_invalidate_token| and |last_token_invalidated| to check the number
69 // of invalidate token operations performed and the last token invalidated.
70 class MockOAuth2TokenService
: public FakeOAuth2TokenService
{
72 MockOAuth2TokenService();
73 ~MockOAuth2TokenService() override
;
75 void SetResponse(const GoogleServiceAuthError
& error
,
76 const std::string
& access_token
,
77 const base::Time
& expiration
);
79 int num_invalidate_token() const { return num_invalidate_token_
; }
81 const std::string
& last_token_invalidated() const {
82 return last_token_invalidated_
;
86 void FetchOAuth2Token(RequestImpl
* request
,
87 const std::string
& account_id
,
88 net::URLRequestContextGetter
* getter
,
89 const std::string
& client_id
,
90 const std::string
& client_secret
,
91 const ScopeSet
& scopes
) override
;
93 void InvalidateOAuth2Token(const std::string
& account_id
,
94 const std::string
& client_id
,
95 const ScopeSet
& scopes
,
96 const std::string
& access_token
) override
;
99 GoogleServiceAuthError response_error_
;
100 std::string response_access_token_
;
101 base::Time response_expiration_
;
102 int num_invalidate_token_
;
103 std::string last_token_invalidated_
;
106 MockOAuth2TokenService::MockOAuth2TokenService()
107 : response_error_(GoogleServiceAuthError::AuthErrorNone()),
108 response_access_token_(kAccessToken
),
109 response_expiration_(base::Time::Max()),
110 num_invalidate_token_(0) {
113 MockOAuth2TokenService::~MockOAuth2TokenService() {
116 void MockOAuth2TokenService::SetResponse(const GoogleServiceAuthError
& error
,
117 const std::string
& access_token
,
118 const base::Time
& expiration
) {
119 response_error_
= error
;
120 response_access_token_
= access_token
;
121 response_expiration_
= expiration
;
124 void MockOAuth2TokenService::FetchOAuth2Token(
125 RequestImpl
* request
,
126 const std::string
& account_id
,
127 net::URLRequestContextGetter
* getter
,
128 const std::string
& client_id
,
129 const std::string
& client_secret
,
130 const ScopeSet
& scopes
) {
131 base::MessageLoop::current()->PostTask(
133 base::Bind(&OAuth2TokenService::RequestImpl::InformConsumer
,
134 request
->AsWeakPtr(),
136 response_access_token_
,
137 response_expiration_
));
140 void MockOAuth2TokenService::InvalidateOAuth2Token(
141 const std::string
& account_id
,
142 const std::string
& client_id
,
143 const ScopeSet
& scopes
,
144 const std::string
& access_token
) {
145 ++num_invalidate_token_
;
146 last_token_invalidated_
= access_token
;
149 class TokenServiceProvider
150 : public OAuth2TokenServiceRequest::TokenServiceProvider
,
151 base::NonThreadSafe
{
153 TokenServiceProvider(OAuth2TokenService
* token_service
);
155 // OAuth2TokenService::TokenServiceProvider implementation.
156 scoped_refptr
<base::SingleThreadTaskRunner
> GetTokenServiceTaskRunner()
158 OAuth2TokenService
* GetTokenService() override
;
161 ~TokenServiceProvider() override
;
163 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner_
;
164 OAuth2TokenService
* token_service_
;
167 TokenServiceProvider::TokenServiceProvider(OAuth2TokenService
* token_service
)
168 : task_runner_(base::ThreadTaskRunnerHandle::Get()),
169 token_service_(token_service
) {
170 DCHECK(token_service_
);
173 TokenServiceProvider::~TokenServiceProvider() {
176 scoped_refptr
<base::SingleThreadTaskRunner
>
177 TokenServiceProvider::GetTokenServiceTaskRunner() {
181 OAuth2TokenService
* TokenServiceProvider::GetTokenService() {
182 DCHECK(task_runner_
->BelongsToCurrentThread());
183 return token_service_
;
186 // Text fixture for AttachmentUploaderImpl test.
188 // This fixture provides an embedded HTTP server and a mock OAuth2 token service
189 // for interacting with AttachmentUploaderImpl
190 class AttachmentUploaderImplTest
: public testing::Test
,
191 public base::NonThreadSafe
{
193 void OnRequestReceived(const HttpRequest
& request
);
196 AttachmentUploaderImplTest();
197 void SetUp() override
;
198 void TearDown() override
;
200 // Run the message loop until UploadDone has been invoked |num_uploads| times.
201 void RunAndWaitFor(int num_uploads
);
203 // Upload an attachment and have the server respond with |status_code|.
205 // Returns the attachment that was uploaded.
206 Attachment
UploadAndRespondWith(const net::HttpStatusCode
& status_code
);
208 scoped_ptr
<AttachmentUploader
>& uploader();
209 const AttachmentUploader::UploadCallback
& upload_callback() const;
210 std::vector
<HttpRequest
>& http_requests_received();
211 std::vector
<AttachmentUploader::UploadResult
>& upload_results();
212 std::vector
<AttachmentId
>& attachment_ids();
213 MockOAuth2TokenService
& token_service();
214 base::MessageLoopForIO
& message_loop();
215 RequestHandler
& request_handler();
218 // An UploadCallback invoked by AttachmentUploaderImpl.
219 void UploadDone(const AttachmentUploader::UploadResult
& result
,
220 const AttachmentId
& attachment_id
);
222 base::MessageLoopForIO message_loop_
;
223 scoped_refptr
<net::URLRequestContextGetter
> url_request_context_getter_
;
224 scoped_ptr
<RequestHandler
> request_handler_
;
225 scoped_ptr
<AttachmentUploader
> uploader_
;
226 AttachmentUploader::UploadCallback upload_callback_
;
227 net::test_server::EmbeddedTestServer server_
;
228 // A closure that signals an upload has finished.
229 base::Closure signal_upload_done_
;
230 std::vector
<HttpRequest
> http_requests_received_
;
231 std::vector
<AttachmentUploader::UploadResult
> upload_results_
;
232 std::vector
<AttachmentId
> attachment_ids_
;
233 scoped_ptr
<MockOAuth2TokenService
> token_service_
;
235 // Must be last data member.
236 base::WeakPtrFactory
<AttachmentUploaderImplTest
> weak_ptr_factory_
;
239 // Handles HTTP requests received by the EmbeddedTestServer.
241 // Responds with HTTP_OK by default. See |SetStatusCode|.
242 class RequestHandler
: public base::NonThreadSafe
{
244 // Construct a RequestHandler that will PostTask to |test| using
245 // |test_task_runner|.
247 const scoped_refptr
<base::SingleThreadTaskRunner
>& test_task_runner
,
248 const base::WeakPtr
<AttachmentUploaderImplTest
>& test
);
252 scoped_ptr
<HttpResponse
> HandleRequest(const HttpRequest
& request
);
254 // Set the HTTP status code to respond with.
255 void SetStatusCode(const net::HttpStatusCode
& status_code
);
257 // Returns the HTTP status code that will be used in responses.
258 net::HttpStatusCode
GetStatusCode() const;
261 // Protects status_code_.
262 mutable base::Lock mutex_
;
263 net::HttpStatusCode status_code_
;
265 scoped_refptr
<base::SingleThreadTaskRunner
> test_task_runner_
;
266 base::WeakPtr
<AttachmentUploaderImplTest
> test_
;
269 AttachmentUploaderImplTest::AttachmentUploaderImplTest()
270 : weak_ptr_factory_(this) {
273 void AttachmentUploaderImplTest::OnRequestReceived(const HttpRequest
& request
) {
274 DCHECK(CalledOnValidThread());
275 http_requests_received_
.push_back(request
);
278 void AttachmentUploaderImplTest::SetUp() {
279 DCHECK(CalledOnValidThread());
280 request_handler_
.reset(new RequestHandler(message_loop_
.message_loop_proxy(),
281 weak_ptr_factory_
.GetWeakPtr()));
282 url_request_context_getter_
=
283 new net::TestURLRequestContextGetter(message_loop_
.message_loop_proxy());
285 ASSERT_TRUE(server_
.InitializeAndWaitUntilReady());
286 server_
.RegisterRequestHandler(
287 base::Bind(&RequestHandler::HandleRequest
,
288 base::Unretained(request_handler_
.get())));
290 GURL
url(base::StringPrintf("http://localhost:%u/", server_
.port()));
292 token_service_
.reset(new MockOAuth2TokenService
);
293 scoped_refptr
<OAuth2TokenServiceRequest::TokenServiceProvider
>
294 token_service_provider(new TokenServiceProvider(token_service_
.get()));
296 OAuth2TokenService::ScopeSet scopes
;
297 scopes
.insert(GaiaConstants::kChromeSyncOAuth2Scope
);
298 uploader().reset(new AttachmentUploaderImpl(
299 url
, url_request_context_getter_
, kAccountId
, scopes
,
300 token_service_provider
, std::string(kStoreBirthday
), kModelType
));
302 upload_callback_
= base::Bind(&AttachmentUploaderImplTest::UploadDone
,
303 base::Unretained(this));
306 void AttachmentUploaderImplTest::TearDown() {
307 base::RunLoop().RunUntilIdle();
310 void AttachmentUploaderImplTest::RunAndWaitFor(int num_uploads
) {
311 for (int i
= 0; i
< num_uploads
; ++i
) {
312 // Run the loop until one upload completes.
313 base::RunLoop run_loop
;
314 signal_upload_done_
= run_loop
.QuitClosure();
319 Attachment
AttachmentUploaderImplTest::UploadAndRespondWith(
320 const net::HttpStatusCode
& status_code
) {
321 token_service().AddAccount(kAccountId
);
322 request_handler().SetStatusCode(status_code
);
323 scoped_refptr
<base::RefCountedString
> some_data(new base::RefCountedString
);
324 some_data
->data() = kAttachmentData
;
325 Attachment attachment
= Attachment::Create(some_data
);
326 uploader()->UploadAttachment(attachment
, upload_callback());
330 scoped_ptr
<AttachmentUploader
>& AttachmentUploaderImplTest::uploader() {
334 const AttachmentUploader::UploadCallback
&
335 AttachmentUploaderImplTest::upload_callback() const {
336 return upload_callback_
;
339 std::vector
<HttpRequest
>& AttachmentUploaderImplTest::http_requests_received() {
340 return http_requests_received_
;
343 std::vector
<AttachmentUploader::UploadResult
>&
344 AttachmentUploaderImplTest::upload_results() {
345 return upload_results_
;
348 std::vector
<AttachmentId
>&
349 AttachmentUploaderImplTest::attachment_ids() {
350 return attachment_ids_
;
353 MockOAuth2TokenService
& AttachmentUploaderImplTest::token_service() {
354 return *token_service_
;
357 base::MessageLoopForIO
& AttachmentUploaderImplTest::message_loop() {
358 return message_loop_
;
361 RequestHandler
& AttachmentUploaderImplTest::request_handler() {
362 return *request_handler_
;
365 void AttachmentUploaderImplTest::UploadDone(
366 const AttachmentUploader::UploadResult
& result
,
367 const AttachmentId
& attachment_id
) {
368 DCHECK(CalledOnValidThread());
369 upload_results_
.push_back(result
);
370 attachment_ids_
.push_back(attachment_id
);
371 DCHECK(!signal_upload_done_
.is_null());
372 signal_upload_done_
.Run();
375 RequestHandler::RequestHandler(
376 const scoped_refptr
<base::SingleThreadTaskRunner
>& test_task_runner
,
377 const base::WeakPtr
<AttachmentUploaderImplTest
>& test
)
378 : status_code_(net::HTTP_OK
),
379 test_task_runner_(test_task_runner
),
384 RequestHandler::~RequestHandler() {
388 scoped_ptr
<HttpResponse
> RequestHandler::HandleRequest(
389 const HttpRequest
& request
) {
390 DCHECK(CalledOnValidThread());
391 test_task_runner_
->PostTask(
394 &AttachmentUploaderImplTest::OnRequestReceived
, test_
, request
));
395 scoped_ptr
<BasicHttpResponse
> response(new BasicHttpResponse
);
396 response
->set_code(GetStatusCode());
397 response
->set_content_type("text/plain");
398 return response
.Pass();
401 void RequestHandler::SetStatusCode(const net::HttpStatusCode
& status_code
) {
402 base::AutoLock
lock(mutex_
);
403 status_code_
= status_code
;
406 net::HttpStatusCode
RequestHandler::GetStatusCode() const {
407 base::AutoLock
lock(mutex_
);
411 TEST_F(AttachmentUploaderImplTest
, GetURLForAttachmentId_NoPath
) {
412 AttachmentId id
= AttachmentId::Create(0, 0);
413 std::string unique_id
= id
.GetProto().unique_id();
414 GURL
sync_service_url("https://example.com");
415 EXPECT_EQ("https://example.com/attachments/" + unique_id
,
416 AttachmentUploaderImpl::GetURLForAttachmentId(sync_service_url
, id
)
420 TEST_F(AttachmentUploaderImplTest
, GetURLForAttachmentId_JustSlash
) {
421 AttachmentId id
= AttachmentId::Create(0, 0);
422 std::string unique_id
= id
.GetProto().unique_id();
423 GURL
sync_service_url("https://example.com/");
424 EXPECT_EQ("https://example.com/attachments/" + unique_id
,
425 AttachmentUploaderImpl::GetURLForAttachmentId(sync_service_url
, id
)
429 TEST_F(AttachmentUploaderImplTest
, GetURLForAttachmentId_Path
) {
430 AttachmentId id
= AttachmentId::Create(0, 0);
431 std::string unique_id
= id
.GetProto().unique_id();
432 GURL
sync_service_url("https://example.com/service");
433 EXPECT_EQ("https://example.com/service/attachments/" + unique_id
,
434 AttachmentUploaderImpl::GetURLForAttachmentId(sync_service_url
, id
)
438 TEST_F(AttachmentUploaderImplTest
, GetURLForAttachmentId_PathAndSlash
) {
439 AttachmentId id
= AttachmentId::Create(0, 0);
440 std::string unique_id
= id
.GetProto().unique_id();
441 GURL
sync_service_url("https://example.com/service/");
442 EXPECT_EQ("https://example.com/service/attachments/" + unique_id
,
443 AttachmentUploaderImpl::GetURLForAttachmentId(sync_service_url
, id
)
447 // Verify the "happy case" of uploading an attachment.
449 // Token is requested, token is returned, HTTP request is made, attachment is
450 // received by server.
451 TEST_F(AttachmentUploaderImplTest
, UploadAttachment_HappyCase
) {
452 Attachment attachment
= UploadAndRespondWith(net::HTTP_OK
);
453 base::HistogramTester histogram_tester
;
455 // Run until the done callback is invoked.
458 // See that the done callback was invoked with the right arguments.
459 ASSERT_EQ(1U, upload_results().size());
460 EXPECT_EQ(AttachmentUploader::UPLOAD_SUCCESS
, upload_results()[0]);
461 ASSERT_EQ(1U, attachment_ids().size());
462 EXPECT_EQ(attachment
.GetId(), attachment_ids()[0]);
463 histogram_tester
.ExpectUniqueSample("Sync.Attachments.UploadResponseCode",
466 // See that the HTTP server received one request.
467 ASSERT_EQ(1U, http_requests_received().size());
468 const HttpRequest
& http_request
= http_requests_received().front();
469 EXPECT_EQ(net::test_server::METHOD_POST
, http_request
.method
);
470 std::string
expected_relative_url(kAttachments
+
471 attachment
.GetId().GetProto().unique_id());
472 EXPECT_EQ(expected_relative_url
, http_request
.relative_url
);
473 EXPECT_TRUE(http_request
.has_content
);
474 EXPECT_EQ(kAttachmentData
, http_request
.content
);
477 // Verify the request contains the appropriate headers.
478 TEST_F(AttachmentUploaderImplTest
, UploadAttachment_Headers
) {
479 Attachment attachment
= UploadAndRespondWith(net::HTTP_OK
);
481 // Run until the done callback is invoked.
484 // See that the done callback was invoked with the right arguments.
485 ASSERT_EQ(1U, upload_results().size());
486 EXPECT_EQ(AttachmentUploader::UPLOAD_SUCCESS
, upload_results()[0]);
487 ASSERT_EQ(1U, attachment_ids().size());
488 EXPECT_EQ(attachment
.GetId(), attachment_ids()[0]);
490 // See that the HTTP server received one request.
491 ASSERT_EQ(1U, http_requests_received().size());
492 const HttpRequest
& http_request
= http_requests_received().front();
494 const std::string
auth_header_value(std::string("Bearer ") + kAccessToken
);
496 EXPECT_THAT(http_request
.headers
,
497 testing::Contains(testing::Pair(
498 net::HttpRequestHeaders::kAuthorization
, auth_header_value
)));
500 http_request
.headers
,
501 testing::Contains(testing::Key(net::HttpRequestHeaders::kContentLength
)));
502 EXPECT_THAT(http_request
.headers
,
503 testing::Contains(testing::Pair(
504 net::HttpRequestHeaders::kContentType
, kContentTypeValue
)));
505 EXPECT_THAT(http_request
.headers
,
506 testing::Contains(testing::Key(kXGoogHash
)));
508 http_request
.headers
,
509 testing::Contains(testing::Key(net::HttpRequestHeaders::kUserAgent
)));
510 EXPECT_THAT(http_request
.headers
,
511 testing::Contains(testing::Pair(kSyncStoreBirthdayHeader
,
512 kBase64URLSafeStoreBirthday
)));
513 EXPECT_THAT(http_request
.headers
,
514 testing::Contains(testing::Pair(
515 kSyncDataTypeIdHeader
,
517 GetSpecificsFieldNumberFromModelType(kModelType
)))));
520 // Verify two overlapping calls to upload the same attachment result in only one
522 TEST_F(AttachmentUploaderImplTest
, UploadAttachment_Collapse
) {
523 Attachment attachment1
= UploadAndRespondWith(net::HTTP_OK
);
524 Attachment attachment2
= attachment1
;
525 uploader()->UploadAttachment(attachment2
, upload_callback());
527 // Wait for upload_callback() to be invoked twice.
529 // See there was only one request.
530 EXPECT_EQ(1U, http_requests_received().size());
533 // Verify that the internal state associated with an upload is removed when the
534 // uplaod finishes. We do this by issuing two non-overlapping uploads for the
535 // same attachment and see that it results in two HTTP requests.
536 TEST_F(AttachmentUploaderImplTest
, UploadAttachment_CleanUpAfterUpload
) {
537 Attachment attachment1
= UploadAndRespondWith(net::HTTP_OK
);
539 // Wait for upload_callback() to be invoked before starting the second upload.
542 Attachment attachment2
= attachment1
;
543 uploader()->UploadAttachment(attachment2
, upload_callback());
545 // Wait for upload_callback() to be invoked a second time.
547 // See there were two requests.
548 ASSERT_EQ(2U, http_requests_received().size());
551 // Verify that we do not issue an HTTP request when we fail to receive an access
554 // Token is requested, no token is returned, no HTTP request is made
555 TEST_F(AttachmentUploaderImplTest
, UploadAttachment_FailToGetToken
) {
556 // Note, we won't receive a token because we did not add kAccountId to the
558 scoped_refptr
<base::RefCountedString
> some_data(new base::RefCountedString
);
559 some_data
->data() = kAttachmentData
;
560 Attachment attachment
= Attachment::Create(some_data
);
561 uploader()->UploadAttachment(attachment
, upload_callback());
562 base::HistogramTester histogram_tester
;
566 // See that the done callback was invoked.
567 ASSERT_EQ(1U, upload_results().size());
568 EXPECT_EQ(AttachmentUploader::UPLOAD_TRANSIENT_ERROR
, upload_results()[0]);
569 ASSERT_EQ(1U, attachment_ids().size());
570 EXPECT_EQ(attachment
.GetId(), attachment_ids()[0]);
571 histogram_tester
.ExpectTotalCount("Sync.Attachments.UploadResponseCode", 0);
573 // See that no HTTP request was received.
574 ASSERT_EQ(0U, http_requests_received().size());
577 // Verify behavior when the server returns "503 Service Unavailable".
578 TEST_F(AttachmentUploaderImplTest
, UploadAttachment_ServiceUnavilable
) {
579 Attachment attachment
= UploadAndRespondWith(net::HTTP_SERVICE_UNAVAILABLE
);
580 base::HistogramTester histogram_tester
;
584 // See that the done callback was invoked.
585 ASSERT_EQ(1U, upload_results().size());
586 EXPECT_EQ(AttachmentUploader::UPLOAD_TRANSIENT_ERROR
, upload_results()[0]);
587 ASSERT_EQ(1U, attachment_ids().size());
588 EXPECT_EQ(attachment
.GetId(), attachment_ids()[0]);
589 histogram_tester
.ExpectUniqueSample("Sync.Attachments.UploadResponseCode",
590 net::HTTP_SERVICE_UNAVAILABLE
, 1);
592 // See that the HTTP server received one request.
593 ASSERT_EQ(1U, http_requests_received().size());
594 const HttpRequest
& http_request
= http_requests_received().front();
595 EXPECT_EQ(net::test_server::METHOD_POST
, http_request
.method
);
596 std::string
expected_relative_url(kAttachments
+
597 attachment
.GetId().GetProto().unique_id());
598 EXPECT_EQ(expected_relative_url
, http_request
.relative_url
);
599 EXPECT_TRUE(http_request
.has_content
);
600 EXPECT_EQ(kAttachmentData
, http_request
.content
);
602 // See that we did not invalidate the token.
603 ASSERT_EQ(0, token_service().num_invalidate_token());
606 // Verify that we "403 Forbidden" as a non-transient error.
607 TEST_F(AttachmentUploaderImplTest
, UploadAttachment_Forbidden
) {
608 Attachment attachment
= UploadAndRespondWith(net::HTTP_FORBIDDEN
);
609 base::HistogramTester histogram_tester
;
613 // See that the done callback was invoked.
614 ASSERT_EQ(1U, upload_results().size());
615 EXPECT_EQ(AttachmentUploader::UPLOAD_UNSPECIFIED_ERROR
, upload_results()[0]);
616 ASSERT_EQ(1U, attachment_ids().size());
617 EXPECT_EQ(attachment
.GetId(), attachment_ids()[0]);
618 histogram_tester
.ExpectUniqueSample("Sync.Attachments.UploadResponseCode",
619 net::HTTP_FORBIDDEN
, 1);
621 // See that the HTTP server received one request.
622 ASSERT_EQ(1U, http_requests_received().size());
623 const HttpRequest
& http_request
= http_requests_received().front();
624 EXPECT_EQ(net::test_server::METHOD_POST
, http_request
.method
);
625 std::string
expected_relative_url(kAttachments
+
626 attachment
.GetId().GetProto().unique_id());
627 EXPECT_EQ(expected_relative_url
, http_request
.relative_url
);
628 EXPECT_TRUE(http_request
.has_content
);
629 EXPECT_EQ(kAttachmentData
, http_request
.content
);
631 // See that we did not invalidate the token.
632 ASSERT_EQ(0, token_service().num_invalidate_token());
635 // Verify that when we receive an "401 Unauthorized" we invalidate the access
637 TEST_F(AttachmentUploaderImplTest
, UploadAttachment_BadToken
) {
638 Attachment attachment
= UploadAndRespondWith(net::HTTP_UNAUTHORIZED
);
639 base::HistogramTester histogram_tester
;
643 // See that the done callback was invoked.
644 ASSERT_EQ(1U, upload_results().size());
645 EXPECT_EQ(AttachmentUploader::UPLOAD_TRANSIENT_ERROR
, upload_results()[0]);
646 ASSERT_EQ(1U, attachment_ids().size());
647 EXPECT_EQ(attachment
.GetId(), attachment_ids()[0]);
648 histogram_tester
.ExpectUniqueSample("Sync.Attachments.UploadResponseCode",
649 net::HTTP_UNAUTHORIZED
, 1);
651 // See that the HTTP server received one request.
652 ASSERT_EQ(1U, http_requests_received().size());
653 const HttpRequest
& http_request
= http_requests_received().front();
654 EXPECT_EQ(net::test_server::METHOD_POST
, http_request
.method
);
655 std::string
expected_relative_url(kAttachments
+
656 attachment
.GetId().GetProto().unique_id());
657 EXPECT_EQ(expected_relative_url
, http_request
.relative_url
);
658 EXPECT_TRUE(http_request
.has_content
);
659 EXPECT_EQ(kAttachmentData
, http_request
.content
);
661 // See that we invalidated the token.
662 ASSERT_EQ(1, token_service().num_invalidate_token());
665 TEST_F(AttachmentUploaderImplTest
, FormatCrc32cHash
) {
666 scoped_refptr
<base::RefCountedString
> empty(new base::RefCountedString
);
668 EXPECT_EQ("AAAAAA==",
669 AttachmentUploaderImpl::FormatCrc32cHash(ComputeCrc32c(empty
)));
671 scoped_refptr
<base::RefCountedString
> hello_world(new base::RefCountedString
);
672 hello_world
->data() = "hello world";
673 EXPECT_EQ("yZRlqg==", AttachmentUploaderImpl::FormatCrc32cHash(
674 ComputeCrc32c(hello_world
)));
677 // TODO(maniscalco): Add test case for when we are uploading an attachment that
678 // already exists. 409 Conflict? (bug 379825)
680 } // namespace syncer