blob: 3d2a7b3bc14fba8033e4c8824e7239012fb85f55 [file] [log] [blame]
// 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 "components/data_sharing/internal/data_sharing_service_impl.h"
#include <memory>
#include <optional>
#include "base/files/scoped_temp_dir.h"
#include "base/memory/raw_ptr.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "base/version_info/channel.h"
#include "components/data_sharing/public/data_sharing_sdk_delegate.h"
#include "components/data_sharing/public/data_sharing_service.h"
#include "components/data_sharing/public/data_sharing_ui_delegate.h"
#include "components/data_sharing/public/features.h"
#include "components/data_sharing/public/group_data.h"
#include "components/data_sharing/public/protocol/data_sharing_sdk.pb.h"
#include "components/data_sharing/public/protocol/group_data.pb.h"
#include "components/data_sharing/test_support/fake_data_sharing_sdk_delegate.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "components/sync/model/entity_change.h"
#include "components/sync/model/metadata_change_list.h"
#include "components/sync/protocol/collaboration_group_specifics.pb.h"
#include "components/sync/test/data_type_store_test_util.h"
#include "google_apis/gaia/gaia_id.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/status/status.h"
namespace data_sharing {
namespace {
using base::test::RunClosure;
using testing::Eq;
const char kGroupId[] = "/?-group_id";
const char kEncodedGroupId[] = "%2F%3F-group_id";
const char kTokenBlob[] = "/?-_token";
const char kEncodedTokenBlob[] = "%2F%3F-_token";
// Enum cases for parameterizing the tests.
enum Variant {
kDelegateAtCreation,
kDelegateAfter,
};
} // namespace
class DataSharingServiceImplTest : public ::testing::TestWithParam<Variant> {
public:
DataSharingServiceImplTest() = default;
~DataSharingServiceImplTest() override { task_environment_.RunUntilIdle(); }
void SetUp() override {
EXPECT_TRUE(profile_dir_.CreateUniqueTempDir());
scoped_refptr<network::SharedURLLoaderFactory> test_url_loader_factory =
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_);
std::unique_ptr<FakeDataSharingSDKDelegate> sdk_delegate;
if (!ShouldSetSDKLater()) {
sdk_delegate = std::make_unique<FakeDataSharingSDKDelegate>();
not_owned_sdk_delegate_ = sdk_delegate.get();
}
data_sharing_service_ = std::make_unique<DataSharingServiceImpl>(
profile_dir_.GetPath(), std::move(test_url_loader_factory),
identity_test_env_.identity_manager(),
syncer::DataTypeStoreTestUtil::FactoryForInMemoryStoreForTest(),
version_info::Channel::UNKNOWN, std::move(sdk_delegate),
/*ui_delegate=*/nullptr);
if (ShouldSetSDKLater()) {
sdk_delegate = std::make_unique<FakeDataSharingSDKDelegate>();
not_owned_sdk_delegate_ = sdk_delegate.get();
data_sharing_service_->SetSDKDelegate(std::move(sdk_delegate));
}
}
protected:
// Returns whether the SDK delegate should be set after the DataSharingService
// creation.
bool ShouldSetSDKLater() {
switch (GetParam()) {
case kDelegateAtCreation:
return false;
case kDelegateAfter:
return true;
}
}
base::test::TaskEnvironment task_environment_;
base::ScopedTempDir profile_dir_;
signin::IdentityTestEnvironment identity_test_env_;
network::TestURLLoaderFactory test_url_loader_factory_;
std::unique_ptr<DataSharingServiceImpl> data_sharing_service_;
raw_ptr<FakeDataSharingSDKDelegate> not_owned_sdk_delegate_;
};
TEST_P(DataSharingServiceImplTest, ConstructionAndEmptyServiceCheck) {
EXPECT_FALSE(data_sharing_service_->IsEmptyService());
}
TEST_P(DataSharingServiceImplTest, GetDataSharingNetworkLoader) {
EXPECT_TRUE(data_sharing_service_->GetDataSharingNetworkLoader());
}
TEST_P(DataSharingServiceImplTest, ShouldCreateGroup) {
// TODO(crbug.com/301390275): add a version of this test for unhappy path.
const std::string display_name = "display_name";
DataSharingService::GroupDataOrFailureOutcome outcome;
base::RunLoop run_loop;
data_sharing_service_->CreateGroup(
display_name,
base::BindLambdaForTesting(
[&run_loop, &outcome](
const DataSharingService::GroupDataOrFailureOutcome& result) {
outcome = result;
run_loop.Quit();
}));
run_loop.Run();
ASSERT_TRUE(outcome.has_value());
EXPECT_THAT(outcome->display_name, Eq(display_name));
ASSERT_TRUE(not_owned_sdk_delegate_->GetGroup(outcome->group_token.group_id)
.has_value());
EXPECT_THAT(not_owned_sdk_delegate_->GetGroup(outcome->group_token.group_id)
->display_name(),
Eq(display_name));
}
TEST_P(DataSharingServiceImplTest, ShouldDeleteGroup) {
// TODO(crbug.com/301390275): add a version of this test for unhappy path.
const GroupId group_id =
not_owned_sdk_delegate_->AddGroupAndReturnId("display_name");
std::optional<data_sharing_pb::GroupData> group_data_pb =
not_owned_sdk_delegate_->GetGroup(group_id);
ASSERT_TRUE(group_data_pb.has_value());
EXPECT_FALSE(data_sharing_service_->IsLeavingOrDeletingGroup(group_id));
base::RunLoop run_loop;
base::MockOnceCallback<void(DataSharingService::PeopleGroupActionOutcome)>
callback;
EXPECT_CALL(callback,
Run(DataSharingService::PeopleGroupActionOutcome::kSuccess))
.WillOnce(RunClosure(run_loop.QuitClosure()));
data_sharing_service_->DeleteGroup(group_id, callback.Get());
run_loop.Run();
EXPECT_FALSE(not_owned_sdk_delegate_->GetGroup(group_id).has_value());
// The group should still be available for lookup through a specific API.
// The GroupDataModel informs the service about deletions, so we invoke that
// method here to mimic that behavior.
std::optional<GroupData> group_data = std::make_optional<GroupData>();
group_data->group_token = GroupToken(group_id, "");
group_data->display_name = group_data_pb->display_name();
data_sharing_service_->OnGroupDeleted(group_id, group_data, base::Time());
std::optional<GroupData> retrieved_group_data =
data_sharing_service_->GetPossiblyRemovedGroup(group_id);
ASSERT_TRUE(retrieved_group_data.has_value());
EXPECT_EQ(retrieved_group_data->group_token.group_id,
group_data->group_token.group_id);
EXPECT_EQ(retrieved_group_data->display_name, group_data->display_name);
EXPECT_TRUE(data_sharing_service_->IsLeavingOrDeletingGroup(group_id));
}
TEST_P(DataSharingServiceImplTest, ShouldReadGroup) {
// TODO(crbug.com/382036119): tested API is deprecated, removed this test once
// there are no callers.
const std::string display_name = "display_name";
const GroupId group_id =
not_owned_sdk_delegate_->AddGroupAndReturnId(display_name);
DataSharingService::GroupDataOrFailureOutcome outcome;
base::RunLoop run_loop;
data_sharing_service_->ReadGroupDeprecated(
group_id,
base::BindLambdaForTesting(
[&run_loop, &outcome](
const DataSharingService::GroupDataOrFailureOutcome& result) {
outcome = result;
run_loop.Quit();
}));
run_loop.Run();
ASSERT_TRUE(outcome.has_value());
EXPECT_THAT(outcome->display_name, Eq(display_name));
EXPECT_THAT(outcome->group_token.group_id, Eq(group_id));
}
TEST_P(DataSharingServiceImplTest, ShouldReadNewGroup) {
const std::string display_name = "display_name";
const GroupId group_id =
not_owned_sdk_delegate_->AddGroupAndReturnId(display_name);
const GroupToken group_token = GroupToken(group_id, "access_token");
DataSharingService::GroupDataOrFailureOutcome outcome;
base::RunLoop run_loop;
data_sharing_service_->ReadNewGroup(
group_token,
base::BindLambdaForTesting(
[&run_loop, &outcome](
const DataSharingService::GroupDataOrFailureOutcome& result) {
outcome = result;
run_loop.Quit();
}));
run_loop.Run();
ASSERT_TRUE(outcome.has_value());
EXPECT_THAT(outcome->display_name, Eq(display_name));
EXPECT_THAT(outcome->group_token.group_id, Eq(group_id));
}
TEST_P(DataSharingServiceImplTest, ReadNewGroupFailure) {
const std::string display_name = "display_name";
const GroupToken group_token =
GroupToken(GroupId("missing_id"), "access_token");
DataSharingService::GroupDataOrFailureOutcome outcome;
base::RunLoop run_loop;
data_sharing_service_->ReadNewGroup(
group_token,
base::BindLambdaForTesting(
[&run_loop, &outcome](
const DataSharingService::GroupDataOrFailureOutcome& result) {
outcome = result;
run_loop.Quit();
}));
run_loop.Run();
EXPECT_EQ(outcome.error(),
DataSharingService::PeopleGroupActionFailure::kPersistentFailure);
}
TEST_P(DataSharingServiceImplTest, ShouldInviteMember) {
// TODO(crbug.com/301390275): add a version of this test for unhappy paths.
const GroupId group_id =
not_owned_sdk_delegate_->AddGroupAndReturnId("display_name");
const std::string email = "user@gmail.com";
const GaiaId gaia_id("123456789");
not_owned_sdk_delegate_->AddAccount(email, gaia_id);
base::RunLoop run_loop;
base::MockOnceCallback<void(DataSharingService::PeopleGroupActionOutcome)>
callback;
EXPECT_CALL(callback,
Run(DataSharingService::PeopleGroupActionOutcome::kSuccess))
.WillOnce(RunClosure(run_loop.QuitClosure()));
data_sharing_service_->InviteMember(group_id, email, callback.Get());
run_loop.Run();
auto group = not_owned_sdk_delegate_->GetGroup(group_id);
ASSERT_TRUE(group.has_value());
ASSERT_THAT(group->members().size(), Eq(1));
EXPECT_THAT(group->members(0).gaia_id(), Eq(gaia_id.ToString()));
}
TEST_P(DataSharingServiceImplTest, ShouldRemoveMember) {
// TODO(crbug.com/301390275): add a version of this test for unhappy paths.
const GroupId group_id =
not_owned_sdk_delegate_->AddGroupAndReturnId("display_name");
const std::string email = "user@gmail.com";
const GaiaId gaia_id("123456789");
not_owned_sdk_delegate_->AddAccount(email, gaia_id);
not_owned_sdk_delegate_->AddMember(group_id, gaia_id);
base::RunLoop run_loop;
base::MockOnceCallback<void(DataSharingService::PeopleGroupActionOutcome)>
callback;
EXPECT_CALL(callback,
Run(DataSharingService::PeopleGroupActionOutcome::kSuccess))
.WillOnce(RunClosure(run_loop.QuitClosure()));
data_sharing_service_->RemoveMember(group_id, email, callback.Get());
run_loop.Run();
auto group = not_owned_sdk_delegate_->GetGroup(group_id);
ASSERT_TRUE(group.has_value());
EXPECT_TRUE(group->members().empty());
}
TEST_P(DataSharingServiceImplTest, ShouldLeaveGroup) {
const GroupId group_id =
not_owned_sdk_delegate_->AddGroupAndReturnId("display_name");
EXPECT_FALSE(data_sharing_service_->IsLeavingOrDeletingGroup(group_id));
const std::string email = "user@gmail.com";
const GaiaId gaia_id("123456789");
not_owned_sdk_delegate_->SetUserGaiaId(gaia_id);
not_owned_sdk_delegate_->AddAccount(email, gaia_id);
not_owned_sdk_delegate_->AddMember(group_id, gaia_id);
base::RunLoop run_loop;
base::MockOnceCallback<void(DataSharingService::PeopleGroupActionOutcome)>
callback;
EXPECT_CALL(callback,
Run(DataSharingService::PeopleGroupActionOutcome::kSuccess))
.WillOnce(RunClosure(run_loop.QuitClosure()));
data_sharing_service_->LeaveGroup(group_id, callback.Get());
run_loop.Run();
auto group = not_owned_sdk_delegate_->GetGroup(group_id);
ASSERT_TRUE(group.has_value());
EXPECT_TRUE(group->members().empty());
EXPECT_TRUE(data_sharing_service_->IsLeavingOrDeletingGroup(group_id));
}
TEST_P(DataSharingServiceImplTest, ShouldNotifyOnSyncBridgeUpdateTypeChanged) {
data_sharing_service_->OnSyncBridgeUpdateTypeChanged(
SyncBridgeUpdateType::kDisableSync);
}
TEST_P(DataSharingServiceImplTest, GetDataSharingUrl) {
GroupData group_data = GroupData();
group_data.group_token =
GroupToken(data_sharing::GroupId(kGroupId), kTokenBlob);
GURL url = GURL(data_sharing::features::kDataSharingURL.Get() +
"?g=" + kEncodedGroupId + "&t=" + kEncodedTokenBlob);
std::unique_ptr<GURL> result_url =
data_sharing_service_->GetDataSharingUrl(group_data);
// Verify valid path.
EXPECT_TRUE(result_url);
EXPECT_EQ(url, *result_url);
// Verify invalid group data.
result_url = data_sharing_service_->GetDataSharingUrl(GroupData());
EXPECT_FALSE(result_url);
}
TEST_P(DataSharingServiceImplTest, AddGroupDataForTesting) {
data_sharing::GroupId group_id = data_sharing::GroupId(kGroupId);
const GaiaId gaia_id("gaia_id");
const std::string display_name = "Invitee Display Name";
const std::string email = "invitee@mail.com";
const MemberRole role = MemberRole::kInvitee;
const GURL avatar_url = GURL("chrome://newtab");
const std::string given_name = "Invitee Given Name";
const std::string access_token = "fake_access_token";
const GaiaId gaia_id2("gaia_id2");
const std::string display_name2 = "Former Member Display Name";
const std::string email2 = "former_member@mail.com";
const MemberRole role2 = MemberRole::kFormerMember;
const GURL avatar_url2 = GURL("chrome://newtab");
const std::string given_name2 = "Former Member Given Name";
GroupMember group_member =
GroupMember(gaia_id, display_name, email, role, avatar_url, given_name);
GroupMember former_group_member = GroupMember(
gaia_id2, display_name2, email2, role2, avatar_url2, given_name2);
GroupData group_data = GroupData(group_id, display_name, {group_member},
{former_group_member}, access_token);
data_sharing_service_->AddGroupDataForTesting(std::move(group_data));
std::optional<GroupData> returned_group_data =
data_sharing_service_->ReadGroup(group_id);
EXPECT_EQ(returned_group_data->members.size(), 1u);
EXPECT_EQ(returned_group_data->display_name, display_name);
EXPECT_EQ(returned_group_data->group_token.group_id, group_id);
EXPECT_EQ(returned_group_data->group_token.access_token, access_token);
EXPECT_EQ(returned_group_data->members[0].gaia_id, gaia_id);
EXPECT_EQ(returned_group_data->members[0].display_name, display_name);
EXPECT_EQ(returned_group_data->members[0].email, email);
EXPECT_EQ(returned_group_data->members[0].role, role);
EXPECT_EQ(returned_group_data->members[0].avatar_url, avatar_url);
EXPECT_EQ(returned_group_data->members[0].given_name, given_name);
EXPECT_EQ(returned_group_data->former_members.size(), 1u);
EXPECT_EQ(returned_group_data->former_members[0].gaia_id, gaia_id2);
EXPECT_EQ(returned_group_data->former_members[0].display_name, display_name2);
EXPECT_EQ(returned_group_data->former_members[0].email, email2);
EXPECT_EQ(returned_group_data->former_members[0].role, role2);
EXPECT_EQ(returned_group_data->former_members[0].avatar_url, avatar_url2);
EXPECT_EQ(returned_group_data->former_members[0].given_name, given_name2);
}
INSTANTIATE_TEST_SUITE_P(DataSharingServiceImplTestInstantiation,
DataSharingServiceImplTest,
::testing::Values(Variant::kDelegateAtCreation,
Variant::kDelegateAfter));
} // namespace data_sharing