Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / sync / internal_api / attachments / attachment_uploader_impl_unittest.cc
blob527b00e601d19db7b15b5a534e07daaaef2db166
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"
8 #include "base/bind.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"
36 namespace {
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"
47 "\x0C";
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;
54 } // namespace
56 namespace syncer {
58 using net::test_server::BasicHttpResponse;
59 using net::test_server::HttpRequest;
60 using net::test_server::HttpResponse;
62 class RequestHandler;
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 {
71 public:
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_;
85 protected:
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;
98 private:
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(
132 FROM_HERE,
133 base::Bind(&OAuth2TokenService::RequestImpl::InformConsumer,
134 request->AsWeakPtr(),
135 response_error_,
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 {
152 public:
153 TokenServiceProvider(OAuth2TokenService* token_service);
155 // OAuth2TokenService::TokenServiceProvider implementation.
156 scoped_refptr<base::SingleThreadTaskRunner> GetTokenServiceTaskRunner()
157 override;
158 OAuth2TokenService* GetTokenService() override;
160 private:
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() {
178 return task_runner_;
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 {
192 public:
193 void OnRequestReceived(const HttpRequest& request);
195 protected:
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();
217 private:
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 {
243 public:
244 // Construct a RequestHandler that will PostTask to |test| using
245 // |test_task_runner|.
246 RequestHandler(
247 const scoped_refptr<base::SingleThreadTaskRunner>& test_task_runner,
248 const base::WeakPtr<AttachmentUploaderImplTest>& test);
250 ~RequestHandler();
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;
260 private:
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();
315 run_loop.Run();
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());
327 return attachment;
330 scoped_ptr<AttachmentUploader>& AttachmentUploaderImplTest::uploader() {
331 return 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),
380 test_(test) {
381 DetachFromThread();
384 RequestHandler::~RequestHandler() {
385 DetachFromThread();
388 scoped_ptr<HttpResponse> RequestHandler::HandleRequest(
389 const HttpRequest& request) {
390 DCHECK(CalledOnValidThread());
391 test_task_runner_->PostTask(
392 FROM_HERE,
393 base::Bind(
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_);
408 return status_code_;
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)
417 .spec());
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)
426 .spec());
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)
435 .spec());
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)
444 .spec());
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.
456 RunAndWaitFor(1);
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",
464 net::HTTP_OK, 1);
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.
482 RunAndWaitFor(1);
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)));
499 EXPECT_THAT(
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)));
507 EXPECT_THAT(
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,
516 base::IntToString(
517 GetSpecificsFieldNumberFromModelType(kModelType)))));
520 // Verify two overlapping calls to upload the same attachment result in only one
521 // HTTP request.
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.
528 RunAndWaitFor(2);
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.
540 RunAndWaitFor(1);
542 Attachment attachment2 = attachment1;
543 uploader()->UploadAttachment(attachment2, upload_callback());
545 // Wait for upload_callback() to be invoked a second time.
546 RunAndWaitFor(1);
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
552 // token.
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
557 // token service.
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;
564 RunAndWaitFor(1);
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;
582 RunAndWaitFor(1);
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;
611 RunAndWaitFor(1);
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
636 // token.
637 TEST_F(AttachmentUploaderImplTest, UploadAttachment_BadToken) {
638 Attachment attachment = UploadAndRespondWith(net::HTTP_UNAUTHORIZED);
639 base::HistogramTester histogram_tester;
641 RunAndWaitFor(1);
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);
667 empty->data() = "";
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