chromium/chrome/browser/ash/policy/networking/euicc_status_uploader_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/ash/policy/networking/euicc_status_uploader.h"

#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "base/json/json_string_value_serializer.h"
#include "base/ranges/algorithm.h"
#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/values_test_util.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chromeos/ash/components/dbus/shill/shill_manager_client.h"
#include "chromeos/ash/components/network/managed_cellular_pref_handler.h"
#include "chromeos/ash/components/network/managed_network_configuration_handler.h"
#include "chromeos/ash/components/network/network_handler_test_helper.h"
#include "chromeos/ash/components/network/network_ui_data.h"
#include "chromeos/ash/components/network/policy_util.h"
#include "chromeos/components/onc/onc_utils.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
#include "components/prefs/testing_pref_service.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"

namespace policy {

namespace {

class FakeCloudPolicyClient : public testing::NiceMock<MockCloudPolicyClient> {
 public:
  using UploadEuiccInfoCallbackHandler =
      base::RepeatingCallback<void(base::OnceCallback<void(bool)>)>;

  void SetHandler(UploadEuiccInfoCallbackHandler handler) {
    handler_ = handler;
  }
  void SetStatus(bool status) { status_ = status; }

  enterprise_management::UploadEuiccInfoRequest* GetLastRequest() {
    if (requests_.empty())
      return nullptr;
    return requests_.back().get();
  }

  int num_requests() const { return requests_.size(); }

 private:
  void UploadEuiccInfo(
      std::unique_ptr<enterprise_management::UploadEuiccInfoRequest> request,
      base::OnceCallback<void(bool)> callback) override {
    requests_.push_back(std::move(request));
    if (handler_) {
      handler_.Run(std::move(callback));
      return;
    }
    std::move(callback).Run(status_);
  }

  std::vector<std::unique_ptr<enterprise_management::UploadEuiccInfoRequest>>
      requests_;
  bool status_ = false;
  // This member is used to control how the callback is executed when
  // UploadEuiccInfo() is called.
  UploadEuiccInfoCallbackHandler handler_;
};

bool RequestsAreEqual(
    const enterprise_management::UploadEuiccInfoRequest& lhs,
    const enterprise_management::UploadEuiccInfoRequest& rhs) {
  const auto proj = [](const auto& profile) {
    return std::tie(profile.iccid(), profile.smdp_address());
  };
  return lhs.euicc_count() == rhs.euicc_count() &&
         base::ranges::equal(lhs.esim_profiles(), rhs.esim_profiles(),
                             std::equal_to<>(), proj, proj) &&
         lhs.clear_profile_list() == rhs.clear_profile_list();
}

const char kDmToken[] = "token";
const char kFakeObjectPath[] = "object-path";
const char kFakeEid[] = "12";
const char kEuiccStatusUploadResultHistogram[] =
    "Network.Cellular.ESim.Policy.EuiccStatusUploadResult";

const char kEuiccStatus_Empty[] =
    R"({
        "esim_profiles": [],
        "euicc_count": 0
       })";
const char kEuiccStatus_OneProfileWithMissingName[] =
    R"({
        "esim_profiles": [
          {
            "iccid": "iccid-1",
            "smdp_activation_code": "smdp-1"
          }
        ],
        "euicc_count": 1
       })";
const char kEuiccStatus_OneProfileWithEmptyActivationCode[] =
    R"({
        "esim_profiles": [],
        "euicc_count": 1
       })";
const char kEuiccStatus_OneProfile[] =
    R"({
        "esim_profiles": [
          {
            "iccid": "iccid-1",
            "network_name": "name-1",
            "smdp_activation_code": "smdp-1"
          }
        ],
        "euicc_count": 1
       })";
const char kEuiccStatus_TwoProfiles[] =
    R"({
        "esim_profiles": [
          {
            "iccid": "iccid-1",
            "network_name": "name-1",
            "smdp_activation_code": "smdp-1"
          },
          {
            "iccid": "iccid-2",
            "network_name": "name-2",
            "smdp_activation_code": "smdp-2"
          }
        ],
        "euicc_count": 2
       })";
