blob: ddfc1675f56091c68eb5d79be7ce41f93fdca9da [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// A complete set of unit tests for all subclasses of BoxApiCallFlow.
#include "chrome/browser/enterprise/connectors/file_system/box_api_call_flow.h"
#include "chrome/browser/enterprise/connectors/file_system/box_api_call_response.h"
#include <memory>
#include "base/base64.h"
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/json/json_writer.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/stringprintf.h"
#include "base/test/task_environment.h"
#include "chrome/browser/enterprise/connectors/file_system/box_api_call_test_helper.h"
#include "net/base/net_errors.h"
#include "net/http/http_status_code.h"
#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "services/network/test/test_url_loader_factory.h"
#include "services/network/test/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace enterprise_connectors {
template <typename ApiCallMiniClass>
class BoxApiCallFlowTest : public testing::Test {
protected:
std::unique_ptr<ApiCallMiniClass> flow_;
void OnGenericResponse(BoxApiCallResponse response) {
processed_success_ = response.success;
response_code_ = response.net_or_http_code;
box_error_code_ = response.box_error_code;
box_request_id_ = response.box_request_id;
}
bool processed_success_ = false;
int response_code_ = -1;
std::string box_error_code_;
std::string box_request_id_;
private:
base::test::TaskEnvironment task_environment_;
data_decoder::test::InProcessDataDecoder decoder_;
};
template <typename ApiCallMiniClass>
class BoxFolderApiCallFlowTest : public BoxApiCallFlowTest<ApiCallMiniClass> {
protected:
void OnResponse(BoxApiCallResponse response, const std::string& folder_id) {
BoxApiCallFlowTest<ApiCallMiniClass>::OnGenericResponse(response);
processed_folder_id_ = folder_id;
}
std::string processed_folder_id_ = "default id";
};
////////////////////////////////////////////////////////////////////////////////
// FindUpstreamFolder
////////////////////////////////////////////////////////////////////////////////
class BoxFindUpstreamFolderApiCallFlowForTest
: public BoxFindUpstreamFolderApiCallFlow {
public:
using BoxFindUpstreamFolderApiCallFlow::BoxFindUpstreamFolderApiCallFlow;
using BoxFindUpstreamFolderApiCallFlow::CreateApiCallUrl;
using BoxFindUpstreamFolderApiCallFlow::ProcessApiCallFailure;
using BoxFindUpstreamFolderApiCallFlow::ProcessApiCallSuccess;
};
class BoxFindUpstreamFolderApiCallFlowTest
: public BoxFolderApiCallFlowTest<BoxFindUpstreamFolderApiCallFlowForTest> {
protected:
void SetUp() override {
flow_ = std::make_unique<BoxFindUpstreamFolderApiCallFlowForTest>(
base::BindOnce(&BoxFindUpstreamFolderApiCallFlowTest::OnResponse,
factory_.GetWeakPtr()));
}
base::WeakPtrFactory<BoxFindUpstreamFolderApiCallFlowTest> factory_{this};
};
TEST_F(BoxFindUpstreamFolderApiCallFlowTest, CreateApiCallUrl) {
GURL url(kFileSystemBoxFindFolderUrl);
ASSERT_EQ(flow_->CreateApiCallUrl(), url);
}
TEST_F(BoxFindUpstreamFolderApiCallFlowTest, ProcessApiCallFailure) {
auto http_head = network::CreateURLResponseHead(net::HTTP_TOO_MANY_REQUESTS);
std::unique_ptr<std::string> body =
std::make_unique<std::string>(CreateFailureResponse(
net::HTTP_TOO_MANY_REQUESTS, "rate_limit_exceeded"));
flow_->ProcessApiCallFailure(net::OK, http_head.get(), std::move(body));
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(processed_success_);
ASSERT_EQ(response_code_, net::HTTP_TOO_MANY_REQUESTS);
ASSERT_EQ(box_error_code_, "rate_limit_exceeded");
ASSERT_EQ(box_request_id_, kFileSystemBoxClientErrorResponseRequestId);
ASSERT_EQ(processed_folder_id_, "");
}
TEST_F(BoxFindUpstreamFolderApiCallFlowTest, ProcessNetworkFailure) {
flow_->ProcessApiCallFailure(net::ERR_TIMED_OUT, {}, {});
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(processed_success_);
ASSERT_EQ(response_code_, net::ERR_TIMED_OUT);
ASSERT_EQ(processed_folder_id_, "");
}
struct FindUpstreamFolderSuccessResponses {
base::StringPiece body;
bool expected_success;
std::string expected_folder_id;
};
class BoxFindUpstreamFolder_ProcessApiCallSuccessTest
: public BoxFindUpstreamFolderApiCallFlowTest,
public testing::WithParamInterface<FindUpstreamFolderSuccessResponses> {
protected:
void ProcessApiCallSuccess(base::StringPiece body) {
network::mojom::URLResponseHeadPtr head =
network::CreateURLResponseHead(net::HTTP_OK);
flow_->ProcessApiCallSuccess(
head.get(), std::make_unique<std::string>(body.data(), body.size()));
base::RunLoop().RunUntilIdle();
}
};
TEST_P(BoxFindUpstreamFolder_ProcessApiCallSuccessTest, ExtractFolderId) {
const auto& body = GetParam().body;
ProcessApiCallSuccess(body);
ASSERT_EQ(response_code_, net::HTTP_OK);
ASSERT_EQ(processed_success_, GetParam().expected_success) << body;
ASSERT_EQ(processed_folder_id_, GetParam().expected_folder_id) << body;
}
const char kFileSystemBoxFindFolderResponseNoFolderId[] = R"({
"entries": [
{
"etag": 1,
"type": "folder",
"sequence_id": 3,
"name": "ChromeDownloads"
}
]
})";
const char kFileSystemBoxFindFolderResponseEmptyFolderId[] = R"({
"entries": [
{
"id": ,
"etag": 1,
"type": "folder",
"sequence_id": 3,
"name": "ChromeDownloads"
}
]
})";
const char kFileSystemBoxFindFolderResponseBodyWithStringFolderId[] = R"({
"entries": [
{
"id": "12345",
"etag": 1,
"type": "folder",
"sequence_id": 3,
"name": "ChromeDownloads"
}
]
})";
const char kFileSystemBoxFindFolderResponseBodyWithFloatFolderId[] = R"({
"entries": [
{
"id": 123.5,
"etag": 1,
"type": "folder",
"sequence_id": 3,
"name": "ChromeDownloads"
}
]
})";
std::vector<FindUpstreamFolderSuccessResponses> kTestSuccessResponses = {
{{}, false, ""}, // No body.
{kEmptyResponseBody, false, ""},
{"adgafdga", false, ""}, // Invalid body.
{kFileSystemBoxFindFolderResponseEmptyEntriesList, true, ""},
{kFileSystemBoxFindFolderResponseNoFolderId, false, ""},
{kFileSystemBoxFindFolderResponseEmptyFolderId, false, ""},
{kFileSystemBoxFindFolderResponseBody, true,
kFileSystemBoxFindFolderResponseFolderId},
{kFileSystemBoxFindFolderResponseBodyWithStringFolderId, true,
kFileSystemBoxFindFolderResponseFolderId},
{kFileSystemBoxFindFolderResponseBodyWithFloatFolderId, false, ""}};
INSTANTIATE_TEST_SUITE_P(BoxFindUpstreamFolderApiCallFlowTest,
BoxFindUpstreamFolder_ProcessApiCallSuccessTest,
testing::ValuesIn(kTestSuccessResponses));
////////////////////////////////////////////////////////////////////////////////
// GetFileFolder
////////////////////////////////////////////////////////////////////////////////
class BoxGetFileFolderApiCallFlowForTest : public BoxGetFileFolderApiCallFlow {
public:
using BoxGetFileFolderApiCallFlow::BoxGetFileFolderApiCallFlow;
using BoxGetFileFolderApiCallFlow::CreateApiCallUrl;
using BoxGetFileFolderApiCallFlow::ProcessApiCallFailure;
using BoxGetFileFolderApiCallFlow::ProcessApiCallSuccess;
};
class BoxGetFileFolderApiCallFlowTest
: public BoxFolderApiCallFlowTest<BoxGetFileFolderApiCallFlowForTest> {
protected:
void SetUp() override {
flow_ = std::make_unique<BoxGetFileFolderApiCallFlowForTest>(
base::BindOnce(&BoxGetFileFolderApiCallFlowTest::OnResponse,
factory_.GetWeakPtr()),
kFileSystemBoxGetFileFolderFileId);
}
base::WeakPtrFactory<BoxGetFileFolderApiCallFlowTest> factory_{this};
};
TEST_F(BoxGetFileFolderApiCallFlowTest, CreateApiCallUrl) {
GURL path(base::StrCat({kFileSystemBoxGetFileFolderUrl, "/",
kFileSystemBoxGetFileFolderFileId}));
ASSERT_EQ(flow_->CreateApiCallUrl(), path);
}
TEST_F(BoxGetFileFolderApiCallFlowTest, ProcessApiCallFailure) {
auto http_head = network::CreateURLResponseHead(net::HTTP_TOO_MANY_REQUESTS);
std::unique_ptr<std::string> body =
std::make_unique<std::string>(CreateFailureResponse(
net::HTTP_TOO_MANY_REQUESTS, "rate_limit_exceeded"));
flow_->ProcessApiCallFailure(net::OK, http_head.get(), std::move(body));
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(processed_success_);
ASSERT_EQ(response_code_, net::HTTP_TOO_MANY_REQUESTS);
ASSERT_EQ(box_error_code_, "rate_limit_exceeded");
ASSERT_EQ(box_request_id_, kFileSystemBoxClientErrorResponseRequestId);
ASSERT_EQ(processed_folder_id_, "");
}
class BoxGetFileFolderApiCallFlowTest_ProcessApiCallSuccess
: public BoxGetFileFolderApiCallFlowTest {
public:
BoxGetFileFolderApiCallFlowTest_ProcessApiCallSuccess()
: head_(network::CreateURLResponseHead(net::HTTP_OK)) {}
protected:
network::mojom::URLResponseHeadPtr head_;
};
TEST_F(BoxGetFileFolderApiCallFlowTest_ProcessApiCallSuccess, Normal) {
auto http_head = network::CreateURLResponseHead(net::HTTP_OK);
flow_->ProcessApiCallSuccess(
http_head.get(),
std::make_unique<std::string>(kFileSystemBoxGetFileFolderResponseBody));
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(processed_success_);
ASSERT_EQ(response_code_, net::HTTP_OK);
ASSERT_EQ(processed_folder_id_, kFileSystemBoxGetFileFolderResponseFolderId);
}
////////////////////////////////////////////////////////////////////////////////
// CreateUpstreamFolder
////////////////////////////////////////////////////////////////////////////////
class BoxCreateUpstreamFolderApiCallFlowForTest
: public BoxCreateUpstreamFolderApiCallFlow {
public:
using BoxCreateUpstreamFolderApiCallFlow::BoxCreateUpstreamFolderApiCallFlow;
using BoxCreateUpstreamFolderApiCallFlow::CreateApiCallBody;
using BoxCreateUpstreamFolderApiCallFlow::CreateApiCallUrl;
using BoxCreateUpstreamFolderApiCallFlow::IsExpectedSuccessCode;
using BoxCreateUpstreamFolderApiCallFlow::ProcessApiCallFailure;
using BoxCreateUpstreamFolderApiCallFlow::ProcessApiCallSuccess;
};
class BoxCreateUpstreamFolderApiCallFlowTest
: public BoxFolderApiCallFlowTest<
BoxCreateUpstreamFolderApiCallFlowForTest> {
protected:
void SetUp() override {
flow_ = std::make_unique<BoxCreateUpstreamFolderApiCallFlowForTest>(
base::BindOnce(&BoxCreateUpstreamFolderApiCallFlowTest::OnResponse,
factory_.GetWeakPtr()));
}
base::WeakPtrFactory<BoxCreateUpstreamFolderApiCallFlowTest> factory_{this};
};
TEST_F(BoxCreateUpstreamFolderApiCallFlowTest, CreateApiCallUrl) {
GURL url(kFileSystemBoxCreateFolderUrl);
ASSERT_EQ(flow_->CreateApiCallUrl(), url);
}
TEST_F(BoxCreateUpstreamFolderApiCallFlowTest, CreateApiCallBody) {
std::string body = flow_->CreateApiCallBody();
std::string expected_body(
R"({"name":"ChromeDownloads","parent":{"id":"0"}})");
ASSERT_EQ(body, expected_body);
}
TEST_F(BoxCreateUpstreamFolderApiCallFlowTest, IsExpectedSuccessCode) {
ASSERT_TRUE(flow_->IsExpectedSuccessCode(201));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(400));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(403));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(404));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(409));
}
TEST_F(BoxCreateUpstreamFolderApiCallFlowTest, ProcessApiCallFailure) {
auto http_head = network::CreateURLResponseHead(net::HTTP_TOO_MANY_REQUESTS);
std::unique_ptr<std::string> body =
std::make_unique<std::string>(CreateFailureResponse(
net::HTTP_TOO_MANY_REQUESTS, "rate_limit_exceeded"));
flow_->ProcessApiCallFailure(net::OK, http_head.get(), std::move(body));
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(processed_success_);
ASSERT_EQ(response_code_, net::HTTP_TOO_MANY_REQUESTS);
ASSERT_EQ(box_error_code_, "rate_limit_exceeded");
ASSERT_EQ(box_request_id_, kFileSystemBoxClientErrorResponseRequestId);
ASSERT_EQ(processed_folder_id_, "");
}
class BoxCreateUpstreamFolderApiCallFlowTest_ProcessApiCallSuccess
: public BoxCreateUpstreamFolderApiCallFlowTest {
public:
BoxCreateUpstreamFolderApiCallFlowTest_ProcessApiCallSuccess()
: head_(network::CreateURLResponseHead(net::HTTP_CREATED)) {}
protected:
network::mojom::URLResponseHeadPtr head_;
};
TEST_F(BoxCreateUpstreamFolderApiCallFlowTest_ProcessApiCallSuccess, Normal) {
auto http_head = network::CreateURLResponseHead(net::HTTP_CREATED);
flow_->ProcessApiCallSuccess(
http_head.get(),
std::make_unique<std::string>(kFileSystemBoxCreateFolderResponseBody));
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(processed_success_);
ASSERT_EQ(response_code_, net::HTTP_CREATED);
ASSERT_EQ(processed_folder_id_, kFileSystemBoxCreateFolderResponseFolderId);
}
////////////////////////////////////////////////////////////////////////////////
// PreflightCheck
////////////////////////////////////////////////////////////////////////////////
class BoxPreflightCheckApiCallFlowForTest
: public BoxPreflightCheckApiCallFlow {
public:
using BoxPreflightCheckApiCallFlow::BoxPreflightCheckApiCallFlow;
using BoxPreflightCheckApiCallFlow::CreateApiCallBody;
using BoxPreflightCheckApiCallFlow::CreateApiCallBodyContentType;
using BoxPreflightCheckApiCallFlow::CreateApiCallUrl;
using BoxPreflightCheckApiCallFlow::IsExpectedSuccessCode;
using BoxPreflightCheckApiCallFlow::ProcessApiCallFailure;
using BoxPreflightCheckApiCallFlow::ProcessApiCallSuccess;
};
class BoxPreflightCheckApiCallFlowTest
: public BoxApiCallFlowTest<BoxPreflightCheckApiCallFlowForTest> {
protected:
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
file_path_ = temp_dir_.GetPath().Append(file_name_);
flow_ = std::make_unique<BoxPreflightCheckApiCallFlowForTest>(
base::BindOnce(&BoxPreflightCheckApiCallFlowTest::OnResponse,
factory_.GetWeakPtr()),
file_name_, folder_id_);
}
void OnResponse(BoxApiCallResponse response) {
OnGenericResponse(response);
if (quit_closure_)
std::move(quit_closure_).Run();
}
const std::string folder_id_{"1337"};
const base::FilePath file_name_{
FILE_PATH_LITERAL("box_preflight_check_test.txt")};
base::FilePath file_path_;
base::ScopedTempDir temp_dir_;
base::OnceClosure quit_closure_;
base::WeakPtrFactory<BoxPreflightCheckApiCallFlowTest> factory_{this};
};
TEST_F(BoxPreflightCheckApiCallFlowTest, CreateApiCallUrl) {
GURL url(kFileSystemBoxPreflightCheckUrl);
ASSERT_EQ(flow_->CreateApiCallUrl(), url);
}
TEST_F(BoxPreflightCheckApiCallFlowTest, IsExpectedSuccessCode) {
ASSERT_TRUE(flow_->IsExpectedSuccessCode(200));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(400));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(403));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(404));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(409));
}
TEST_F(BoxPreflightCheckApiCallFlowTest, ProcessApiCallSuccess) {
auto http_head = network::CreateURLResponseHead(net::HTTP_OK);
flow_->ProcessApiCallSuccess(http_head.get(), {});
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(processed_success_);
ASSERT_EQ(response_code_, net::HTTP_OK);
}
////////////////////////////////////////////////////////////////////////////////
// WholeFileUpload
////////////////////////////////////////////////////////////////////////////////
class BoxWholeFileUploadApiCallFlowForTest
: public BoxWholeFileUploadApiCallFlow {
public:
using BoxWholeFileUploadApiCallFlow::BoxWholeFileUploadApiCallFlow;
using BoxWholeFileUploadApiCallFlow::CreateApiCallBody;
using BoxWholeFileUploadApiCallFlow::CreateApiCallBodyContentType;
using BoxWholeFileUploadApiCallFlow::CreateApiCallUrl;
using BoxWholeFileUploadApiCallFlow::IsExpectedSuccessCode;
using BoxWholeFileUploadApiCallFlow::ProcessApiCallFailure;
using BoxWholeFileUploadApiCallFlow::ProcessApiCallSuccess;
using BoxWholeFileUploadApiCallFlow::SetFileReadForTesting;
};
class BoxWholeFileUploadApiCallFlowTest
: public BoxApiCallFlowTest<BoxWholeFileUploadApiCallFlowForTest> {
protected:
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
file_path_ = temp_dir_.GetPath().Append(file_name_);
flow_ = std::make_unique<BoxWholeFileUploadApiCallFlowForTest>(
base::BindOnce(&BoxWholeFileUploadApiCallFlowTest::OnResponse,
factory_.GetWeakPtr()),
folder_id_, file_name_, file_path_);
}
void OnResponse(BoxApiCallResponse response, const std::string& file_id) {
OnGenericResponse(response);
file_id_ = file_id;
if (quit_closure_)
std::move(quit_closure_).Run();
}
std::string MakeExpectedBody() {
// Body format for multipart/form-data reference:
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type
// Request body fields reference:
// https://developer.box.com/reference/post-files-content/
std::string content_type = flow_->CreateApiCallBodyContentType();
std::string expected_type("multipart/form-data; boundary=");
std::string multipart_boundary =
"--" + content_type.substr(expected_type.size());
std::string expected_body(multipart_boundary + "\r\n");
expected_body +=
"Content-Disposition: form-data; name=\"attributes\"\r\n"
"Content-Type: application/json\r\n\r\n"
"{\"name\":\"";
expected_body +=
file_name_.AsUTF8Unsafe() + // AsUTF8Unsafe() to compile on Windows
"\","
"\"parent\":{\"id\":\"" +
folder_id_ + "\"}}\r\n";
expected_body += multipart_boundary + "\r\n";
expected_body +=
"Content-Disposition: form-data; name=\"file\"; filename=\"";
expected_body +=
file_name_.AsUTF8Unsafe() + // AsUTF8Unsafe() to compile on Windows
"\"\r\nContent-Type: " + mime_type_ + "\r\n\r\n";
expected_body += file_content_ + "\r\n";
expected_body += multipart_boundary + "--\r\n";
return expected_body;
}
base::FilePath file_path_;
const std::string folder_id_{"13579"};
const std::string mime_type_{"text/plain"};
const base::FilePath file_name_{
FILE_PATH_LITERAL("box_whole_file_upload_test.txt")};
const std::string file_content_{"<TestContent>~~~123456789~~~</TestContent>"};
std::string file_id_;
base::ScopedTempDir temp_dir_;
base::OnceClosure quit_closure_;
base::WeakPtrFactory<BoxWholeFileUploadApiCallFlowTest> factory_{this};
};
TEST_F(BoxWholeFileUploadApiCallFlowTest, CreateApiCallUrl) {
GURL url(kFileSystemBoxDirectUploadUrl);
ASSERT_EQ(flow_->CreateApiCallUrl(), url);
}
TEST_F(BoxWholeFileUploadApiCallFlowTest, CreateApiCallBodyAndContentType) {
std::string content_type = flow_->CreateApiCallBodyContentType();
std::string expected_type("multipart/form-data; boundary=");
ASSERT_EQ(content_type.substr(0, expected_type.size()), expected_type);
flow_->SetFileReadForTesting(file_content_, mime_type_);
ASSERT_EQ(flow_->CreateApiCallBody(), MakeExpectedBody());
}
TEST_F(BoxWholeFileUploadApiCallFlowTest, IsExpectedSuccessCode) {
ASSERT_TRUE(flow_->IsExpectedSuccessCode(201));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(400));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(403));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(404));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(409));
}
TEST_F(BoxWholeFileUploadApiCallFlowTest, ProcessApiCallSuccess_EmptyBody) {
// Create a temporary file to be deleted in ProcessApiCallSuccess().
ASSERT_TRUE(base::WriteFile(file_path_, "BoxWholeFileUploadApiCallFlowTest"))
<< file_path_;
auto http_head = network::CreateURLResponseHead(net::HTTP_CREATED);
// Because we post tasks to base::ThreadPool, cannot use
// base::RunLoop().RunUntilIdle().
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
flow_->ProcessApiCallSuccess(http_head.get(),
std::make_unique<std::string>());
run_loop.Run();
ASSERT_EQ(response_code_, net::HTTP_CREATED);
ASSERT_TRUE(processed_success_) << "Failed with file " << file_path_;
ASSERT_TRUE(file_id_.empty());
ASSERT_EQ(BoxApiCallFlow::MakeUrlToShowFile(file_id_), GURL());
ASSERT_TRUE(base::PathExists(file_path_))
<< "File " << file_path_
<< " must still exist / not have been deleted by another thread so that "
"BoxUploader can delete it.";
}
TEST_F(BoxWholeFileUploadApiCallFlowTest, ProcessApiCallSuccess_ValidUrl) {
// Create a temporary file to be deleted in ProcessApiCallSuccess().
ASSERT_TRUE(base::WriteFile(file_path_, "BoxWholeFileUploadApiCallFlowTest"))
<< file_path_;
auto http_head = network::CreateURLResponseHead(net::HTTP_CREATED);
std::string body(kFileSystemBoxUploadResponseBody);
// Because we post tasks to base::ThreadPool, cannot use
// base::RunLoop().RunUntilIdle().
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
flow_->ProcessApiCallSuccess(http_head.get(),
std::make_unique<std::string>(body));
run_loop.Run();
ASSERT_EQ(response_code_, net::HTTP_CREATED);
ASSERT_TRUE(processed_success_) << "Failed with file " << file_path_;
ASSERT_FALSE(file_id_.empty());
ASSERT_EQ(BoxApiCallFlow::MakeUrlToShowFile(file_id_),
GURL(kFileSystemBoxUploadResponseFileUrl));
ASSERT_TRUE(base::PathExists(file_path_))
<< "File " << file_path_
<< " must still exist / not have been deleted by another thread so that "
"BoxUploader can delete it.";
}
TEST_F(BoxWholeFileUploadApiCallFlowTest,
ProcessApiCallSuccess_NoFileToDelete) {
auto http_head = network::CreateURLResponseHead(net::HTTP_CREATED);
ASSERT_FALSE(base::PathExists(file_path_)); // Make sure file doesn't exist.
// Because we post tasks to base::ThreadPool, cannot use
// base::RunLoop().RunUntilIdle().
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
flow_->ProcessApiCallSuccess(http_head.get(),
std::make_unique<std::string>());
// Empty placeholder body since we don't read from body for now.
run_loop.Run();
ASSERT_EQ(response_code_, net::HTTP_CREATED);
ASSERT_TRUE(processed_success_) << "API call flow success should not depend "
"on whether file exists to be deleted.";
ASSERT_FALSE(base::PathExists(file_path_)); // Make sure no file was created.
}
TEST_F(BoxWholeFileUploadApiCallFlowTest, ProcessApiCallFailure) {
auto http_head = network::CreateURLResponseHead(net::HTTP_CONFLICT);
std::unique_ptr<std::string> body = std::make_unique<std::string>(
CreateFailureResponse(net::HTTP_CONFLICT, "storage_limit_exceeded"));
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
flow_->ProcessApiCallFailure(net::OK, http_head.get(), std::move(body));
run_loop.Run();
ASSERT_FALSE(processed_success_);
ASSERT_EQ(response_code_, net::HTTP_CONFLICT);
ASSERT_EQ(box_error_code_, "storage_limit_exceeded");
ASSERT_EQ(box_request_id_, kFileSystemBoxClientErrorResponseRequestId);
}
class BoxWholeFileUploadApiCallFlowFileReadTest
: public BoxWholeFileUploadApiCallFlowTest {
public:
BoxWholeFileUploadApiCallFlowFileReadTest()
: url_factory_(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_)) {
test_url_loader_factory_.SetInterceptor(base::BindRepeating(
&BoxWholeFileUploadApiCallFlowFileReadTest::VerifyRequest,
base::Unretained(this)));
}
protected:
void VerifyRequest(const network::ResourceRequest& request) {
ASSERT_EQ(request.url, kFileSystemBoxDirectUploadUrl);
// Check that file was read and formatted into request body string properly,
// without going down the rabbit hole of request.request_body->elements()->
// front().As<network::DataElementBytes>().AsStringPiece().
ASSERT_EQ(flow_->CreateApiCallBody(), MakeExpectedBody());
++request_sent_count_;
}
size_t request_sent_count_ = 0;
network::TestURLLoaderFactory test_url_loader_factory_;
scoped_refptr<network::SharedURLLoaderFactory> url_factory_;
};
TEST_F(BoxWholeFileUploadApiCallFlowFileReadTest, GoodUpload) {
ASSERT_TRUE(base::WriteFile(file_path_, file_content_)) << file_path_;
test_url_loader_factory_.AddResponse(kFileSystemBoxDirectUploadUrl,
std::string(), net::HTTP_CREATED);
// Empty placeholder body since we don't read from body for now.
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
flow_->Start(url_factory_, "test_token");
run_loop.Run();
ASSERT_EQ(request_sent_count_, 1U);
ASSERT_EQ(response_code_, net::HTTP_CREATED);
ASSERT_TRUE(processed_success_) << "Failed with file " << file_path_;
ASSERT_TRUE(base::PathExists(file_path_))
<< "File " << file_path_
<< " must still exist / not have been deleted by another thread so that "
"BoxUploader can delete it.";
}
TEST_F(BoxWholeFileUploadApiCallFlowFileReadTest, NoFile) {
ASSERT_FALSE(base::PathExists(file_path_));
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
flow_->Start(url_factory_, "test_token");
run_loop.Run();
// Because file read already failed before any actual API calls are made,
// there should be no API calls made, and therefore no HTTP response code.
ASSERT_EQ(request_sent_count_, 0U);
ASSERT_EQ(response_code_, 0);
ASSERT_FALSE(processed_success_);
}
////////////////////////////////////////////////////////////////////////////////
// CreateUploadSession
////////////////////////////////////////////////////////////////////////////////
class BoxCreateUploadSessionApiCallFlowForTest
: public BoxCreateUploadSessionApiCallFlow {
public:
using BoxCreateUploadSessionApiCallFlow::BoxCreateUploadSessionApiCallFlow;
using BoxCreateUploadSessionApiCallFlow::CreateApiCallBody;
using BoxCreateUploadSessionApiCallFlow::CreateApiCallUrl;
using BoxCreateUploadSessionApiCallFlow::IsExpectedSuccessCode;
using BoxCreateUploadSessionApiCallFlow::ProcessApiCallFailure;
using BoxCreateUploadSessionApiCallFlow::ProcessApiCallSuccess;
};
class BoxCreateUploadSessionApiCallFlowTest
: public BoxApiCallFlowTest<BoxCreateUploadSessionApiCallFlowForTest> {
protected:
void SetUp() override {
flow_ = std::make_unique<BoxCreateUploadSessionApiCallFlowForTest>(
base::BindOnce(&BoxCreateUploadSessionApiCallFlowTest::OnResponse,
factory_.GetWeakPtr()),
/*folder_id = */ "13579", /*file_size = */ 60 * 1024 * 1024,
/*file_name = */
base::FilePath{FILE_PATH_LITERAL("box_chunked_upload_test.txt")});
}
void OnResponse(BoxApiCallResponse response,
base::Value session_endpoints,
size_t part_size) {
OnGenericResponse(response);
if (response.success) {
ASSERT_TRUE(session_endpoints.is_dict());
session_upload_endpoint_ =
session_endpoints.FindPath("upload_part")->GetString();
session_abort_endpoint_ =
session_endpoints.FindPath("abort")->GetString();
session_commit_endpoint_ =
session_endpoints.FindPath("commit")->GetString();
part_size_ = part_size;
}
if (quit_closure_)
std::move(quit_closure_).Run();
}
std::string session_upload_endpoint_;
std::string session_abort_endpoint_;
std::string session_commit_endpoint_;
size_t part_size_ = 0;
base::OnceClosure quit_closure_;
base::WeakPtrFactory<BoxCreateUploadSessionApiCallFlowTest> factory_{this};
};
TEST_F(BoxCreateUploadSessionApiCallFlowTest, CreateApiCallUrl) {
GURL url(kFileSystemBoxChunkedUploadCreateSessionUrl);
ASSERT_EQ(flow_->CreateApiCallUrl(), url);
}
TEST_F(BoxCreateUploadSessionApiCallFlowTest, IsExpectedSuccessCode) {
ASSERT_TRUE(flow_->IsExpectedSuccessCode(201));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(400));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(403));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(404));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(409));
}
TEST_F(BoxCreateUploadSessionApiCallFlowTest, CreateApiCallBody) {
std::string body = flow_->CreateApiCallBody();
std::string expected_body(R"({"file_name":"box_chunked_upload_test.txt")");
expected_body += R"(,"file_size":62914560,"folder_id":"13579"})";
ASSERT_EQ(body, expected_body);
}
TEST_F(BoxCreateUploadSessionApiCallFlowTest, ProcessApiCallSuccess) {
auto http_head = network::CreateURLResponseHead(net::HTTP_CREATED);
// Because we post tasks to base::ThreadPool, cannot use
// base::RunLoop().RunUntilIdle().
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
flow_->ProcessApiCallSuccess(
http_head.get(),
std::make_unique<std::string>(
kFileSystemBoxChunkedUploadCreateSessionResponseBody));
run_loop.Run();
ASSERT_EQ(response_code_, net::HTTP_CREATED);
ASSERT_TRUE(processed_success_);
EXPECT_EQ(session_upload_endpoint_, kFileSystemBoxChunkedUploadSessionUrl);
EXPECT_EQ(session_abort_endpoint_, kFileSystemBoxChunkedUploadSessionUrl);
EXPECT_EQ(session_commit_endpoint_, kFileSystemBoxChunkedUploadCommitUrl);
EXPECT_EQ(part_size_,
kFileSystemBoxChunkedUploadCreateSessionResponsePartSize);
}
TEST_F(BoxCreateUploadSessionApiCallFlowTest,
ProcessApiCallSuccess_ParseFailure) {
auto http_head = network::CreateURLResponseHead(net::HTTP_CREATED);
// Because we post tasks to base::ThreadPool, cannot use
// base::RunLoop().RunUntilIdle().
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
flow_->ProcessApiCallSuccess(
http_head.get(),
std::make_unique<std::string>()); // Empty body for parse failure.
run_loop.Run();
ASSERT_EQ(response_code_, net::HTTP_CREATED);
ASSERT_FALSE(processed_success_);
}
TEST_F(BoxCreateUploadSessionApiCallFlowTest,
ProcessApiCallSuccess_NoEndpoints) {
auto http_head = network::CreateURLResponseHead(net::HTTP_CREATED);
std::string body(R"({
"id": "F971964745A5CD0C001BBE4E58196BFD",
"type": "upload_session",
"session_expires_at": "2012-12-12T10:53:43-08:00",
"total_parts": 1000
})");
// Because we post tasks to base::ThreadPool, cannot use
// base::RunLoop().RunUntilIdle().
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
flow_->ProcessApiCallSuccess(http_head.get(),
std::make_unique<std::string>(body));
run_loop.Run();
ASSERT_EQ(response_code_, net::HTTP_CREATED);
ASSERT_FALSE(processed_success_);
}
TEST_F(BoxCreateUploadSessionApiCallFlowTest, ProcessApiCallFailure) {
auto http_head = network::CreateURLResponseHead(net::HTTP_BAD_REQUEST);
std::unique_ptr<std::string> body = std::make_unique<std::string>(
CreateFailureResponse(net::HTTP_BAD_REQUEST, "item_name_invalid"));
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
flow_->ProcessApiCallFailure(net::OK, http_head.get(), std::move(body));
run_loop.Run();
ASSERT_FALSE(processed_success_);
ASSERT_EQ(response_code_, net::HTTP_BAD_REQUEST);
ASSERT_EQ(box_error_code_, "item_name_invalid");
ASSERT_EQ(box_request_id_, kFileSystemBoxClientErrorResponseRequestId);
}
////////////////////////////////////////////////////////////////////////////////
// PartFileUpload
////////////////////////////////////////////////////////////////////////////////
class BoxPartFileUploadApiCallFlowForTest
: public BoxPartFileUploadApiCallFlow {
public:
using BoxPartFileUploadApiCallFlow::BoxPartFileUploadApiCallFlow;
using BoxPartFileUploadApiCallFlow::CreateApiCallBody;
using BoxPartFileUploadApiCallFlow::CreateApiCallBodyContentType;
using BoxPartFileUploadApiCallFlow::CreateApiCallHeaders;
using BoxPartFileUploadApiCallFlow::CreateApiCallUrl;
using BoxPartFileUploadApiCallFlow::CreateFileDigest;
using BoxPartFileUploadApiCallFlow::GetRequestTypeForBody;
using BoxPartFileUploadApiCallFlow::IsExpectedSuccessCode;
using BoxPartFileUploadApiCallFlow::ProcessApiCallFailure;
using BoxPartFileUploadApiCallFlow::ProcessApiCallSuccess;
};
class BoxPartFileUploadApiCallFlowTest
: public BoxApiCallFlowTest<BoxPartFileUploadApiCallFlowForTest> {
protected:
BoxPartFileUploadApiCallFlowTest()
: file_content_("random file part content"),
expected_sha_(
BoxPartFileUploadApiCallFlow::CreateFileDigest(file_content_)),
expected_content_range_(base::StringPrintf("bytes 0-%zu/%zu",
file_content_.size(),
file_content_.size())) {}
void SetUp() override {
flow_ = std::make_unique<BoxPartFileUploadApiCallFlowForTest>(
base::BindOnce(&BoxPartFileUploadApiCallFlowTest::OnResponse,
factory_.GetWeakPtr()),
kFileSystemBoxChunkedUploadSessionUrl, file_content_, 0,
file_content_.size(), file_content_.size());
}
void OnResponse(BoxApiCallResponse response, base::Value) {
OnGenericResponse(response);
}
const std::string file_content_;
const std::string expected_sha_;
const std::string expected_content_range_;
base::WeakPtrFactory<BoxPartFileUploadApiCallFlowTest> factory_{this};
};
TEST_F(BoxPartFileUploadApiCallFlowTest, CreateApiCallUrl) {
ASSERT_EQ(flow_->CreateApiCallUrl(), kFileSystemBoxChunkedUploadSessionUrl);
}
TEST_F(BoxPartFileUploadApiCallFlowTest, CreateApiCallHeaders) {
net::HttpRequestHeaders headers = flow_->CreateApiCallHeaders();
std::string digest;
headers.GetHeader("digest", &digest);
base::Base64Decode(digest, &digest);
EXPECT_EQ(digest, expected_sha_);
std::string content_range;
headers.GetHeader("content-range", &content_range);
EXPECT_EQ(content_range, expected_content_range_) << headers.ToString();
}
TEST_F(BoxPartFileUploadApiCallFlowTest, CreateApiCallBody) {
std::string body = flow_->CreateApiCallBody();
ASSERT_EQ(body, file_content_);
}
TEST_F(BoxPartFileUploadApiCallFlowTest, CreateApiCallBodyContentType) {
std::string type = flow_->CreateApiCallBodyContentType();
ASSERT_EQ(type, "application/octet-stream");
}
TEST_F(BoxPartFileUploadApiCallFlowTest, GetRequestTypeForBody) {
std::string type = flow_->GetRequestTypeForBody(file_content_);
ASSERT_EQ(type, "PUT");
}
TEST_F(BoxPartFileUploadApiCallFlowTest, IsExpectedSuccessCode) {
ASSERT_TRUE(flow_->IsExpectedSuccessCode(200));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(201));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(202));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(400));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(404));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(409));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(412));
}
TEST_F(BoxPartFileUploadApiCallFlowTest, ProcessApiCallSuccess) {
auto http_head = network::CreateURLResponseHead(net::HTTP_OK);
std::string body(R"({
"part": {
"offset": 16777216,
"part_id": "6F2D3486",
"sha1": "134b65991ed521fcfe4724b7d814ab8ded5185dc",
"size": 3222784
}
})");
flow_->ProcessApiCallSuccess(http_head.get(),
std::make_unique<std::string>(body));
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(processed_success_);
ASSERT_EQ(response_code_, net::HTTP_OK);
}
TEST_F(BoxPartFileUploadApiCallFlowTest, ProcessApiCallSuccess_ParseError) {
auto http_head = network::CreateURLResponseHead(net::HTTP_OK);
flow_->ProcessApiCallSuccess(http_head.get(),
std::make_unique<std::string>());
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(processed_success_);
ASSERT_EQ(response_code_, net::HTTP_OK);
}
TEST_F(BoxPartFileUploadApiCallFlowTest, ProcessApiCallSuccess_EmptyResponse) {
auto http_head = network::CreateURLResponseHead(net::HTTP_OK);
flow_->ProcessApiCallSuccess(
http_head.get(), std::make_unique<std::string>(kEmptyResponseBody));
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(processed_success_);
ASSERT_EQ(response_code_, net::HTTP_OK);
}
TEST_F(BoxPartFileUploadApiCallFlowTest, ProcessApiCallFailure) {
auto http_head = network::CreateURLResponseHead(net::HTTP_CONFLICT);
std::unique_ptr<std::string> body = std::make_unique<std::string>(
CreateFailureResponse(net::HTTP_CONFLICT, "name_in_use"));
flow_->ProcessApiCallFailure(net::OK, http_head.get(), std::move(body));
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(processed_success_);
ASSERT_EQ(response_code_, net::HTTP_CONFLICT);
ASSERT_EQ(box_error_code_, "name_in_use");
ASSERT_EQ(box_request_id_, kFileSystemBoxClientErrorResponseRequestId);
}
////////////////////////////////////////////////////////////////////////////////
// AbortUploadSession
////////////////////////////////////////////////////////////////////////////////
class BoxAbortUploadSessionApiCallFlowForTest
: public BoxAbortUploadSessionApiCallFlow {
public:
using BoxAbortUploadSessionApiCallFlow::BoxAbortUploadSessionApiCallFlow;
using BoxAbortUploadSessionApiCallFlow::CreateApiCallBody;
using BoxAbortUploadSessionApiCallFlow::CreateApiCallUrl;
using BoxAbortUploadSessionApiCallFlow::GetRequestTypeForBody;
using BoxAbortUploadSessionApiCallFlow::IsExpectedSuccessCode;
using BoxAbortUploadSessionApiCallFlow::ProcessApiCallFailure;
using BoxAbortUploadSessionApiCallFlow::ProcessApiCallSuccess;
};
class BoxAbortUploadSessionApiCallFlowTest
: public BoxApiCallFlowTest<BoxAbortUploadSessionApiCallFlowForTest> {
void SetUp() override {
flow_ = std::make_unique<BoxAbortUploadSessionApiCallFlowForTest>(
base::BindOnce(&BoxAbortUploadSessionApiCallFlowTest::OnGenericResponse,
factory_.GetWeakPtr()),
kFileSystemBoxChunkedUploadSessionUrl);
}
base::WeakPtrFactory<BoxAbortUploadSessionApiCallFlowTest> factory_{this};
};
TEST_F(BoxAbortUploadSessionApiCallFlowTest, CreateApiCallUrl) {
ASSERT_EQ(flow_->CreateApiCallUrl(), kFileSystemBoxChunkedUploadSessionUrl);
}
TEST_F(BoxAbortUploadSessionApiCallFlowTest, CreateApiCallBody) {
ASSERT_TRUE(flow_->CreateApiCallBody().empty());
}
TEST_F(BoxAbortUploadSessionApiCallFlowTest, GetRequestTypeForBody) {
ASSERT_EQ(flow_->GetRequestTypeForBody(std::string()), "DELETE");
}
TEST_F(BoxAbortUploadSessionApiCallFlowTest, IsExpectedSuccessCode) {
ASSERT_TRUE(flow_->IsExpectedSuccessCode(204));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(200));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(400));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(404));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(409));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(412));
}
TEST_F(BoxAbortUploadSessionApiCallFlowTest, ProcessApiCallSuccess) {
auto http_head = network::CreateURLResponseHead(net::HTTP_NO_CONTENT);
flow_->ProcessApiCallSuccess(http_head.get(), {});
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(processed_success_);
ASSERT_EQ(response_code_, net::HTTP_NO_CONTENT);
}
TEST_F(BoxAbortUploadSessionApiCallFlowTest, ProcessApiCallFailure) {
auto http_head = network::CreateURLResponseHead(net::HTTP_CONFLICT);
std::unique_ptr<std::string> body = std::make_unique<std::string>(
CreateFailureResponse(net::HTTP_CONFLICT, "operation_blocked_temporary"));
flow_->ProcessApiCallFailure(net::OK, http_head.get(), std::move(body));
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(processed_success_);
ASSERT_EQ(response_code_, net::HTTP_CONFLICT);
ASSERT_EQ(box_error_code_, "operation_blocked_temporary");
ASSERT_EQ(box_request_id_, kFileSystemBoxClientErrorResponseRequestId);
}
////////////////////////////////////////////////////////////////////////////////
// CommitUploadSession
////////////////////////////////////////////////////////////////////////////////
class BoxCommitUploadSessionApiCallFlowForTest
: public BoxCommitUploadSessionApiCallFlow {
public:
using BoxCommitUploadSessionApiCallFlow::BoxCommitUploadSessionApiCallFlow;
using BoxCommitUploadSessionApiCallFlow::CreateApiCallBody;
using BoxCommitUploadSessionApiCallFlow::CreateApiCallHeaders;
using BoxCommitUploadSessionApiCallFlow::CreateApiCallUrl;
using BoxCommitUploadSessionApiCallFlow::IsExpectedSuccessCode;
using BoxCommitUploadSessionApiCallFlow::ProcessApiCallFailure;
using BoxCommitUploadSessionApiCallFlow::ProcessApiCallSuccess;
};
class BoxCommitUploadSessionApiCallFlowTest
: public BoxApiCallFlowTest<BoxCommitUploadSessionApiCallFlowForTest> {
protected:
BoxCommitUploadSessionApiCallFlowTest()
: upload_session_parts_(base::Value::Type::LIST) {
base::Value part1(base::Value::Type::DICTIONARY);
part1.SetStringKey("part_id", "BFDF5379");
part1.SetIntKey("offset", 0);
part1.SetIntKey("size", 8388608);
part1.SetStringKey("sha1", "134b65991ed521fcfe4724b7d814ab8ded5185dc");
base::Value part2(base::Value::Type::DICTIONARY);
part2.SetStringKey("part_id", "E8A3ED8E");
part2.SetIntKey("offset", 8388608);
part2.SetIntKey("size", 1611392);
part2.SetStringKey("sha1", "234b65934ed521fcfe3424b7d814ab8ded5185dc");
upload_session_parts_.Append(std::move(part1));
upload_session_parts_.Append(std::move(part2));
base::Value parts_body(base::Value::Type::DICTIONARY);
parts_body.SetKey("parts", upload_session_parts_.Clone());
// The request body should be in the form of "parts": [list of parts], but
// only the list is passed into the class.
base::JSONWriter::Write(parts_body, &expected_body_);
}
void SetUp() override {
flow_ = std::make_unique<BoxCommitUploadSessionApiCallFlowForTest>(
base::BindOnce(&BoxCommitUploadSessionApiCallFlowTest::OnResponse,
factory_.GetWeakPtr()),
kFileSystemBoxChunkedUploadCommitUrl, upload_session_parts_,
kFileSystemBoxChunkedUploadSha);
}
void OnResponse(BoxApiCallResponse response,
base::TimeDelta retry_after,
const std::string& file_id) {
OnGenericResponse(response);
retry_after_ = retry_after;
file_id_ = file_id;
if (quit_closure_)
std::move(quit_closure_).Run();
}
base::Value upload_session_parts_;
std::string expected_body_;
base::TimeDelta retry_after_;
std::string file_id_;
base::OnceClosure quit_closure_;
base::WeakPtrFactory<BoxCommitUploadSessionApiCallFlowTest> factory_{this};
};
TEST_F(BoxCommitUploadSessionApiCallFlowTest, CreateApiCallUrl) {
ASSERT_EQ(flow_->CreateApiCallUrl(), kFileSystemBoxChunkedUploadCommitUrl);
}
TEST_F(BoxCommitUploadSessionApiCallFlowTest, CreateApiCallHeaders) {
net::HttpRequestHeaders headers = flow_->CreateApiCallHeaders();
std::string digest;
headers.GetHeader("digest", &digest);
ASSERT_EQ(digest,
BoxApiCallFlow::FormatSHA1Digest(kFileSystemBoxChunkedUploadSha));
}
TEST_F(BoxCommitUploadSessionApiCallFlowTest, CreateApiCallBody) {
std::string body = flow_->CreateApiCallBody();
ASSERT_EQ(body, expected_body_);
}
TEST_F(BoxCommitUploadSessionApiCallFlowTest, IsExpectedSuccessCode) {
ASSERT_TRUE(flow_->IsExpectedSuccessCode(201));
ASSERT_TRUE(flow_->IsExpectedSuccessCode(202));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(400));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(404));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(409));
ASSERT_FALSE(flow_->IsExpectedSuccessCode(412));
}
TEST_F(BoxCommitUploadSessionApiCallFlowTest, ProcessApiCallFailure) {
auto http_head = network::CreateURLResponseHead(net::HTTP_GONE);
std::unique_ptr<std::string> body = std::make_unique<std::string>(
CreateFailureResponse(net::HTTP_GONE, "session_expired"));
flow_->ProcessApiCallFailure(net::OK, http_head.get(), std::move(body));
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(processed_success_);
ASSERT_EQ(response_code_, net::HTTP_GONE);
ASSERT_EQ(box_error_code_, "session_expired");
ASSERT_EQ(box_request_id_, kFileSystemBoxClientErrorResponseRequestId);
ASSERT_EQ(retry_after_, base::TimeDelta());
}
TEST_F(BoxCommitUploadSessionApiCallFlowTest, ProcessApiCallSuccess_Retry) {
auto http_head = network::CreateURLResponseHead(net::HTTP_ACCEPTED);
http_head->headers->AddHeader("Retry-After", "120");
flow_->ProcessApiCallSuccess(http_head.get(), {});
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(processed_success_);
ASSERT_EQ(response_code_, net::HTTP_ACCEPTED);
ASSERT_EQ(retry_after_, base::TimeDelta::FromSeconds(120));
}
TEST_F(BoxCommitUploadSessionApiCallFlowTest, ProcessApiCallSuccess_Created) {
auto http_head = network::CreateURLResponseHead(net::HTTP_CREATED);
std::string body(kFileSystemBoxUploadResponseBody);
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
flow_->ProcessApiCallSuccess(http_head.get(),
std::make_unique<std::string>(body));
run_loop.Run();
ASSERT_TRUE(processed_success_);
ASSERT_EQ(response_code_, net::HTTP_CREATED);
ASSERT_EQ(retry_after_, base::TimeDelta::FromSeconds(0));
ASSERT_FALSE(file_id_.empty());
ASSERT_EQ(BoxApiCallFlow::MakeUrlToShowFile(file_id_),
GURL(kFileSystemBoxUploadResponseFileUrl));
}
} // namespace enterprise_connectors