blob: 71980018ffa2564c80188c19baced1d4cb1f3d75 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/supervised_user/core/browser/kids_external_fetcher.h"
#include <memory>
#include <string>
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "base/types/expected.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "components/supervised_user/core/browser/fetcher_config_test_utils.h"
#include "components/supervised_user/core/browser/kids_external_fetcher_config.h"
#include "components/supervised_user/core/browser/proto/kidschromemanagement_messages.pb.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "net/http/http_status_code.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.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 {
using ::base::BindOnce;
using ::base::Time;
using ::kids_chrome_management::FamilyRole;
using ::kids_chrome_management::ListFamilyMembersRequest;
using ::kids_chrome_management::ListFamilyMembersResponse;
using ::network::GetUploadData;
using ::network::TestURLLoaderFactory;
using ::signin::ConsentLevel;
using ::signin::IdentityTestEnvironment;
using ::supervised_user::FetcherConfig;
using ::supervised_user::FetcherTestConfigBuilder;
using ::testing::Test;
// Tests the Kids External API fetchers functionality.
class KidsExternalFetcherTest : public Test {
protected:
FetcherConfig test_fetcher_config_ =
FetcherTestConfigBuilder::FromConfig(
supervised_user::kListFamilyMembersConfig)
.WithServiceEndpoint("http://example.com")
.Build();
network::TestURLLoaderFactory test_url_loader_factory_;
base::test::TaskEnvironment task_environment_;
IdentityTestEnvironment identity_test_env_;
};
template <typename Request, typename Response>
class Receiver {
public:
const base::expected<std::unique_ptr<Response>, KidsExternalFetcherStatus>&
GetResult() const {
return result_;
}
void Receive(KidsExternalFetcherStatus fetch_status,
std::unique_ptr<Response> response) {
if (!fetch_status.IsOk()) {
result_ = base::unexpected(fetch_status);
return;
}
result_ = std::move(response);
}
private:
base::expected<std::unique_ptr<Response>, KidsExternalFetcherStatus> result_;
};
TEST_F(KidsExternalFetcherTest, AcceptsRequests) {
AccountInfo account = identity_test_env_.MakePrimaryAccountAvailable(
"bob@gmail.com", ConsentLevel::kSignin);
Receiver<ListFamilyMembersRequest, ListFamilyMembersResponse> receiver;
ListFamilyMembersResponse response;
auto fetcher = FetchListFamilyMembers(
*identity_test_env_.identity_manager(),
test_url_loader_factory_.GetSafeWeakWrapper(),
BindOnce(&Receiver<ListFamilyMembersRequest,
ListFamilyMembersResponse>::Receive,
base::Unretained(&receiver)),
test_fetcher_config_);
identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
"access_token", Time::Max());
TestURLLoaderFactory::PendingRequest* pending_request =
test_url_loader_factory_.GetPendingRequest(0);
EXPECT_EQ(pending_request->request.url,
"http://example.com/families/mine/members?alt=proto");
EXPECT_EQ(pending_request->request.method, "GET");
test_url_loader_factory_.SimulateResponseForPendingRequest(
pending_request->request.url.spec(), response.SerializeAsString());
ASSERT_TRUE(receiver.GetResult().has_value());
}
TEST_F(KidsExternalFetcherTest, NoAccessToken) {
AccountInfo account = identity_test_env_.MakePrimaryAccountAvailable(
"bob@gmail.com", ConsentLevel::kSignin);
Receiver<ListFamilyMembersRequest, ListFamilyMembersResponse> receiver;
auto fetcher = FetchListFamilyMembers(
*identity_test_env_.identity_manager(),
test_url_loader_factory_.GetSafeWeakWrapper(),
BindOnce(&Receiver<ListFamilyMembersRequest,
ListFamilyMembersResponse>::Receive,
base::Unretained(&receiver)),
test_fetcher_config_);
identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
GoogleServiceAuthError(
GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS));
EXPECT_EQ(test_url_loader_factory_.NumPending(), 0);
EXPECT_EQ(receiver.GetResult().error().state(),
KidsExternalFetcherStatus::State::GOOGLE_SERVICE_AUTH_ERROR);
EXPECT_EQ(receiver.GetResult().error().google_service_auth_error().state(),
GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS);
}
TEST_F(KidsExternalFetcherTest, HandlesMalformedResponse) {
AccountInfo account = identity_test_env_.MakePrimaryAccountAvailable(
"bob@gmail.com", ConsentLevel::kSignin);
Receiver<ListFamilyMembersRequest, ListFamilyMembersResponse> receiver;
auto fetcher = FetchListFamilyMembers(
*identity_test_env_.identity_manager(),
test_url_loader_factory_.GetSafeWeakWrapper(),
BindOnce(&Receiver<ListFamilyMembersRequest,
ListFamilyMembersResponse>::Receive,
base::Unretained(&receiver)),
test_fetcher_config_);
identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
"access_token", Time::Max());
TestURLLoaderFactory::PendingRequest* pending_request =
test_url_loader_factory_.GetPendingRequest(0);
ASSERT_NE(nullptr, pending_request);
EXPECT_EQ(pending_request->request.url,
"http://example.com/families/mine/members?alt=proto");
EXPECT_EQ(pending_request->request.method, "GET");
std::string malformed_value("garbage"); // Not a valid marshaled proto.
test_url_loader_factory_.SimulateResponseForPendingRequest(
pending_request->request.url.spec(), malformed_value);
EXPECT_FALSE(receiver.GetResult().has_value());
EXPECT_EQ(receiver.GetResult().error().state(),
KidsExternalFetcherStatus::State::INVALID_RESPONSE);
}
TEST_F(KidsExternalFetcherTest, HandlesServerError) {
AccountInfo account = identity_test_env_.MakePrimaryAccountAvailable(
"bob@gmail.com", ConsentLevel::kSignin);
Receiver<ListFamilyMembersRequest, ListFamilyMembersResponse> receiver;
auto fetcher = FetchListFamilyMembers(
*identity_test_env_.identity_manager(),
test_url_loader_factory_.GetSafeWeakWrapper(),
BindOnce(&Receiver<ListFamilyMembersRequest,
ListFamilyMembersResponse>::Receive,
base::Unretained(&receiver)),
test_fetcher_config_);
identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
"access_token", Time::Max());
TestURLLoaderFactory::PendingRequest* pending_request =
test_url_loader_factory_.GetPendingRequest(0);
ASSERT_NE(nullptr, pending_request);
EXPECT_EQ(pending_request->request.url,
"http://example.com/families/mine/members?alt=proto");
EXPECT_EQ(pending_request->request.method, "GET");
test_url_loader_factory_.SimulateResponseForPendingRequest(
pending_request->request.url.spec(), /*content=*/"",
net::HTTP_BAD_REQUEST);
EXPECT_FALSE(receiver.GetResult().has_value());
EXPECT_EQ(receiver.GetResult().error().state(),
KidsExternalFetcherStatus::State::NET_OR_HTTP_ERROR);
EXPECT_EQ(receiver.GetResult().error().net_or_http_error_code(),
KidsExternalFetcherStatus::NetOrHttpErrorType(
net::ERR_HTTP_RESPONSE_CODE_FAILURE));
}
} // namespace