const char kEuiccStatus_FourProfiles[] =
    R"({
        "esim_profiles": [
          {
            "iccid": "iccid-1",
            "network_name": "name-1",
            "smdp_activation_code": "smdp-1"
          },
          {
            "iccid": "iccid-2",
            "network_name": "name-2",
            "smdp_activation_code": "smdp-2"
          },
          {
            "iccid": "iccid-3",
            "network_name": "name-3",
            "smds_activation_code": "smds-3"
          },
          {
            "iccid": "iccid-4",
            "network_name": "name-4",
            "smds_activation_code": "smds-4"
          }
        ],
        "euicc_count": 3
       })";
const char kEuiccStatus_AfterReset[] =
    R"({
        "esim_profiles": [],
        "euicc_count": 4
      })";

const char kCellularServicePath0[] = "/service/cellular0";
const char kCellularServicePath1[] = "/service/cellular1";
const char kCellularServicePath2[] = "/service/cellular2";
const char kCellularServicePath3[] = "/service/cellular3";
const char kCellularProfilePath0[] = "/org/chromium/Hermes/Profile/0";
const char kCellularProfilePath1[] = "/org/chromium/Hermes/Profile/1";
const char kCellularProfilePath2[] = "/org/chromium/Hermes/Profile/2";
const char kCellularProfilePath3[] = "/org/chromium/Hermes/Profile/3";

struct FakeESimProfile {
  std::string profile_path;
  std::string service_path;
  std::string guid;
  std::string iccid;
  ash::policy_util::SmdxActivationCode::Type activation_code_type;
  std::string activation_code_value;
  std::string network_name;
  hermes::profile::State state;
  bool managed = true;
};
struct EuiccTestData {
  int euicc_count = 0;
  std::vector<FakeESimProfile> profiles;
};

const EuiccTestData kEuiccTestData_OneProfile = {
    1,
    {
        {kCellularProfilePath0, kCellularServicePath0, "guid-1", "iccid-1",
         ash::policy_util::SmdxActivationCode::Type::SMDP, "smdp-1", "name-1",
         hermes::profile::State::kActive, true},
    },
};
const EuiccTestData kEuiccTestData_OneProfileWithMissingName = {
    1,
    {
        {kCellularProfilePath0, kCellularServicePath0, "guid-1", "iccid-1",
         ash::policy_util::SmdxActivationCode::Type::SMDP, "smdp-1", "",
         hermes::profile::State::kActive, true},
    },
};
const EuiccTestData kEuiccTestData_OneProfileWithEmptyActivationCode = {
    1,
    {
        {kCellularProfilePath0, kCellularServicePath0, "guid-1", "iccid-1",
         ash::policy_util::SmdxActivationCode::Type::SMDP, "", "name-1",
         hermes::profile::State::kActive, true},
    },
};
const EuiccTestData kEuiccTestData_TwoProfiles = {
    2,
    {
        {kCellularProfilePath0, kCellularServicePath0, "guid-1", "iccid-1",
         ash::policy_util::SmdxActivationCode::Type::SMDP, "smdp-1", "name-1",
         hermes::profile::State::kActive, true},
        {kCellularProfilePath1, kCellularServicePath1, "guid-2", "iccid-2",
         ash::policy_util::SmdxActivationCode::Type::SMDP, "smdp-2", "name-2",
         hermes::profile::State::kInactive, true},
    },
};
const EuiccTestData kEuiccTestData_FourProfiles = {
    3,
    {
        {kCellularProfilePath0, kCellularServicePath0, "guid-1", "iccid-1",
         ash::policy_util::SmdxActivationCode::Type::SMDP, "smdp-1", "name-1",
         hermes::profile::State::kActive, true},
        {kCellularProfilePath1, kCellularServicePath1, "guid-2", "iccid-2",
         ash::policy_util::SmdxActivationCode::Type::SMDP, "smdp-2", "name-2",
         hermes::profile::State::kInactive, true},
        {kCellularProfilePath2, kCellularServicePath2, "guid-3", "iccid-3",
         ash::policy_util::SmdxActivationCode::Type::SMDS, "smds-3", "name-3",
         hermes::profile::State::kActive, true},
        {kCellularProfilePath3, kCellularServicePath3, "guid-4", "iccid-4",
         ash::policy_util::SmdxActivationCode::Type::SMDS, "smds-4", "name-4",
         hermes::profile::State::kInactive, true},
    },
};
const EuiccTestData kEuiccTestData_AfterReset = {4, {}};

