chromium/chrome/browser/ash/nearby/presence/credential_storage/nearby_presence_credential_storage_unittest.cc

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

#include "chrome/browser/ash/nearby/presence/credential_storage/nearby_presence_credential_storage.h"

#include <optional>

#include "base/memory/raw_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "chromeos/ash/services/nearby/public/mojom/nearby_presence.mojom.h"
#include "components/leveldb_proto/testing/fake_db.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/nearby/internal/proto/credential.pb.h"
#include "third_party/nearby/internal/proto/local_credential.pb.h"

namespace {

constexpr int64_t kId_1 = 111;
const std::vector<uint8_t> kMetadataEncryptionKeyV0_1 = {
    0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e};
constexpr int64_t kStartTimeMillis_1 = 255486129307;
constexpr int64_t kEndTimeMillis_1 = 265486239507;
const std::vector<uint8_t> kKeySeed_1 = {
    0x21, 0x22, 0x23, 0x24, 0x2A, 0x21, 0x27, 0x28, 0x29, 0x2A, 0x2B,
    0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x37, 0x32, 0x33, 0x34, 0x35, 0x36,
    0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40};
const std::vector<uint8_t> kEncryptedMetadataBytesV0_1 = {0x33, 0x33, 0x33,
                                                          0x33, 0x33, 0x33};
const std::vector<uint8_t> kMetadataEncryptionTag_1 = {0x44, 0x44, 0x44,
                                                       0x44, 0x44, 0x44};
const std::vector<uint8_t> kConnectionSignatureVerificationKey_1 = {
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
const std::vector<uint8_t> kAdvertisementSignatureVerificationKey_1 = {
    0x66, 0x66, 0x66, 0x66, 0x66, 0x66};
const std::vector<uint8_t> kVersion_1 = {0x77, 0x77, 0x77, 0x77, 0x77, 0x77};
const std::vector<uint8_t> kEncryptedMetadataBytesV1_1 = {0x81, 0x81, 0x81,
                                                          0x81, 0x81, 0x81};
const std::vector<uint8_t> kIdentityTokenShortSaltAdvHmacKeyV1_1 = {
    0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1};
const std::vector<uint8_t> kIdentityTokenExtendedSaltAdvHmacKeyV1_1 = {
    0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2};
const std::vector<uint8_t> kIdentityTokenSignedAdvHmacKeyV1_1 = {
    0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3};
const char kSignatureVersion_1[] = "2981212593";
const char AdvertisementSigningKeyCertificateAlias_1[] =
    "NearbySharingABCDEF123456";
const char kDusi_1[] = "11";
const std::vector<uint8_t> kAdvertisementPrivateKey_1 = {0x41, 0x42, 0x43,
                                                         0x44, 0x45, 0x46};
const char ConnectionSigningKeyCertificateAlias_1[] = "NearbySharingXYZ789";
const std::vector<uint8_t> kConnectionPrivateKey_1 = {0x51, 0x52, 0x53,
                                                      0x54, 0x55, 0x56};
const std::vector<uint8_t> kIdentityTokenV1_1 = {
    0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
    0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70};
const base::flat_map<uint32_t, bool> kConsumedSalts_1 = {{0xb412, true},
                                                         {0x34b2, false},
                                                         {0x5171, false}};

constexpr int64_t kId_2 = 222;
const std::vector<uint8_t> kMetadataEncryptionKeyV0_2 = {
    0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3,
    0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA};
constexpr int64_t kStartTimeMillis_2 = 255486129307;
constexpr int64_t kEndTimeMillis_2 = 265486239725;
const std::vector<uint8_t> kKeySeed_2 = {
    0x21, 0x22, 0x23, 0x24, 0x2A, 0x24, 0x27, 0x28, 0x29, 0x2A, 0x2B,
    0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x32, 0x31, 0x23, 0x14, 0x12, 0x21,
    0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3A, 0x3E, 0x3F, 0x31};
const std::vector<uint8_t> kEncryptedMetadataBytesV0_2 = {0x44, 0x44, 0x44,
                                                          0x44, 0x44, 0x44};
const std::vector<uint8_t> kMetadataEncryptionTag_2 = {0x55, 0x55, 0x55,
                                                       0x55, 0x55, 0x55};
const std::vector<uint8_t> kConnectionSignatureVerificationKey_2 = {
    0x66, 0x66, 0x66, 0x66, 0x66, 0x66};
const std::vector<uint8_t> kAdvertisementSignatureVerificationKey_2 = {
    0x66, 0x66, 0x66, 0x66, 0x66, 0x66};
const std::vector<uint8_t> kVersion_2 = {0x88, 0x88, 0x88, 0x88, 0x88, 0x88};
const std::vector<uint8_t> kEncryptedMetadataBytesV1_2 = {0x82, 0x82, 0x82,
                                                          0x82, 0x82, 0x82};
const std::vector<uint8_t> kIdentityTokenShortSaltAdvHmacKeyV1_2 = {
    0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2};
const std::vector<uint8_t> kIdentityTokenExtendedSaltAdvHmacKeyV1_2 = {
    0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3};
const std::vector<uint8_t> kIdentityTokenSignedAdvHmacKeyV1_2 = {
    0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4};
const char kSignatureVersion_2[] = "2998055602";
const char kDusi_2[] = "22";
const char AdvertisementSigningKeyCertificateAlias_2[] =
    "NearbySharingFEDCBA987654";
const std::vector<uint8_t> kAdvertisementPrivateKey_2 = {0xBB, 0xBC, 0xBD,
                                                         0xBE, 0xBF, 0xC0};
const char ConnectionSigningKeyCertificateAlias_2[] = "NearbySharingZYX543";
const std::vector<uint8_t> kConnectionPrivateKey_2 = {0xC1, 0xC2, 0xC3,
                                                      0xC4, 0xC5, 0xC6};
const std::vector<uint8_t> kIdentityTokenV1_2 = {
    0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE,
    0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6};
const base::flat_map<uint32_t, bool> kConsumedSalts_2 = {{0xb412, false},
                                                         {0x34b2, true},
                                                         {0x5171, false}};

constexpr int64_t kId_3 = 333;
const std::vector<uint8_t> kMetadataEncryptionKeyV0_3 = {
    0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13,
    0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A};
constexpr int64_t kStartTimeMillis_3 = 255486129307;
constexpr int64_t kEndTimeMillis_3 = 263485225725;
const std::vector<uint8_t> kKeySeed_3 = {
    0x21, 0x22, 0x23, 0x24, 0x2A, 0x22, 0x27, 0x21, 0x29, 0x2A, 0x2B,
    0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x31, 0x22, 0x14, 0x12, 0x21,
    0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3B, 0x3E, 0x3F, 0x31};
const std::vector<uint8_t> kEncryptedMetadataBytesV0_3 = {0x55, 0x55, 0x55,
                                                          0x55, 0x55, 0x55};
const std::vector<uint8_t> kMetadataEncryptionTag_3 = {0x66, 0x66, 0x66,
                                                       0x66, 0x66, 0x66};
const std::vector<uint8_t> kConnectionSignatureVerificationKey_3 = {
    0x77, 0x77, 0x77, 0x77, 0x77, 0x77};
const std::vector<uint8_t> kAdvertisementSignatureVerificationKey_3 = {
    0x88, 0x88, 0x88, 0x88, 0x88, 0x88};
const std::vector<uint8_t> kVersion_3 = {0x11, 0x12, 0x13, 0x14, 0x15, 0x16};
const std::vector<uint8_t> kEncryptedMetadataBytesV1_3 = {0x83, 0x83, 0x83,
                                                          0x83, 0x83, 0x83};
const std::vector<uint8_t> kIdentityTokenShortSaltAdvHmacKeyV1_3 = {
    0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3};
const std::vector<uint8_t> kIdentityTokenExtendedSaltAdvHmacKeyV1_3 = {
    0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4};
const std::vector<uint8_t> kIdentityTokenSignedAdvHmacKeyV1_3 = {
    0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5};
const char kSignatureVersion_3[] = "3014898611";
const char kDusi_3[] = "33";
const char AdvertisementSigningKeyCertificateAlias_3[] =
    "NearbySharingJIHGFED3210";
const std::vector<uint8_t> kAdvertisementPrivateKey_3 = {0x1B, 0x1C, 0x1D,
                                                         0x1E, 0x1F, 0x20};
const char ConnectionSigningKeyCertificateAlias_3[] = "NearbySharingWVU109";
const std::vector<uint8_t> kConnectionPrivateKey_3 = {0x21, 0x22, 0x23,
                                                      0x24, 0x25, 0x26};
const std::vector<uint8_t> kIdentityTokenV1_3 = {
    0x27, 0x28, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE,
    0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6};
const base::flat_map<uint32_t, bool> kConsumedSalts_3 = {{0xb402, false},
                                                         {0x3202, false},
                                                         {0x5b71, true}};

class TestNearbyPresenceCredentialStorage
    : public ash::nearby::presence::NearbyPresenceCredentialStorage {
 public:
  TestNearbyPresenceCredentialStorage(
      mojo::PendingReceiver<
          ash::nearby::presence::mojom::NearbyPresenceCredentialStorage>
          pending_receiver,
      std::unique_ptr<
          leveldb_proto::ProtoDatabase<::nearby::internal::LocalCredential>>
          private_db,
      std::unique_ptr<
          leveldb_proto::ProtoDatabase<::nearby::internal::SharedCredential>>
          local_public_db,
      std::unique_ptr<
          leveldb_proto::ProtoDatabase<::nearby::internal::SharedCredential>>
          remote_public_db)
      : ash::nearby::presence::NearbyPresenceCredentialStorage(
            std::move(pending_receiver),
            std::move(private_db),
            std::move(local_public_db),
            std::move(remote_public_db)) {}
};

ash::nearby::presence::mojom::LocalCredentialPtr CreateLocalCredential(
    const int64_t id,
    const std::vector<uint8_t>& key_seed,
    const int64_t start_time_millis,
    const int64_t end_time_millis,
    const std::vector<uint8_t>& metadata_encryption_key_v0,
    const std::string& advertisement_signing_key_certificate_alias,
    const std::vector<uint8_t>& advertisement_signing_key_data,
    const std::string& connection_signing_key_certificate_alias,
    const std::vector<uint8_t>& connection_signing_key_data,
    const ash::nearby::presence::mojom::IdentityType identity_type,
    const base::flat_map<uint32_t, bool>& consumed_salts,
    const std::vector<uint8_t>& identity_token_v1,
    const std::string& signature_version) {
  return ash::nearby::presence::mojom::LocalCredential::New(
      /*secret_id=*/std::vector<uint8_t>(), key_seed, start_time_millis,
      end_time_millis, metadata_encryption_key_v0,
      ash::nearby::presence::mojom::PrivateKey::New(
          advertisement_signing_key_certificate_alias,
          advertisement_signing_key_data),
      ash::nearby::presence::mojom::PrivateKey::New(
          connection_signing_key_certificate_alias,
          connection_signing_key_data),
      identity_type, consumed_salts, identity_token_v1, id, signature_version);
}

ash::nearby::presence::mojom::SharedCredentialPtr CreateSharedCredential(
    const std::vector<uint8_t>& key_seed,
    const int64_t start_time_millis,
    const int64_t end_time_millis,
    const std::vector<uint8_t>& encrypted_metadata_bytes_v0,
    const std::vector<uint8_t>& metadata_encryption_key_tag_v0,
    const std::vector<uint8_t>& connection_signature_verification_key,
    const std::vector<uint8_t>& advertisement_signature_verification_key,
    const ash::nearby::presence::mojom::IdentityType identity_type,
    const std::vector<uint8_t>& version,
    const ash::nearby::presence::mojom::CredentialType credential_type,
    const std::vector<uint8_t>& encrypted_metadata_bytes_v1,
    const std::vector<uint8_t>& identity_token_short_salt_adv_hmac_key_v1,
    const int64_t id,
    const std::string& dusi,
    const std::string& signature_version,
    const std::vector<uint8_t>& identity_token_extended_salt_adv_hmac_key_v1,
    const std::vector<uint8_t>& identity_token_signed_adv_hmac_key_v1) {
  return ash::nearby::presence::mojom::SharedCredential::New(
      key_seed, start_time_millis, end_time_millis, encrypted_metadata_bytes_v0,
      metadata_encryption_key_tag_v0, connection_signature_verification_key,
      advertisement_signature_verification_key, identity_type, version,
      credential_type, encrypted_metadata_bytes_v1,
      identity_token_short_salt_adv_hmac_key_v1, id, dusi, signature_version,
      identity_token_extended_salt_adv_hmac_key_v1,
      identity_token_signed_adv_hmac_key_v1);
}

}  // namespace

namespace ash::nearby::presence {

class NearbyPresenceCredentialStorageTest : public testing::Test {
 public:
  NearbyPresenceCredentialStorageTest() = default;

