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