// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "google_apis/tasks/tasks_api_requests.h"
#include <memory>
#include "base/command_line.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/gmock_expected_support.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/types/expected.h"
#include "google_apis/common/api_error_codes.h"
#include "google_apis/common/dummy_auth_service.h"
#include "google_apis/common/request_sender.h"
#include "google_apis/common/test_util.h"
#include "google_apis/gaia/gaia_urls.h"
#include "google_apis/gaia/gaia_urls_overrider_for_testing.h"
#include "google_apis/tasks/tasks_api_request_types.h"
#include "google_apis/tasks/tasks_api_response_types.h"
#include "google_apis/tasks/tasks_api_task_status.h"
#include "google_apis/tasks/tasks_api_url_generator_utils.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/network/test/test_shared_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace google_apis::tasks {
namespace {
constexpr char kTaskListId[] = "random-task-list-id";
constexpr char kTaskId[] = "random-task-id";
} // namespace
class TasksApiRequestsTest : public testing::Test {
public:
TasksApiRequestsTest()
: test_shared_loader_factory_(
base::MakeRefCounted<network::TestSharedURLLoaderFactory>(
/*network_service=*/nullptr,
/*is_trusted=*/true)) {}
void SetUp() override {
request_sender_ = std::make_unique<RequestSender>(
std::make_unique<DummyAuthService>(), test_shared_loader_factory_,
task_environment_.GetMainThreadTaskRunner(), "test-user-agent",
TRAFFIC_ANNOTATION_FOR_TESTS);
test_server_.RegisterRequestHandler(base::BindRepeating(
&TasksApiRequestsTest::HandleDataFileRequest, base::Unretained(this)));
ASSERT_TRUE(test_server_.Start());
gaia_urls_overrider_ = std::make_unique<GaiaUrlsOverriderForTesting>(
base::CommandLine::ForCurrentProcess(), "tasks_api_origin_url",
test_server_.base_url().spec());
ASSERT_EQ(GaiaUrls::GetInstance()->tasks_api_origin_url(),
test_server_.base_url().spec());
}
RequestSender* request_sender() { return request_sender_.get(); }
net::test_server::HttpRequest last_request() { return last_request_; }
void set_test_file_path(const std::string& test_file_path) {
test_file_path_ = test_file_path;
}
private:
std::unique_ptr<net::test_server::HttpResponse> HandleDataFileRequest(
const net::test_server::HttpRequest& request) {
last_request_ = request;
return test_util::CreateHttpResponseFromFile(
test_util::GetTestFilePath(test_file_path_));
}
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::MainThreadType::IO};
net::EmbeddedTestServer test_server_;
std::unique_ptr<RequestSender> request_sender_;
scoped_refptr<network::TestSharedURLLoaderFactory>
test_shared_loader_factory_;
std::unique_ptr<GaiaUrlsOverriderForTesting> gaia_urls_overrider_;
net::test_server::HttpRequest last_request_;
std::string test_file_path_;
};
TEST_F(TasksApiRequestsTest, ListTaskListsRequest) {
set_test_file_path("tasks/task_lists.json");
base::test::TestFuture<
base::expected<std::unique_ptr<TaskLists>, ApiErrorCode>>
future;
auto request = std::make_unique<ListTaskListsRequest>(
request_sender(), /*page_token=*/"", future.GetCallback());
request_sender()->StartRequestWithAuthRetry(std::move(request));
ASSERT_TRUE(future.Wait());
EXPECT_TRUE(future.Get().has_value());
EXPECT_EQ(last_request().method, net::test_server::METHOD_GET);
EXPECT_EQ(last_request().GetURL(),
GetListTaskListsUrl(/*max_results=*/100, /*page_token=*/""));
EXPECT_TRUE(future.Get().value());
EXPECT_EQ(future.Get().value()->items().size(), 2u);
}
TEST_F(TasksApiRequestsTest, ListTaskListsWithOptionalArgsRequest) {
set_test_file_path("tasks/task_lists.json");
base::test::TestFuture<
base::expected<std::unique_ptr<TaskLists>, ApiErrorCode>>
future;
auto request = std::make_unique<ListTaskListsRequest>(
request_sender(), /*page_token=*/"qwerty", future.GetCallback());
request_sender()->StartRequestWithAuthRetry(std::move(request));
ASSERT_TRUE(future.Wait());
EXPECT_TRUE(future.Get().has_value());
EXPECT_EQ(last_request().method, net::test_server::METHOD_GET);
EXPECT_EQ(last_request().GetURL(),
GetListTaskListsUrl(/*max_results=*/100, /*page_token=*/"qwerty"));
EXPECT_TRUE(future.Get().value());
EXPECT_EQ(future.Get().value()->items().size(), 2u);
}
TEST_F(TasksApiRequestsTest, ListTaskListsRequestHandlesError) {
set_test_file_path("tasks/invalid_file_to_simulate_404_error.json");
base::test::TestFuture<
base::expected<std::unique_ptr<TaskLists>, ApiErrorCode>>
future;
auto request = std::make_unique<ListTaskListsRequest>(request_sender(),
/*page_token=*/"",
future.GetCallback());
request_sender()->StartRequestWithAuthRetry(std::move(request));
ASSERT_TRUE(future.Wait());
EXPECT_THAT(future.Get(), base::test::ErrorIs(HTTP_NOT_FOUND));
}
TEST_F(TasksApiRequestsTest, ListTasksRequest) {
set_test_file_path("tasks/tasks.json");
base::test::TestFuture<base::expected<std::unique_ptr<Tasks>, ApiErrorCode>>
future;
auto request = std::make_unique<ListTasksRequest>(
request_sender(), kTaskListId, /*page_token=*/"",
/*include_assigned=*/false, future.GetCallback());
request_sender()->StartRequestWithAuthRetry(std::move(request));
ASSERT_TRUE(future.Wait());
EXPECT_TRUE(future.Get().has_value());
EXPECT_EQ(last_request().method, net::test_server::METHOD_GET);
EXPECT_EQ(last_request().GetURL(),
GetListTasksUrl(kTaskListId, /*include_completed=*/false,
/*include_assigned=*/false,
/*max_results=*/100,
/*page_token=*/""));
EXPECT_TRUE(future.Get().value());
EXPECT_EQ(future.Get().value()->items().size(), 2u);
}
TEST_F(TasksApiRequestsTest, ListTasksWithOptionalArgsRequest) {
set_test_file_path("tasks/tasks.json");
base::test::TestFuture<base::expected<std::unique_ptr<Tasks>, ApiErrorCode>>
future;
auto request = std::make_unique<ListTasksRequest>(
request_sender(), kTaskListId, /*page_token=*/"qwerty",
/*include_assigned=*/false, future.GetCallback());
request_sender()->StartRequestWithAuthRetry(std::move(request));
ASSERT_TRUE(future.Wait());
EXPECT_TRUE(future.Get().has_value());
EXPECT_EQ(last_request().method, net::test_server::METHOD_GET);
EXPECT_EQ(last_request().GetURL(),
GetListTasksUrl(kTaskListId, /*include_completed=*/false,
/*include_assigned=*/false,
/*max_results=*/100,
/*page_token=*/"qwerty"));
EXPECT_TRUE(future.Get().value());
EXPECT_EQ(future.Get().value()->items().size(), 2u);
}
TEST_F(TasksApiRequestsTest, ListTasksRequestHandlesError) {
set_test_file_path("tasks/invalid_file_to_simulate_404_error.json");
base::test::TestFuture<base::expected<std::unique_ptr<Tasks>, ApiErrorCode>>
future;
auto request = std::make_unique<ListTasksRequest>(
request_sender(), kTaskListId, /*page_token=*/"",
/*include_assigned=*/false, future.GetCallback());
request_sender()->StartRequestWithAuthRetry(std::move(request));
ASSERT_TRUE(future.Wait());
EXPECT_THAT(future.Get(), base::test::ErrorIs(HTTP_NOT_FOUND));
}
TEST_F(TasksApiRequestsTest, PatchTaskRequest) {
set_test_file_path("tasks/task.json");
base::test::TestFuture<base::expected<std::unique_ptr<Task>, ApiErrorCode>>
future;
auto request = std::make_unique<PatchTaskRequest>(
request_sender(), kTaskListId, kTaskId,
TaskRequestPayload{.status = TaskStatus::kCompleted},
future.GetCallback());
request_sender()->StartRequestWithAuthRetry(std::move(request));
ASSERT_TRUE(future.Wait());
EXPECT_TRUE(future.Get().has_value());
EXPECT_EQ(last_request().method, net::test_server::METHOD_PATCH);
EXPECT_EQ(last_request().GetURL(), GetPatchTaskUrl(kTaskListId, kTaskId));
EXPECT_EQ(last_request().headers.at("Content-Type"),
"application/json; charset=utf-8");
EXPECT_EQ(last_request().content, "{\"status\":\"completed\"}");
}
TEST_F(TasksApiRequestsTest, PatchTaskRequestHandlesError) {
set_test_file_path("tasks/invalid_file_to_simulate_404_error.json");
base::test::TestFuture<base::expected<std::unique_ptr<Task>, ApiErrorCode>>
future;
auto request = std::make_unique<PatchTaskRequest>(
request_sender(), kTaskListId, kTaskId,
TaskRequestPayload{.status = TaskStatus::kCompleted},
future.GetCallback());
request_sender()->StartRequestWithAuthRetry(std::move(request));
ASSERT_TRUE(future.Wait());
EXPECT_THAT(future.Get(), base::test::ErrorIs(HTTP_NOT_FOUND));
}
TEST_F(TasksApiRequestsTest, InsertTaskRequest) {
set_test_file_path("tasks/task.json");
base::test::TestFuture<base::expected<std::unique_ptr<Task>, ApiErrorCode>>
future;
auto request = std::make_unique<InsertTaskRequest>(
request_sender(), kTaskListId, /*previous_task_id=*/"",
TaskRequestPayload{.title = "New task",
.status = TaskStatus::kNeedsAction},
future.GetCallback());
request_sender()->StartRequestWithAuthRetry(std::move(request));
ASSERT_TRUE(future.Wait());
EXPECT_EQ(last_request().method, net::test_server::METHOD_POST);
EXPECT_EQ(last_request().GetURL(),
GetInsertTaskUrl(kTaskListId, /*previous_task_id=*/""));
EXPECT_EQ(last_request().headers.at("Content-Type"),
"application/json; charset=utf-8");
EXPECT_EQ(last_request().content,
"{\"status\":\"needsAction\",\"title\":\"New task\"}");
ASSERT_TRUE(future.Get().has_value());
EXPECT_EQ(future.Get().value()->id(), "qwe");
}
TEST_F(TasksApiRequestsTest, InsertTaskRequestHandlesError) {
set_test_file_path("tasks/invalid_file_to_simulate_404_error.json");
base::test::TestFuture<base::expected<std::unique_ptr<Task>, ApiErrorCode>>
future;
auto request = std::make_unique<InsertTaskRequest>(
request_sender(), kTaskListId, /*previous_task_id=*/"",
TaskRequestPayload{.title = "New task",
.status = TaskStatus::kNeedsAction},
future.GetCallback());
request_sender()->StartRequestWithAuthRetry(std::move(request));
ASSERT_TRUE(future.Wait());
EXPECT_THAT(future.Get(), base::test::ErrorIs(HTTP_NOT_FOUND));
}
} // namespace google_apis::tasks