  ~NearbyPresenceCredentialStorageTest() override = default;

  // testing::Test:
  void SetUp() override {
    auto private_db = std::make_unique<
        leveldb_proto::test::FakeDB<::nearby::internal::LocalCredential>>(
        &private_db_entries_);
    auto local_public_db = std::make_unique<
        leveldb_proto::test::FakeDB<::nearby::internal::SharedCredential>>(
        &local_public_db_entries_);
    auto remote_public_db = std::make_unique<
        leveldb_proto::test::FakeDB<::nearby::internal::SharedCredential>>(
        &remote_public_db_entries_);

    private_db_ = private_db.get();
    local_public_db_ = local_public_db.get();
    remote_public_db_ = remote_public_db.get();

    // Since `NearbyPresenceCredentialStorage` binds to a `Receiver`, it
    // must be entangled with a valid `Remote`.
    mojo::PendingReceiver<mojom::NearbyPresenceCredentialStorage>
        pending_receiver = remote_.BindNewPipeAndPassReceiver();

    credential_storage_ = std::make_unique<TestNearbyPresenceCredentialStorage>(
        std::move(pending_receiver), std::move(private_db),
        std::move(local_public_db), std::move(remote_public_db));
  }

  void InitializeCredentialStorage(base::RunLoop& run_loop,
                                   bool expected_success) {
    credential_storage_->Initialize(
        base::BindLambdaForTesting([expected_success, &run_loop](bool success) {
          EXPECT_EQ(expected_success, success);
          run_loop.Quit();
        }));
  }

