1 // Copyright (c) 2015 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 "chrome/browser/chromeos/policy/upload_job.h"
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/location.h"
14 #include "base/run_loop.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/thread_task_runner_handle.h"
17 #include "base/time/time.h"
18 #include "chrome/browser/chromeos/policy/upload_job_impl.h"
19 #include "content/public/test/test_browser_thread_bundle.h"
20 #include "google_apis/gaia/fake_oauth2_token_service.h"
21 #include "google_apis/gaia/google_service_auth_error.h"
22 #include "net/http/http_status_code.h"
23 #include "net/test/embedded_test_server/embedded_test_server.h"
24 #include "net/test/embedded_test_server/http_request.h"
25 #include "net/test/embedded_test_server/http_response.h"
26 #include "net/url_request/url_request_test_util.h"
27 #include "testing/gtest/include/gtest/gtest.h"
33 const char kUploadPath
[] = "/upload";
34 const char kRobotAccountId
[] = "robot@gmail.com";
35 const char kCustomField1
[] = "customfield1";
36 const char kCustomField2
[] = "customfield2";
37 const char kTestPayload1
[] = "**||--||PAYLOAD1||--||**";
38 const char kTestPayload2
[] = "**||--||PAYLOAD2||--||**";
39 const char kTokenExpired
[] = "EXPIRED_TOKEN";
40 const char kTokenInvalid
[] = "INVALID_TOKEN";
41 const char kTokenValid
[] = "VALID_TOKEN";
43 class RepeatingMimeBoundaryGenerator
44 : public UploadJobImpl::MimeBoundaryGenerator
{
46 explicit RepeatingMimeBoundaryGenerator(char character
)
47 : character_(character
) {}
48 ~RepeatingMimeBoundaryGenerator() override
{}
50 // MimeBoundaryGenerator:
51 std::string
GenerateBoundary(size_t length
) const override
{
52 return std::string(length
, character_
);
56 const char character_
;
58 DISALLOW_COPY_AND_ASSIGN(RepeatingMimeBoundaryGenerator
);
61 class MockOAuth2TokenService
: public FakeOAuth2TokenService
{
63 MockOAuth2TokenService();
64 ~MockOAuth2TokenService() override
;
66 // OAuth2TokenService:
67 void FetchOAuth2Token(RequestImpl
* request
,
68 const std::string
& account_id
,
69 net::URLRequestContextGetter
* getter
,
70 const std::string
& client_id
,
71 const std::string
& client_secret
,
72 const ScopeSet
& scopes
) override
;
74 // OAuth2TokenService:
75 void InvalidateAccessTokenImpl(const std::string
& account_id
,
76 const std::string
& client_id
,
77 const ScopeSet
& scopes
,
78 const std::string
& access_token
) override
;
80 void AddTokenToQueue(const std::string
& token
);
81 bool IsTokenValid(const std::string
& token
) const;
82 void SetTokenValid(const std::string
& token
);
83 void SetTokenInvalid(const std::string
& token
);
86 std::queue
<std::string
> token_replies_
;
87 std::set
<std::string
> valid_tokens_
;
89 DISALLOW_COPY_AND_ASSIGN(MockOAuth2TokenService
);
92 MockOAuth2TokenService::MockOAuth2TokenService() {
95 MockOAuth2TokenService::~MockOAuth2TokenService() {
98 void MockOAuth2TokenService::FetchOAuth2Token(
99 OAuth2TokenService::RequestImpl
* request
,
100 const std::string
& account_id
,
101 net::URLRequestContextGetter
* getter
,
102 const std::string
& client_id
,
103 const std::string
& client_secret
,
104 const FakeOAuth2TokenService::ScopeSet
& scopes
) {
105 GoogleServiceAuthError response_error
=
106 GoogleServiceAuthError::AuthErrorNone();
107 const base::Time response_expiration
= base::Time::Now();
108 std::string access_token
;
109 if (token_replies_
.empty()) {
111 GoogleServiceAuthError::FromServiceError("Service unavailable.");
113 access_token
= token_replies_
.front();
114 token_replies_
.pop();
116 base::ThreadTaskRunnerHandle::Get()->PostTask(
117 FROM_HERE
, base::Bind(&OAuth2TokenService::RequestImpl::InformConsumer
,
118 request
->AsWeakPtr(), response_error
, access_token
,
119 response_expiration
));
122 void MockOAuth2TokenService::AddTokenToQueue(const std::string
& token
) {
123 token_replies_
.push(token
);
126 bool MockOAuth2TokenService::IsTokenValid(const std::string
& token
) const {
127 return valid_tokens_
.find(token
) != valid_tokens_
.end();
130 void MockOAuth2TokenService::SetTokenValid(const std::string
& token
) {
131 valid_tokens_
.insert(token
);
134 void MockOAuth2TokenService::SetTokenInvalid(const std::string
& token
) {
135 valid_tokens_
.erase(token
);
138 void MockOAuth2TokenService::InvalidateAccessTokenImpl(
139 const std::string
& account_id
,
140 const std::string
& client_id
,
141 const ScopeSet
& scopes
,
142 const std::string
& access_token
) {
143 SetTokenInvalid(access_token
);
148 class UploadJobTestBase
: public testing::Test
, public UploadJob::Delegate
{
151 : test_browser_thread_bundle_(
152 content::TestBrowserThreadBundle::IO_MAINLOOP
) {}
154 // policy::UploadJob::Delegate:
155 void OnSuccess() override
{
156 if (!expected_error_
)
162 // policy::UploadJob::Delegate:
163 void OnFailure(UploadJob::ErrorCode error_code
) override
{
164 if (expected_error_
&& *expected_error_
.get() == error_code
)
170 const GURL
GetServerURL() const { return test_server_
.GetURL(kUploadPath
); }
172 void SetExpectedError(scoped_ptr
<UploadJob::ErrorCode
> expected_error
) {
173 expected_error_
= expected_error
.Pass();
177 void SetUp() override
{
178 request_context_getter_
= new net::TestURLRequestContextGetter(
179 base::ThreadTaskRunnerHandle::Get());
180 oauth2_service_
.AddAccount("robot@gmail.com");
181 ASSERT_TRUE(test_server_
.InitializeAndWaitUntilReady());
185 void TearDown() override
{
186 ASSERT_TRUE(test_server_
.ShutdownAndWaitUntilComplete());
190 scoped_ptr
<UploadJob
> PrepareUploadJob(scoped_ptr
<
191 UploadJobImpl::MimeBoundaryGenerator
> mime_boundary_generator
) {
192 scoped_ptr
<UploadJob
> upload_job(new UploadJobImpl(
193 GetServerURL(), kRobotAccountId
, &oauth2_service_
,
194 request_context_getter_
.get(), this, mime_boundary_generator
.Pass()));
196 std::map
<std::string
, std::string
> header_entries
;
197 header_entries
.insert(std::make_pair(kCustomField1
, "CUSTOM1"));
198 scoped_ptr
<std::string
> data(new std::string(kTestPayload1
));
199 upload_job
->AddDataSegment("Name1", "file1.ext", header_entries
,
202 header_entries
.insert(std::make_pair(kCustomField2
, "CUSTOM2"));
203 scoped_ptr
<std::string
> data2(new std::string(kTestPayload2
));
204 upload_job
->AddDataSegment("Name2", "", header_entries
, data2
.Pass());
205 return upload_job
.Pass();
208 content::TestBrowserThreadBundle test_browser_thread_bundle_
;
209 base::RunLoop run_loop_
;
210 net::test_server::EmbeddedTestServer test_server_
;
211 scoped_refptr
<net::TestURLRequestContextGetter
> request_context_getter_
;
212 MockOAuth2TokenService oauth2_service_
;
214 scoped_ptr
<UploadJob::ErrorCode
> expected_error_
;
217 class UploadFlowTest
: public UploadJobTestBase
{
221 // UploadJobTestBase:
222 void SetUp() override
{
223 UploadJobTestBase::SetUp();
224 test_server_
.RegisterRequestHandler(
225 base::Bind(&UploadFlowTest::HandlePostRequest
, base::Unretained(this)));
228 scoped_ptr
<net::test_server::HttpResponse
> HandlePostRequest(
229 const net::test_server::HttpRequest
& request
) {
230 EXPECT_TRUE(request
.headers
.find("Authorization") != request
.headers
.end());
231 const std::string authorization_header
=
232 request
.headers
.at("Authorization");
233 scoped_ptr
<net::test_server::BasicHttpResponse
> response(
234 new net::test_server::BasicHttpResponse
);
235 const size_t pos
= authorization_header
.find(" ");
236 if (pos
== std::string::npos
) {
237 response
->set_code(net::HTTP_UNAUTHORIZED
);
238 return response
.Pass();
241 const std::string token
= authorization_header
.substr(pos
+ 1);
242 response
->set_code(oauth2_service_
.IsTokenValid(token
)
244 : net::HTTP_UNAUTHORIZED
);
245 return response
.Pass();
249 TEST_F(UploadFlowTest
, SuccessfulUpload
) {
250 oauth2_service_
.SetTokenValid(kTokenValid
);
251 oauth2_service_
.AddTokenToQueue(kTokenValid
);
252 scoped_ptr
<UploadJob
> upload_job
= PrepareUploadJob(
253 make_scoped_ptr(new UploadJobImpl::RandomMimeBoundaryGenerator
));
258 TEST_F(UploadFlowTest
, TokenExpired
) {
259 oauth2_service_
.SetTokenValid(kTokenValid
);
260 oauth2_service_
.AddTokenToQueue(kTokenExpired
);
261 oauth2_service_
.AddTokenToQueue(kTokenValid
);
262 scoped_ptr
<UploadJob
> upload_job
= PrepareUploadJob(
263 make_scoped_ptr(new UploadJobImpl::RandomMimeBoundaryGenerator
));
268 TEST_F(UploadFlowTest
, TokenInvalid
) {
269 oauth2_service_
.AddTokenToQueue(kTokenInvalid
);
270 oauth2_service_
.AddTokenToQueue(kTokenInvalid
);
271 SetExpectedError(scoped_ptr
<UploadJob::ErrorCode
>(
272 new UploadJob::ErrorCode(UploadJob::AUTHENTICATION_ERROR
)));
274 scoped_ptr
<UploadJob
> upload_job
= PrepareUploadJob(
275 make_scoped_ptr(new UploadJobImpl::RandomMimeBoundaryGenerator
));
280 TEST_F(UploadFlowTest
, TokenFetchFailure
) {
281 SetExpectedError(scoped_ptr
<UploadJob::ErrorCode
>(
282 new UploadJob::ErrorCode(UploadJob::AUTHENTICATION_ERROR
)));
284 scoped_ptr
<UploadJob
> upload_job
= PrepareUploadJob(
285 make_scoped_ptr(new UploadJobImpl::RandomMimeBoundaryGenerator
));
290 class UploadRequestTest
: public UploadJobTestBase
{
292 UploadRequestTest() {}
294 // UploadJobTestBase:
295 void SetUp() override
{
296 UploadJobTestBase::SetUp();
297 test_server_
.RegisterRequestHandler(base::Bind(
298 &UploadRequestTest::HandlePostRequest
, base::Unretained(this)));
301 scoped_ptr
<net::test_server::HttpResponse
> HandlePostRequest(
302 const net::test_server::HttpRequest
& request
) {
303 scoped_ptr
<net::test_server::BasicHttpResponse
> response(
304 new net::test_server::BasicHttpResponse
);
305 response
->set_code(net::HTTP_OK
);
306 EXPECT_EQ(expected_content_
, request
.content
);
307 return response
.Pass();
310 void SetExpectedRequestContent(const std::string
& expected_content
) {
311 expected_content_
= expected_content
;
315 std::string expected_content_
;
318 TEST_F(UploadRequestTest
, TestRequestStructure
) {
319 oauth2_service_
.SetTokenValid(kTokenValid
);
320 oauth2_service_
.AddTokenToQueue(kTokenValid
);
321 scoped_ptr
<UploadJob
> upload_job
= PrepareUploadJob(
322 make_scoped_ptr(new RepeatingMimeBoundaryGenerator('A')));
323 SetExpectedRequestContent(
324 "--AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n"
325 "Content-Disposition: form-data; "
326 "name=\"Name1\"; filename=\"file1.ext\"\r\n"
327 "customfield1: CUSTOM1\r\n"
329 "**||--||PAYLOAD1||--||**\r\n"
330 "--AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n"
331 "Content-Disposition: form-data; name=\"Name2\"\r\n"
332 "customfield1: CUSTOM1\r\n"
333 "customfield2: CUSTOM2\r\n"
335 "**||--||PAYLOAD2||--||**\r\n--"
336 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA--\r\n");
342 } // namespace policy