Provide GUID in ONC for all networks in ManagedNetworkConfigurationHandler
[chromium-blink-merge.git] / sync / internal_api / attachments / attachment_downloader_impl_unittest.cc
blob1d026eb56a5f6de7e01c241b1e18dd332c30057a
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.
5 #include "sync/internal_api/public/attachments/attachment_downloader_impl.h"
7 #include "base/bind.h"
8 #include "base/memory/weak_ptr.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/run_loop.h"
11 #include "base/test/histogram_tester.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "google_apis/gaia/fake_oauth2_token_service.h"
14 #include "google_apis/gaia/gaia_constants.h"
15 #include "net/http/http_response_headers.h"
16 #include "net/url_request/test_url_fetcher_factory.h"
17 #include "net/url_request/url_request_test_util.h"
18 #include "sync/api/attachments/attachment.h"
19 #include "sync/internal_api/public/attachments/attachment_uploader_impl.h"
20 #include "sync/internal_api/public/attachments/attachment_util.h"
21 #include "sync/internal_api/public/base/model_type.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "third_party/leveldatabase/src/util/crc32c.h"
25 namespace syncer {
27 namespace {
29 const char kAccountId[] = "attachments@gmail.com";
30 const char kAccessToken[] = "access.token";
31 const char kAttachmentServerUrl[] = "http://attachments.com/";
32 const char kAttachmentContent[] = "attachment.content";
33 const char kStoreBirthday[] = "z00000000-0000-007b-0000-0000000004d2";
34 const syncer::ModelType kModelType = syncer::ModelType::ARTICLES;
36 // MockOAuth2TokenService remembers last request for access token and verifies
37 // that only one request is active at a time.
38 // Call RespondToAccessTokenRequest to respond to it.
39 class MockOAuth2TokenService : public FakeOAuth2TokenService {
40 public:
41 MockOAuth2TokenService() : num_invalidate_token_(0) {}
43 ~MockOAuth2TokenService() override {}
45 void RespondToAccessTokenRequest(GoogleServiceAuthError error);
47 int num_invalidate_token() const { return num_invalidate_token_; }
49 protected:
50 void FetchOAuth2Token(RequestImpl* request,
51 const std::string& account_id,
52 net::URLRequestContextGetter* getter,
53 const std::string& client_id,
54 const std::string& client_secret,
55 const ScopeSet& scopes) override;
57 void InvalidateOAuth2Token(const std::string& account_id,
58 const std::string& client_id,
59 const ScopeSet& scopes,
60 const std::string& access_token) override;
62 private:
63 base::WeakPtr<RequestImpl> last_request_;
64 int num_invalidate_token_;
67 void MockOAuth2TokenService::RespondToAccessTokenRequest(
68 GoogleServiceAuthError error) {
69 EXPECT_TRUE(last_request_ != NULL);
70 std::string access_token;
71 base::Time expiration_time;
72 if (error == GoogleServiceAuthError::AuthErrorNone()) {
73 access_token = kAccessToken;
74 expiration_time = base::Time::Max();
76 base::MessageLoop::current()->PostTask(
77 FROM_HERE,
78 base::Bind(&OAuth2TokenService::RequestImpl::InformConsumer,
79 last_request_,
80 error,
81 access_token,
82 expiration_time));
85 void MockOAuth2TokenService::FetchOAuth2Token(
86 RequestImpl* request,
87 const std::string& account_id,
88 net::URLRequestContextGetter* getter,
89 const std::string& client_id,
90 const std::string& client_secret,
91 const ScopeSet& scopes) {
92 // Only one request at a time is allowed.
93 EXPECT_TRUE(last_request_ == NULL);
94 last_request_ = request->AsWeakPtr();
97 void MockOAuth2TokenService::InvalidateOAuth2Token(
98 const std::string& account_id,
99 const std::string& client_id,
100 const ScopeSet& scopes,
101 const std::string& access_token) {
102 ++num_invalidate_token_;
105 class TokenServiceProvider
106 : public OAuth2TokenServiceRequest::TokenServiceProvider,
107 base::NonThreadSafe {
108 public:
109 TokenServiceProvider(OAuth2TokenService* token_service);
111 // OAuth2TokenService::TokenServiceProvider implementation.
112 scoped_refptr<base::SingleThreadTaskRunner> GetTokenServiceTaskRunner()
113 override;
114 OAuth2TokenService* GetTokenService() override;
116 private:
117 ~TokenServiceProvider() override;
119 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
120 OAuth2TokenService* token_service_;
123 TokenServiceProvider::TokenServiceProvider(OAuth2TokenService* token_service)
124 : task_runner_(base::ThreadTaskRunnerHandle::Get()),
125 token_service_(token_service) {
126 DCHECK(token_service_);
129 TokenServiceProvider::~TokenServiceProvider() {
132 scoped_refptr<base::SingleThreadTaskRunner>
133 TokenServiceProvider::GetTokenServiceTaskRunner() {
134 return task_runner_;
137 OAuth2TokenService* TokenServiceProvider::GetTokenService() {
138 DCHECK(task_runner_->BelongsToCurrentThread());
139 return token_service_;
142 } // namespace
144 class AttachmentDownloaderImplTest : public testing::Test {
145 protected:
146 typedef std::map<AttachmentId, AttachmentDownloader::DownloadResult>
147 ResultsMap;
149 enum HashHeaderType {
150 HASH_HEADER_NONE,
151 HASH_HEADER_VALID,
152 HASH_HEADER_INVALID
155 AttachmentDownloaderImplTest() : num_completed_downloads_(0) {}
157 void SetUp() override;
158 void TearDown() override;
160 AttachmentDownloader* downloader() { return attachment_downloader_.get(); }
162 MockOAuth2TokenService* token_service() { return token_service_.get(); }
164 int num_completed_downloads() { return num_completed_downloads_; }
166 AttachmentDownloader::DownloadCallback download_callback(
167 const AttachmentId& id) {
168 return base::Bind(&AttachmentDownloaderImplTest::DownloadDone,
169 base::Unretained(this),
170 id);
173 // Respond with |response_code| and hash header of type |hash_header_type|.
174 void CompleteDownload(int response_code, HashHeaderType hash_header_type);
176 void DownloadDone(const AttachmentId& attachment_id,
177 const AttachmentDownloader::DownloadResult& result,
178 scoped_ptr<Attachment> attachment);
180 void VerifyDownloadResult(const AttachmentId& attachment_id,
181 const AttachmentDownloader::DownloadResult& result);
183 void RunMessageLoop();
185 private:
186 static void AddHashHeader(HashHeaderType hash_header_type,
187 net::TestURLFetcher* fetcher);
189 base::MessageLoopForIO message_loop_;
190 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
191 net::TestURLFetcherFactory url_fetcher_factory_;
192 scoped_ptr<MockOAuth2TokenService> token_service_;
193 scoped_ptr<AttachmentDownloader> attachment_downloader_;
194 ResultsMap download_results_;
195 int num_completed_downloads_;
198 void AttachmentDownloaderImplTest::SetUp() {
199 url_request_context_getter_ =
200 new net::TestURLRequestContextGetter(message_loop_.message_loop_proxy());
201 url_fetcher_factory_.set_remove_fetcher_on_delete(true);
202 token_service_.reset(new MockOAuth2TokenService());
203 token_service_->AddAccount(kAccountId);
204 scoped_refptr<OAuth2TokenServiceRequest::TokenServiceProvider>
205 token_service_provider(new TokenServiceProvider(token_service_.get()));
207 OAuth2TokenService::ScopeSet scopes;
208 scopes.insert(GaiaConstants::kChromeSyncOAuth2Scope);
209 attachment_downloader_ = AttachmentDownloader::Create(
210 GURL(kAttachmentServerUrl), url_request_context_getter_, kAccountId,
211 scopes, token_service_provider, std::string(kStoreBirthday), kModelType);
214 void AttachmentDownloaderImplTest::TearDown() {
215 RunMessageLoop();
218 void AttachmentDownloaderImplTest::CompleteDownload(
219 int response_code,
220 HashHeaderType hash_header_type) {
221 // TestURLFetcherFactory remembers last active URLFetcher.
222 net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0);
223 // There should be outstanding url fetch request.
224 EXPECT_TRUE(fetcher != NULL);
225 fetcher->set_status(net::URLRequestStatus());
226 fetcher->set_response_code(response_code);
227 if (response_code == net::HTTP_OK) {
228 fetcher->SetResponseString(kAttachmentContent);
230 AddHashHeader(hash_header_type, fetcher);
232 // Call URLFetcherDelegate.
233 net::URLFetcherDelegate* delegate = fetcher->delegate();
234 delegate->OnURLFetchComplete(fetcher);
235 RunMessageLoop();
236 // Once result is processed URLFetcher should be deleted.
237 fetcher = url_fetcher_factory_.GetFetcherByID(0);
238 EXPECT_TRUE(fetcher == NULL);
241 void AttachmentDownloaderImplTest::DownloadDone(
242 const AttachmentId& attachment_id,
243 const AttachmentDownloader::DownloadResult& result,
244 scoped_ptr<Attachment> attachment) {
245 download_results_.insert(std::make_pair(attachment_id, result));
246 if (result == AttachmentDownloader::DOWNLOAD_SUCCESS) {
247 // Successful download should be accompanied by valid attachment with
248 // matching id and valid data.
249 EXPECT_TRUE(attachment != NULL);
250 EXPECT_EQ(attachment_id, attachment->GetId());
252 scoped_refptr<base::RefCountedMemory> data = attachment->GetData();
253 std::string data_as_string(data->front_as<char>(), data->size());
254 EXPECT_EQ(data_as_string, kAttachmentContent);
255 } else {
256 EXPECT_TRUE(attachment == NULL);
258 ++num_completed_downloads_;
261 void AttachmentDownloaderImplTest::VerifyDownloadResult(
262 const AttachmentId& attachment_id,
263 const AttachmentDownloader::DownloadResult& result) {
264 ResultsMap::const_iterator iter = download_results_.find(attachment_id);
265 EXPECT_TRUE(iter != download_results_.end());
266 EXPECT_EQ(iter->second, result);
269 void AttachmentDownloaderImplTest::RunMessageLoop() {
270 base::RunLoop run_loop;
271 run_loop.RunUntilIdle();
274 void AttachmentDownloaderImplTest::AddHashHeader(
275 HashHeaderType hash_header_type,
276 net::TestURLFetcher* fetcher) {
277 std::string header = "X-Goog-Hash: crc32c=";
278 scoped_refptr<net::HttpResponseHeaders> headers(
279 new net::HttpResponseHeaders(""));
280 switch (hash_header_type) {
281 case HASH_HEADER_NONE:
282 break;
283 case HASH_HEADER_VALID:
284 header += AttachmentUploaderImpl::FormatCrc32cHash(leveldb::crc32c::Value(
285 kAttachmentContent, strlen(kAttachmentContent)));
286 headers->AddHeader(header);
287 break;
288 case HASH_HEADER_INVALID:
289 header += "BOGUS1==";
290 headers->AddHeader(header);
291 break;
293 fetcher->set_response_headers(headers);
296 TEST_F(AttachmentDownloaderImplTest, HappyCase) {
297 AttachmentId id1 = AttachmentId::Create();
298 // DownloadAttachment should trigger RequestAccessToken.
299 downloader()->DownloadAttachment(id1, download_callback(id1));
300 RunMessageLoop();
301 // Return valid access token.
302 token_service()->RespondToAccessTokenRequest(
303 GoogleServiceAuthError::AuthErrorNone());
304 RunMessageLoop();
305 // Catch histogram entries.
306 base::HistogramTester histogram_tester;
307 // Check that there is outstanding URLFetcher request and complete it.
308 CompleteDownload(net::HTTP_OK, HASH_HEADER_VALID);
309 // Verify that the response code was logged properly.
310 histogram_tester.ExpectUniqueSample("Sync.Attachments.DownloadResponseCode",
311 net::HTTP_OK, 1);
312 // Verify that callback was called for the right id with the right result.
313 VerifyDownloadResult(id1, AttachmentDownloader::DOWNLOAD_SUCCESS);
316 TEST_F(AttachmentDownloaderImplTest, SameIdMultipleDownloads) {
317 AttachmentId id1 = AttachmentId::Create();
318 base::HistogramTester histogram_tester;
319 // Call DownloadAttachment two times for the same id.
320 downloader()->DownloadAttachment(id1, download_callback(id1));
321 downloader()->DownloadAttachment(id1, download_callback(id1));
322 RunMessageLoop();
323 // Return valid access token.
324 token_service()->RespondToAccessTokenRequest(
325 GoogleServiceAuthError::AuthErrorNone());
326 RunMessageLoop();
327 // Start one more download after access token is received.
328 downloader()->DownloadAttachment(id1, download_callback(id1));
329 // Complete URLFetcher request.
330 CompleteDownload(net::HTTP_OK, HASH_HEADER_VALID);
331 // Verify that all download requests completed.
332 VerifyDownloadResult(id1, AttachmentDownloader::DOWNLOAD_SUCCESS);
333 EXPECT_EQ(3, num_completed_downloads());
335 // Let's download the same attachment again.
336 downloader()->DownloadAttachment(id1, download_callback(id1));
337 RunMessageLoop();
338 // Verify that it didn't finish prematurely.
339 EXPECT_EQ(3, num_completed_downloads());
340 // Return valid access token.
341 token_service()->RespondToAccessTokenRequest(
342 GoogleServiceAuthError::AuthErrorNone());
343 RunMessageLoop();
344 // Complete URLFetcher request.
345 CompleteDownload(net::HTTP_OK, HASH_HEADER_VALID);
346 // Verify that all download requests completed.
347 VerifyDownloadResult(id1, AttachmentDownloader::DOWNLOAD_SUCCESS);
348 EXPECT_EQ(4, num_completed_downloads());
349 histogram_tester.ExpectUniqueSample("Sync.Attachments.DownloadResponseCode",
350 net::HTTP_OK, 2);
353 TEST_F(AttachmentDownloaderImplTest, RequestAccessTokenFails) {
354 AttachmentId id1 = AttachmentId::Create();
355 AttachmentId id2 = AttachmentId::Create();
356 // Trigger first RequestAccessToken.
357 downloader()->DownloadAttachment(id1, download_callback(id1));
358 RunMessageLoop();
359 // Return valid access token.
360 token_service()->RespondToAccessTokenRequest(
361 GoogleServiceAuthError::AuthErrorNone());
362 RunMessageLoop();
363 // Trigger second RequestAccessToken.
364 downloader()->DownloadAttachment(id2, download_callback(id2));
365 RunMessageLoop();
366 // Fail RequestAccessToken.
367 token_service()->RespondToAccessTokenRequest(
368 GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
369 RunMessageLoop();
370 // Only id2 should fail.
371 VerifyDownloadResult(id2, AttachmentDownloader::DOWNLOAD_TRANSIENT_ERROR);
372 // Complete request for id1.
373 CompleteDownload(net::HTTP_OK, HASH_HEADER_VALID);
374 VerifyDownloadResult(id1, AttachmentDownloader::DOWNLOAD_SUCCESS);
377 TEST_F(AttachmentDownloaderImplTest, URLFetcher_BadToken) {
378 AttachmentId id1 = AttachmentId::Create();
379 downloader()->DownloadAttachment(id1, download_callback(id1));
380 RunMessageLoop();
381 // Return valid access token.
382 token_service()->RespondToAccessTokenRequest(
383 GoogleServiceAuthError::AuthErrorNone());
384 RunMessageLoop();
385 // Fail URLFetcher. This should trigger download failure and access token
386 // invalidation.
387 base::HistogramTester histogram_tester;
388 CompleteDownload(net::HTTP_UNAUTHORIZED, HASH_HEADER_VALID);
389 EXPECT_EQ(1, token_service()->num_invalidate_token());
390 VerifyDownloadResult(id1, AttachmentDownloader::DOWNLOAD_TRANSIENT_ERROR);
391 histogram_tester.ExpectUniqueSample("Sync.Attachments.DownloadResponseCode",
392 net::HTTP_UNAUTHORIZED, 1);
395 TEST_F(AttachmentDownloaderImplTest, URLFetcher_ServiceUnavailable) {
396 AttachmentId id1 = AttachmentId::Create();
397 downloader()->DownloadAttachment(id1, download_callback(id1));
398 RunMessageLoop();
399 // Return valid access token.
400 token_service()->RespondToAccessTokenRequest(
401 GoogleServiceAuthError::AuthErrorNone());
402 RunMessageLoop();
403 // Fail URLFetcher. This should trigger download failure. Access token
404 // shouldn't be invalidated.
405 base::HistogramTester histogram_tester;
406 CompleteDownload(net::HTTP_SERVICE_UNAVAILABLE, HASH_HEADER_VALID);
407 EXPECT_EQ(0, token_service()->num_invalidate_token());
408 VerifyDownloadResult(id1, AttachmentDownloader::DOWNLOAD_TRANSIENT_ERROR);
409 histogram_tester.ExpectUniqueSample("Sync.Attachments.DownloadResponseCode",
410 net::HTTP_SERVICE_UNAVAILABLE, 1);
413 // Verify that if no hash is present on the response the downloader accepts the
414 // received attachment.
415 TEST_F(AttachmentDownloaderImplTest, NoHash) {
416 AttachmentId id1 = AttachmentId::Create();
417 downloader()->DownloadAttachment(id1, download_callback(id1));
418 RunMessageLoop();
419 token_service()->RespondToAccessTokenRequest(
420 GoogleServiceAuthError::AuthErrorNone());
421 RunMessageLoop();
422 CompleteDownload(net::HTTP_OK, HASH_HEADER_NONE);
423 VerifyDownloadResult(id1, AttachmentDownloader::DOWNLOAD_SUCCESS);
426 // Verify that if an invalid hash is present on the response the downloader
427 // treats it as a transient error.
428 TEST_F(AttachmentDownloaderImplTest, InvalidHash) {
429 AttachmentId id1 = AttachmentId::Create();
430 downloader()->DownloadAttachment(id1, download_callback(id1));
431 RunMessageLoop();
432 token_service()->RespondToAccessTokenRequest(
433 GoogleServiceAuthError::AuthErrorNone());
434 RunMessageLoop();
435 CompleteDownload(net::HTTP_OK, HASH_HEADER_INVALID);
436 VerifyDownloadResult(id1, AttachmentDownloader::DOWNLOAD_TRANSIENT_ERROR);
439 // Verify that extract fails when there is no headers object.
440 TEST_F(AttachmentDownloaderImplTest, ExtractCrc32c_NoHeaders) {
441 uint32_t extracted;
442 ASSERT_FALSE(AttachmentDownloaderImpl::ExtractCrc32c(nullptr, &extracted));
445 // Verify that extract fails when there is no crc32c value.
446 TEST_F(AttachmentDownloaderImplTest, ExtractCrc32c_Empty) {
447 std::string raw;
448 raw += "HTTP/1.1 200 OK\n";
449 raw += "Foo: bar\n";
450 raw += "X-Goog-HASH: crc32c=\n";
451 raw += "\n";
452 std::replace(raw.begin(), raw.end(), '\n', '\0');
453 scoped_refptr<net::HttpResponseHeaders> headers(
454 new net::HttpResponseHeaders(raw));
455 uint32_t extracted;
456 ASSERT_FALSE(
457 AttachmentDownloaderImpl::ExtractCrc32c(headers.get(), &extracted));
460 // Verify that extract finds the first crc32c and ignores others.
461 TEST_F(AttachmentDownloaderImplTest, ExtractCrc32c_First) {
462 const std::string expected_encoded = "z8SuHQ==";
463 const uint32_t expected = 3485773341;
464 std::string raw;
465 raw += "HTTP/1.1 200 OK\n";
466 raw += "Foo: bar\n";
467 // Ignored because it's the wrong header.
468 raw += "X-Goog-Hashes: crc32c=AAAAAA==\n";
469 // Header name matches. The md5 item is ignored.
470 raw += "X-Goog-HASH: md5=rL0Y20zC+Fzt72VPzMSk2A==,crc32c=" +
471 expected_encoded + "\n";
472 // Ignored because we already found a crc32c in the one above.
473 raw += "X-Goog-HASH: crc32c=AAAAAA==\n";
474 raw += "\n";
475 std::replace(raw.begin(), raw.end(), '\n', '\0');
476 scoped_refptr<net::HttpResponseHeaders> headers(
477 new net::HttpResponseHeaders(raw));
478 uint32_t extracted;
479 ASSERT_TRUE(
480 AttachmentDownloaderImpl::ExtractCrc32c(headers.get(), &extracted));
481 ASSERT_EQ(expected, extracted);
484 // Verify that extract fails when encoded value is too long.
485 TEST_F(AttachmentDownloaderImplTest, ExtractCrc32c_TooLong) {
486 std::string raw;
487 raw += "HTTP/1.1 200 OK\n";
488 raw += "Foo: bar\n";
489 raw += "X-Goog-HASH: crc32c=AAAAAAAA\n";
490 raw += "\n";
491 std::replace(raw.begin(), raw.end(), '\n', '\0');
492 scoped_refptr<net::HttpResponseHeaders> headers(
493 new net::HttpResponseHeaders(raw));
494 uint32_t extracted;
495 ASSERT_FALSE(
496 AttachmentDownloaderImpl::ExtractCrc32c(headers.get(), &extracted));
499 // Verify that extract fails if there is no crc32c.
500 TEST_F(AttachmentDownloaderImplTest, ExtractCrc32c_None) {
501 std::string raw;
502 raw += "HTTP/1.1 200 OK\n";
503 raw += "Foo: bar\n";
504 raw += "X-Goog-Hash: md5=rL0Y20zC+Fzt72VPzMSk2A==\n";
505 raw += "\n";
506 std::replace(raw.begin(), raw.end(), '\n', '\0');
507 scoped_refptr<net::HttpResponseHeaders> headers(
508 new net::HttpResponseHeaders(raw));
509 uint32_t extracted;
510 ASSERT_FALSE(
511 AttachmentDownloaderImpl::ExtractCrc32c(headers.get(), &extracted));
514 } // namespace syncer