chromium/chrome/browser/ash/policy/remote_commands/device_command_run_routine_job_unittest.cc

// Copyright 2020 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/ash/policy/remote_commands/device_command_run_routine_job.h"

#include <limits>
#include <memory>
#include <optional>

#include "base/json/json_writer.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chromeos/ash/components/mojo_service_manager/fake_mojo_service_manager.h"
#include "chromeos/ash/services/cros_healthd/public/cpp/fake_cros_healthd.h"
#include "chromeos/ash/services/cros_healthd/public/mojom/cros_healthd_diagnostics.mojom.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace policy {

namespace em = enterprise_management;

namespace {

// String constant identifying the id field in the result payload.
constexpr char kIdFieldName[] = "id";
// String constant identifying the status field in the result payload.
constexpr char kStatusFieldName[] = "status";

// String constant identifying the routine enum field in the command payload.
constexpr char kRoutineEnumFieldName[] = "routineEnum";
// String constant identifying the parameter dictionary field in the command
// payload.
constexpr char kParamsFieldName[] = "params";

// String constants identifying the parameter fields for the routine.
constexpr char kLengthSecondsFieldName[] = "lengthSeconds";

// String constants identifying the parameter fields for the AC power routine.
constexpr char kExpectedStatusFieldName[] = "expectedStatus";
constexpr char kExpectedPowerTypeFieldName[] = "expectedPowerType";

// String constants identifying the parameter fields for the NVMe self-test
// routine.
constexpr char kNvmeSelfTestTypeFieldName[] = "nvmeSelfTestType";

// String constants identifying the parameter fields for the disk read routine
constexpr char kTypeFieldName[] = "type";
constexpr char kFileSizeMbFieldName[] = "fileSizeMb";

// String constants identifying the parameter field for the battery discharge
// routine.
constexpr char kMaximumDischargePercentAllowedFieldName[] =
    "maximumDischargePercentAllowed";

// String constants identifying the parameter field for the battery charge
// routine.
constexpr char kMinimumChargePercentRequiredFieldName[] =
    "minimumChargePercentRequired";

// String constants identifying the parameter field for the privacy screen
// routine.
constexpr char kPrivacyScreenTargetStateFieldName[] = "targetState";

// String constants identifying the parameter fields for the smartctl check
// routine.
constexpr char kPercentageUsedThresholdFieldName[] = "percentageUsedThreshold";

constexpr uint32_t kId = 11;
constexpr auto kStatus =
    ash::cros_healthd::mojom::DiagnosticRoutineStatusEnum::kRunning;

constexpr RemoteCommandJob::UniqueIDType kUniqueID = 987123;

constexpr int kPositiveInt = 8789;
constexpr int kNegativeInt = -231;
constexpr int kValidSmartctlCheckPercentageUsedValue = 100;
constexpr auto kValidAcPowerStatusEnum =
    ash::cros_healthd::mojom::AcPowerStatusEnum::kConnected;
constexpr char kValidExpectedAcPowerType[] = "power_type";
constexpr auto kValidDiskReadRoutineTypeEnum =
    ash::cros_healthd::mojom::DiskReadRoutineTypeEnum::kLinearRead;
constexpr char kValidStunServerHostname[] = "www.stun_server_name";

em::RemoteCommand GenerateCommandProto(
    RemoteCommandJob::UniqueIDType unique_id,
    base::TimeDelta age_of_command,
    base::TimeDelta idleness_cutoff,
    bool terminate_upon_input,
    std::optional<ash::cros_healthd::mojom::DiagnosticRoutineEnum> routine,
    std::optional<base::Value::Dict> params) {
  em::RemoteCommand command_proto;
  command_proto.set_type(em::RemoteCommand_Type_DEVICE_RUN_DIAGNOSTIC_ROUTINE);
  command_proto.set_command_id(unique_id);
  command_proto.set_age_of_command(age_of_command.InMilliseconds());
  base::Value::Dict root_dict;
  if (routine.has_value()) {
    root_dict.Set(kRoutineEnumFieldName, static_cast<int>(routine.value()));
  }
  if (params.has_value()) {
    root_dict.Set(kParamsFieldName, std::move(params).value());
  }
  std::string payload;
  base::JSONWriter::Write(root_dict, &payload);
  command_proto.set_payload(payload);
  return command_proto;
}

std::string CreateSuccessPayload(
    uint32_t id,
    ash::cros_healthd::mojom::DiagnosticRoutineStatusEnum status) {
  std::string payload;
  auto root_dict = base::Value::Dict()
                       .Set(kIdFieldName, static_cast<int>(id))
                       .Set(kStatusFieldName, static_cast<int>(status));
  base::JSONWriter::Write(root_dict, &payload);
  return payload;
}

std::string CreateInvalidParametersFailurePayload() {
  std::string payload;
  auto root_dict =
      base::Value::Dict()
          .Set(kIdFieldName,
               static_cast<int>(ash::cros_healthd::mojom::kFailedToStartId))
          .Set(kStatusFieldName,
               static_cast<int>(
                   ash::cros_healthd::mojom::DiagnosticRoutineStatusEnum::
                       kFailedToStart));
  base::JSONWriter::Write(root_dict, &payload);
  return payload;
}

}  // namespace