  void FullyInitializeDatabases(base::RunLoop& run_loop) {
    InitializeCredentialStorage(run_loop, /*expected_success=*/true);

    private_db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
    local_public_db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
    remote_public_db_->InitStatusCallback(
        leveldb_proto::Enums::InitStatus::kOK);
  }

  void SaveCredentialsWithExpectedResult(
      base::RunLoop& run_loop,
      mojo_base::mojom::AbslStatusCode expected_result,
      std::vector<mojom::LocalCredentialPtr> local_credentials,
      std::vector<mojom::SharedCredentialPtr> shared_credentials,
      ash::nearby::presence::mojom::PublicCredentialType
          public_credential_type) {
    credential_storage_->SaveCredentials(
        std::move(local_credentials), std::move(shared_credentials),
        public_credential_type,
        base::BindLambdaForTesting(
            [&run_loop,
             expected_result](mojo_base::mojom::AbslStatusCode status) {
              EXPECT_EQ(status, expected_result);
              run_loop.Quit();
            }));
  }

  // Pre-populates the shared credential database of the specified type with
  // 3 SharedCredentials. Also pre-populates the private credential database
  // with 3 LocalCredentials if the 'public_credential_type' is
  // kLocalPublicCredential. Credentials are generated and inserted in the order
  // of the anonymous namespace constants.
  void PrepopulateCredentials(base::RunLoop& run_loop,
                              ash::nearby::presence::mojom::PublicCredentialType
                                  public_credential_type) {
    std::vector<mojom::SharedCredentialPtr> shared_credentials;
    shared_credentials.emplace_back(CreateSharedCredential(
        kKeySeed_1, kStartTimeMillis_1, kEndTimeMillis_1,
        kEncryptedMetadataBytesV0_1, kMetadataEncryptionTag_1,
        kConnectionSignatureVerificationKey_1,
        kAdvertisementSignatureVerificationKey_1,
        mojom::IdentityType::kIdentityTypePrivateGroup, kVersion_1,
        mojom::CredentialType::kCredentialTypeDevice,
        kEncryptedMetadataBytesV1_1, kIdentityTokenShortSaltAdvHmacKeyV1_1,
        kId_1, kDusi_1, kSignatureVersion_1,
        kIdentityTokenExtendedSaltAdvHmacKeyV1_1,
        kIdentityTokenSignedAdvHmacKeyV1_1));
    shared_credentials.emplace_back(CreateSharedCredential(
        kKeySeed_2, kStartTimeMillis_2, kEndTimeMillis_2,
        kEncryptedMetadataBytesV0_2, kMetadataEncryptionTag_2,
        kConnectionSignatureVerificationKey_2,
        kAdvertisementSignatureVerificationKey_2,
        mojom::IdentityType::kIdentityTypePrivateGroup, kVersion_2,
        mojom::CredentialType::kCredentialTypeDevice,
        kEncryptedMetadataBytesV1_2, kIdentityTokenShortSaltAdvHmacKeyV1_2,
        kId_2, kDusi_2, kSignatureVersion_2,
        kIdentityTokenExtendedSaltAdvHmacKeyV1_2,
        kIdentityTokenSignedAdvHmacKeyV1_2));
    shared_credentials.emplace_back(CreateSharedCredential(
        kKeySeed_3, kStartTimeMillis_3, kEndTimeMillis_3,
        kEncryptedMetadataBytesV0_3, kMetadataEncryptionTag_3,
        kConnectionSignatureVerificationKey_3,
        kAdvertisementSignatureVerificationKey_3,
        mojom::IdentityType::kIdentityTypePrivateGroup, kVersion_3,
        mojom::CredentialType::kCredentialTypeDevice,
        kEncryptedMetadataBytesV1_3, kIdentityTokenShortSaltAdvHmacKeyV1_3,
        kId_3, kDusi_3, kSignatureVersion_3,
        kIdentityTokenExtendedSaltAdvHmacKeyV1_3,
        kIdentityTokenSignedAdvHmacKeyV1_3));

    std::vector<mojom::LocalCredentialPtr> local_credentials;
    // Prevent passing local credentials to a remote credential save.
    if (public_credential_type ==
        ash::nearby::presence::mojom::PublicCredentialType::
            kLocalPublicCredential) {
      local_credentials.emplace_back(CreateLocalCredential(
          kId_1, kKeySeed_1, kStartTimeMillis_1, kEndTimeMillis_1,
          kMetadataEncryptionKeyV0_1, AdvertisementSigningKeyCertificateAlias_1,
          kAdvertisementPrivateKey_1, ConnectionSigningKeyCertificateAlias_1,
          kConnectionPrivateKey_1,
          mojom::IdentityType::kIdentityTypePrivateGroup, kConsumedSalts_1,
          kIdentityTokenV1_1, kSignatureVersion_1));
      local_credentials.emplace_back(CreateLocalCredential(
          kId_2, kKeySeed_2, kStartTimeMillis_2, kEndTimeMillis_2,
          kMetadataEncryptionKeyV0_2, AdvertisementSigningKeyCertificateAlias_2,
          kAdvertisementPrivateKey_2, ConnectionSigningKeyCertificateAlias_2,
          kConnectionPrivateKey_2,
          mojom::IdentityType::kIdentityTypePrivateGroup, kConsumedSalts_2,
          kIdentityTokenV1_2, kSignatureVersion_2));
      local_credentials.emplace_back(CreateLocalCredential(
          kId_3, kKeySeed_3, kStartTimeMillis_3, kEndTimeMillis_3,
          kMetadataEncryptionKeyV0_3, AdvertisementSigningKeyCertificateAlias_3,
          kAdvertisementPrivateKey_3, ConnectionSigningKeyCertificateAlias_3,
          kConnectionPrivateKey_3,
          mojom::IdentityType::kIdentityTypePrivateGroup, kConsumedSalts_3,
          kIdentityTokenV1_3, kSignatureVersion_3));
    }

    SaveCredentialsWithExpectedResult(
        run_loop, mojo_base::mojom::AbslStatusCode::kOk,
        std::move(local_credentials), std::move(shared_credentials),
        public_credential_type);

    // Only set the callbacks of the databases that are being updated.
    if (public_credential_type ==
        ash::nearby::presence::mojom::PublicCredentialType::
            kRemotePublicCredential) {
      remote_public_db_->UpdateCallback(true);
    } else {
      local_public_db_->UpdateCallback(true);
      private_db_->UpdateCallback(true);
    }
  }

