Home | History | Annotate | Download | only in attachments
      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 
     62   int num_invalidate_token() const { return num_invalidate_token_; }
     63 
     64   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 
     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 
     96 MockOAuth2TokenService::~MockOAuth2TokenService() {
     97 }
     98 
     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 
    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 
    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 
    149 TokenServiceProvider::TokenServiceProvider(OAuth2TokenService* token_service)
    150     : task_runner_(base::ThreadTaskRunnerHandle::Get()),
    151       token_service_(token_service) {
    152   DCHECK(token_service_);
    153 }
    154 
    155 TokenServiceProvider::~TokenServiceProvider() {
    156 }
    157 
    158 scoped_refptr<base::SingleThreadTaskRunner>
    159 TokenServiceProvider::GetTokenServiceTaskRunner() {
    160   return task_runner_;
    161 }
    162 
    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 
    246 AttachmentUploaderImplTest::AttachmentUploaderImplTest()
    247     : weak_ptr_factory_(this) {
    248 }
    249 
    250 void AttachmentUploaderImplTest::OnRequestReceived(const HttpRequest& request) {
    251   DCHECK(CalledOnValidThread());
    252   http_requests_received_.push_back(request);
    253 }
    254 
    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 
    286 void AttachmentUploaderImplTest::TearDown() {
    287   base::RunLoop().RunUntilIdle();
    288 }
    289 
    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 
    299 scoped_ptr<AttachmentUploader>& AttachmentUploaderImplTest::uploader() {
    300   return uploader_;
    301 }
    302 
    303 const AttachmentUploader::UploadCallback&
    304 AttachmentUploaderImplTest::upload_callback() const {
    305   return upload_callback_;
    306 }
    307 
    308 std::vector<HttpRequest>& AttachmentUploaderImplTest::http_requests_received() {
    309   return http_requests_received_;
    310 }
    311 
    312 std::vector<AttachmentUploader::UploadResult>&
    313 AttachmentUploaderImplTest::upload_results() {
    314   return upload_results_;
    315 }
    316 
    317 std::vector<AttachmentId>&
    318 AttachmentUploaderImplTest::updated_attachment_ids() {
    319   return updated_attachment_ids_;
    320 }
    321 
    322 MockOAuth2TokenService& AttachmentUploaderImplTest::token_service() {
    323   return *token_service_;
    324 }
    325 
    326 base::MessageLoopForIO& AttachmentUploaderImplTest::message_loop() {
    327   return message_loop_;
    328 }
    329 
    330 RequestHandler& AttachmentUploaderImplTest::request_handler() {
    331   return *request_handler_;
    332 }
    333 
    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 
    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 
    353 RequestHandler::~RequestHandler() {
    354   DetachFromThread();
    355 }
    356 
    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 
    370 void RequestHandler::SetStatusCode(const net::HttpStatusCode& status_code) {
    371   base::AutoLock lock(mutex_);
    372   status_code_ = status_code;
    373 }
    374 
    375 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.
    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.
    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.
    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
    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".
    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.
    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