std::string GetEid(int euicc_id) {
  return base::StringPrintf("%s%d", kFakeObjectPath, euicc_id);
}

std::string GetEuiccPath(int euicc_id) {
  return base::StringPrintf("%s%d", kFakeEid, euicc_id);
}

}  // namespace

class EuiccStatusUploaderTest : public testing::Test {
 public:

  void SetUp() override {
    helper_ = std::make_unique<ash::NetworkHandlerTestHelper>();

    EuiccStatusUploader::RegisterLocalStatePrefs(local_state_.registry());
    helper_->RegisterPrefs(nullptr, local_state_.registry());
    helper_->InitializePrefs(nullptr, &local_state_);
    SetPolicyClientIsRegistered(/*is_registered=*/true);
  }

  std::unique_ptr<EuiccStatusUploader> CreateStatusUploader(
      bool is_policy_fetched = true) {
    auto status_uploader = base::WrapUnique(new EuiccStatusUploader(
        &cloud_policy_client_, &local_state_,
        base::BindRepeating(&EuiccStatusUploaderTest::is_device_active,
                            base::Unretained(this))));
    if (is_policy_fetched) {
      SetPolicyFetched(status_uploader.get());
    }
    return status_uploader;
  }

  void SetPolicyClientIsRegistered(bool is_registered) {
    cloud_policy_client_.dm_token_ = is_registered ? kDmToken : std::string();
  }

  void SetPolicyFetched(EuiccStatusUploader* status_uploader) {
    status_uploader->OnPolicyFetched(&cloud_policy_client_);
  }

  void SetServerSuccessStatus(bool success) {
    cloud_policy_client_.SetStatus(success);
  }

  void SetPolicyClientHandler(
      FakeCloudPolicyClient::UploadEuiccInfoCallbackHandler handler) {
    cloud_policy_client_.SetHandler(std::move(handler));
  }

  const base::Value& GetStoredPref() {
    return local_state_.GetValue(
        EuiccStatusUploader::kLastUploadedEuiccStatusPref);
  }

  std::string GetStoredPrefString() {
    const base::Value& last_uploaded_pref = GetStoredPref();
    std::string result;
    JSONStringValueSerializer sz(&result);
    sz.Serialize(last_uploaded_pref);
    return result;
  }

  void UpdateUploader(EuiccStatusUploader* status_uploader) {
    (static_cast<ash::NetworkPolicyObserver*>(status_uploader))
        ->PoliciesApplied(/*userhash=*/std::string());
    status_uploader->FireRetryTimerIfExistsForTesting();
  }

  void SetupEuicc(int euicc_id = 0) {
    ash::HermesManagerClient::Get()->GetTestInterface()->AddEuicc(
        dbus::ObjectPath(GetEuiccPath(euicc_id)), GetEid(euicc_id),
        /*is_active=*/true, euicc_id);
  }

  void SetUpDeviceProfiles(const EuiccTestData& data) {

    // Create |data.euicc_count| fake EUICCs.
    ash::HermesManagerClient::Get()->GetTestInterface()->ClearEuiccs();
    for (int euicc_id = 0; euicc_id < data.euicc_count; euicc_id++) {
      SetupEuicc(euicc_id);
    }

    for (const auto& test_profile : data.profiles) {
      ash::HermesEuiccClient::Get()->GetTestInterface()->AddCarrierProfile(
          dbus::ObjectPath(test_profile.profile_path),
          dbus::ObjectPath(GetEuiccPath(/*euicc_id=*/0)), test_profile.iccid,
          test_profile.guid, "nickname", "service_provider",
          test_profile.activation_code_value, test_profile.service_path,
          test_profile.state, hermes::profile::ProfileClass::kOperational,
          ash::HermesEuiccClient::TestInterface::AddCarrierProfileBehavior::
              kAddProfileWithService);

      if (test_profile.managed) {
        // We set the prefs directly so that we can force situations that
        // otherwise would not be acceptable by the API of
        // ManagedCellularPrefHandler, e.g. an empty name or activation code
        // value, that we want to protect against uploading.
        base::Value::Dict esim_metadata;
        esim_metadata.Set(::onc::network_config::kName,
                          test_profile.network_name);
        esim_metadata.Set(
            test_profile.activation_code_type ==
                    ash::policy_util::SmdxActivationCode::Type::SMDP
                ? ::onc::cellular::kSMDPAddress
                : ::onc::cellular::kSMDSAddress,
            test_profile.activation_code_value);

        base::Value::Dict existing_prefs =
            local_state_.GetDict(ash::prefs::kManagedCellularESimMetadata)
                .Clone();
        existing_prefs.Set(test_profile.iccid, std::move(esim_metadata));
        local_state_.Set(ash::prefs::kManagedCellularESimMetadata,
                         base::Value(std::move(existing_prefs)));
      }
    }

    // Wait for Shill device and service change notifications to propagate.
    base::RunLoop().RunUntilIdle();
  }

