chromium/chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/win_key_rotation_command_unittest.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/win_key_rotation_command.h"

#include <winerror.h>

#include <optional>

#include "base/base64.h"
#include "base/functional/bind.h"
#include "base/notreached.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "chrome/browser/enterprise/connectors/device_trust/key_management/browser/commands/metrics_utils.h"
#include "chrome/install_static/test/scoped_install_details.h"
#include "chrome/installer/util/util_constants.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace enterprise_connectors {

namespace {

const char kNonce[] = "nonce";
const char kFakeDMToken[] = "fake-browser-dm-token";
const char kFakeDmServerUrl[] =
    "https://example.com/"
    "management_service?retry=false&agent=Chrome+1.2.3(456)&apptype=Chrome&"
    "critical=true&deviceid=fake-client-id&devicetype=2&platform=Test%7CUnit%"
    "7C1.2.3&request=browser_public_key_upload";

void CheckCommandArgs(const std::vector<std::string>& args) {
  std::string token_base64 = base::Base64Encode(kFakeDMToken);
  std::string nonce_base64 = base::Base64Encode(kNonce);
  EXPECT_EQ(token_base64, args[0]);
  EXPECT_EQ(kFakeDmServerUrl, args[1]);
  EXPECT_EQ(nonce_base64, args[2]);
}

}  // namespace

class WinKeyRotationCommandTest : public testing::Test {
 protected:
  void RunUntilIdle() { task_environment_.RunUntilIdle(); }

  void RunTest(
      HRESULT command_hresult,
      std::optional<int> exit_code,
      KeyRotationCommand::Status expected_status,
      std::optional<KeyRotationCommandError> logged_error = std::nullopt,
      bool skip_wait = false) {
    install_static::ScopedInstallDetails install_details(true);

    KeyRotationCommand::Params params = {kFakeDMToken, kFakeDmServerUrl,
                                         kNonce};

    WinKeyRotationCommand command(base::BindLambdaForTesting(
        [&command_hresult, &exit_code](const wchar_t* command,
                                       const std::vector<std::string>& args,
                                       std::optional<DWORD>* return_code) {
          CheckCommandArgs(args);
          if (exit_code) {
            *return_code = exit_code.value();
          }
          return command_hresult;
        }));

    if (skip_wait) {
      command.enable_waiting_for_testing(false);
    }

    base::test::TestFuture<KeyRotationCommand::Status> future_status;
    command.Trigger(params, future_status.GetCallback());

    EXPECT_EQ(future_status.Get(), expected_status);

    VerifyHistograms(logged_error, exit_code);
  }

  void VerifyHistograms(std::optional<KeyRotationCommandError> error,
                        std::optional<int> exit_code) {
    if (error) {
      histogram_tester_.ExpectUniqueSample(
          "Enterprise.DeviceTrust.KeyRotationCommand.Error", error.value(), 1);
    } else {
      histogram_tester_.ExpectTotalCount(
          "Enterprise.DeviceTrust.KeyRotationCommand.Error", 0);
    }

    if (exit_code) {
      histogram_tester_.ExpectUniqueSample(
          "Enterprise.DeviceTrust.KeyRotationCommand.ExitCode",
          exit_code.value(), 1);
    } else {
      histogram_tester_.ExpectTotalCount(
          "Enterprise.DeviceTrust.KeyRotationCommand.ExitCode", 0);
    }
  }

  base::test::TaskEnvironment task_environment_;
  base::HistogramTester histogram_tester_;
};

TEST_F(WinKeyRotationCommandTest, RotateSuccess) {
  RunTest(S_OK, installer::ROTATE_DTKEY_SUCCESS,
          KeyRotationCommand::Status::SUCCEEDED);
}

TEST_F(WinKeyRotationCommandTest, RotateFailure) {
  RunTest(S_OK, installer::ROTATE_DTKEY_FAILED,
          KeyRotationCommand::Status::FAILED);
}

TEST_F(WinKeyRotationCommandTest, RotateTimeout) {
  RunTest(E_ABORT, std::nullopt, KeyRotationCommand::Status::TIMED_OUT,
          KeyRotationCommandError::kTimeout);
}

TEST_F(WinKeyRotationCommandTest, GoogleUpdateConcurrencyIssue) {
  RunTest(WinKeyRotationCommand::GOOPDATE_E_APP_USING_EXTERNAL_UPDATER,
          std::nullopt, KeyRotationCommand::Status::FAILED,
          KeyRotationCommandError::kUpdaterConcurrency, /*skip_wait=*/true);
}

TEST_F(WinKeyRotationCommandTest, GeneralFailure) {
  RunTest(S_OK, std::nullopt, KeyRotationCommand::Status::FAILED,
          KeyRotationCommandError::kUnknown);
}

TEST_F(WinKeyRotationCommandTest, COMClassNotRegistered) {
  RunTest(REGDB_E_CLASSNOTREG, std::nullopt,
          KeyRotationCommand::Status::FAILED_INVALID_INSTALLATION,
          KeyRotationCommandError::kClassNotRegistered);
}

TEST_F(WinKeyRotationCommandTest, COMClassNoInterface) {
  RunTest(E_NOINTERFACE, std::nullopt,
          KeyRotationCommand::Status::FAILED_INVALID_INSTALLATION,
          KeyRotationCommandError::kNoInterface);
}

TEST_F(WinKeyRotationCommandTest, UnknownHresult) {
  RunTest(CLASS_E_NOAGGREGATION, std::nullopt,
          KeyRotationCommand::Status::FAILED,
          KeyRotationCommandError::kUnknown);
  histogram_tester_.ExpectUniqueSample(
      "Enterprise.DeviceTrust.KeyRotationCommand.Error.Hresult",
      static_cast<int>(CLASS_E_NOAGGREGATION), 1);
}

TEST_F(WinKeyRotationCommandTest, UserLevelInstall) {
  install_static::ScopedInstallDetails install_details(false);
  KeyRotationCommand::Params params = {kFakeDMToken, kFakeDmServerUrl, kNonce};

  WinKeyRotationCommand command(base::BindLambdaForTesting(
      [](const wchar_t* command, const std::vector<std::string>& args,
         std::optional<DWORD>* return_code) {
        NOTREACHED_IN_MIGRATION() << "Should not get to launching the command.";
        return S_OK;
      }));

  base::test::TestFuture<KeyRotationCommand::Status> future_status;
  command.Trigger(params, future_status.GetCallback());

  EXPECT_EQ(future_status.Get(),
            KeyRotationCommand::Status::FAILED_INVALID_INSTALLATION);
  VerifyHistograms(KeyRotationCommandError::kUserInstallation, std::nullopt);
}

}  // namespace enterprise_connectors