chromium/remoting/host/chromeos/remote_support_host_ash_unittest.cc

// 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 "remoting/host/chromeos/remote_support_host_ash.h"

#include <memory>

#include "base/functional/callback_helpers.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/values.h"
#include "components/policy/core/common/fake_async_policy_loader.h"
#include "remoting/base/auto_thread_task_runner.h"
#include "remoting/host/chromeos/browser_interop.h"
#include "remoting/host/chromeos/features.h"
#include "remoting/host/chromeos/session_storage.h"
#include "remoting/host/chromoting_host_context.h"
#include "remoting/host/it2me/it2me_host.h"
#include "remoting/host/mojom/remote_support.mojom.h"
#include "remoting/host/policy_watcher.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"

namespace remoting {

namespace {

using base::test::TestFuture;
using remoting::features::kEnableCrdAdminRemoteAccessV2;

constexpr char kRemoteAdminEmail[] = "[email protected]";

// Matcher that checks if the result of a `StartSupportSession` request
// indicates we failed to start the session
auto IsError() {
  return testing::Pointee(testing::Property(
      &mojom::StartSupportSessionResponse::is_support_session_error,
      testing::Eq(true)));
}

// Matcher that checks if the result of a `StartSupportSession` request
// indicates we could start the session.
auto IsSuccessful() {
  return testing::Pointee(testing::Property(
      &mojom::StartSupportSessionResponse::is_support_session_error,
      testing::Eq(false)));
}

class FakeIt2MeHost : public It2MeHost {
 public:
  FakeIt2MeHost() = default;
  FakeIt2MeHost(const FakeIt2MeHost&) = delete;
  FakeIt2MeHost& operator=(const FakeIt2MeHost&) = delete;

  // `It2MeHost` implementation:
  void Connect(std::unique_ptr<ChromotingHostContext> context,
               base::Value::Dict policies,
               std::unique_ptr<It2MeConfirmationDialogFactory> dialog_factory,
               base::WeakPtr<It2MeHost::Observer> observer,
               CreateDeferredConnectContext create_context,
               const std::string& user_name,
               const protocol::IceConfig& ice_config) override {
    observer_ = observer;
    user_name_ = user_name;
    connect_waiter_.SetValue();
  }
  void Disconnect() override {}

  std::optional<ReconnectParams> CreateReconnectParams() const override {
    if (is_enterprise_session() && enterprise_params().allow_reconnections) {
      ReconnectParams reconnect_params;
      reconnect_params.support_id = "1234567";
      reconnect_params.host_secret = "12345";
      reconnect_params.private_key = std::string(384, 'a');
      reconnect_params.ftl_device_id = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
      reconnect_params.client_ftl_address =
          "[email protected]"
          "/chromoting_ftl_11111111-2222-3333-4444-555555555555";
      return reconnect_params;
    }
    return std::nullopt;
  }

  bool WaitForConnectCall() {
    bool success = connect_waiter_.Wait();
    connect_waiter_.Clear();
    return success;
  }

  std::string user_name() const { return user_name_; }
  const ChromeOsEnterpriseParams& enterprise_params() const {
    return chrome_os_enterprise_params();
  }

  It2MeHost::Observer& observer() {
    CHECK(observer_) << "`Connect()` has not been invoked (or `Disconnect()` "
                        "was already called)";
    CHECK(observer_.MaybeValid());
    return *observer_;
  }

 private:
  ~FakeIt2MeHost() override = default;

  base::WeakPtr<It2MeHost::Observer> observer_;
  std::string user_name_;
  TestFuture<void> connect_waiter_;
};

class FakeIt2MeHostFactory : public It2MeHostFactory {
 public:
  explicit FakeIt2MeHostFactory(scoped_refptr<FakeIt2MeHost> host)
      : host_(host) {}

  ~FakeIt2MeHostFactory() override = default;

  std::unique_ptr<It2MeHostFactory> Clone() const override {
    return std::make_unique<FakeIt2MeHostFactory>(host_);
  }

  scoped_refptr<It2MeHost> CreateIt2MeHost() override { return host_; }

 private:
  scoped_refptr<FakeIt2MeHost> host_;
};

// Implementation of `BrowserInterop` that returns testing instances of
// the requested resources, and which will wait in its resources until
// everything is properly cleaned up.
class FakeBrowserInterop : public BrowserInterop {
 public:
  FakeBrowserInterop() = default;
  FakeBrowserInterop(const FakeBrowserInterop&) = delete;
  FakeBrowserInterop& operator=(const FakeBrowserInterop&) = delete;