class DeviceCommandRunRoutineJobTest : public testing::Test {
 protected:
  DeviceCommandRunRoutineJobTest();
  DeviceCommandRunRoutineJobTest(const DeviceCommandRunRoutineJobTest&) =
      delete;
  DeviceCommandRunRoutineJobTest& operator=(
      const DeviceCommandRunRoutineJobTest&) = delete;
  ~DeviceCommandRunRoutineJobTest() override;

  void InitializeJob(RemoteCommandJob* job,
                     RemoteCommandJob::UniqueIDType unique_id,
                     base::TimeTicks issued_time,
                     base::TimeDelta idleness_cutoff,
                     bool terminate_upon_input,
                     ash::cros_healthd::mojom::DiagnosticRoutineEnum routine,
                     base::Value::Dict params);

  bool RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum routine,
              base::Value::Dict params_dict,
              base::RepeatingCallback<void(RemoteCommandJob*)> callback);

  base::test::TaskEnvironment task_environment_{
      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
  ::ash::mojo_service_manager::FakeMojoServiceManager fake_service_manager_;

  base::TimeTicks test_start_time_;
};

DeviceCommandRunRoutineJobTest::DeviceCommandRunRoutineJobTest() {
  ash::cros_healthd::FakeCrosHealthd::Initialize();
  test_start_time_ = base::TimeTicks::Now();
}

DeviceCommandRunRoutineJobTest::~DeviceCommandRunRoutineJobTest() {
  ash::cros_healthd::FakeCrosHealthd::Shutdown();
}

void DeviceCommandRunRoutineJobTest::InitializeJob(
    RemoteCommandJob* job,
    RemoteCommandJob::UniqueIDType unique_id,
    base::TimeTicks issued_time,
    base::TimeDelta idleness_cutoff,
    bool terminate_upon_input,
    ash::cros_healthd::mojom::DiagnosticRoutineEnum routine,
    base::Value::Dict params) {
  EXPECT_TRUE(job->Init(
      base::TimeTicks::Now(),
      GenerateCommandProto(unique_id, base::TimeTicks::Now() - issued_time,
                           idleness_cutoff, terminate_upon_input, routine,
                           std::move(params)),
      em::SignedData()));

  EXPECT_EQ(unique_id, job->unique_id());
  EXPECT_EQ(RemoteCommandJob::NOT_STARTED, job->status());
}

bool DeviceCommandRunRoutineJobTest::RunJob(
    ash::cros_healthd::mojom::DiagnosticRoutineEnum routine,
    base::Value::Dict params_dict,
    base::RepeatingCallback<void(RemoteCommandJob*)> callback) {
  auto job = std::make_unique<DeviceCommandRunRoutineJob>();
  InitializeJob(job.get(), kUniqueID, test_start_time_, base::Seconds(30),
                /*terminate_upon_input=*/false, routine,
                std::move(params_dict));
  base::test::TestFuture<void> job_finished_future;
  bool success = job->Run(base::Time::Now(), base::TimeTicks::Now(),
                          job_finished_future.GetCallback());
  EXPECT_TRUE(success);
  EXPECT_TRUE(job_finished_future.Wait()) << "Job did not finish.";
  std::move(callback).Run(job.get());
  return success;
}

TEST_F(DeviceCommandRunRoutineJobTest, InvalidRoutineEnumInCommandPayload) {
  constexpr auto kInvalidRoutineEnum =
      static_cast<ash::cros_healthd::mojom::DiagnosticRoutineEnum>(
          std::numeric_limits<std::underlying_type<
              ash::cros_healthd::mojom::DiagnosticRoutineEnum>::type>::max());
  auto job = std::make_unique<DeviceCommandRunRoutineJob>();
  base::Value::Dict params_dict;
  EXPECT_FALSE(job->Init(
      base::TimeTicks::Now(),
      GenerateCommandProto(kUniqueID, base::TimeTicks::Now() - test_start_time_,
                           base::Seconds(30),
                           /*terminate_upon_input=*/false, kInvalidRoutineEnum,
                           std::move(params_dict)),
      em::SignedData()));

  EXPECT_EQ(kUniqueID, job->unique_id());
  EXPECT_EQ(RemoteCommandJob::INVALID, job->status());
}

// Test that not specifying a routine causes the job initialization to fail.
TEST_F(DeviceCommandRunRoutineJobTest, CommandPayloadMissingRoutine) {
  auto job = std::make_unique<DeviceCommandRunRoutineJob>();
  base::Value::Dict params_dict;
  EXPECT_FALSE(job->Init(
      base::TimeTicks::Now(),
      GenerateCommandProto(kUniqueID, base::TimeTicks::Now() - test_start_time_,
                           base::Seconds(30),
                           /*terminate_upon_input=*/false,
                           /*routine=*/std::nullopt, std::move(params_dict)),
      em::SignedData()));

  EXPECT_EQ(kUniqueID, job->unique_id());
  EXPECT_EQ(RemoteCommandJob::INVALID, job->status());
}