 protected:
  base::test::SingleThreadTaskEnvironment task_environment;

  std::unique_ptr<NearbyPresenceCredentialStorage> credential_storage_;

  mojo::Remote<mojom::NearbyPresenceCredentialStorage> remote_;

  std::map<std::string, ::nearby::internal::LocalCredential>
      private_db_entries_;
  std::map<std::string, ::nearby::internal::SharedCredential>
      local_public_db_entries_;
  std::map<std::string, ::nearby::internal::SharedCredential>
      remote_public_db_entries_;

  raw_ptr<leveldb_proto::test::FakeDB<::nearby::internal::LocalCredential>>
      private_db_;
  raw_ptr<leveldb_proto::test::FakeDB<::nearby::internal::SharedCredential>>
      local_public_db_;
  raw_ptr<leveldb_proto::test::FakeDB<::nearby::internal::SharedCredential>>
      remote_public_db_;

  base::HistogramTester histogram_tester_;
};

TEST_F(NearbyPresenceCredentialStorageTest, InitializeDatabases_Successful) {
  base::RunLoop run_loop;

  InitializeCredentialStorage(run_loop, /*expected_success=*/true);

  private_db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
  local_public_db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
  remote_public_db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);

  run_loop.Run();

  histogram_tester_.ExpectUniqueSample(
      "Nearby.Presence.Credentials.Storage.PrivateDatabaseInitializationResult",
      /*bucket: success=*/true, 1);
  histogram_tester_.ExpectUniqueSample(
      "Nearby.Presence.Credentials.Storage."
      "LocalPublicDatabaseInitializationResult",
      /*bucket: success=*/true, 1);
  histogram_tester_.ExpectUniqueSample(
      "Nearby.Presence.Credentials.Storage."
      "RemotePublicDatabaseInitializationResult",
      /*bucket: success=*/true, 1);
}

TEST_F(NearbyPresenceCredentialStorageTest, InitializeDatabases_PrivateFails) {
  base::RunLoop run_loop;

  InitializeCredentialStorage(run_loop, /*expected_success=*/false);

  // Only the private status callback is set, as the public callbacks will
  // never be bound.
  private_db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kCorrupt);

  run_loop.Run();

  histogram_tester_.ExpectUniqueSample(
      "Nearby.Presence.Credentials.Storage.PrivateDatabaseInitializationResult",
      /*bucket: success=*/false, 1);
}

TEST_F(NearbyPresenceCredentialStorageTest,
       InitializeDatabases_LocalPublicFails) {
  base::RunLoop run_loop;

  InitializeCredentialStorage(run_loop, /*expected_success=*/false);

  // Failure of the local public database will cause the remote public database
  // callbacks to never be bound.
  private_db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
  local_public_db_->InitStatusCallback(
      leveldb_proto::Enums::InitStatus::kCorrupt);

  run_loop.Run();

  histogram_tester_.ExpectUniqueSample(
      "Nearby.Presence.Credentials.Storage.PrivateDatabaseInitializationResult",
      /*bucket: success=*/true, 1);
  histogram_tester_.ExpectUniqueSample(
      "Nearby.Presence.Credentials.Storage."
      "LocalPublicDatabaseInitializationResult",
      /*bucket: success=*/false, 1);
}

TEST_F(NearbyPresenceCredentialStorageTest,
       InitializeDatabases_RemotePublicFails) {
  base::RunLoop run_loop;

  InitializeCredentialStorage(run_loop, /*expected_success=*/false);

  private_db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
  local_public_db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
  remote_public_db_->InitStatusCallback(
      leveldb_proto::Enums::InitStatus::kCorrupt);

  run_loop.Run();

  histogram_tester_.ExpectUniqueSample(
      "Nearby.Presence.Credentials.Storage.PrivateDatabaseInitializationResult",
      /*bucket: success=*/true, 1);
  histogram_tester_.ExpectUniqueSample(
      "Nearby.Presence.Credentials.Storage."
      "LocalPublicDatabaseInitializationResult",
      /*bucket: success=*/true, 1);
  histogram_tester_.ExpectUniqueSample(
      "Nearby.Presence.Credentials.Storage."
      "RemotePublicDatabaseInitializationResult",
      /*bucket: success=*/false, 1);
}

