chromium/chromeos/ash/services/device_sync/cryptauth_device_registry_impl_unittest.cc

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

#include "chromeos/ash/services/device_sync/cryptauth_device_registry_impl.h"

#include <map>
#include <memory>
#include <string>
#include <utility>

#include "base/no_destructor.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chromeos/ash/components/multidevice/software_feature.h"
#include "chromeos/ash/components/multidevice/software_feature_state.h"
#include "chromeos/ash/services/device_sync/cryptauth_device_registry.h"
#include "chromeos/ash/services/device_sync/pref_names.h"
#include "chromeos/ash/services/device_sync/proto/cryptauth_better_together_device_metadata.pb.h"
#include "chromeos/ash/services/device_sync/proto/cryptauth_v2_test_util.h"
#include "chromeos/ash/services/device_sync/value_string_encoding.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash::device_sync {

namespace {

const char kInstanceId0[] = "instance_id_0";
const char kInstanceId1[] = "instance_id_1";

}  // namespace

class DeviceSyncCryptAuthDeviceRegistryImplTest : public testing::Test {
 public:
  DeviceSyncCryptAuthDeviceRegistryImplTest(
      const DeviceSyncCryptAuthDeviceRegistryImplTest&) = delete;
  DeviceSyncCryptAuthDeviceRegistryImplTest& operator=(
      const DeviceSyncCryptAuthDeviceRegistryImplTest&) = delete;

 protected:
  DeviceSyncCryptAuthDeviceRegistryImplTest() = default;

  ~DeviceSyncCryptAuthDeviceRegistryImplTest() override = default;

  void SetUp() override {
    CryptAuthDeviceRegistryImpl::RegisterPrefs(pref_service_.registry());
  }

  void CreateDeviceRegistry() {
    device_registry_ =
        CryptAuthDeviceRegistryImpl::Factory::Create(&pref_service_);
  }

  const CryptAuthDevice& GetDeviceForTest(size_t index) const {
    static const base::NoDestructor<std::vector<CryptAuthDevice>> devices([] {
      const char kDeviceName0[] = "device_name_0";
      const char kDeviceName1[] = "device_name_1";
      const char kDeviceBetterTogetherPublicKey0[] =
          "device_better_together_public_key_0";
      const char kDeviceBetterTogetherPublicKey1[] =
          "device_better_together_public_key_1";
      const base::Time kLastUpdateTime0 =
          base::Time::FromSecondsSinceUnixEpoch(100);
      const base::Time kLastUpdateTime1 =
          base::Time::FromSecondsSinceUnixEpoch(200);
      const std::map<multidevice::SoftwareFeature,
                     multidevice::SoftwareFeatureState>
          kFakeFeatureStates0 = {
              {multidevice::SoftwareFeature::kBetterTogetherClient,
               multidevice::SoftwareFeatureState::kEnabled}};
      const std::map<multidevice::SoftwareFeature,
                     multidevice::SoftwareFeatureState>
          kFakeFeatureStates1 = {
              {multidevice::SoftwareFeature::kBetterTogetherHost,
               multidevice::SoftwareFeatureState::kEnabled}};

      return std::vector<CryptAuthDevice>{
          CryptAuthDevice(kInstanceId0, kDeviceName0,
                          kDeviceBetterTogetherPublicKey0, kLastUpdateTime0,
                          cryptauthv2::GetBetterTogetherDeviceMetadataForTest(),
                          kFakeFeatureStates0),
          CryptAuthDevice(kInstanceId1, kDeviceName1,
                          kDeviceBetterTogetherPublicKey1, kLastUpdateTime1,
                          std::nullopt /* better_together_device_metadata */,
                          kFakeFeatureStates1)};
    }());

    EXPECT_LT(index, devices->size());
    return devices->at(index);
  }

  base::Value::Dict AsDictionary(
      const CryptAuthDeviceRegistry::InstanceIdToDeviceMap& devices) const {
    base::Value::Dict dict;
    for (const std::pair<std::string, CryptAuthDevice>& id_device_pair :
         devices) {
      dict.Set(util::EncodeAsString(id_device_pair.first),
               id_device_pair.second.AsDictionary());
    }

    return dict;
  }