// Test that not including a parameters dictionary causes the routine
// initialization to fail.
TEST_F(DeviceCommandRunRoutineJobTest, CommandPayloadMissingParamDict) {
  constexpr auto kValidRoutineEnum =
      ash::cros_healthd::mojom::DiagnosticRoutineEnum::kSmartctlCheck;
  auto job = std::make_unique<DeviceCommandRunRoutineJob>();
  EXPECT_FALSE(job->Init(
      base::TimeTicks::Now(),
      GenerateCommandProto(kUniqueID, base::TimeTicks::Now() - test_start_time_,
                           base::Seconds(30),
                           /*terminate_upon_input=*/false, kValidRoutineEnum,
                           /*params=*/std::nullopt),
      em::SignedData()));

  EXPECT_EQ(kUniqueID, job->unique_id());
  EXPECT_EQ(RemoteCommandJob::INVALID, job->status());
}

// Note that the battery capacity routine has no parameters, so it's enough to
// ensure the routine can be run.
TEST_F(DeviceCommandRunRoutineJobTest, RunBatteryCapacityRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kBatteryCapacity,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
             })));
}

// Note that the battery health routine has no parameters, so it's enough to
// ensure the routine can be run.
TEST_F(DeviceCommandRunRoutineJobTest, RunBatteryHealthRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kBatteryHealth,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
             })));
}

TEST_F(DeviceCommandRunRoutineJobTest, RunUrandomRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  auto params_dict =
      base::Value::Dict().Set(kLengthSecondsFieldName, kPositiveInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kBatteryHealth,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
             })));
}

// Test that the urandom routine handles the optional length_seconds parameter
// being missing.
TEST_F(DeviceCommandRunRoutineJobTest, RunUrandomRoutineMissingLengthSeconds) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kUrandom,
                     std::move(params_dict),
                     base::BindLambdaForTesting([](RemoteCommandJob* job) {
                       EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
                       std::unique_ptr<std::string> payload =
                           job->GetResultPayload();
                       EXPECT_TRUE(payload);
                       EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
                     })));
}

// Test that a negative lengthSeconds parameter causes the urandom routine to
// fail.
TEST_F(DeviceCommandRunRoutineJobTest, RunUrandomRoutineInvalidLengthSeconds) {
  auto params_dict =
      base::Value::Dict().Set(kLengthSecondsFieldName, kNegativeInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kUrandom,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

// Note that the smartctl check routine (without percentage_used) has no
// parameters, so we only need to test that it can be run successfully.
TEST_F(DeviceCommandRunRoutineJobTest, RunSmartctlCheckRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kSmartctlCheck,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
             })));
}

// Test that the smartctl check routine (with percentage_used) handles the
// optional percentage_used_threshold parameter being persent.
TEST_F(DeviceCommandRunRoutineJobTest,
       RunSmartctlCheckRoutineWithPercentageUsedSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  auto params_dict =
      base::Value::Dict().Set(kPercentageUsedThresholdFieldName,
                              kValidSmartctlCheckPercentageUsedValue);
  EXPECT_TRUE(RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::
                         kSmartctlCheckWithPercentageUsed,
                     std::move(params_dict),
                     base::BindLambdaForTesting([](RemoteCommandJob* job) {
                       EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
                       std::unique_ptr<std::string> payload =
                           job->GetResultPayload();
                       EXPECT_TRUE(payload);
                       EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
                     })));
}

// Test that the smartctl check routine (with percentage_used) handles the
// optional percentage_used_threshold parameter being missing.
TEST_F(DeviceCommandRunRoutineJobTest,
       RunSmartctlCheckRoutineWithPercentageUsedMissingParam) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::
                         kSmartctlCheckWithPercentageUsed,
                     std::move(params_dict),
                     base::BindLambdaForTesting([](RemoteCommandJob* job) {
                       EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
                       std::unique_ptr<std::string> payload =
                           job->GetResultPayload();
                       EXPECT_TRUE(payload);
                       EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
                     })));
}

// Test that a negative percentage_used_threshold parameter causes the smartctl
// check routine (with percentage_used) to fail.
TEST_F(DeviceCommandRunRoutineJobTest,
       RunSmartctlCheckRoutineWithPercentageUsedInvalidParam) {
  auto params_dict =
      base::Value::Dict().Set(kPercentageUsedThresholdFieldName, kNegativeInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::
                 kSmartctlCheckWithPercentageUsed,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

// Test that the AC power routine succeeds with all parameters specified.
TEST_F(DeviceCommandRunRoutineJobTest, RunAcPowerRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  auto params_dict =
      base::Value::Dict()
          .Set(kExpectedStatusFieldName,
               static_cast<int>(kValidAcPowerStatusEnum))
          .Set(kExpectedPowerTypeFieldName, kValidExpectedAcPowerType);
  EXPECT_TRUE(RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kAcPower,
                     std::move(params_dict),
                     base::BindLambdaForTesting([](RemoteCommandJob* job) {
                       EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
                       std::unique_ptr<std::string> payload =
                           job->GetResultPayload();
                       EXPECT_TRUE(payload);
                       EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
                     })));
}

// Test that the AC power routine succeeds without the optional parameter
// expectedPowerType specified.
TEST_F(DeviceCommandRunRoutineJobTest,
       RunAcPowerRoutineNoOptionalExpectedPowerType) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  auto params_dict = base::Value::Dict().Set(
      kExpectedStatusFieldName, static_cast<int>(kValidAcPowerStatusEnum));
  EXPECT_TRUE(RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kAcPower,
                     std::move(params_dict),
                     base::BindLambdaForTesting([](RemoteCommandJob* job) {
                       EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
                       std::unique_ptr<std::string> payload =
                           job->GetResultPayload();
                       EXPECT_TRUE(payload);
                       EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
                     })));
}