TEST_F(NearbyPresenceCredentialStorageTest, SaveCredentials_Local_Success) {
  {
    base::RunLoop run_loop;
    FullyInitializeDatabases(run_loop);
  }

  std::vector<mojom::LocalCredentialPtr> local_credentials;
  local_credentials.emplace_back(CreateLocalCredential(
      kId_1, kKeySeed_1, kStartTimeMillis_1, kEndTimeMillis_1,
      kMetadataEncryptionKeyV0_1, AdvertisementSigningKeyCertificateAlias_1,
      kAdvertisementPrivateKey_1, ConnectionSigningKeyCertificateAlias_1,
      kConnectionPrivateKey_1, mojom::IdentityType::kIdentityTypePrivateGroup,
      kConsumedSalts_1, kIdentityTokenV1_1, kSignatureVersion_1));
  local_credentials.emplace_back(CreateLocalCredential(
      kId_2, kKeySeed_2, kStartTimeMillis_2, kEndTimeMillis_2,
      kMetadataEncryptionKeyV0_2, AdvertisementSigningKeyCertificateAlias_2,
      kAdvertisementPrivateKey_2, ConnectionSigningKeyCertificateAlias_2,
      kConnectionPrivateKey_2, mojom::IdentityType::kIdentityTypePrivateGroup,
      kConsumedSalts_2, kIdentityTokenV1_2, kSignatureVersion_2));
  local_credentials.emplace_back(CreateLocalCredential(
      kId_3, kKeySeed_3, kStartTimeMillis_3, kEndTimeMillis_3,
      kMetadataEncryptionKeyV0_3, AdvertisementSigningKeyCertificateAlias_3,
      kAdvertisementPrivateKey_3, ConnectionSigningKeyCertificateAlias_3,
      kConnectionPrivateKey_3, mojom::IdentityType::kIdentityTypePrivateGroup,
      kConsumedSalts_3, kIdentityTokenV1_3, kSignatureVersion_3));

  std::vector<mojom::SharedCredentialPtr> shared_credentials;
  shared_credentials.emplace_back(CreateSharedCredential(
      kKeySeed_1, kStartTimeMillis_1, kEndTimeMillis_1,
      kEncryptedMetadataBytesV0_1, kMetadataEncryptionTag_1,
      kConnectionSignatureVerificationKey_1,
      kAdvertisementSignatureVerificationKey_1,
      mojom::IdentityType::kIdentityTypePrivateGroup, kVersion_1,
      mojom::CredentialType::kCredentialTypeDevice, kEncryptedMetadataBytesV1_1,
      kIdentityTokenShortSaltAdvHmacKeyV1_1, kId_1, kDusi_1,
      kSignatureVersion_1, kIdentityTokenExtendedSaltAdvHmacKeyV1_1,
      kIdentityTokenSignedAdvHmacKeyV1_1));
  shared_credentials.emplace_back(CreateSharedCredential(
      kKeySeed_2, kStartTimeMillis_2, kEndTimeMillis_2,
      kEncryptedMetadataBytesV0_2, kMetadataEncryptionTag_2,
      kConnectionSignatureVerificationKey_2,
      kAdvertisementSignatureVerificationKey_2,
      mojom::IdentityType::kIdentityTypePrivateGroup, kVersion_2,
      mojom::CredentialType::kCredentialTypeDevice, kEncryptedMetadataBytesV1_2,
      kIdentityTokenShortSaltAdvHmacKeyV1_2, kId_2, kDusi_2,
      kSignatureVersion_2, kIdentityTokenExtendedSaltAdvHmacKeyV1_2,
      kIdentityTokenSignedAdvHmacKeyV1_2));
  shared_credentials.emplace_back(CreateSharedCredential(
      kKeySeed_3, kStartTimeMillis_3, kEndTimeMillis_3,
      kEncryptedMetadataBytesV0_3, kMetadataEncryptionTag_3,
      kConnectionSignatureVerificationKey_3,
      kAdvertisementSignatureVerificationKey_3,
      mojom::IdentityType::kIdentityTypePrivateGroup, kVersion_3,
      mojom::CredentialType::kCredentialTypeDevice, kEncryptedMetadataBytesV1_3,
      kIdentityTokenShortSaltAdvHmacKeyV1_3, kId_3, kDusi_3,
      kSignatureVersion_3, kIdentityTokenExtendedSaltAdvHmacKeyV1_3,
      kIdentityTokenSignedAdvHmacKeyV1_3));

  {
    base::RunLoop run_loop;

    SaveCredentialsWithExpectedResult(
        run_loop, mojo_base::mojom::AbslStatusCode::kOk,
        std::move(local_credentials), std::move(shared_credentials),
        ash::nearby::presence::mojom::PublicCredentialType::
            kLocalPublicCredential);
    local_public_db_->UpdateCallback(true);
    private_db_->UpdateCallback(true);

    run_loop.Run();
  }

  ASSERT_EQ(3u, local_public_db_entries_.size());
  ASSERT_EQ(3u, private_db_entries_.size());

  histogram_tester_.ExpectUniqueSample(
      "Nearby.Presence.Credentials.Storage.SaveLocalPublicCredentials.Result",
      /*bucket: success=*/true, 1);
  histogram_tester_.ExpectUniqueSample(
      "Nearby.Presence.Credentials.Storage.SavePrivateCredentials.Result",
      /*bucket: success=*/true, 1);
}

TEST_F(NearbyPresenceCredentialStorageTest, SaveCredentials_Local_PublicFails) {
  {
    base::RunLoop run_loop;
    FullyInitializeDatabases(run_loop);
  }

  std::vector<mojom::LocalCredentialPtr> local_credentials;
  local_credentials.emplace_back(CreateLocalCredential(
      kId_1, kKeySeed_1, kStartTimeMillis_1, kEndTimeMillis_1,
      kMetadataEncryptionKeyV0_1, AdvertisementSigningKeyCertificateAlias_1,
      kAdvertisementPrivateKey_1, ConnectionSigningKeyCertificateAlias_1,
      kConnectionPrivateKey_1, mojom::IdentityType::kIdentityTypePrivateGroup,
      kConsumedSalts_1, kIdentityTokenV1_1, kSignatureVersion_1));

  std::vector<mojom::SharedCredentialPtr> shared_credentials;
  shared_credentials.emplace_back(CreateSharedCredential(
      kKeySeed_1, kStartTimeMillis_1, kEndTimeMillis_1,
      kEncryptedMetadataBytesV0_1, kMetadataEncryptionTag_1,
      kConnectionSignatureVerificationKey_1,
      kAdvertisementSignatureVerificationKey_1,
      mojom::IdentityType::kIdentityTypePrivateGroup, kVersion_1,
      mojom::CredentialType::kCredentialTypeDevice, kEncryptedMetadataBytesV1_1,
      kIdentityTokenShortSaltAdvHmacKeyV1_1, kId_1, kDusi_1,
      kSignatureVersion_1, kIdentityTokenExtendedSaltAdvHmacKeyV1_1,
      kIdentityTokenSignedAdvHmacKeyV1_1));

  {
    base::RunLoop run_loop;

    SaveCredentialsWithExpectedResult(
        run_loop, mojo_base::mojom::AbslStatusCode::kAborted,
        std::move(local_credentials), std::move(shared_credentials),
        ash::nearby::presence::mojom::PublicCredentialType::
            kLocalPublicCredential);
    // Only local public database will have its callback bound as we cancel
    // saving to the private database on public save failure.
    local_public_db_->UpdateCallback(false);

    run_loop.Run();
  }

  // Private credentials should not have an entry, as a save attempt did
  // not take place.
  histogram_tester_.ExpectUniqueSample(
      "Nearby.Presence.Credentials.Storage.SaveLocalPublicCredentials.Result",
      /*bucket: success=*/false, 1);
  histogram_tester_.ExpectTotalCount(
      "Nearby.Presence.Credentials.Storage.SavePrivateCredentials.Result",
      /*count=*/0);
}

TEST_F(NearbyPresenceCredentialStorageTest,
       SaveCredentials_Local_PrivateFails) {
  {
    base::RunLoop run_loop;
    FullyInitializeDatabases(run_loop);
  }

  std::vector<mojom::LocalCredentialPtr> local_credentials;
  local_credentials.emplace_back(CreateLocalCredential(
      kId_1, kKeySeed_1, kStartTimeMillis_1, kEndTimeMillis_1,
      kMetadataEncryptionKeyV0_1, AdvertisementSigningKeyCertificateAlias_1,
      kAdvertisementPrivateKey_1, ConnectionSigningKeyCertificateAlias_1,
      kConnectionPrivateKey_1, mojom::IdentityType::kIdentityTypePrivateGroup,
      kConsumedSalts_1, kIdentityTokenV1_1, kSignatureVersion_1));

  std::vector<mojom::SharedCredentialPtr> shared_credentials;
  shared_credentials.emplace_back(CreateSharedCredential(
      kKeySeed_1, kStartTimeMillis_1, kEndTimeMillis_1,
      kEncryptedMetadataBytesV0_1, kMetadataEncryptionTag_1,
      kConnectionSignatureVerificationKey_1,
      kAdvertisementSignatureVerificationKey_1,
      mojom::IdentityType::kIdentityTypePrivateGroup, kVersion_1,
      mojom::CredentialType::kCredentialTypeDevice, kEncryptedMetadataBytesV1_1,
      kIdentityTokenShortSaltAdvHmacKeyV1_1, kId_1, kDusi_1,
      kSignatureVersion_1, kIdentityTokenExtendedSaltAdvHmacKeyV1_1,
      kIdentityTokenSignedAdvHmacKeyV1_1));

  {
    base::RunLoop run_loop;

    SaveCredentialsWithExpectedResult(
        run_loop, mojo_base::mojom::AbslStatusCode::kAborted,
        std::move(local_credentials), std::move(shared_credentials),
        ash::nearby::presence::mojom::PublicCredentialType::
            kLocalPublicCredential);
    local_public_db_->UpdateCallback(true);
    private_db_->UpdateCallback(false);

    run_loop.Run();
  }

  histogram_tester_.ExpectUniqueSample(
      "Nearby.Presence.Credentials.Storage.SaveLocalPublicCredentials.Result",
      /*bucket: success=*/true, 1);
  histogram_tester_.ExpectUniqueSample(
      "Nearby.Presence.Credentials.Storage.SavePrivateCredentials.Result",
      /*bucket: success=*/false, 1);
}

