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