// Test that leaving out the expectedStatus parameter causes the AC power
// routine to fail.
TEST_F(DeviceCommandRunRoutineJobTest, RunAcPowerRoutineMissingExpectedStatus) {
  auto params_dict = base::Value::Dict().Set(kExpectedPowerTypeFieldName,
                                             kValidExpectedAcPowerType);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kAcPower,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

// Test that an invalid value for the expectedStatus parameter causes the AC
// power routine to fail.
TEST_F(DeviceCommandRunRoutineJobTest, RunAcPowerRoutineInvalidExpectedStatus) {
  constexpr auto kInvalidAcPowerStatusEnum =
      static_cast<ash::cros_healthd::mojom::AcPowerStatusEnum>(
          std::numeric_limits<std::underlying_type<
              ash::cros_healthd::mojom::AcPowerStatusEnum>::type>::max());
  auto params_dict =
      base::Value::Dict()
          .Set(kExpectedStatusFieldName,
               static_cast<int>(kInvalidAcPowerStatusEnum))
          .Set(kExpectedPowerTypeFieldName, kValidExpectedAcPowerType);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kAcPower,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

TEST_F(DeviceCommandRunRoutineJobTest, RunCpuCacheRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  auto params_dict =
      base::Value::Dict().Set(kLengthSecondsFieldName, kPositiveInt);
  EXPECT_TRUE(RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kCpuCache,
                     std::move(params_dict),
                     base::BindLambdaForTesting([](RemoteCommandJob* job) {
                       EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
                       std::unique_ptr<std::string> payload =
                           job->GetResultPayload();
                       EXPECT_TRUE(payload);
                       EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
                     })));
}

// Test that the CPU cache routine handles the optional length_seconds parameter
// being missing.
TEST_F(DeviceCommandRunRoutineJobTest, RunCpuCacheRoutineMissingLengthSeconds) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kCpuCache,
                     std::move(params_dict),
                     base::BindLambdaForTesting([](RemoteCommandJob* job) {
                       EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
                       std::unique_ptr<std::string> payload =
                           job->GetResultPayload();
                       EXPECT_TRUE(payload);
                       EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
                     })));
}

// Test that a negative lengthSeconds parameter causes the CPU cache routine to
// fail.
TEST_F(DeviceCommandRunRoutineJobTest, RunCpuCacheRoutineInvalidLengthSeconds) {
  auto params_dict =
      base::Value::Dict().Set(kLengthSecondsFieldName, kNegativeInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kCpuCache,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

TEST_F(DeviceCommandRunRoutineJobTest, RunCpuStressRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  auto params_dict =
      base::Value::Dict().Set(kLengthSecondsFieldName, kPositiveInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kCpuStress,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
             })));
}

// Test that the CPU stress routine handles the optional length_seconds
// parameter being missing.
TEST_F(DeviceCommandRunRoutineJobTest,
       RunCpuStressRoutineMissingLengthSeconds) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kCpuStress,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
             })));
}

// Test that a negative lengthSeconds parameter causes the CPU stress routine to
// fail.
TEST_F(DeviceCommandRunRoutineJobTest,
       RunCpuStressRoutineInvalidLengthSeconds) {
  auto params_dict =
      base::Value::Dict().Set(kLengthSecondsFieldName, kNegativeInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kCpuStress,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

TEST_F(DeviceCommandRunRoutineJobTest, RunFloatingPointAccuracyRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  auto params_dict =
      base::Value::Dict().Set(kLengthSecondsFieldName, kPositiveInt);
  EXPECT_TRUE(RunJob(
      ash::cros_healthd::mojom::DiagnosticRoutineEnum::kFloatingPointAccuracy,
      std::move(params_dict),
      base::BindLambdaForTesting([](RemoteCommandJob* job) {
        EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
        std::unique_ptr<std::string> payload = job->GetResultPayload();
        EXPECT_TRUE(payload);
        EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
      })));
}

// Test that the floating point accuracy routine handles the optional
// length_seconds parameter being missing.
TEST_F(DeviceCommandRunRoutineJobTest,
       RunFloatingPointAccuracyRoutineMissingLengthSeconds) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(RunJob(
      ash::cros_healthd::mojom::DiagnosticRoutineEnum::kFloatingPointAccuracy,
      std::move(params_dict),
      base::BindLambdaForTesting([](RemoteCommandJob* job) {
        EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
        std::unique_ptr<std::string> payload = job->GetResultPayload();
        EXPECT_TRUE(payload);
        EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
      })));
}

// Test that a negative lengthSeconds parameter causes the floating point
// accuracy routine to fail.
TEST_F(DeviceCommandRunRoutineJobTest,
       RunFloatingPointAccuracyRoutineInvalidLengthSeconds) {
  auto params_dict =
      base::Value::Dict().Set(kLengthSecondsFieldName, kNegativeInt);
  EXPECT_TRUE(RunJob(
      ash::cros_healthd::mojom::DiagnosticRoutineEnum::kFloatingPointAccuracy,
      std::move(params_dict),
      base::BindLambdaForTesting([](RemoteCommandJob* job) {
        EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
        std::unique_ptr<std::string> payload = job->GetResultPayload();
        EXPECT_TRUE(payload);
        EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
      })));
}

TEST_F(DeviceCommandRunRoutineJobTest, RunNvmeSelfTestRoutineSuccess) {
  constexpr auto kValidNvmeSelfTestTypeEnum =
      ash::cros_healthd::mojom::NvmeSelfTestTypeEnum::kShortSelfTest;
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  params_dict.Set(kNvmeSelfTestTypeFieldName,
                  static_cast<int>(kValidNvmeSelfTestTypeEnum));
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kNvmeSelfTest,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
             })));
}

