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