  void ValidateUploadedStatus(const std::string& expected_status_str,
                              bool clear_profile_list) {
    base::Value expected_status = base::test::ParseJson(expected_status_str);
    EXPECT_EQ(expected_status, GetStoredPref());
    EXPECT_TRUE(cloud_policy_client_.GetLastRequest());
    EXPECT_TRUE(
        RequestsAreEqual(*EuiccStatusUploader::ConstructRequestFromStatus(
                             expected_status.GetDict(), clear_profile_list),
                         *cloud_policy_client_.GetLastRequest()));
  }

  void SetLastUploadedValue(const std::string& last_value) {
    local_state_.Set(EuiccStatusUploader::kLastUploadedEuiccStatusPref,
                     base::test::ParseJson(last_value));
  }

  void TriggerManagedCellularPrefChanged(EuiccStatusUploader* status_uploader) {
    static_cast<ash::ManagedCellularPrefHandler::Observer*>(status_uploader)
        ->OnManagedCellularPrefChanged();
  }

  void ExecuteResetCommand(EuiccStatusUploader* status_uploader) {
    SetUpDeviceProfiles(kEuiccTestData_AfterReset);

    // TODO(crbug.com/40205133): Make FakeHermesEuiccClient trigger OnEuiccReset
    // directly.
    static_cast<ash::HermesEuiccClient::Observer*>(status_uploader)
        ->OnEuiccReset(dbus::ObjectPath());
  }

  int GetRequestCount() { return cloud_policy_client_.num_requests(); }

  enterprise_management::UploadEuiccInfoRequest* GetLastRequest() {
    return cloud_policy_client_.GetLastRequest();
  }

  void CheckHistogram(int total_count, int success_count, int failed_count) {
    histogram_tester_.ExpectTotalCount(kEuiccStatusUploadResultHistogram,
                                       total_count);
    histogram_tester_.ExpectBucketCount(kEuiccStatusUploadResultHistogram, true,
                                        /*expected_count=*/success_count);
    histogram_tester_.ExpectBucketCount(kEuiccStatusUploadResultHistogram,
                                        false,
                                        /*expected_count=*/failed_count);
  }

  void SetIsDeviceActive(bool value) { is_device_active_ = value; }

 private:
  bool is_device_active() { return is_device_active_; }

  bool is_device_active_ = true;
  content::BrowserTaskEnvironment task_environment_;
  FakeCloudPolicyClient cloud_policy_client_;
  TestingPrefServiceSimple local_state_;
  std::unique_ptr<ash::NetworkHandlerTestHelper> helper_;
  base::HistogramTester histogram_tester_;
};

TEST_F(EuiccStatusUploaderTest, EmptySetup) {
  auto status_uploader = CreateStatusUploader();
  EXPECT_EQ(GetRequestCount(), 0);
  // No value is uploaded yet.
  EXPECT_EQ("{}", GetStoredPrefString());

  // Make server accept requests.
  SetServerSuccessStatus(true);
  UpdateUploader(status_uploader.get());
  // Verify that no status is uploaded if there is no EUICC.
  EXPECT_EQ(GetRequestCount(), 0);
  CheckHistogram(/*total_count=*/0, /*success_count=*/0, /*failed_count=*/0);
}