  // `BrowserInterop` implementation:
  std::unique_ptr<ChromotingHostContext> CreateChromotingHostContext()
      override {
    return ChromotingHostContext::CreateForTesting(
        auto_thread_task_runner_, url_loader_factory_.GetSafeWeakWrapper());
  }

  std::unique_ptr<PolicyWatcher> CreatePolicyWatcher() override {
    return PolicyWatcher::CreateFromPolicyLoaderForTesting(
        std::make_unique<policy::FakeAsyncPolicyLoader>(
            base::SingleThreadTaskRunner::GetCurrentDefault()));
  }

 private:
  ~FakeBrowserInterop() override {
    // We must wait until all `ChromotingHostContext` instances we created
    // are destroyed (and until the `AutoThreadTaskRunner` instances they
    // create are destroyed as well).
    // We know this is the case when they stop referencing
    // `auto_thread_task_runner_`, so we drop our reference to it and wait until
    // the refcount reaches zero.
    auto_thread_task_runner_.reset();
    EXPECT_TRUE(on_auto_thread_task_runner_deleted_.Wait());
  }

  base::test::TestFuture<void> on_auto_thread_task_runner_deleted_;

  scoped_refptr<AutoThreadTaskRunner> auto_thread_task_runner_ =
      base::MakeRefCounted<AutoThreadTaskRunner>(
          base::SingleThreadTaskRunner::GetCurrentDefault(),
          on_auto_thread_task_runner_deleted_.GetCallback());

  network::TestURLLoaderFactory url_loader_factory_;
};

class InMemorySessionStorage : public SessionStorage {
 public:
  InMemorySessionStorage() = default;
  InMemorySessionStorage(const InMemorySessionStorage&) = delete;
  InMemorySessionStorage& operator=(const InMemorySessionStorage&) = delete;
  ~InMemorySessionStorage() override = default;

  // `SessionStorage` implementation:
  void StoreSession(const base::Value::Dict& information,
                    base::OnceClosure on_done) override {
    session_ = information.Clone();
    std::move(on_done).Run();
  }
  void DeleteSession(base::OnceClosure on_done) override {
    session_.reset();
    std::move(on_done).Run();
  }
  void RetrieveSession(
      base::OnceCallback<void(std::optional<base::Value::Dict>)> on_done)
      override {
    if (session_.has_value()) {
      std::move(on_done).Run(session_.value().Clone());
    } else {
      std::move(on_done).Run(std::nullopt);
    }
  }
  void HasSession(base::OnceCallback<void(bool)> on_done) const override {
    std::move(on_done).Run(session_.has_value());
  }

 private:
  std::optional<base::Value::Dict> session_;
};

bool HasSession(SessionStorage& storage) {
  base::test::TestFuture<bool> waiter_;
  storage.HasSession(waiter_.GetCallback());
  return waiter_.Get();
}

}  // namespace

class RemoteSupportHostAshTest : public testing::TestWithParam<bool> {
 public:
  RemoteSupportHostAshTest() = default;
  RemoteSupportHostAshTest(const RemoteSupportHostAshTest&) = delete;
  RemoteSupportHostAshTest& operator=(const RemoteSupportHostAshTest&) = delete;
  ~RemoteSupportHostAshTest() override = default;

  FakeIt2MeHost& it2me_host() { return *host_.get(); }
  RemoteSupportHostAsh& support_host() { return support_host_; }

  mojom::SupportSessionParams GetSupportSessionParams() {
    mojom::SupportSessionParams params;
    params.user_name = "<the-user>";
    params.oauth_access_token = "VALID_ACCESS_TOKEN";
    params.authorized_helper = kRemoteAdminEmail;
    return params;
  }

  mojom::StartSupportSessionResponsePtr StartSession(
      std::optional<ChromeOsEnterpriseParams> enterprise_params) {
    return StartSession(GetSupportSessionParams(), enterprise_params);
  }

  mojom::StartSupportSessionResponsePtr StartSession(
      const mojom::SupportSessionParams& params,
      std::optional<ChromeOsEnterpriseParams> enterprise_params) {
    TestFuture<mojom::StartSupportSessionResponsePtr> connect_result;
    support_host().StartSession(params, enterprise_params,
                                connect_result.GetCallback());

    EXPECT_TRUE(connect_result.Wait());
    return connect_result.Take();
  }