TEST_F(NearbyPresenceCredentialStorageTest, SaveCredentials_Remote_Success) {
  {
    base::RunLoop run_loop;
    FullyInitializeDatabases(run_loop);
  }

  {
    base::RunLoop run_loop;
    // Nearby Presence provides an empty vector of private credentials when
    // remote public credentials are saved.
    std::vector<mojom::LocalCredentialPtr> local_credentials;
    std::vector<mojom::SharedCredentialPtr> shared_credentials;
    shared_credentials.emplace_back(CreateSharedCredential(
        kKeySeed_1, kStartTimeMillis_1, kEndTimeMillis_1,
        kEncryptedMetadataBytesV0_1, kMetadataEncryptionTag_1,
        kConnectionSignatureVerificationKey_1,
        kAdvertisementSignatureVerificationKey_1,
        mojom::IdentityType::kIdentityTypePrivateGroup, kVersion_1,
        mojom::CredentialType::kCredentialTypeDevice,
        kEncryptedMetadataBytesV1_1, kIdentityTokenShortSaltAdvHmacKeyV1_1,
        kId_1, kDusi_1, kSignatureVersion_1,
        kIdentityTokenExtendedSaltAdvHmacKeyV1_1,
        kIdentityTokenSignedAdvHmacKeyV1_1));

    SaveCredentialsWithExpectedResult(
        run_loop, mojo_base::mojom::AbslStatusCode::kOk,
        std::move(local_credentials), std::move(shared_credentials),
        ash::nearby::presence::mojom::PublicCredentialType::
            kRemotePublicCredential);
    // The local credential database callback is never set as it is never
    // updated.
    remote_public_db_->UpdateCallback(true);

    run_loop.Run();
  }

  ASSERT_EQ(1u, remote_public_db_entries_.size());

  histogram_tester_.ExpectUniqueSample(
      "Nearby.Presence.Credentials.Storage.SaveRemotePublicCredentials.Result",
      /*bucket: success=*/true, 1);
}

TEST_F(NearbyPresenceCredentialStorageTest,
       SaveCredentials_Remote_PublicFails) {
  {
    base::RunLoop run_loop;
    FullyInitializeDatabases(run_loop);
  }

  {
    base::RunLoop run_loop;
    std::vector<mojom::LocalCredentialPtr> local_credentials;
    std::vector<mojom::SharedCredentialPtr> shared_credentials;
    shared_credentials.emplace_back(CreateSharedCredential(
        kKeySeed_1, kStartTimeMillis_1, kEndTimeMillis_1,
        kEncryptedMetadataBytesV0_1, kMetadataEncryptionTag_1,
        kConnectionSignatureVerificationKey_1,
        kAdvertisementSignatureVerificationKey_1,
        mojom::IdentityType::kIdentityTypePrivateGroup, kVersion_1,
        mojom::CredentialType::kCredentialTypeDevice,
        kEncryptedMetadataBytesV1_1, kIdentityTokenShortSaltAdvHmacKeyV1_1,
        kId_1, kDusi_1, kSignatureVersion_1,
        kIdentityTokenExtendedSaltAdvHmacKeyV1_1,
        kIdentityTokenSignedAdvHmacKeyV1_1));

    SaveCredentialsWithExpectedResult(
        run_loop, mojo_base::mojom::AbslStatusCode::kAborted,
        std::move(local_credentials), std::move(shared_credentials),
        ash::nearby::presence::mojom::PublicCredentialType::
            kRemotePublicCredential);
    remote_public_db_->UpdateCallback(false);

    run_loop.Run();
  }

  histogram_tester_.ExpectUniqueSample(
      "Nearby.Presence.Credentials.Storage.SaveRemotePublicCredentials.Result",
      /*bucket: success=*/false, 1);
}

TEST_F(NearbyPresenceCredentialStorageTest,
       SaveCredentials_RemotePublicSaveDoesNotClearPrivateEntries) {
  {
    base::RunLoop run_loop;
    FullyInitializeDatabases(run_loop);
  }

  {
    base::RunLoop run_loop;
    std::vector<mojom::LocalCredentialPtr> local_credentials;
    std::vector<mojom::SharedCredentialPtr> shared_credentials;
    local_credentials.emplace_back(CreateLocalCredential(
        kId_1, kKeySeed_1, kStartTimeMillis_1, kEndTimeMillis_1,
        kMetadataEncryptionKeyV0_1, AdvertisementSigningKeyCertificateAlias_1,
        kAdvertisementPrivateKey_1, ConnectionSigningKeyCertificateAlias_1,
        kConnectionPrivateKey_1, mojom::IdentityType::kIdentityTypePrivateGroup,
        kConsumedSalts_1, kIdentityTokenV1_1, kSignatureVersion_1));
    shared_credentials.emplace_back(CreateSharedCredential(
        kKeySeed_1, kStartTimeMillis_1, kEndTimeMillis_1,
        kEncryptedMetadataBytesV0_1, kMetadataEncryptionTag_1,
        kConnectionSignatureVerificationKey_1,
        kAdvertisementSignatureVerificationKey_1,
        mojom::IdentityType::kIdentityTypePrivateGroup, kVersion_1,
        mojom::CredentialType::kCredentialTypeDevice,
        kEncryptedMetadataBytesV1_1, kIdentityTokenShortSaltAdvHmacKeyV1_1,
        kId_1, kDusi_1, kSignatureVersion_1,
        kIdentityTokenExtendedSaltAdvHmacKeyV1_1,
        kIdentityTokenSignedAdvHmacKeyV1_1));

    SaveCredentialsWithExpectedResult(
        run_loop, mojo_base::mojom::AbslStatusCode::kOk,
        std::move(local_credentials), std::move(shared_credentials),
        ash::nearby::presence::mojom::PublicCredentialType::
            kLocalPublicCredential);
    local_public_db_->UpdateCallback(true);
    private_db_->UpdateCallback(true);

    run_loop.Run();
  }

  EXPECT_EQ(1u, private_db_entries_.size());

  {
    base::RunLoop run_loop;
    // When the library saves remote public credentials, it provides an
    // empty vector of local credentials.
    std::vector<mojom::LocalCredentialPtr> local_credentials;
    std::vector<mojom::SharedCredentialPtr> shared_credentials;
    shared_credentials.emplace_back(CreateSharedCredential(
        kKeySeed_1, kStartTimeMillis_1, kEndTimeMillis_1,
        kEncryptedMetadataBytesV0_1, kMetadataEncryptionTag_1,
        kConnectionSignatureVerificationKey_1,
        kAdvertisementSignatureVerificationKey_1,
        mojom::IdentityType::kIdentityTypePrivateGroup, kVersion_1,
        mojom::CredentialType::kCredentialTypeDevice,
        kEncryptedMetadataBytesV1_1, kIdentityTokenShortSaltAdvHmacKeyV1_1,
        kId_1, kDusi_1, kSignatureVersion_1,
        kIdentityTokenExtendedSaltAdvHmacKeyV1_1,
        kIdentityTokenSignedAdvHmacKeyV1_1));

    SaveCredentialsWithExpectedResult(
        run_loop, mojo_base::mojom::AbslStatusCode::kOk,
        std::move(local_credentials), std::move(shared_credentials),
        ash::nearby::presence::mojom::PublicCredentialType::
            kRemotePublicCredential);
    remote_public_db_->UpdateCallback(true);

    run_loop.Run();
  }

  // The private credentials should be preserved despite an empty vector of
  // private credentials being provided in the remote public credential save.
  EXPECT_EQ(1u, private_db_entries_.size());
}