// Test that leaving out the nvmeSelfTestType parameter causes the NVMe self
// test routine to fail.
TEST_F(DeviceCommandRunRoutineJobTest,
       RunNvmeSelfTestRoutineMissingSelfTestType) {
  base::Value::Dict params_dict;
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kNvmeSelfTest,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

// Test that an invalid value for the nvmeSelfTestType parameter causes the NVMe
// self test routine to fail.
TEST_F(DeviceCommandRunRoutineJobTest,
       RunNvmeSelfTestRoutineInvalidSelfTestType) {
  constexpr auto kInvalidNvmeSelfTestTypeEnum =
      static_cast<ash::cros_healthd::mojom::NvmeSelfTestTypeEnum>(
          std::numeric_limits<std::underlying_type<
              ash::cros_healthd::mojom::NvmeSelfTestTypeEnum>::type>::max());
  auto params_dict =
      base::Value::Dict().Set(kNvmeSelfTestTypeFieldName,
                              static_cast<int>(kInvalidNvmeSelfTestTypeEnum));
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kNvmeSelfTest,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

// Test that the disk read routine succeeds with all parameters specified.
TEST_F(DeviceCommandRunRoutineJobTest, RunDiskReadRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  auto params_dict =
      base::Value::Dict()
          .Set(kTypeFieldName, static_cast<int>(kValidDiskReadRoutineTypeEnum))
          .Set(kLengthSecondsFieldName, kPositiveInt)
          .Set(kFileSizeMbFieldName, kPositiveInt);
  EXPECT_TRUE(RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kDiskRead,
                     std::move(params_dict),
                     base::BindLambdaForTesting([](RemoteCommandJob* job) {
                       EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
                       std::unique_ptr<std::string> payload =
                           job->GetResultPayload();
                       EXPECT_TRUE(payload);
                       EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
                     })));
}

// Test that leaving out the type parameter causes the disk read routine to
// fail.
TEST_F(DeviceCommandRunRoutineJobTest, RunDiskReadRoutineMissingType) {
  auto params_dict = base::Value::Dict()
                         .Set(kLengthSecondsFieldName, kPositiveInt)
                         .Set(kFileSizeMbFieldName, kPositiveInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kDiskRead,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

// Test that leaving out the lengthSeconds parameter causes the disk read
// routine to fail.
TEST_F(DeviceCommandRunRoutineJobTest, RunDiskReadRoutineMissingLengthSeconds) {
  auto params_dict =
      base::Value::Dict()
          .Set(kTypeFieldName, static_cast<int>(kValidDiskReadRoutineTypeEnum))
          .Set(kFileSizeMbFieldName, kPositiveInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kDiskRead,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

// Test that leaving out the fileSizeMb parameter causes the disk read routine
// to fail.
TEST_F(DeviceCommandRunRoutineJobTest, RunDiskReadRoutineMissingFileSizeMb) {
  auto params_dict =
      base::Value::Dict()
          .Set(kTypeFieldName, static_cast<int>(kValidDiskReadRoutineTypeEnum))
          .Set(kLengthSecondsFieldName, kPositiveInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kDiskRead,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

// Test that an invalid value for the type parameter causes the disk read
// routine to fail.
TEST_F(DeviceCommandRunRoutineJobTest, RunDiskReadRoutineInvalidType) {
  constexpr auto kInvalidDiskReadRoutineTypeEnum =
      static_cast<ash::cros_healthd::mojom::DiskReadRoutineTypeEnum>(
          std::numeric_limits<std::underlying_type<
              ash::cros_healthd::mojom::DiskReadRoutineTypeEnum>::type>::max());
  auto params_dict = base::Value::Dict()
                         .Set(kTypeFieldName,
                              static_cast<int>(kInvalidDiskReadRoutineTypeEnum))
                         .Set(kLengthSecondsFieldName, kPositiveInt)
                         .Set(kFileSizeMbFieldName, kPositiveInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kDiskRead,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

// Test that an invalid value for the lengthSeconds parameter causes the disk
// read routine to fail.
TEST_F(DeviceCommandRunRoutineJobTest, RunDiskReadRoutineInvalidLengthSeconds) {
  auto params_dict =
      base::Value::Dict()
          .Set(kTypeFieldName, static_cast<int>(kValidDiskReadRoutineTypeEnum))
          .Set(kLengthSecondsFieldName, kNegativeInt)
          .Set(kFileSizeMbFieldName, kPositiveInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kDiskRead,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

// Test that an invalid value for the fileSizeMb parameter causes the disk read
// routine to fail.
TEST_F(DeviceCommandRunRoutineJobTest, RunDiskReadRoutineInvalidFileSizeMb) {
  auto params_dict =
      base::Value::Dict()
          .Set(kTypeFieldName, static_cast<int>(kValidDiskReadRoutineTypeEnum))
          .Set(kLengthSecondsFieldName, kPositiveInt)
          .Set(kFileSizeMbFieldName, kNegativeInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kDiskRead,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

// Test that the prime search routine succeeds with all parameters specified.
TEST_F(DeviceCommandRunRoutineJobTest, RunPrimeSearchRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  auto params_dict =
      base::Value::Dict().Set(kLengthSecondsFieldName, kPositiveInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kPrimeSearch,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
             })));
}

// Test that the prime search routine handles the optional length_seconds
// parameter being missing.
TEST_F(DeviceCommandRunRoutineJobTest,
       RunPrimeSearchRoutineMissingLengthSeconds) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kPrimeSearch,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
             })));
}

// Test that an invalid value for the lengthSeconds parameter causes the prime
// search routine to fail.
TEST_F(DeviceCommandRunRoutineJobTest,
       RunPrimeSearchRoutineInvalidLengthSeconds) {
  auto params_dict =
      base::Value::Dict().Set(kLengthSecondsFieldName, kNegativeInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kPrimeSearch,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

TEST_F(DeviceCommandRunRoutineJobTest, RunBatteryDischargeRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  auto params_dict =
      base::Value::Dict()
          .Set(kLengthSecondsFieldName, kPositiveInt)
          .Set(kMaximumDischargePercentAllowedFieldName, kPositiveInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kBatteryDischarge,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
             })));
}

TEST_F(DeviceCommandRunRoutineJobTest,
       RunBatteryDischargeRoutineMissingLengthSeconds) {
  // Test that leaving out the lengthSeconds parameter causes the routine to
  // fail.
  auto params_dict = base::Value::Dict().Set(
      kMaximumDischargePercentAllowedFieldName, kPositiveInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kBatteryDischarge,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

TEST_F(DeviceCommandRunRoutineJobTest,
       RunBatteryDischargeRoutineMissingMaximumDischargePercentAllowed) {
  // Test that leaving out the maximumDischargePercentAllowed parameter causes
  // the routine to fail.
  auto params_dict =
      base::Value::Dict().Set(kLengthSecondsFieldName, kPositiveInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kBatteryDischarge,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

TEST_F(DeviceCommandRunRoutineJobTest,
       RunBatteryDischargeRoutineInvalidLengthSeconds) {
  // Test that a negative lengthSeconds parameter causes the routine to fail.
  auto params_dict =
      base::Value::Dict()
          .Set(kLengthSecondsFieldName, kNegativeInt)
          .Set(kMaximumDischargePercentAllowedFieldName, kPositiveInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kBatteryDischarge,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

TEST_F(DeviceCommandRunRoutineJobTest,
       RunBatteryDischargeRoutineInvalidMaximumDischargePercentAllowed) {
  // Test that a negative maximumDischargePercentAllowed parameter causes the
  // routine to fail.
  auto params_dict =
      base::Value::Dict()
          .Set(kLengthSecondsFieldName, kPositiveInt)
          .Set(kMaximumDischargePercentAllowedFieldName, kNegativeInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kBatteryDischarge,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

// Test that the battery charge routine can be run.
TEST_F(DeviceCommandRunRoutineJobTest, RunBatteryChargeRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  auto params_dict =
      base::Value::Dict()
          .Set(kLengthSecondsFieldName, kPositiveInt)
          .Set(kMinimumChargePercentRequiredFieldName, kPositiveInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kBatteryCharge,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               ASSERT_TRUE(payload);
               EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
             })));
}

// Test that leaving out the lengthSeconds parameter causes the battery charge
// routine to fail.
TEST_F(DeviceCommandRunRoutineJobTest,
       RunBatteryChargeRoutineMissingLengthSeconds) {
  auto params_dict = base::Value::Dict().Set(
      kMinimumChargePercentRequiredFieldName, kPositiveInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kBatteryCharge,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

// Test that leaving out the minimumChargePercentRequired parameter causes the
// battery charge routine to fail.
TEST_F(DeviceCommandRunRoutineJobTest,
       RunBatteryChargeRoutineMissingMinimumChargePercentRequired) {
  auto params_dict =
      base::Value::Dict().Set(kLengthSecondsFieldName, kPositiveInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kBatteryCharge,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

// Test that a negative lengthSeconds parameter causes the battery charge
// routine to fail.
TEST_F(DeviceCommandRunRoutineJobTest,
       RunBatteryChargeRoutineInvalidLengthSeconds) {
  auto params_dict =
      base::Value::Dict()
          .Set(kLengthSecondsFieldName, kNegativeInt)
          .Set(kMinimumChargePercentRequiredFieldName, kPositiveInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kBatteryCharge,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

// Test that a negative minimumChargePercentRequired parameter causes the
// battery charge routine to fail.
TEST_F(DeviceCommandRunRoutineJobTest,
       RunBatteryChargeRoutineInvalidMinimumChargePercentRequired) {
  auto params_dict =
      base::Value::Dict()
          .Set(kLengthSecondsFieldName, kPositiveInt)
          .Set(kMinimumChargePercentRequiredFieldName, kNegativeInt);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kBatteryCharge,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateInvalidParametersFailurePayload(), *payload);
             })));
}

// Note that the memory routine has no parameters, so we only need to test that
// it can be run successfully.
TEST_F(DeviceCommandRunRoutineJobTest, RunMemoryRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kMemory,
                     std::move(params_dict),
                     base::BindLambdaForTesting([](RemoteCommandJob* job) {
                       EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
                       std::unique_ptr<std::string> payload =
                           job->GetResultPayload();
                       EXPECT_TRUE(payload);
                       EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
                     })));
}

// Note that the LAN connectivity routine has no parameters, so we only need to
// test that it can be run successfully.
TEST_F(DeviceCommandRunRoutineJobTest, RunLanConnectivityRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kLanConnectivity,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
             })));
}

// Note that the signal strength routine has no parameters, so we only need to
// test that it can be run successfully.
TEST_F(DeviceCommandRunRoutineJobTest, RunSignalStrengthRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kSignalStrength,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
             })));
}