TEST_F(EuiccStatusUploaderTest, InactiveDevice) {
  SetIsDeviceActive(false);
  auto status_uploader = CreateStatusUploader();
  EXPECT_EQ(GetRequestCount(), 0);
  // No value is uploaded yet.
  EXPECT_EQ("{}", GetStoredPrefString());

  // Make server accept requests.
  SetServerSuccessStatus(true);
  UpdateUploader(status_uploader.get());
  // Verify that no status is uploaded if the device is inactive.
  EXPECT_EQ(GetRequestCount(), 0);
  CheckHistogram(/*total_count=*/0, /*success_count=*/0, /*failed_count=*/0);
}

TEST_F(EuiccStatusUploaderTest, ClientNotRegistered) {
  SetupEuicc();
  base::RunLoop().RunUntilIdle();
  SetPolicyClientIsRegistered(/*is_registered=*/false);

  auto status_uploader = CreateStatusUploader();
  EXPECT_EQ(GetRequestCount(), 0);
  // No value is uploaded yet.
  EXPECT_EQ("{}", GetStoredPrefString());

  UpdateUploader(status_uploader.get());
  // Verify that no requests are made if client is not registered.
  EXPECT_EQ(GetRequestCount(), 0);
  EXPECT_EQ("{}", GetStoredPrefString());
  CheckHistogram(/*total_count=*/0, /*success_count=*/0, /*failed_count=*/0);
}

TEST_F(EuiccStatusUploaderTest, ServerError) {
  SetupEuicc();
  base::RunLoop().RunUntilIdle();
  auto status_uploader = CreateStatusUploader();
  UpdateUploader(status_uploader.get());
  EXPECT_EQ(GetRequestCount(), 2);
  // Nothing is stored when requests fail.
  EXPECT_EQ("{}", GetStoredPrefString());
  CheckHistogram(/*total_count=*/2, /*success_count=*/0, /*failed_count=*/2);
}

TEST_F(EuiccStatusUploaderTest, WaitForPolicyFetch) {
  SetUpDeviceProfiles(kEuiccTestData_OneProfile);

  auto status_uploader = CreateStatusUploader(/*is_policy_fetched=*/false);
  EXPECT_EQ(GetRequestCount(), 0);
  // No value is uploaded yet.
  EXPECT_EQ("{}", GetStoredPrefString());

  // Verify that no requests are made when policy has not been fetched.
  SetServerSuccessStatus(true);
  UpdateUploader(status_uploader.get());
  EXPECT_EQ(GetRequestCount(), 0);

  // Verify that status is uploaded correctly when policy is fetched.
  SetPolicyFetched(status_uploader.get());
  ValidateUploadedStatus(kEuiccStatus_OneProfile,
                         /*clear_profile_list=*/false);
  CheckHistogram(/*total_count=*/1, /*success_count=*/1, /*failed_count=*/0);
}

TEST_F(EuiccStatusUploaderTest, Basic) {
  SetUpDeviceProfiles(kEuiccTestData_OneProfile);

  auto status_uploader = CreateStatusUploader();
  // Initial upload request.
  EXPECT_EQ(GetRequestCount(), 1);
  // No value is uploaded yet.
  EXPECT_EQ("{}", GetStoredPrefString());
  CheckHistogram(/*total_count=*/1, /*success_count=*/0, /*failed_count=*/1);

  // Make server accept requests.
  SetServerSuccessStatus(true);
  UpdateUploader(status_uploader.get());
  EXPECT_EQ(GetRequestCount(), 2);
  // Verify that last uploaded configuration is stored.
  ValidateUploadedStatus(kEuiccStatus_OneProfile,
                         /*clear_profile_list=*/false);
  CheckHistogram(/*total_count=*/2, /*success_count=*/1, /*failed_count=*/1);
}