TEST_F(NearbyPresenceCredentialStorageTest,
       GetPublicCredentials_Local_Success) {
  {
    base::RunLoop run_loop;
    FullyInitializeDatabases(run_loop);
    run_loop.Run();
  }

  {
    base::RunLoop run_loop;
    PrepopulateCredentials(run_loop,
                           ash::nearby::presence::mojom::PublicCredentialType::
                               kLocalPublicCredential);
    run_loop.Run();
  }

  ASSERT_EQ(3u, local_public_db_entries_.size());

  {
    base::RunLoop run_loop;
    credential_storage_->GetPublicCredentials(
        ash::nearby::presence::mojom::PublicCredentialType::
            kLocalPublicCredential,
        base::BindLambdaForTesting(
            [&run_loop](mojo_base::mojom::AbslStatusCode status,
                        std::optional<std::vector<mojom::SharedCredentialPtr>>
                            credentials) {
              EXPECT_EQ(status, mojo_base::mojom::AbslStatusCode::kOk);
              EXPECT_TRUE(credentials.has_value());
              EXPECT_EQ(credentials->size(), 3u);
              run_loop.Quit();
            }));

    local_public_db_->LoadCallback(true);

    run_loop.Run();
  }

  histogram_tester_.ExpectTotalCount(
      "Nearby.Presence.Credentials.Storage."
      "RetrieveLocalPublicCredentialsDuration",
      1);
  histogram_tester_.ExpectTotalCount(
      "Nearby.Presence.Credentials.Storage."
      "RetrieveRemotePublicCredentialsDuration",
      0);
}

TEST_F(NearbyPresenceCredentialStorageTest, GetPublicCredentials_Local_Fail) {
  {
    base::RunLoop run_loop;
    FullyInitializeDatabases(run_loop);
    run_loop.Run();
  }

  {
    base::RunLoop run_loop;
    PrepopulateCredentials(run_loop,
                           ash::nearby::presence::mojom::PublicCredentialType::
                               kLocalPublicCredential);
    run_loop.Run();
  }

  ASSERT_EQ(3u, local_public_db_entries_.size());

  {
    base::RunLoop run_loop;
    credential_storage_->GetPublicCredentials(
        ash::nearby::presence::mojom::PublicCredentialType::
            kLocalPublicCredential,
        base::BindLambdaForTesting(
            [&run_loop](mojo_base::mojom::AbslStatusCode status,
                        std::optional<std::vector<mojom::SharedCredentialPtr>>
                            credentials) {
              EXPECT_EQ(status, mojo_base::mojom::AbslStatusCode::kAborted);
              EXPECT_FALSE(credentials.has_value());
              run_loop.Quit();
            }));

    local_public_db_->LoadCallback(false);

    run_loop.Run();
  }

  // Only record duration for successful loads.
  histogram_tester_.ExpectTotalCount(
      "Nearby.Presence.Credentials.Storage."
      "RetrieveLocalPublicCredentialsDuration",
      0);
  histogram_tester_.ExpectTotalCount(
      "Nearby.Presence.Credentials.Storage."
      "RetrieveRemotePublicCredentialsDuration",
      0);
}

TEST_F(NearbyPresenceCredentialStorageTest,
       GetPublicCredentials_Remote_Success) {
  {
    base::RunLoop run_loop;
    FullyInitializeDatabases(run_loop);
    run_loop.Run();
  }

  {
    base::RunLoop run_loop;
    PrepopulateCredentials(run_loop,
                           ash::nearby::presence::mojom::PublicCredentialType::
                               kRemotePublicCredential);
    run_loop.Run();
  }

  ASSERT_EQ(3u, remote_public_db_entries_.size());

  {
    base::RunLoop run_loop;
    credential_storage_->GetPublicCredentials(
        ash::nearby::presence::mojom::PublicCredentialType::
            kRemotePublicCredential,
        base::BindLambdaForTesting(
            [&run_loop](mojo_base::mojom::AbslStatusCode status,
                        std::optional<std::vector<mojom::SharedCredentialPtr>>
                            credentials) {
              EXPECT_EQ(status, mojo_base::mojom::AbslStatusCode::kOk);
              EXPECT_TRUE(credentials.has_value());
              EXPECT_EQ(credentials->size(), 3u);
              run_loop.Quit();
            }));

    remote_public_db_->LoadCallback(true);

    run_loop.Run();
  }

  histogram_tester_.ExpectTotalCount(
      "Nearby.Presence.Credentials.Storage."
      "RetrieveLocalPublicCredentialsDuration",
      0);
  histogram_tester_.ExpectTotalCount(
      "Nearby.Presence.Credentials.Storage."
      "RetrieveRemotePublicCredentialsDuration",
      1);
}