// Note that the gateway can be pinged routine has no parameters, so we only
// need to test that it can be run successfully.
TEST_F(DeviceCommandRunRoutineJobTest, RunGatewayCanBePingedRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(RunJob(
      ash::cros_healthd::mojom::DiagnosticRoutineEnum::kGatewayCanBePinged,
      std::move(params_dict),
      base::BindLambdaForTesting([](RemoteCommandJob* job) {
        EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
        std::unique_ptr<std::string> payload = job->GetResultPayload();
        EXPECT_TRUE(payload);
        EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
      })));
}

// Note that the has secure WiFi connection routine has no parameters, so we
// only need to test that it can be run successfully.
TEST_F(DeviceCommandRunRoutineJobTest,
       RunHasSecureWiFiConnectionRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(RunJob(
      ash::cros_healthd::mojom::DiagnosticRoutineEnum::kHasSecureWiFiConnection,
      std::move(params_dict),
      base::BindLambdaForTesting([](RemoteCommandJob* job) {
        EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
        std::unique_ptr<std::string> payload = job->GetResultPayload();
        EXPECT_TRUE(payload);
        EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
      })));
}

// Note that the DNS resolver present routine has no parameters, so we only need
// to test that it can be run successfully.
TEST_F(DeviceCommandRunRoutineJobTest, RunDnsResolverPresentRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(RunJob(
      ash::cros_healthd::mojom::DiagnosticRoutineEnum::kDnsResolverPresent,
      std::move(params_dict),
      base::BindLambdaForTesting([](RemoteCommandJob* job) {
        EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
        std::unique_ptr<std::string> payload = job->GetResultPayload();
        EXPECT_TRUE(payload);
        EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
      })));
}