  void VerifyDeviceRegistry(
      const CryptAuthDeviceRegistry::InstanceIdToDeviceMap& expected_devices) {
    EXPECT_EQ(expected_devices, device_registry()->instance_id_to_device_map());

    // Verify pref.
    EXPECT_EQ(AsDictionary(expected_devices),
              pref_service_.GetDict(prefs::kCryptAuthDeviceRegistry));
  }

  PrefService* pref_service() { return &pref_service_; }

  CryptAuthDeviceRegistry* device_registry() { return device_registry_.get(); }

 private:
  TestingPrefServiceSimple pref_service_;

  std::unique_ptr<CryptAuthDeviceRegistry> device_registry_;
};

TEST_F(DeviceSyncCryptAuthDeviceRegistryImplTest, AddAndGetDevices) {
  CreateDeviceRegistry();

  EXPECT_TRUE(device_registry()->AddDevice(GetDeviceForTest(0)));
  EXPECT_FALSE(device_registry()->AddDevice(GetDeviceForTest(0)));
  EXPECT_TRUE(device_registry()->AddDevice(GetDeviceForTest(1)));

  VerifyDeviceRegistry(
      {{kInstanceId0, GetDeviceForTest(0)},
       {kInstanceId1, GetDeviceForTest(1)}} /* expected_devices */);
  EXPECT_EQ(GetDeviceForTest(0), *device_registry()->GetDevice(kInstanceId0));
  EXPECT_EQ(GetDeviceForTest(1), *device_registry()->GetDevice(kInstanceId1));
}

TEST_F(DeviceSyncCryptAuthDeviceRegistryImplTest, OverwriteDevice) {
  CreateDeviceRegistry();

  EXPECT_TRUE(device_registry()->AddDevice(GetDeviceForTest(0)));
  EXPECT_EQ(GetDeviceForTest(0), *device_registry()->GetDevice(kInstanceId0));

  CryptAuthDevice device_with_same_instance_id(
      kInstanceId0, "name", "key", base::Time::FromSecondsSinceUnixEpoch(5000),
      cryptauthv2::BetterTogetherDeviceMetadata(), {});
  EXPECT_TRUE(device_registry()->AddDevice(device_with_same_instance_id));
  EXPECT_EQ(device_with_same_instance_id,
            *device_registry()->GetDevice(kInstanceId0));
}

TEST_F(DeviceSyncCryptAuthDeviceRegistryImplTest, OverwriteRegistry) {
  CreateDeviceRegistry();

  CryptAuthDeviceRegistry::InstanceIdToDeviceMap old_devices = {
      {kInstanceId0, GetDeviceForTest(0)}};
  EXPECT_TRUE(device_registry()->SetRegistry(old_devices));
  VerifyDeviceRegistry(old_devices);
  EXPECT_FALSE(device_registry()->SetRegistry(old_devices));

  CryptAuthDeviceRegistry::InstanceIdToDeviceMap new_devices = {
      {kInstanceId1, GetDeviceForTest(1)}};
  EXPECT_TRUE(device_registry()->SetRegistry(new_devices));
  VerifyDeviceRegistry(new_devices);
}

TEST_F(DeviceSyncCryptAuthDeviceRegistryImplTest, DeleteDevice) {
  CreateDeviceRegistry();

  EXPECT_TRUE(device_registry()->AddDevice(GetDeviceForTest(0)));
  VerifyDeviceRegistry(
      {{kInstanceId0, GetDeviceForTest(0)}} /* expected_devices */);

  EXPECT_TRUE(device_registry()->DeleteDevice(kInstanceId0));
  EXPECT_FALSE(device_registry()->GetDevice(kInstanceId0));
  VerifyDeviceRegistry({} /* expected_devices */);
  EXPECT_FALSE(device_registry()->DeleteDevice(kInstanceId0));
}

TEST_F(DeviceSyncCryptAuthDeviceRegistryImplTest, PopulateRegistryFromPref) {
  CryptAuthDeviceRegistry::InstanceIdToDeviceMap expected_devices = {
      {kInstanceId0, GetDeviceForTest(0)}, {kInstanceId1, GetDeviceForTest(1)}};
  pref_service()->SetDict(prefs::kCryptAuthDeviceRegistry,
                          AsDictionary(expected_devices));

  CreateDeviceRegistry();

  VerifyDeviceRegistry(expected_devices);
}

}  // namespace ash::device_sync