TEST_F(NearbyPresenceCredentialStorageTest, GetPublicCredentials_Remote_Fail) {
  {
    base::RunLoop run_loop;
    FullyInitializeDatabases(run_loop);
    run_loop.Run();
  }

  {
    base::RunLoop run_loop;
    PrepopulateCredentials(run_loop,
                           ash::nearby::presence::mojom::PublicCredentialType::
                               kRemotePublicCredential);
    run_loop.Run();
  }

  ASSERT_EQ(3u, remote_public_db_entries_.size());

  {
    base::RunLoop run_loop;
    credential_storage_->GetPublicCredentials(
        ash::nearby::presence::mojom::PublicCredentialType::
            kRemotePublicCredential,
        base::BindLambdaForTesting(
            [&run_loop](mojo_base::mojom::AbslStatusCode status,
                        std::optional<std::vector<mojom::SharedCredentialPtr>>
                            credentials) {
              EXPECT_EQ(status, mojo_base::mojom::AbslStatusCode::kAborted);
              EXPECT_FALSE(credentials.has_value());
              run_loop.Quit();
            }));
    remote_public_db_->LoadCallback(false);

    run_loop.Run();
  }

  histogram_tester_.ExpectTotalCount(
      "Nearby.Presence.Credentials.Storage."
      "RetrieveLocalPublicCredentialsDuration",
      0);
  histogram_tester_.ExpectTotalCount(
      "Nearby.Presence.Credentials.Storage."
      "RetrieveRemotePublicCredentialsDuration",
      0);
}

TEST_F(NearbyPresenceCredentialStorageTest, GetPrivateCredentials_Success) {
  {
    base::RunLoop run_loop;
    FullyInitializeDatabases(run_loop);
  }

  {
    base::RunLoop run_loop;
    PrepopulateCredentials(run_loop,
                           ash::nearby::presence::mojom::PublicCredentialType::
                               kLocalPublicCredential);
    run_loop.Run();
  }

  ASSERT_EQ(private_db_entries_.size(), 3u);

  {
    base::RunLoop run_loop;
    credential_storage_->GetPrivateCredentials(base::BindLambdaForTesting(
        [&run_loop](
            mojo_base::mojom::AbslStatusCode status,
            std::optional<std::vector<mojom::LocalCredentialPtr>> credentials) {
          EXPECT_EQ(status, mojo_base::mojom::AbslStatusCode::kOk);
          EXPECT_TRUE(credentials.has_value());
          EXPECT_EQ(credentials->size(), 3u);
          run_loop.Quit();
        }));
    private_db_->LoadCallback(true);

    run_loop.Run();
  }

  histogram_tester_.ExpectTotalCount(
      "Nearby.Presence.Credentials.Storage.RetrievePrivateCredentialsDuration",
      1);
}

TEST_F(NearbyPresenceCredentialStorageTest, GetPrivateCredentials_Fail) {
  {
    base::RunLoop run_loop;
    FullyInitializeDatabases(run_loop);
    run_loop.Run();
  }

  {
    base::RunLoop run_loop;
    PrepopulateCredentials(run_loop,
                           ash::nearby::presence::mojom::PublicCredentialType::
                               kLocalPublicCredential);
    run_loop.Run();
  }

  ASSERT_EQ(private_db_entries_.size(), 3u);

  {
    base::RunLoop run_loop;
    credential_storage_->GetPrivateCredentials(base::BindLambdaForTesting(
        [&run_loop](
            mojo_base::mojom::AbslStatusCode status,
            std::optional<std::vector<mojom::LocalCredentialPtr>> credentials) {
          EXPECT_EQ(status, mojo_base::mojom::AbslStatusCode::kAborted);
          EXPECT_FALSE(credentials.has_value());
          run_loop.Quit();
        }));

    private_db_->LoadCallback(false);

    run_loop.Run();
  }

  histogram_tester_.ExpectTotalCount(
      "Nearby.Presence.Credentials.Storage.RetrievePrivateCredentialsDuration",
      0);
}

TEST_F(NearbyPresenceCredentialStorageTest, UpdateLocalCredential_Success) {
  {
    base::RunLoop run_loop;
    FullyInitializeDatabases(run_loop);
    run_loop.Run();
  }

  {
    base::RunLoop run_loop;
    PrepopulateCredentials(run_loop,
                           ash::nearby::presence::mojom::PublicCredentialType::
                               kLocalPublicCredential);
    run_loop.Run();
  }

  // Since the pre-population step populates credentials with each parameter
  // to the matching number (ie, _1 values are assigned to kId_1),
  // update the credential details for _1 to _2.
  auto local_credential_to_be_updated = CreateLocalCredential(
      kId_1, kKeySeed_2, kStartTimeMillis_2, kEndTimeMillis_2,
      kMetadataEncryptionKeyV0_2, AdvertisementSigningKeyCertificateAlias_2,
      kAdvertisementPrivateKey_2, ConnectionSigningKeyCertificateAlias_2,
      kConnectionPrivateKey_2, mojom::IdentityType::kIdentityTypePrivateGroup,
      kConsumedSalts_2, kIdentityTokenV1_2, kSignatureVersion_2);

  {
    base::RunLoop run_loop;
    credential_storage_->UpdateLocalCredential(
        std::move(local_credential_to_be_updated),
        base::BindLambdaForTesting(
            [&run_loop](mojo_base::mojom::AbslStatusCode status) {
              EXPECT_EQ(status, mojo_base::mojom::AbslStatusCode::kOk);
              run_loop.Quit();
            }));

    private_db_->UpdateCallback(true);
    run_loop.Run();
  }

  auto it = private_db_entries_.find(base::NumberToString(kId_1));
  ASSERT_NE(it, private_db_entries_.end());
  auto updated_local_credential = it->second;

  EXPECT_EQ(std::vector<uint8_t>(updated_local_credential.key_seed().begin(),
                                 updated_local_credential.key_seed().end()),
            kKeySeed_2);
  EXPECT_EQ(
      std::vector<uint8_t>(updated_local_credential.identity_token_v1().begin(),
                           updated_local_credential.identity_token_v1().end()),
      kIdentityTokenV1_2);
}

TEST_F(NearbyPresenceCredentialStorageTest, UpdateLocalCredential_Failure) {
  {
    base::RunLoop run_loop;
    FullyInitializeDatabases(run_loop);
    run_loop.Run();
  }

  {
    base::RunLoop run_loop;
    PrepopulateCredentials(run_loop,
                           ash::nearby::presence::mojom::PublicCredentialType::
                               kLocalPublicCredential);
    run_loop.Run();
  }

  auto local_credential_to_be_updated = CreateLocalCredential(
      kId_1, kKeySeed_2, kStartTimeMillis_2, kEndTimeMillis_2,
      kMetadataEncryptionKeyV0_2, AdvertisementSigningKeyCertificateAlias_2,
      kAdvertisementPrivateKey_2, ConnectionSigningKeyCertificateAlias_2,
      kConnectionPrivateKey_2, mojom::IdentityType::kIdentityTypePrivateGroup,
      kConsumedSalts_2, kIdentityTokenV1_2, kSignatureVersion_2);

  {
    base::RunLoop run_loop;
    credential_storage_->UpdateLocalCredential(
        std::move(local_credential_to_be_updated),
        base::BindLambdaForTesting(
            [&run_loop](mojo_base::mojom::AbslStatusCode status) {
              EXPECT_EQ(status, mojo_base::mojom::AbslStatusCode::kAborted);
              run_loop.Quit();
            }));

    private_db_->UpdateCallback(false);
    run_loop.Run();
  }
}

}  // namespace ash::nearby::presence