// Note that the DNS latency routine has no parameters, so we only need
// to test that it can be run successfully.
TEST_F(DeviceCommandRunRoutineJobTest, RunDnsLatencyRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kDnsLatency,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
             })));
}

// Note that the DNS resolution routine has no parameters, so we only need
// to test that it can be run successfully.
TEST_F(DeviceCommandRunRoutineJobTest, RunDnsResolutionRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kDnsResolution,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
             })));
}

// Note that the captive portal routine has no parameters, so we only need to
// test that it can be run successfully.
TEST_F(DeviceCommandRunRoutineJobTest, RunCaptivePortalRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kCaptivePortal,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
             })));
}

// Note that the HTTP firewall routine has no parameters, so we only need to
// test that it can be run successfully.
TEST_F(DeviceCommandRunRoutineJobTest, RunHttpFirewallRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kHttpFirewall,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
             })));
}

// Note that the HTTPS firewall routine has no parameters, so we only need to
// test that it can be run successfully.
TEST_F(DeviceCommandRunRoutineJobTest, RunHttpsFirewallRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kHttpsFirewall,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
             })));
}

// Note that the HTTPS latency routine has no parameters, so we only need to
// test that it can be run successfully.
TEST_F(DeviceCommandRunRoutineJobTest, RunHttpsLatencyRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kHttpsLatency,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
             })));
}

// Test that the video conferencing routine succeeds with all parameters
// specified.
TEST_F(DeviceCommandRunRoutineJobTest, RunVideoConferencingRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  auto params_dict = base::Value::Dict().Set(
      DeviceCommandRunRoutineJob::kStunServerHostnameFieldName,
      kValidStunServerHostname);
  EXPECT_TRUE(RunJob(
      ash::cros_healthd::mojom::DiagnosticRoutineEnum::kVideoConferencing,
      std::move(params_dict),
      base::BindLambdaForTesting([](RemoteCommandJob* job) {
        EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
        std::unique_ptr<std::string> payload = job->GetResultPayload();
        EXPECT_TRUE(payload);
        EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
      })));
}

// Test that the video conferencing routine succeeds without the optional
// parameter stunServerHostname specified.
TEST_F(DeviceCommandRunRoutineJobTest,
       RunVideoConferencingRoutineNoOptionalStunServerHostname) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(RunJob(
      ash::cros_healthd::mojom::DiagnosticRoutineEnum::kVideoConferencing,
      std::move(params_dict),
      base::BindLambdaForTesting([](RemoteCommandJob* job) {
        EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
        std::unique_ptr<std::string> payload = job->GetResultPayload();
        EXPECT_TRUE(payload);
        EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
      })));
}