  mojom::StartSupportSessionResponsePtr ReconnectToSession(
      SessionId id = kEnterpriseSessionId) {
    TestFuture<mojom::StartSupportSessionResponsePtr> connect_result;
    support_host().ReconnectToSession(id, "faux access token",
                                      connect_result.GetCallback());
    return connect_result.Take();
  }

  // This signal is normally sent from the chromoting code when the remote
  // user has connected.
  void SignalHostStateConnected() {
    it2me_host().observer().OnStateChanged(It2MeHostState::kConnected,
                                           protocol::ErrorCode::OK);
  }

  // This signal is normally sent from the chromoting code when the remote
  // user has disconnected.
  void SignalHostStateDisconnected() {
    it2me_host().observer().OnStateChanged(It2MeHostState::kDisconnected,
                                           protocol::ErrorCode::OK);
  }

  InMemorySessionStorage& session_storage() { return session_storage_; }

  bool StoreReconnectableSessionInformation(
      mojom::SupportSessionParams params,
      ChromeOsEnterpriseParams enterprise_params = {
          .allow_reconnections = true}) {
    // Reconnectable sessions can only be stored if reconnections are allowed.
    CHECK(enterprise_params.allow_reconnections);

    // We do not want our test to make any assumptions about how the
    // reconnectable session information is stored, so we store the
    // reconnectable session information by creating a second
    // `RemoteSupportHostAsh` and using it to start a reconnectable session.
    scoped_refptr<FakeIt2MeHost> it2me_host{
        base::MakeRefCounted<FakeIt2MeHost>()};
    RemoteSupportHostAsh support_host{
        std::make_unique<FakeIt2MeHostFactory>(it2me_host), browser_interop_,
        session_storage_, base::DoNothing()};

    support_host.StartSession(params, enterprise_params, base::DoNothing());
    EXPECT_TRUE(it2me_host->WaitForConnectCall());
    it2me_host->observer().OnClientAuthenticated(kRemoteAdminEmail);
    it2me_host->observer().OnStateChanged(It2MeHostState::kConnected,
                                          protocol::ErrorCode::OK);

    return HasSession(session_storage());
  }

  void EnableFeature(const base::Feature& feature) {
    feature_.Reset();
    feature_.InitAndEnableFeature(feature);
  }

  void DisableFeature(const base::Feature& feature) {
    feature_.Reset();
    feature_.InitAndDisableFeature(feature);
  }

 private:
  base::test::SingleThreadTaskEnvironment environment_;
  base::test::ScopedFeatureList feature_;

  scoped_refptr<FakeBrowserInterop> browser_interop_{
      base::MakeRefCounted<FakeBrowserInterop>()};