TEST_F(EuiccStatusUploaderTest, BasicWithMissingName) {
  SetUpDeviceProfiles(kEuiccTestData_OneProfileWithMissingName);

  auto status_uploader = CreateStatusUploader();
  // Initial upload request.
  EXPECT_EQ(GetRequestCount(), 1);
  // No value is uploaded yet.
  EXPECT_EQ("{}", GetStoredPrefString());
  CheckHistogram(/*total_count=*/1, /*success_count=*/0, /*failed_count=*/1);

  // Make server accept requests.
  SetServerSuccessStatus(true);
  UpdateUploader(status_uploader.get());
  EXPECT_EQ(GetRequestCount(), 2);
  // Verify that last uploaded configuration is stored.
  ValidateUploadedStatus(kEuiccStatus_OneProfileWithMissingName,
                         /*clear_profile_list=*/false);
  CheckHistogram(/*total_count=*/2, /*success_count=*/1, /*failed_count=*/1);
}

TEST_F(EuiccStatusUploaderTest, BasicWithEmptyActivationCode) {
  SetUpDeviceProfiles(kEuiccTestData_OneProfileWithEmptyActivationCode);

  auto status_uploader = CreateStatusUploader();
  // Initial upload request.
  EXPECT_EQ(GetRequestCount(), 1);
  // No value is uploaded yet.
  EXPECT_EQ("{}", GetStoredPrefString());
  CheckHistogram(/*total_count=*/1, /*success_count=*/0, /*failed_count=*/1);

  // Make server accept requests.
  SetServerSuccessStatus(true);
  UpdateUploader(status_uploader.get());
  EXPECT_EQ(GetRequestCount(), 2);
  // Verify that last uploaded configuration is stored.
  ValidateUploadedStatus(kEuiccStatus_OneProfileWithEmptyActivationCode,
                         /*clear_profile_list=*/false);
  CheckHistogram(/*total_count=*/2, /*success_count=*/1, /*failed_count=*/1);
}

TEST_F(EuiccStatusUploaderTest, TwoProfiles) {
  SetUpDeviceProfiles(kEuiccTestData_TwoProfiles);

  auto status_uploader = CreateStatusUploader();
  // Initial upload request.
  EXPECT_EQ(GetRequestCount(), 1);
  // No value is uploaded yet.
  EXPECT_EQ("{}", GetStoredPrefString());
  CheckHistogram(/*total_count=*/1, /*success_count=*/0, /*failed_count=*/1);

  // Make server accept requests.
  SetServerSuccessStatus(true);
  UpdateUploader(status_uploader.get());
  EXPECT_EQ(GetRequestCount(), 2);

  // Verify that last uploaded configuration is stored.
  ValidateUploadedStatus(kEuiccStatus_TwoProfiles,
                         /*clear_profile_list=*/false);
  CheckHistogram(/*total_count=*/2, /*success_count=*/1, /*failed_count=*/1);
}

TEST_F(EuiccStatusUploaderTest, FourProfilesWithSmds) {
  SetUpDeviceProfiles(kEuiccTestData_FourProfiles);

  auto status_uploader = CreateStatusUploader();
  // Initial upload request.
  EXPECT_EQ(GetRequestCount(), 1);
  // No value is uploaded yet.
  EXPECT_EQ("{}", GetStoredPrefString());
  CheckHistogram(/*total_count=*/1, /*success_count=*/0, /*failed_count=*/1);

  // Make server accept requests.
  SetServerSuccessStatus(true);
  UpdateUploader(status_uploader.get());
  EXPECT_EQ(GetRequestCount(), 2);

  // Verify that last uploaded configuration is stored.
  ValidateUploadedStatus(kEuiccStatus_FourProfiles,
                         /*clear_profile_list=*/false);
  CheckHistogram(/*total_count=*/2, /*success_count=*/1, /*failed_count=*/1);
}

TEST_F(EuiccStatusUploaderTest, SameValueAsBefore) {
  // Make server accept requests.
  SetServerSuccessStatus(true);
  // Mark the current state as already uploaded.
  SetUpDeviceProfiles(kEuiccTestData_OneProfile);
  SetLastUploadedValue(kEuiccStatus_OneProfile);

  auto status_uploader = CreateStatusUploader();
  // No value is uploaded since it has been previously sent.
  EXPECT_EQ(GetRequestCount(), 0);
  CheckHistogram(/*total_count=*/0, /*success_count=*/0, /*failed_count=*/0);
}

