Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / sync / internal_api / attachments / attachment_uploader_impl_unittest.cc
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.
4
5 #include "sync/internal_api/public/attachments/attachment_uploader_impl.h"
6
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/memory/ref_counted.h"
10 #include "base/memory/ref_counted_memory.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/run_loop.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/synchronization/lock.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "base/threading/non_thread_safe.h"
17 #include "base/threading/thread.h"
18 #include "google_apis/gaia/fake_oauth2_token_service.h"
19 #include "google_apis/gaia/gaia_constants.h"
20 #include "google_apis/gaia/oauth2_token_service_request.h"
21 #include "net/test/embedded_test_server/embedded_test_server.h"
22 #include "net/test/embedded_test_server/http_request.h"
23 #include "net/test/embedded_test_server/http_response.h"
24 #include "net/url_request/url_request_test_util.h"
25 #include "sync/api/attachments/attachment.h"
26 #include "sync/protocol/sync.pb.h"
27 #include "testing/gmock/include/gmock/gmock-matchers.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29
30 namespace {
31
32 const char kAttachmentData[] = "some data";
33 const char kAccountId[] = "some-account-id";
34 const char kAccessToken[] = "some-access-token";
35 const char kAuthorization[] = "Authorization";
36 const char kContentLength[] = "Content-Length";
37 const char kContentType[] = "Content-Type";
38 const char kContentTypeValue[] = "application/octet-stream";
39 const char kXGoogHash[] = "X-Goog-Hash";
40 const char kAttachments[] = "/attachments/";
41
42 }  // namespace
43
44 namespace syncer {
45
46 using net::test_server::BasicHttpResponse;
47 using net::test_server::HttpRequest;
48 using net::test_server::HttpResponse;
49
50 class RequestHandler;
51
52 // A mock implementation of an OAuth2TokenService.
53 //
54 // Use |SetResponse| to vary the response to token requests.
55 //
56 // Use |num_invalidate_token| and |last_token_invalidated| to check the number
57 // of invalidate token operations performed and the last token invalidated.
58 class MockOAuth2TokenService : public FakeOAuth2TokenService {
59  public:
60   MockOAuth2TokenService();
61   ~MockOAuth2TokenService() override;
62
63   void SetResponse(const GoogleServiceAuthError& error,
64                    const std::string& access_token,
65                    const base::Time& expiration);
66
67   int num_invalidate_token() const { return num_invalidate_token_; }
68
69   const std::string& last_token_invalidated() const {
70     return last_token_invalidated_;
71   }
72
73  protected:
74   void FetchOAuth2Token(RequestImpl* request,
75                         const std::string& account_id,
76                         net::URLRequestContextGetter* getter,
77                         const std::string& client_id,
78                         const std::string& client_secret,
79                         const ScopeSet& scopes) override;
80
81   void InvalidateOAuth2Token(const std::string& account_id,
82                              const std::string& client_id,
83                              const ScopeSet& scopes,
84                              const std::string& access_token) override;
85
86  private:
87   GoogleServiceAuthError response_error_;
88   std::string response_access_token_;
89   base::Time response_expiration_;
90   int num_invalidate_token_;
91   std::string last_token_invalidated_;
92 };
93
94 MockOAuth2TokenService::MockOAuth2TokenService()
95     : response_error_(GoogleServiceAuthError::AuthErrorNone()),
96       response_access_token_(kAccessToken),
97       response_expiration_(base::Time::Max()),
98       num_invalidate_token_(0) {
99 }
100
101 MockOAuth2TokenService::~MockOAuth2TokenService() {
102 }
103
104 void MockOAuth2TokenService::SetResponse(const GoogleServiceAuthError& error,
105                                          const std::string& access_token,
106                                          const base::Time& expiration) {
107   response_error_ = error;
108   response_access_token_ = access_token;
109   response_expiration_ = expiration;
110 }
111
112 void MockOAuth2TokenService::FetchOAuth2Token(
113     RequestImpl* request,
114     const std::string& account_id,
115     net::URLRequestContextGetter* getter,
116     const std::string& client_id,
117     const std::string& client_secret,
118     const ScopeSet& scopes) {
119   base::MessageLoop::current()->PostTask(
120       FROM_HERE,
121       base::Bind(&OAuth2TokenService::RequestImpl::InformConsumer,
122                  request->AsWeakPtr(),
123                  response_error_,
124                  response_access_token_,
125                  response_expiration_));
126 }
127
128 void MockOAuth2TokenService::InvalidateOAuth2Token(
129     const std::string& account_id,
130     const std::string& client_id,
131     const ScopeSet& scopes,
132     const std::string& access_token) {
133   ++num_invalidate_token_;
134   last_token_invalidated_ = access_token;
135 }
136
137 class TokenServiceProvider
138     : public OAuth2TokenServiceRequest::TokenServiceProvider,
139       base::NonThreadSafe {
140  public:
141   TokenServiceProvider(OAuth2TokenService* token_service);
142
143   // OAuth2TokenService::TokenServiceProvider implementation.
144   scoped_refptr<base::SingleThreadTaskRunner> GetTokenServiceTaskRunner()
145       override;
146   OAuth2TokenService* GetTokenService() override;
147
148  private:
149   ~TokenServiceProvider() override;
150
151   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
152   OAuth2TokenService* token_service_;
153 };
154
155 TokenServiceProvider::TokenServiceProvider(OAuth2TokenService* token_service)
156     : task_runner_(base::ThreadTaskRunnerHandle::Get()),
157       token_service_(token_service) {
158   DCHECK(token_service_);
159 }
160
161 TokenServiceProvider::~TokenServiceProvider() {
162 }
163
164 scoped_refptr<base::SingleThreadTaskRunner>
165 TokenServiceProvider::GetTokenServiceTaskRunner() {
166   return task_runner_;
167 }
168
169 OAuth2TokenService* TokenServiceProvider::GetTokenService() {
170   DCHECK(task_runner_->BelongsToCurrentThread());
171   return token_service_;
172 }
173
174 // Text fixture for AttachmentUploaderImpl test.
175 //
176 // This fixture provides an embedded HTTP server and a mock OAuth2 token service
177 // for interacting with AttachmentUploaderImpl
178 class AttachmentUploaderImplTest : public testing::Test,
179                                    public base::NonThreadSafe {
180  public:
181   void OnRequestReceived(const HttpRequest& request);
182
183  protected:
184   AttachmentUploaderImplTest();
185   virtual void SetUp();
186   virtual void TearDown();
187
188   // Run the message loop until UploadDone has been invoked |num_uploads| times.
189   void RunAndWaitFor(int num_uploads);
190
191   // Upload an attachment and have the server respond with |status_code|.
192   //
193   // Returns the attachment that was uploaded.
194   Attachment UploadAndRespondWith(const net::HttpStatusCode& status_code);
195
196   scoped_ptr<AttachmentUploader>& uploader();
197   const AttachmentUploader::UploadCallback& upload_callback() const;
198   std::vector<HttpRequest>& http_requests_received();
199   std::vector<AttachmentUploader::UploadResult>& upload_results();
200   std::vector<AttachmentId>& attachment_ids();
201   MockOAuth2TokenService& token_service();
202   base::MessageLoopForIO& message_loop();
203   RequestHandler& request_handler();
204
205  private:
206   // An UploadCallback invoked by AttachmentUploaderImpl.
207   void UploadDone(const AttachmentUploader::UploadResult& result,
208                   const AttachmentId& attachment_id);
209
210   base::MessageLoopForIO message_loop_;
211   scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
212   scoped_ptr<RequestHandler> request_handler_;
213   scoped_ptr<AttachmentUploader> uploader_;
214   AttachmentUploader::UploadCallback upload_callback_;
215   net::test_server::EmbeddedTestServer server_;
216   // A closure that signals an upload has finished.
217   base::Closure signal_upload_done_;
218   std::vector<HttpRequest> http_requests_received_;
219   std::vector<AttachmentUploader::UploadResult> upload_results_;
220   std::vector<AttachmentId> attachment_ids_;
221   scoped_ptr<MockOAuth2TokenService> token_service_;
222
223   // Must be last data member.
224   base::WeakPtrFactory<AttachmentUploaderImplTest> weak_ptr_factory_;
225 };
226
227 // Handles HTTP requests received by the EmbeddedTestServer.
228 //
229 // Responds with HTTP_OK by default.  See |SetStatusCode|.
230 class RequestHandler : public base::NonThreadSafe {
231  public:
232   // Construct a RequestHandler that will PostTask to |test| using
233   // |test_task_runner|.
234   RequestHandler(
235       const scoped_refptr<base::SingleThreadTaskRunner>& test_task_runner,
236       const base::WeakPtr<AttachmentUploaderImplTest>& test);
237
238   ~RequestHandler();
239
240   scoped_ptr<HttpResponse> HandleRequest(const HttpRequest& request);
241
242   // Set the HTTP status code to respond with.
243   void SetStatusCode(const net::HttpStatusCode& status_code);
244
245   // Returns the HTTP status code that will be used in responses.
246   net::HttpStatusCode GetStatusCode() const;
247
248  private:
249   // Protects status_code_.
250   mutable base::Lock mutex_;
251   net::HttpStatusCode status_code_;
252
253   scoped_refptr<base::SingleThreadTaskRunner> test_task_runner_;
254   base::WeakPtr<AttachmentUploaderImplTest> test_;
255 };
256
257 AttachmentUploaderImplTest::AttachmentUploaderImplTest()
258     : weak_ptr_factory_(this) {
259 }
260
261 void AttachmentUploaderImplTest::OnRequestReceived(const HttpRequest& request) {
262   DCHECK(CalledOnValidThread());
263   http_requests_received_.push_back(request);
264 }
265
266 void AttachmentUploaderImplTest::SetUp() {
267   DCHECK(CalledOnValidThread());
268   request_handler_.reset(new RequestHandler(message_loop_.message_loop_proxy(),
269                                             weak_ptr_factory_.GetWeakPtr()));
270   url_request_context_getter_ =
271       new net::TestURLRequestContextGetter(message_loop_.message_loop_proxy());
272
273   ASSERT_TRUE(server_.InitializeAndWaitUntilReady());
274   server_.RegisterRequestHandler(
275       base::Bind(&RequestHandler::HandleRequest,
276                  base::Unretained(request_handler_.get())));
277
278   GURL url(base::StringPrintf("http://localhost:%d/", server_.port()));
279
280   token_service_.reset(new MockOAuth2TokenService);
281   scoped_refptr<OAuth2TokenServiceRequest::TokenServiceProvider>
282       token_service_provider(new TokenServiceProvider(token_service_.get()));
283
284   OAuth2TokenService::ScopeSet scopes;
285   scopes.insert(GaiaConstants::kChromeSyncOAuth2Scope);
286   uploader().reset(new AttachmentUploaderImpl(url,
287                                               url_request_context_getter_,
288                                               kAccountId,
289                                               scopes,
290                                               token_service_provider));
291
292   upload_callback_ = base::Bind(&AttachmentUploaderImplTest::UploadDone,
293                                 base::Unretained(this));
294 }
295
296 void AttachmentUploaderImplTest::TearDown() {
297   base::RunLoop().RunUntilIdle();
298 }
299
300 void AttachmentUploaderImplTest::RunAndWaitFor(int num_uploads) {
301   for (int i = 0; i < num_uploads; ++i) {
302     // Run the loop until one upload completes.
303     base::RunLoop run_loop;
304     signal_upload_done_ = run_loop.QuitClosure();
305     run_loop.Run();
306   }
307 }
308
309 Attachment AttachmentUploaderImplTest::UploadAndRespondWith(
310     const net::HttpStatusCode& status_code) {
311   token_service().AddAccount(kAccountId);
312   request_handler().SetStatusCode(status_code);
313   scoped_refptr<base::RefCountedString> some_data(new base::RefCountedString);
314   some_data->data() = kAttachmentData;
315   Attachment attachment = Attachment::Create(some_data);
316   uploader()->UploadAttachment(attachment, upload_callback());
317   return attachment;
318 }
319
320 scoped_ptr<AttachmentUploader>& AttachmentUploaderImplTest::uploader() {
321   return uploader_;
322 }
323
324 const AttachmentUploader::UploadCallback&
325 AttachmentUploaderImplTest::upload_callback() const {
326   return upload_callback_;
327 }
328
329 std::vector<HttpRequest>& AttachmentUploaderImplTest::http_requests_received() {
330   return http_requests_received_;
331 }
332
333 std::vector<AttachmentUploader::UploadResult>&
334 AttachmentUploaderImplTest::upload_results() {
335   return upload_results_;
336 }
337
338 std::vector<AttachmentId>&
339 AttachmentUploaderImplTest::attachment_ids() {
340   return attachment_ids_;
341 }
342
343 MockOAuth2TokenService& AttachmentUploaderImplTest::token_service() {
344   return *token_service_;
345 }
346
347 base::MessageLoopForIO& AttachmentUploaderImplTest::message_loop() {
348   return message_loop_;
349 }
350
351 RequestHandler& AttachmentUploaderImplTest::request_handler() {
352   return *request_handler_;
353 }
354
355 void AttachmentUploaderImplTest::UploadDone(
356     const AttachmentUploader::UploadResult& result,
357     const AttachmentId& attachment_id) {
358   DCHECK(CalledOnValidThread());
359   upload_results_.push_back(result);
360   attachment_ids_.push_back(attachment_id);
361   DCHECK(!signal_upload_done_.is_null());
362   signal_upload_done_.Run();
363 }
364
365 RequestHandler::RequestHandler(
366     const scoped_refptr<base::SingleThreadTaskRunner>& test_task_runner,
367     const base::WeakPtr<AttachmentUploaderImplTest>& test)
368     : status_code_(net::HTTP_OK),
369       test_task_runner_(test_task_runner),
370       test_(test) {
371   DetachFromThread();
372 }
373
374 RequestHandler::~RequestHandler() {
375   DetachFromThread();
376 }
377
378 scoped_ptr<HttpResponse> RequestHandler::HandleRequest(
379     const HttpRequest& request) {
380   DCHECK(CalledOnValidThread());
381   test_task_runner_->PostTask(
382       FROM_HERE,
383       base::Bind(
384           &AttachmentUploaderImplTest::OnRequestReceived, test_, request));
385   scoped_ptr<BasicHttpResponse> response(new BasicHttpResponse);
386   response->set_code(GetStatusCode());
387   response->set_content_type("text/plain");
388   return response.Pass();
389 }
390
391 void RequestHandler::SetStatusCode(const net::HttpStatusCode& status_code) {
392   base::AutoLock lock(mutex_);
393   status_code_ = status_code;
394 }
395
396 net::HttpStatusCode RequestHandler::GetStatusCode() const {
397   base::AutoLock lock(mutex_);
398   return status_code_;
399 }
400
401 TEST_F(AttachmentUploaderImplTest, GetURLForAttachmentId_NoPath) {
402   AttachmentId id = AttachmentId::Create();
403   std::string unique_id = id.GetProto().unique_id();
404   GURL sync_service_url("https://example.com");
405   EXPECT_EQ("https://example.com/attachments/" + unique_id,
406             AttachmentUploaderImpl::GetURLForAttachmentId(sync_service_url, id)
407                 .spec());
408 }
409
410 TEST_F(AttachmentUploaderImplTest, GetURLForAttachmentId_JustSlash) {
411   AttachmentId id = AttachmentId::Create();
412   std::string unique_id = id.GetProto().unique_id();
413   GURL sync_service_url("https://example.com/");
414   EXPECT_EQ("https://example.com/attachments/" + unique_id,
415             AttachmentUploaderImpl::GetURLForAttachmentId(sync_service_url, id)
416                 .spec());
417 }
418
419 TEST_F(AttachmentUploaderImplTest, GetURLForAttachmentId_Path) {
420   AttachmentId id = AttachmentId::Create();
421   std::string unique_id = id.GetProto().unique_id();
422   GURL sync_service_url("https://example.com/service");
423   EXPECT_EQ("https://example.com/service/attachments/" + unique_id,
424             AttachmentUploaderImpl::GetURLForAttachmentId(sync_service_url, id)
425                 .spec());
426 }
427
428 TEST_F(AttachmentUploaderImplTest, GetURLForAttachmentId_PathAndSlash) {
429   AttachmentId id = AttachmentId::Create();
430   std::string unique_id = id.GetProto().unique_id();
431   GURL sync_service_url("https://example.com/service/");
432   EXPECT_EQ("https://example.com/service/attachments/" + unique_id,
433             AttachmentUploaderImpl::GetURLForAttachmentId(sync_service_url, id)
434                 .spec());
435 }
436
437 // Verify the "happy case" of uploading an attachment.
438 //
439 // Token is requested, token is returned, HTTP request is made, attachment is
440 // received by server.
441 TEST_F(AttachmentUploaderImplTest, UploadAttachment_HappyCase) {
442   Attachment attachment = UploadAndRespondWith(net::HTTP_OK);
443
444   // Run until the done callback is invoked.
445   RunAndWaitFor(1);
446
447   // See that the done callback was invoked with the right arguments.
448   ASSERT_EQ(1U, upload_results().size());
449   EXPECT_EQ(AttachmentUploader::UPLOAD_SUCCESS, upload_results()[0]);
450   ASSERT_EQ(1U, attachment_ids().size());
451   EXPECT_EQ(attachment.GetId(), attachment_ids()[0]);
452
453   // See that the HTTP server received one request.
454   ASSERT_EQ(1U, http_requests_received().size());
455   const HttpRequest& http_request = http_requests_received().front();
456   EXPECT_EQ(net::test_server::METHOD_POST, http_request.method);
457   std::string expected_relative_url(kAttachments +
458                                     attachment.GetId().GetProto().unique_id());
459   EXPECT_EQ(expected_relative_url, http_request.relative_url);
460   EXPECT_TRUE(http_request.has_content);
461   EXPECT_EQ(kAttachmentData, http_request.content);
462 }
463
464 // Verify the request contains the appropriate headers.
465 TEST_F(AttachmentUploaderImplTest, UploadAttachment_Headers) {
466   Attachment attachment = UploadAndRespondWith(net::HTTP_OK);
467
468   // Run until the done callback is invoked.
469   RunAndWaitFor(1);
470
471   // See that the done callback was invoked with the right arguments.
472   ASSERT_EQ(1U, upload_results().size());
473   EXPECT_EQ(AttachmentUploader::UPLOAD_SUCCESS, upload_results()[0]);
474   ASSERT_EQ(1U, attachment_ids().size());
475   EXPECT_EQ(attachment.GetId(), attachment_ids()[0]);
476
477   // See that the HTTP server received one request.
478   ASSERT_EQ(1U, http_requests_received().size());
479   const HttpRequest& http_request = http_requests_received().front();
480
481   const std::string auth_header_name(kAuthorization);
482   const std::string auth_header_value(std::string("Bearer ") + kAccessToken);
483
484   EXPECT_THAT(
485       http_request.headers,
486       testing::Contains(testing::Pair(kAuthorization, auth_header_value)));
487   EXPECT_THAT(http_request.headers,
488               testing::Contains(testing::Key(kContentLength)));
489   EXPECT_THAT(
490       http_request.headers,
491       testing::Contains(testing::Pair(kContentType, kContentTypeValue)));
492   EXPECT_THAT(http_request.headers,
493               testing::Contains(testing::Key(kXGoogHash)));
494 }
495
496 // Verify two overlapping calls to upload the same attachment result in only one
497 // HTTP request.
498 TEST_F(AttachmentUploaderImplTest, UploadAttachment_Collapse) {
499   Attachment attachment1 = UploadAndRespondWith(net::HTTP_OK);
500   Attachment attachment2 = attachment1;
501   uploader()->UploadAttachment(attachment2, upload_callback());
502
503   // Wait for upload_callback() to be invoked twice.
504   RunAndWaitFor(2);
505   // See there was only one request.
506   EXPECT_EQ(1U, http_requests_received().size());
507 }
508
509 // Verify that the internal state associated with an upload is removed when the
510 // uplaod finishes.  We do this by issuing two non-overlapping uploads for the
511 // same attachment and see that it results in two HTTP requests.
512 TEST_F(AttachmentUploaderImplTest, UploadAttachment_CleanUpAfterUpload) {
513   Attachment attachment1 = UploadAndRespondWith(net::HTTP_OK);
514
515   // Wait for upload_callback() to be invoked before starting the second upload.
516   RunAndWaitFor(1);
517
518   Attachment attachment2 = attachment1;
519   uploader()->UploadAttachment(attachment2, upload_callback());
520
521   // Wait for upload_callback() to be invoked a second time.
522   RunAndWaitFor(1);
523   // See there were two requests.
524   ASSERT_EQ(2U, http_requests_received().size());
525 }
526
527 // Verify that we do not issue an HTTP request when we fail to receive an access
528 // token.
529 //
530 // Token is requested, no token is returned, no HTTP request is made
531 TEST_F(AttachmentUploaderImplTest, UploadAttachment_FailToGetToken) {
532   // Note, we won't receive a token because we did not add kAccountId to the
533   // token service.
534   scoped_refptr<base::RefCountedString> some_data(new base::RefCountedString);
535   some_data->data() = kAttachmentData;
536   Attachment attachment = Attachment::Create(some_data);
537   uploader()->UploadAttachment(attachment, upload_callback());
538
539   RunAndWaitFor(1);
540
541   // See that the done callback was invoked.
542   ASSERT_EQ(1U, upload_results().size());
543   EXPECT_EQ(AttachmentUploader::UPLOAD_TRANSIENT_ERROR, upload_results()[0]);
544   ASSERT_EQ(1U, attachment_ids().size());
545   EXPECT_EQ(attachment.GetId(), attachment_ids()[0]);
546
547   // See that no HTTP request was received.
548   ASSERT_EQ(0U, http_requests_received().size());
549 }
550
551 // Verify behavior when the server returns "503 Service Unavailable".
552 TEST_F(AttachmentUploaderImplTest, UploadAttachment_ServiceUnavilable) {
553   Attachment attachment = UploadAndRespondWith(net::HTTP_SERVICE_UNAVAILABLE);
554
555   RunAndWaitFor(1);
556
557   // See that the done callback was invoked.
558   ASSERT_EQ(1U, upload_results().size());
559   EXPECT_EQ(AttachmentUploader::UPLOAD_TRANSIENT_ERROR, upload_results()[0]);
560   ASSERT_EQ(1U, attachment_ids().size());
561   EXPECT_EQ(attachment.GetId(), attachment_ids()[0]);
562
563   // See that the HTTP server received one request.
564   ASSERT_EQ(1U, http_requests_received().size());
565   const HttpRequest& http_request = http_requests_received().front();
566   EXPECT_EQ(net::test_server::METHOD_POST, http_request.method);
567   std::string expected_relative_url(kAttachments +
568                                     attachment.GetId().GetProto().unique_id());
569   EXPECT_EQ(expected_relative_url, http_request.relative_url);
570   EXPECT_TRUE(http_request.has_content);
571   EXPECT_EQ(kAttachmentData, http_request.content);
572
573   // See that we did not invalidate the token.
574   ASSERT_EQ(0, token_service().num_invalidate_token());
575 }
576
577 // Verify that we "403 Forbidden" as a non-transient error.
578 TEST_F(AttachmentUploaderImplTest, UploadAttachment_Forbidden) {
579   Attachment attachment = UploadAndRespondWith(net::HTTP_FORBIDDEN);
580
581   RunAndWaitFor(1);
582
583   // See that the done callback was invoked.
584   ASSERT_EQ(1U, upload_results().size());
585   EXPECT_EQ(AttachmentUploader::UPLOAD_UNSPECIFIED_ERROR, upload_results()[0]);
586   ASSERT_EQ(1U, attachment_ids().size());
587   EXPECT_EQ(attachment.GetId(), attachment_ids()[0]);
588
589   // See that the HTTP server received one request.
590   ASSERT_EQ(1U, http_requests_received().size());
591   const HttpRequest& http_request = http_requests_received().front();
592   EXPECT_EQ(net::test_server::METHOD_POST, http_request.method);
593   std::string expected_relative_url(kAttachments +
594                                     attachment.GetId().GetProto().unique_id());
595   EXPECT_EQ(expected_relative_url, http_request.relative_url);
596   EXPECT_TRUE(http_request.has_content);
597   EXPECT_EQ(kAttachmentData, http_request.content);
598
599   // See that we did not invalidate the token.
600   ASSERT_EQ(0, token_service().num_invalidate_token());
601 }
602
603 // Verify that when we receive an "401 Unauthorized" we invalidate the access
604 // token.
605 TEST_F(AttachmentUploaderImplTest, UploadAttachment_BadToken) {
606   Attachment attachment = UploadAndRespondWith(net::HTTP_UNAUTHORIZED);
607
608   RunAndWaitFor(1);
609
610   // See that the done callback was invoked.
611   ASSERT_EQ(1U, upload_results().size());
612   EXPECT_EQ(AttachmentUploader::UPLOAD_TRANSIENT_ERROR, upload_results()[0]);
613   ASSERT_EQ(1U, attachment_ids().size());
614   EXPECT_EQ(attachment.GetId(), attachment_ids()[0]);
615
616   // See that the HTTP server received one request.
617   ASSERT_EQ(1U, http_requests_received().size());
618   const HttpRequest& http_request = http_requests_received().front();
619   EXPECT_EQ(net::test_server::METHOD_POST, http_request.method);
620   std::string expected_relative_url(kAttachments +
621                                     attachment.GetId().GetProto().unique_id());
622   EXPECT_EQ(expected_relative_url, http_request.relative_url);
623   EXPECT_TRUE(http_request.has_content);
624   EXPECT_EQ(kAttachmentData, http_request.content);
625
626   // See that we invalidated the token.
627   ASSERT_EQ(1, token_service().num_invalidate_token());
628 }
629
630 TEST_F(AttachmentUploaderImplTest, ComputeCrc32cHash) {
631   scoped_refptr<base::RefCountedString> empty(new base::RefCountedString);
632   empty->data() = "";
633   EXPECT_EQ("AAAAAA==",
634             AttachmentUploaderImpl::ComputeCrc32cHash(empty->front_as<char>(),
635                                                       empty->size()));
636
637   scoped_refptr<base::RefCountedString> hello_world(new base::RefCountedString);
638   hello_world->data() = "hello world";
639   EXPECT_EQ("yZRlqg==",
640             AttachmentUploaderImpl::ComputeCrc32cHash(
641                 hello_world->front_as<char>(), hello_world->size()));
642 }
643
644 // TODO(maniscalco): Add test case for when we are uploading an attachment that
645 // already exists.  409 Conflict? (bug 379825)
646
647 }  // namespace syncer