  InMemorySessionStorage session_storage_;
  scoped_refptr<FakeIt2MeHost> host_{base::MakeRefCounted<FakeIt2MeHost>()};
  RemoteSupportHostAsh support_host_{
      std::make_unique<FakeIt2MeHostFactory>(host_), browser_interop_,
      session_storage_, base::DoNothing()};
};

TEST_F(RemoteSupportHostAshTest, ShouldSendConnectMessageWhenStarting) {
  support_host().StartSession(GetSupportSessionParams(),
                              ChromeOsEnterpriseParams{}, base::DoNothing());

  EXPECT_TRUE(it2me_host().WaitForConnectCall());
}

TEST_F(RemoteSupportHostAshTest, ShouldInvokeConnectCallbackWhenStarted) {
  TestFuture<mojom::StartSupportSessionResponsePtr> connect_result;
  support_host().StartSession(GetSupportSessionParams(),
                              ChromeOsEnterpriseParams{},
                              connect_result.GetCallback());

  ASSERT_TRUE(connect_result.Wait());
  EXPECT_FALSE(connect_result.Get()->is_support_session_error());
}

TEST_F(RemoteSupportHostAshTest, ShouldPassUserNameToIt2MeHostWhenStarting) {
  mojom::SupportSessionParams params = GetSupportSessionParams();
  params.user_name = "<the-user-name>";

  StartSession(params, ChromeOsEnterpriseParams{});

  EXPECT_EQ(it2me_host().user_name(), params.user_name);
}

// TODO(b/309958013): Remove this test when we remove the oauth prefix logic.
TEST_F(RemoteSupportHostAshTest, ValidLegacyAccessTokenFormatSucceeds) {
  mojom::SupportSessionParams params = GetSupportSessionParams();
  params.oauth_access_token = "oauth2:<the-oauth-token>";

  StartSession(params, ChromeOsEnterpriseParams{});

  EXPECT_TRUE(it2me_host().WaitForConnectCall());
}

TEST_P(RemoteSupportHostAshTest,
       ShouldPassSuppressNotificationsToIt2MeHostWhenStarting) {
  const bool value = GetParam();

  StartSession(ChromeOsEnterpriseParams{.suppress_notifications = value});

  EXPECT_EQ(it2me_host().enterprise_params().suppress_notifications, value);
}
TEST_P(RemoteSupportHostAshTest,
       ShouldPassTerminateUponInputToIt2MeHostWhenStarting) {
  const bool value = GetParam();

  StartSession(ChromeOsEnterpriseParams{.terminate_upon_input = value});

  EXPECT_EQ(it2me_host().enterprise_params().terminate_upon_input, value);
}

TEST_P(RemoteSupportHostAshTest,
       ShouldPassCurtainLocalUserSessionToIt2MeHostWhenStarting) {
  const bool value = GetParam();

  StartSession(ChromeOsEnterpriseParams{.curtain_local_user_session = value});

  EXPECT_EQ(it2me_host().enterprise_params().curtain_local_user_session, value);
}

TEST_P(RemoteSupportHostAshTest,
       ShouldPassShowTroubleshootingToolsToIt2MeHostWhenStarting) {
  const bool value = GetParam();

  StartSession(ChromeOsEnterpriseParams{.show_troubleshooting_tools = value});

  EXPECT_EQ(it2me_host().enterprise_params().show_troubleshooting_tools, value);
}

TEST_P(RemoteSupportHostAshTest,
       ShouldPassAllowTroubleshootingToolsToIt2MeHostWhenStarting) {
  const bool value = GetParam();

  StartSession(ChromeOsEnterpriseParams{.allow_troubleshooting_tools = value});

  EXPECT_EQ(it2me_host().enterprise_params().allow_troubleshooting_tools,
            value);
}

TEST_P(RemoteSupportHostAshTest,
       ShouldPassAllowReconnectionsToIt2MeHostWhenStarting) {
  const bool value = GetParam();

  StartSession(ChromeOsEnterpriseParams{.allow_reconnections = value});

  EXPECT_EQ(it2me_host().enterprise_params().allow_reconnections, value);
}

TEST_P(RemoteSupportHostAshTest,
       ShouldPassAllowFileTransferToIt2MeHostWhenStarting) {
  const bool value = GetParam();

  StartSession(ChromeOsEnterpriseParams{.allow_file_transfer = value});

  EXPECT_EQ(it2me_host().enterprise_params().allow_file_transfer, value);
}

TEST_P(RemoteSupportHostAshTest,
       ShouldPassSuppressUserDialogsToIt2MeHostWhenStarting) {
  const bool value = GetParam();

  StartSession(ChromeOsEnterpriseParams{.suppress_user_dialogs = value});

  EXPECT_EQ(it2me_host().enterprise_params().suppress_user_dialogs, value);
}

TEST_F(RemoteSupportHostAshTest,
       ShouldNotStoreSessionInfoBeforeClientConnects) {
  StartSession(ChromeOsEnterpriseParams{.allow_reconnections = true});

  ASSERT_FALSE(HasSession(session_storage()));
}

TEST_F(RemoteSupportHostAshTest,
       ShouldStoreSessionInfoWhenClientConnectsToReconnectableSession) {
  EnableFeature(kEnableCrdAdminRemoteAccessV2);

  StartSession(ChromeOsEnterpriseParams{.allow_reconnections = true});
  SignalHostStateConnected();

  ASSERT_TRUE(HasSession(session_storage()));
}

TEST_F(RemoteSupportHostAshTest, ShouldNotStoreSessionInfoIfFeatureIsDisabled) {
  DisableFeature(kEnableCrdAdminRemoteAccessV2);

  StartSession(ChromeOsEnterpriseParams{.allow_reconnections = true});
  SignalHostStateConnected();

  ASSERT_FALSE(HasSession(session_storage()));
}

TEST_F(RemoteSupportHostAshTest,
       ShouldNotStoreSessionInfoIfSessionIsNotReconnectable) {
  EnableFeature(kEnableCrdAdminRemoteAccessV2);

  StartSession(ChromeOsEnterpriseParams{.allow_reconnections = false});
  SignalHostStateConnected();

  ASSERT_FALSE(HasSession(session_storage()));
}

TEST_F(RemoteSupportHostAshTest,
       ShouldNotStoreSessionInfoIfEnterpriseParamsAreUnset) {
  EnableFeature(kEnableCrdAdminRemoteAccessV2);

  StartSession(std::nullopt);
  SignalHostStateConnected();

  ASSERT_FALSE(HasSession(session_storage()));
}

TEST_F(RemoteSupportHostAshTest,
       ShouldAllowReconnectingToStoredReconnectableSession) {
  EnableFeature(kEnableCrdAdminRemoteAccessV2);

  ASSERT_TRUE(StoreReconnectableSessionInformation(GetSupportSessionParams()));

  EXPECT_THAT(ReconnectToSession(kEnterpriseSessionId), IsSuccessful());
}

TEST_F(RemoteSupportHostAshTest,
       ShouldNotAllowReconnectingIfFeatureIsDisabled) {
  // We start by enabling the feature so we can store the reconnectable session
  // information...
  EnableFeature(kEnableCrdAdminRemoteAccessV2);
  ASSERT_TRUE(StoreReconnectableSessionInformation(GetSupportSessionParams()));

  // ... so we can test that the reconnect code itself also checks the feature
  // flag.
  DisableFeature(kEnableCrdAdminRemoteAccessV2);
  EXPECT_THAT(ReconnectToSession(kEnterpriseSessionId), IsError());
}

TEST_F(RemoteSupportHostAshTest,
       ShouldFailReconnectingIfThereIsNoStoredReconnectableSession) {
  EnableFeature(kEnableCrdAdminRemoteAccessV2);

  // Not setting up any reconnectable session.
  ASSERT_FALSE(HasSession(session_storage()));

  EXPECT_THAT(ReconnectToSession(kEnterpriseSessionId), IsError());
}

TEST_F(RemoteSupportHostAshTest, ShouldFailReconnectingIfTheSessionIdIsWrong) {
  EnableFeature(kEnableCrdAdminRemoteAccessV2);

  ASSERT_TRUE(StoreReconnectableSessionInformation(GetSupportSessionParams()));

  EXPECT_THAT(ReconnectToSession(SessionId{666}), IsError());
}

TEST_F(RemoteSupportHostAshTest, ShouldPassUserNameWhenReconnectingToSession) {
  EnableFeature(kEnableCrdAdminRemoteAccessV2);

  auto params = GetSupportSessionParams();
  params.user_name = "the-user";
  ASSERT_TRUE(StoreReconnectableSessionInformation(params));

  ReconnectToSession(kEnterpriseSessionId);

  EXPECT_EQ(it2me_host().user_name(), "the-user");
}

TEST_P(RemoteSupportHostAshTest,
       ShouldPassSuppressUserDialogsFieldWhenReconnectingToSession) {
  EnableFeature(kEnableCrdAdminRemoteAccessV2);

  const bool value = GetParam();

  ASSERT_TRUE(StoreReconnectableSessionInformation(
      GetSupportSessionParams(),
      {.suppress_user_dialogs = value, .allow_reconnections = true}));

  ReconnectToSession(kEnterpriseSessionId);

  EXPECT_EQ(it2me_host().enterprise_params().suppress_user_dialogs, value);
}

TEST_P(RemoteSupportHostAshTest,
       ShouldPassSuppressNotificationsFieldWhenReconnectingToSession) {
  EnableFeature(kEnableCrdAdminRemoteAccessV2);

  const bool value = GetParam();

  ASSERT_TRUE(StoreReconnectableSessionInformation(
      GetSupportSessionParams(),
      {.suppress_notifications = value, .allow_reconnections = true}));

  ReconnectToSession(kEnterpriseSessionId);

  EXPECT_EQ(it2me_host().enterprise_params().suppress_notifications, value);
}

TEST_P(RemoteSupportHostAshTest,
       ShouldPassTerminateUponInputFieldWhenReconnectingToSession) {
  EnableFeature(kEnableCrdAdminRemoteAccessV2);

  const bool value = GetParam();

  ASSERT_TRUE(StoreReconnectableSessionInformation(
      GetSupportSessionParams(),
      {.terminate_upon_input = value, .allow_reconnections = true}));

  ReconnectToSession(kEnterpriseSessionId);

  EXPECT_EQ(it2me_host().enterprise_params().terminate_upon_input, value);
}

TEST_P(RemoteSupportHostAshTest,
       ShouldPassCurtainLocalUserSessionFieldWhenReconnectingToSession) {
  EnableFeature(kEnableCrdAdminRemoteAccessV2);

  const bool value = GetParam();

  ASSERT_TRUE(StoreReconnectableSessionInformation(
      GetSupportSessionParams(),
      {.curtain_local_user_session = value, .allow_reconnections = true}));

  ReconnectToSession(kEnterpriseSessionId);

  EXPECT_EQ(it2me_host().enterprise_params().curtain_local_user_session, value);
}

TEST_P(RemoteSupportHostAshTest,
       ShouldPassShowTroubleshootingToolsFieldWhenReconnectingToSession) {
  EnableFeature(kEnableCrdAdminRemoteAccessV2);

  const bool value = GetParam();

  ASSERT_TRUE(StoreReconnectableSessionInformation(
      GetSupportSessionParams(),
      {.show_troubleshooting_tools = value, .allow_reconnections = true}));

  ReconnectToSession(kEnterpriseSessionId);

  EXPECT_EQ(it2me_host().enterprise_params().show_troubleshooting_tools, value);
}

TEST_P(RemoteSupportHostAshTest,
       ShouldPassAllowTroubleshootingToolsFieldWhenReconnectingToSession) {
  EnableFeature(kEnableCrdAdminRemoteAccessV2);

  const bool value = GetParam();

  ASSERT_TRUE(StoreReconnectableSessionInformation(
      GetSupportSessionParams(),
      {.allow_troubleshooting_tools = value, .allow_reconnections = true}));

  ReconnectToSession(kEnterpriseSessionId);

  EXPECT_EQ(it2me_host().enterprise_params().allow_troubleshooting_tools,
            value);
}

TEST_F(RemoteSupportHostAshTest,
       ShouldPassAllowReconnectionsFieldWhenReconnectingToSession) {
  EnableFeature(kEnableCrdAdminRemoteAccessV2);

  // We can't test 'false' since there is no way to reconnect to a session
  // with `allow_reconnections` set to false.
  const bool value = true;

  ASSERT_TRUE(StoreReconnectableSessionInformation(
      GetSupportSessionParams(), {.allow_reconnections = true}));

  ReconnectToSession(kEnterpriseSessionId);

  EXPECT_EQ(it2me_host().enterprise_params().allow_reconnections, value);
}

TEST_P(RemoteSupportHostAshTest,
       ShouldPassAllowFileTransferFieldWhenReconnectingToSession) {
  EnableFeature(kEnableCrdAdminRemoteAccessV2);

  const bool value = GetParam();

  ASSERT_TRUE(StoreReconnectableSessionInformation(
      GetSupportSessionParams(),
      {.allow_reconnections = true, .allow_file_transfer = value}));

  ReconnectToSession(kEnterpriseSessionId);

  EXPECT_EQ(it2me_host().enterprise_params().allow_file_transfer, value);
}

TEST_F(RemoteSupportHostAshTest,
       ShouldUseRemoteUserAsAuthorizedHelperWhenReconnectingToSession) {
  EnableFeature(kEnableCrdAdminRemoteAccessV2);

  ASSERT_TRUE(StoreReconnectableSessionInformation(
      GetSupportSessionParams(), {.allow_reconnections = true}));

  ReconnectToSession(kEnterpriseSessionId);

  EXPECT_EQ(it2me_host().authorized_helper(), kRemoteAdminEmail);
}

TEST_F(RemoteSupportHostAshTest,
       ShouldClearReconnectableInformationWhenClientDisconnectsCleanly) {
  EnableFeature(kEnableCrdAdminRemoteAccessV2);

  StartSession(ChromeOsEnterpriseParams{.allow_reconnections = true});
  SignalHostStateConnected();
  ASSERT_TRUE(HasSession(session_storage()));

  SignalHostStateDisconnected();
  EXPECT_FALSE(HasSession(session_storage()));
}

TEST_F(RemoteSupportHostAshTest,
       ShouldClearReconnectableInformationWhenAnotherSessionIsStarted) {
  EnableFeature(kEnableCrdAdminRemoteAccessV2);

  StartSession(ChromeOsEnterpriseParams{.allow_reconnections = true});
  SignalHostStateConnected();
  it2me_host().WaitForConnectCall();
  ASSERT_TRUE(HasSession(session_storage()));

  StartSession(/*enterprise_params=*/std::nullopt);

  EXPECT_FALSE(HasSession(session_storage()));
}

INSTANTIATE_TEST_SUITE_P(RemoteSupportHostAshTest,
                         RemoteSupportHostAshTest,
                         testing::Bool());

}  // namespace remoting