TEST_F(EuiccStatusUploaderTest, NewValue) {
  // Make server accept requests.
  SetServerSuccessStatus(true);
  // Set up a value different from one that was previously uploaded.
  SetUpDeviceProfiles(kEuiccTestData_OneProfile);
  SetLastUploadedValue(kEuiccStatus_Empty);

  auto status_uploader = CreateStatusUploader();
  // Verify that last uploaded configuration is stored.
  ValidateUploadedStatus(kEuiccStatus_OneProfile,
                         /*clear_profile_list=*/false);
  CheckHistogram(/*total_count=*/1, /*success_count=*/1, /*failed_count=*/0);
}

TEST_F(EuiccStatusUploaderTest, ResetRequest) {
  // Make server accept requests.
  SetServerSuccessStatus(true);
  // Set up a value different from one that was previously uploaded.
  SetUpDeviceProfiles(kEuiccTestData_OneProfile);
  SetLastUploadedValue(kEuiccStatus_Empty);

  auto status_uploader = CreateStatusUploader();
  // Verify that last uploaded configuration is stored.
  ValidateUploadedStatus(kEuiccStatus_OneProfile,
                         /*clear_profile_list=*/false);

  // Reset remote command was received and executed.
  ExecuteResetCommand(status_uploader.get());
  // Request has been sent.
  EXPECT_EQ(GetRequestCount(), 3);

  ValidateUploadedStatus(kEuiccStatus_AfterReset,
                         /*clear_profile_list=*/true);

  // Send the reset command again.
  ExecuteResetCommand(status_uploader.get());
  // Request will be force-sent again because we've received a reset command..
  EXPECT_EQ(GetRequestCount(), 4);

  ValidateUploadedStatus(kEuiccStatus_AfterReset,
                         /*clear_profile_list=*/true);
}

TEST_F(EuiccStatusUploaderTest, ClearProfileListRaceCondition) {
  SetUpDeviceProfiles(kEuiccTestData_OneProfile);

  // Create the status uploader but do not trigger an upload via the policies
  // being fetched.
  auto status_uploader = CreateStatusUploader(/*is_policy_fetched=*/false);
  EXPECT_EQ(GetRequestCount(), 0);

  // Capture the callback that the status uploader provides to the cloud policy
  // client to allow us to block the completion of the first upload.
  base::OnceCallback<void(bool)> callback;
  SetPolicyClientHandler(base::BindRepeating(
      [](base::OnceCallback<void(bool)>* callback_out,
         base::OnceCallback<void(bool)> callback_in) {
        *callback_out = std::move(callback_in);
      },
      &callback));

  // Trigger the first upload.
  SetPolicyFetched(status_uploader.get());

  EXPECT_FALSE(GetLastRequest()->clear_profile_list());
  EXPECT_EQ(GetRequestCount(), 1);

  // Simulate a race condition where an upload is in progress and an EUICC reset
  // causes the "clear profile list" pref to be set to |true|. This pref should
  // not be cleared when the first upload finishes. This won't affect the value
  // of |callback| since an ongoing upload is blocking.
  ExecuteResetCommand(status_uploader.get());

  // Complete the first status upload. When this completes the status uploader
  // class will check if it should perform another upload; this class will
  // identify that the EUICC was reset and will trigger a second upload.
  ASSERT_TRUE(callback);
  std::move(callback).Run(true);

  EXPECT_TRUE(GetLastRequest()->clear_profile_list());
  EXPECT_EQ(GetRequestCount(), 2);
}

TEST_F(EuiccStatusUploaderTest, UnexpectedNetworkHandlerShutdown) {
  SetUpDeviceProfiles(kEuiccTestData_OneProfile);
  // NetworkHandler has not been initialized.
  auto status_uploader = CreateStatusUploader();

  // Initial Request
  EXPECT_EQ(GetRequestCount(), 1);

  // Requests made normally.
  UpdateUploader(status_uploader.get());
  EXPECT_EQ(GetRequestCount(), 2);

  // NetworkHandler::Shutdown() has already been called before
  // EuiccStatusUploader is deleted
  ash::NetworkHandler::Shutdown();

  // No requests made as NetworkHandler is not available.
  UpdateUploader(status_uploader.get());
  EXPECT_EQ(GetRequestCount(), 2);

  // Need to reinitialize before exiting test.
  ash::NetworkHandler::Initialize();
}

}  // namespace policy