blob: 8b15402d1c9a44dacf25fc58c4c2622dc45a9d72 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "peridot/bin/sessionmgr/sessionmgr_impl.h"
#include <lib/component/cpp/connect.h>
#include <lib/component/cpp/testing/fake_launcher.h>
#include <lib/gtest/test_loop_fixture.h>
#include <lib/sys/cpp/outgoing_directory.h>
#include <lib/sys/cpp/testing/component_context_provider.h>
#include <lib/sys/cpp/testing/fake_component.h>
#include <lib/ui/scenic/cpp/view_token_pair.h>
// GMock is only used for its testing matchers.
#include "fuchsia/ledger/internal/cpp/fidl.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "lib/fidl/cpp/interface_request.h"
#include "lib/fit/function.h"
#include "lib/ui/scenic/cpp/view_token_pair.h"
#include "lib/vfs/cpp/pseudo_dir.h"
#include "peridot/lib/fidl/clone.h"
#include "peridot/lib/ledger_client/constants.h"
#include "src/lib/fxl/logging.h"
namespace modular {
namespace testing {
namespace {
using ::testing::Eq;
using ::testing::SizeIs;
using SessionmgrImplTest = gtest::TestLoopFixture;
constexpr char kProfileId[] = "profile-id";
// Fake TokenManager for testing. It answers to |ListProfileId| with a single profile.
class FakeTokenManager : public fuchsia::auth::TokenManager {
public:
explicit FakeTokenManager(fidl::InterfaceRequest<fuchsia::auth::TokenManager> request)
: binding_(this, std::move(request)) {}
~FakeTokenManager() override {}
// fuchsia::auth::TokenManager:
void Authorize(fuchsia::auth::AppConfig app_config,
fidl::InterfaceHandle<fuchsia::auth::AuthenticationUIContext> auth_ui_context,
std::vector<std::string> app_scopes, fidl::StringPtr user_profile_id,
fidl::StringPtr auth_code, AuthorizeCallback callback) override {
FXL_NOTIMPLEMENTED();
callback(fuchsia::auth::Status::INTERNAL_ERROR, nullptr);
}
void GetAccessToken(fuchsia::auth::AppConfig app_config, std::string user_profile_id,
std::vector<std::string> app_scopes,
GetAccessTokenCallback callback) override {
FXL_NOTIMPLEMENTED();
callback(fuchsia::auth::Status::INTERNAL_ERROR, nullptr);
}
void GetIdToken(fuchsia::auth::AppConfig app_config, std::string user_profile_id,
fidl::StringPtr audience, GetIdTokenCallback callback) override {
FXL_NOTIMPLEMENTED();
callback(fuchsia::auth::Status::INTERNAL_ERROR, nullptr);
}
void GetFirebaseToken(fuchsia::auth::AppConfig app_config, std::string user_profile_id,
std::string audience, std::string firebase_api_key,
GetFirebaseTokenCallback callback) override {
FXL_NOTIMPLEMENTED();
callback(fuchsia::auth::Status::INTERNAL_ERROR, nullptr);
}
void DeleteAllTokens(fuchsia::auth::AppConfig app_config, std::string user_profile_id, bool force,
DeleteAllTokensCallback callback) override {
FXL_NOTIMPLEMENTED();
callback(fuchsia::auth::Status::INTERNAL_ERROR);
}
void ListProfileIds(fuchsia::auth::AppConfig app_config,
ListProfileIdsCallback callback) override {
callback(fuchsia::auth::Status::OK, {kProfileId});
}
private:
fidl::Binding<fuchsia::auth::TokenManager> binding_;
};
// Fake ComponentController used to connect back to services provided by the launcher.
class FakeComponentController : public fuchsia::sys::ComponentController {
public:
FakeComponentController(fuchsia::sys::LaunchInfo launch_info,
fidl::InterfaceRequest<fuchsia::sys::ComponentController> request)
: binding_(this, std::move(request)) {
if (launch_info.additional_services) {
services_ = launch_info.additional_services->provider.Bind();
}
}
~FakeComponentController() override {}
void Kill() override { FXL_NOTIMPLEMENTED(); }
void Detach() override { FXL_NOTIMPLEMENTED(); }
template <typename Interface>
fidl::InterfacePtr<Interface> Connect() const {
return component::ConnectToService<Interface>(services_.get());
}
private:
fuchsia::sys::ServiceProviderPtr services_;
fidl::Binding<fuchsia::sys::ComponentController> binding_;
};
// Fake CloudProviderFactory that records calls to |GetCloudProvider|.
class FakeCloudProviderFactory : public fuchsia::ledger::cloud::firestore::Factory {
public:
struct GetCloudProviderRequest {
fuchsia::ledger::cloud::firestore::Config config;
fidl::InterfaceHandle<fuchsia::auth::TokenManager> token_manager;
fidl::InterfaceRequest<fuchsia::ledger::cloud::CloudProvider> cloud_provider;
fit::function<void(fuchsia::ledger::cloud::Status)> callback;
GetCloudProviderRequest(
fuchsia::ledger::cloud::firestore::Config config,
fidl::InterfaceHandle<fuchsia::auth::TokenManager> token_manager,
fidl::InterfaceRequest<fuchsia::ledger::cloud::CloudProvider> cloud_provider,
fit::function<void(fuchsia::ledger::cloud::Status)> callback)
: config(std::move(config)),
token_manager(std::move(token_manager)),
cloud_provider(std::move(cloud_provider)),
callback(std::move(callback)) {}
};
explicit FakeCloudProviderFactory(
fidl::InterfaceRequest<fuchsia::ledger::cloud::firestore::Factory> request)
: binding_(this, std::move(request)) {}
void GetCloudProvider(
fuchsia::ledger::cloud::firestore::Config config,
fidl::InterfaceHandle<fuchsia::auth::TokenManager> token_manager,
fidl::InterfaceRequest<fuchsia::ledger::cloud::CloudProvider> cloud_provider,
fit::function<void(fuchsia::ledger::cloud::Status)> callback) override {
get_cloud_provider_requests.emplace_back(std::move(config), std::move(token_manager),
std::move(cloud_provider), std::move(callback));
}
std::vector<GetCloudProviderRequest> get_cloud_provider_requests;
private:
fidl::Binding<fuchsia::ledger::cloud::firestore::Factory> binding_;
};
// Fake LedgerRepositoryFactory that records calls to |GetRepository|.
class FakeLedgerRepositoryFactory : public fuchsia::ledger::internal::LedgerRepositoryFactory {
public:
struct GetRepositoryCall {
zx::channel repository_handle;
fidl::InterfaceHandle<fuchsia::ledger::cloud::CloudProvider> cloud_provider;
std::string user_id;
fidl::InterfaceRequest<fuchsia::ledger::internal::LedgerRepository> repository_request;
GetRepositoryCall(
zx::channel repository_handle,
fidl::InterfaceHandle<fuchsia::ledger::cloud::CloudProvider> cloud_provider,
std::string user_id,
fidl::InterfaceRequest<fuchsia::ledger::internal::LedgerRepository> repository_request)
: repository_handle(std::move(repository_handle)),
cloud_provider(std::move(cloud_provider)),
user_id(std::move(user_id)),
repository_request(std::move(repository_request)) {}
};
explicit FakeLedgerRepositoryFactory(
fidl::InterfaceRequest<fuchsia::ledger::internal::LedgerRepositoryFactory> request)
: binding_(this, std::move(request)) {}
~FakeLedgerRepositoryFactory() override {}
void Sync(fit::closure callback) override {
FXL_NOTIMPLEMENTED();
callback();
}
void GetRepository(zx::channel repository_handle,
fidl::InterfaceHandle<fuchsia::ledger::cloud::CloudProvider> cloud_provider,
std::string user_id,
fidl::InterfaceRequest<fuchsia::ledger::internal::LedgerRepository>
repository_request) override {
get_repository_calls.emplace_back(std::move(repository_handle), std::move(cloud_provider),
std::move(user_id), std::move(repository_request));
}
std::vector<GetRepositoryCall> get_repository_calls;
private:
fidl::Binding<fuchsia::ledger::internal::LedgerRepositoryFactory> binding_;
};
// Fake LedgerController that does nothing.
class FakeLedgerController : public fuchsia::ledger::internal::LedgerController {
public:
explicit FakeLedgerController(
fidl::InterfaceRequest<fuchsia::ledger::internal::LedgerController> request)
: binding_(this, std::move(request)) {}
~FakeLedgerController() override{};
void Terminate() override { FXL_NOTIMPLEMENTED(); }
private:
fidl::Binding<fuchsia::ledger::internal::LedgerController> binding_;
};
// Verifies that Ledger is initialized with the right User ID from TokenManager.
TEST_F(SessionmgrImplTest, LedgerInitializedWithUserId) {
sys::testing::ComponentContextProvider component_context_provider;
fuchsia::modular::session::SessionmgrConfig config;
config.set_component_args({});
config.set_agent_service_index({});
config.set_session_agents({});
config.set_startup_agents({});
config.set_enable_cobalt(false);
config.set_enable_story_shell_preload(false);
config.set_use_memfs_for_ledger(true);
config.set_cloud_provider(fuchsia::modular::session::CloudProvider::LET_LEDGER_DECIDE);
// URL of the shells. Actual value unimportant.
std::string url = "test_url_string";
// Sessionmgr asks for an environment. Let's provide one.
std::vector<fidl::InterfaceRequest<fuchsia::sys::Environment>> environments;
component_context_provider.service_directory_provider()->AddService<fuchsia::sys::Environment>(
[&environments](fidl::InterfaceRequest<fuchsia::sys::Environment> request) {
environments.push_back(std::move(request));
});
// Sessionmgr asks for a launcher, used to launch services (including Ledger). We need to
// configure one and provide it.
sys::testing::FakeLauncher fake_launcher;
std::vector<std::unique_ptr<FakeComponentController>> component_controllers;
fake_launcher.RegisterComponent(
url, [&component_controllers](
fuchsia::sys::LaunchInfo launch_info,
fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller) {
auto fake = std::make_unique<FakeComponentController>(std::move(launch_info),
std::move(controller));
component_controllers.push_back(std::move(fake));
});
// Ledger is started as a component, and services are requested from it.
sys::testing::FakeComponent ledger_component;
std::vector<std::unique_ptr<FakeLedgerController>> ledger_controllers;
std::vector<std::unique_ptr<FakeLedgerRepositoryFactory>> ledger_repository_factories;
ledger_component.AddPublicService<fuchsia::ledger::internal::LedgerController>(
[&ledger_controllers](
fidl::InterfaceRequest<fuchsia::ledger::internal::LedgerController> handler) {
ledger_controllers.push_back(std::make_unique<FakeLedgerController>(std::move(handler)));
});
ledger_component.AddPublicService<fuchsia::ledger::internal::LedgerRepositoryFactory>(
[&ledger_repository_factories](
fidl::InterfaceRequest<fuchsia::ledger::internal::LedgerRepositoryFactory> handler) {
ledger_repository_factories.push_back(
std::make_unique<FakeLedgerRepositoryFactory>(std::move(handler)));
});
ledger_component.Register(kLedgerAppUrl, fake_launcher);
sys::testing::FakeComponent cloud_provider_component;
std::vector<std::unique_ptr<FakeCloudProviderFactory>> cloud_provider_factories;
cloud_provider_component.AddPublicService<fuchsia::ledger::cloud::firestore::Factory>(
[&cloud_provider_factories](
fidl::InterfaceRequest<fuchsia::ledger::cloud::firestore::Factory> handler) {
cloud_provider_factories.push_back(
std::make_unique<FakeCloudProviderFactory>(std::move(handler)));
});
cloud_provider_component.Register(kCloudProviderFirestoreAppUrl, fake_launcher);
component_context_provider.service_directory_provider()->AddService(fake_launcher.GetHandler());
inspect::Node root_node;
fuchsia::modular::internal::SessionmgrPtr sessionmgr;
SessionmgrImpl sessionmgr_impl(component_context_provider.context(), std::move(config),
std::move(root_node));
component_context_provider.ConnectToPublicService(sessionmgr.NewRequest());
sessionmgr.set_error_handler([](zx_status_t status) {
FXL_LOG(ERROR) << "Error when connected to Sessionmgr: " << status;
});
RunLoopUntilIdle();
fuchsia::modular::AppConfig app_config;
app_config.url = url;
// We create the channels, even if we don't bind them on this side.
fuchsia::auth::TokenManagerPtr agent_token_manager;
auto agent_token_manager_request = agent_token_manager.NewRequest();
fuchsia::modular::internal::SessionContextPtr session_context;
auto session_context_request = session_context.NewRequest();
fuchsia::auth::TokenManagerPtr ledger_token_manager;
FakeTokenManager fake_token_manager(ledger_token_manager.NewRequest());
auto account = std::make_unique<fuchsia::modular::auth::Account>();
// Account ID, different from the user id.
account->id = "1234567890";
auto [view_token, view_token_holder] = scenic::ViewTokenPair::New();
sessionmgr->Initialize("session_id", std::move(account), CloneStruct(app_config),
CloneStruct(app_config), false, std::move(ledger_token_manager),
std::move(agent_token_manager), std::move(session_context),
std::move(view_token));
RunLoopUntilIdle();
// Sessionmgr only finishes initialization once someone connects to it. Let's do that!
ASSERT_THAT(component_controllers, SizeIs(1));
fuchsia::modular::SessionShellContextPtr shell_context =
component_controllers[0]->Connect<fuchsia::modular::SessionShellContext>();
shell_context.set_error_handler(
[](zx_status_t status) { FXL_LOG(ERROR) << "SessionShellContext disconnected: " << status; });
RunLoopUntilIdle();
// Verify that Ledger started with the right user profile ID.
ASSERT_THAT(ledger_repository_factories, SizeIs(1));
ASSERT_THAT(ledger_repository_factories[0]->get_repository_calls, SizeIs(1));
EXPECT_THAT(ledger_repository_factories[0]->get_repository_calls[0].user_id, Eq(kProfileId));
// Verify that the cloud provider started with the right user profile ID.
ASSERT_THAT(cloud_provider_factories, SizeIs(1));
ASSERT_THAT(cloud_provider_factories[0]->get_cloud_provider_requests, SizeIs(1));
EXPECT_THAT(cloud_provider_factories[0]->get_cloud_provider_requests[0].config.user_profile_id,
Eq(kProfileId));
}
} // namespace
} // namespace testing
} // namespace modular