// Note that the ARC HTTP routine has no parameters, so we only need to
// test that it can be run successfully.
TEST_F(DeviceCommandRunRoutineJobTest, RunArcHttpRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(RunJob(
      ash::cros_healthd::mojom::DiagnosticRoutineEnum::kArcHttp,
      std::move(params_dict),
      base::BindLambdaForTesting([](RemoteCommandJob* job) {
        EXPECT_EQ(ash::cros_healthd::FakeCrosHealthd::Get()
                      ->GetLastRunRoutine()
                      .value(),
                  ash::cros_healthd::mojom::DiagnosticRoutineEnum::kArcHttp);
        EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
        std::unique_ptr<std::string> payload = job->GetResultPayload();
        EXPECT_TRUE(payload);
        EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
      })));
}

// Note that the ARC Ping routine has no parameters, so we only need to
// test that it can be run successfully.
TEST_F(DeviceCommandRunRoutineJobTest, RunArcPingRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(RunJob(
      ash::cros_healthd::mojom::DiagnosticRoutineEnum::kArcPing,
      std::move(params_dict),
      base::BindLambdaForTesting([](RemoteCommandJob* job) {
        EXPECT_EQ(ash::cros_healthd::FakeCrosHealthd::Get()
                      ->GetLastRunRoutine()
                      .value(),
                  ash::cros_healthd::mojom::DiagnosticRoutineEnum::kArcPing);
        EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
        std::unique_ptr<std::string> payload = job->GetResultPayload();
        EXPECT_TRUE(payload);
        EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
      })));
}

// Note that the ARC DNS Resolution routine has no parameters, so we only need
// to test that it can be run successfully.
TEST_F(DeviceCommandRunRoutineJobTest, RunArcDnsResolutionRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(RunJob(
      ash::cros_healthd::mojom::DiagnosticRoutineEnum::kArcDnsResolution,
      std::move(params_dict),
      base::BindLambdaForTesting([](RemoteCommandJob* job) {
        EXPECT_EQ(
            ash::cros_healthd::FakeCrosHealthd::Get()
                ->GetLastRunRoutine()
                .value(),
            ash::cros_healthd::mojom::DiagnosticRoutineEnum::kArcDnsResolution);
        EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
        std::unique_ptr<std::string> payload = job->GetResultPayload();
        EXPECT_TRUE(payload);
        EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
      })));
}

// Note that the sensitive sensor routine has no parameters, so we only need to
// test that it can be run successfully.
TEST_F(DeviceCommandRunRoutineJobTest, RunSensitiveSensorRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(RunJob(
      ash::cros_healthd::mojom::DiagnosticRoutineEnum::kSensitiveSensor,
      std::move(params_dict),
      base::BindLambdaForTesting([](RemoteCommandJob* job) {
        EXPECT_EQ(
            ash::cros_healthd::FakeCrosHealthd::Get()
                ->GetLastRunRoutine()
                .value(),
            ash::cros_healthd::mojom::DiagnosticRoutineEnum::kSensitiveSensor);
        EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
        std::unique_ptr<std::string> payload = job->GetResultPayload();
        EXPECT_TRUE(payload);
        EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
      })));
}

// Test that privacy screen routine succeeds with all parameters specified.
TEST_F(DeviceCommandRunRoutineJobTest, RunPrivacyScreenRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  auto params_dict =
      base::Value::Dict().Set(kPrivacyScreenTargetStateFieldName, true);
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kPrivacyScreen,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
             })));
}

// Test that privacy screen routine succeeds without the optional parameter
// |targetState| specified.
TEST_F(DeviceCommandRunRoutineJobTest,
       RunPrivacyScreenRoutineSuccessNoOptionalTargetState) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(
      RunJob(ash::cros_healthd::mojom::DiagnosticRoutineEnum::kPrivacyScreen,
             std::move(params_dict),
             base::BindLambdaForTesting([](RemoteCommandJob* job) {
               EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
               std::unique_ptr<std::string> payload = job->GetResultPayload();
               EXPECT_TRUE(payload);
               EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
             })));
}

// Note that the eMMC lifetime routine has no parameters, so we only need to
// test that it can be run successfully.
TEST_F(DeviceCommandRunRoutineJobTest, RunEmmcLifetimeRoutineSuccess) {
  auto run_routine_response =
      ash::cros_healthd::mojom::RunRoutineResponse::New(kId, kStatus);
  ash::cros_healthd::FakeCrosHealthd::Get()->SetRunRoutineResponseForTesting(
      run_routine_response);
  base::Value::Dict params_dict;
  EXPECT_TRUE(RunJob(
      ash::cros_healthd::mojom::DiagnosticRoutineEnum::kEmmcLifetime,
      std::move(params_dict),
      base::BindLambdaForTesting([](RemoteCommandJob* job) {
        EXPECT_EQ(
            ash::cros_healthd::FakeCrosHealthd::Get()
                ->GetLastRunRoutine()
                .value(),
            ash::cros_healthd::mojom::DiagnosticRoutineEnum::kEmmcLifetime);
        EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
        std::unique_ptr<std::string> payload = job->GetResultPayload();
        EXPECT_TRUE(payload);
        EXPECT_EQ(CreateSuccessPayload(kId, kStatus), *payload);
      })));
}

}  // namespace policy