chromium/ash/quick_pair/keyed_service/quick_pair_mediator_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 "ash/quick_pair/keyed_service/quick_pair_mediator.h"

#include <memory>
#include <optional>

#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/quick_pair/common/account_key_failure.h"
#include "ash/quick_pair/common/device.h"
#include "ash/quick_pair/common/mock_quick_pair_browser_delegate.h"
#include "ash/quick_pair/common/pair_failure.h"
#include "ash/quick_pair/common/protocol.h"
#include "ash/quick_pair/companion_app/mock_companion_app_broker.h"
#include "ash/quick_pair/fast_pair_handshake/fake_fast_pair_handshake.h"
#include "ash/quick_pair/fast_pair_handshake/fast_pair_data_encryptor.h"
#include "ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client.h"
#include "ash/quick_pair/fast_pair_handshake/fast_pair_handshake_lookup.h"
#include "ash/quick_pair/feature_status_tracker/fake_feature_status_tracker.h"
#include "ash/quick_pair/feature_status_tracker/mock_quick_pair_feature_status_tracker.h"
#include "ash/quick_pair/feature_status_tracker/quick_pair_feature_status_tracker.h"
#include "ash/quick_pair/keyed_service/fast_pair_bluetooth_config_delegate.h"
#include "ash/quick_pair/message_stream/fake_message_stream_lookup.h"
#include "ash/quick_pair/message_stream/message_stream_lookup.h"
#include "ash/quick_pair/pairing/fake_retroactive_pairing_detector.h"
#include "ash/quick_pair/pairing/mock_pairer_broker.h"
#include "ash/quick_pair/pairing/pairer_broker.h"
#include "ash/quick_pair/pairing/retroactive_pairing_detector.h"
#include "ash/quick_pair/repository/mock_fast_pair_repository.h"
#include "ash/quick_pair/scanning/mock_scanner_broker.h"
#include "ash/quick_pair/scanning/scanner_broker.h"
#include "ash/quick_pair/ui/mock_ui_broker.h"
#include "ash/quick_pair/ui/ui_broker.h"
#include "ash/test/ash_test_base.h"
#include "ash/test/ash_test_helper.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "chromeos/ash/services/bluetooth_config/adapter_state_controller.h"
#include "chromeos/ash/services/bluetooth_config/fake_adapter_state_controller.h"
#include "chromeos/ash/services/bluetooth_config/fake_discovery_session_manager.h"
#include "chromeos/ash/services/bluetooth_config/in_process_instance.h"
#include "chromeos/ash/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom.h"
#include "chromeos/ash/services/quick_pair/quick_pair_process_manager_impl.h"
#include "components/prefs/pref_registry.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

using testing::Return;

constexpr char kTestMetadataId[] = "718C17";
constexpr char kTestMetadataId2[] = "FF1B63";
constexpr char kTestAddress[] = "test_address";

constexpr base::TimeDelta kDismissedDiscoveryNotificationBanTime =
    base::Seconds(2);
constexpr base::TimeDelta kShortBanDiscoveryNotificationBanTime =
    base::Minutes(5);

// Represents an arbitrary length ban time to demonstrate that the long ban
// is until the state is reset.
constexpr base::TimeDelta kLongBanDiscoveryNotificationBanTime =
    base::Minutes(15);

const std::vector<uint8_t> kAccountKey1{0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
                                        0x77, 0x88, 0x99, 0x00, 0xAA, 0xBB,
                                        0xCC, 0xDD, 0xEE, 0xFF};

}  // namespace

namespace ash {
namespace quick_pair {

class MediatorTest : public AshTestBase {
 public:
  MediatorTest()
      : AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}

  void SetUp() override {
    set_create_quick_pair_mediator(false);

    AshTestBase::SetUp();

    adapter_ =
        base::MakeRefCounted<testing::NiceMock<device::MockBluetoothAdapter>>();
    ON_CALL(*adapter_, IsPresent()).WillByDefault(testing::Return(true));
    ON_CALL(*adapter_, GetLowEnergyScanSessionHardwareOffloadingStatus())
        .WillByDefault(testing::Return(
            device::BluetoothAdapter::
                LowEnergyScanSessionHardwareOffloadingStatus::kSupported));
    device::BluetoothAdapterFactory::SetAdapterForTesting(adapter_);

    std::unique_ptr<FeatureStatusTracker> tracker =
        std::make_unique<FakeFeatureStatusTracker>();
    feature_status_tracker_ =
        static_cast<FakeFeatureStatusTracker*>(tracker.get());

    std::unique_ptr<ScannerBroker> scanner_broker =
        std::make_unique<MockScannerBroker>();
    mock_scanner_broker_ =
        static_cast<MockScannerBroker*>(scanner_broker.get());

    std::unique_ptr<RetroactivePairingDetector> retroactive_pairing_detector =
        std::make_unique<FakeRetroactivePairingDetector>();
    fake_retroactive_pairing_detector_ =
        static_cast<FakeRetroactivePairingDetector*>(
            retroactive_pairing_detector.get());

    std::unique_ptr<PairerBroker> pairer_broker =
        std::make_unique<MockPairerBroker>();
    mock_pairer_broker_ = static_cast<MockPairerBroker*>(pairer_broker.get());

    ON_CALL(*mock_pairer_broker_, PairDevice)
        .WillByDefault([this](scoped_refptr<Device> device) {
          // Subsequent Pair protocol never attempts to write the account key to
          // the device: |FastPairPairerImpl::AttemptSendAccountKey()|.
          //
          // V1 devices are paired via the Bluetooth Pairing Dialog and no
          // account key is written to the device:
          // |FastPairPairerImpl::FastPairPairerImpl(...)|.
          if (device->protocol() != Protocol::kFastPairSubsequent &&
              device->version() != DeviceFastPairVersion::kV1) {
            mock_pairer_broker_->NotifyAccountKeyWrite(device, std::nullopt);
          }
        });

    std::unique_ptr<UIBroker> ui_broker = std::make_unique<MockUIBroker>();
    mock_ui_broker_ = static_cast<MockUIBroker*>(ui_broker.get());

    std::unique_ptr<CompanionAppBroker> companion_app_broker =
        std::make_unique<MockCompanionAppBroker>();
    mock_companion_app_broker_ =
        static_cast<MockCompanionAppBroker*>(companion_app_broker.get());

    std::unique_ptr<FastPairRepository> fast_pair_repository =
        std::make_unique<MockFastPairRepository>();
    mock_fast_pair_repository_ =
        static_cast<MockFastPairRepository*>(fast_pair_repository.get());

    FastPairHandshakeLookup::UseFakeInstance();
    mediator_ = std::make_unique<Mediator>(
        std::move(tracker), std::move(scanner_broker),
        std::move(retroactive_pairing_detector),
        std::make_unique<FakeMessageStreamLookup>(), std::move(pairer_broker),
        std::move(ui_broker), std::move(companion_app_broker),
        std::move(fast_pair_repository),
        std::make_unique<QuickPairProcessManagerImpl>());

    initial_device_ = base::MakeRefCounted<Device>(
        kTestMetadataId, kTestAddress, Protocol::kFastPairInitial);
    initial_device2_ = base::MakeRefCounted<Device>(
        kTestMetadataId2, kTestAddress, Protocol::kFastPairInitial);
    subsequent_device_ = base::MakeRefCounted<Device>(
        kTestMetadataId, kTestAddress, Protocol::kFastPairSubsequent);
    retroactive_device_ = base::MakeRefCounted<Device>(
        kTestMetadataId, kTestAddress, Protocol::kFastPairRetroactive);
    base::RunLoop().RunUntilIdle();
  }

  void SetHasAtLeastOneDiscoverySessionChanged(
      bool has_at_least_one_discovery_session) {
    fake_discovery_session_manager()->SetIsDiscoverySessionActive(
        /*has_at_least_one_discovery_session = */
        has_at_least_one_discovery_session);
    base::RunLoop().RunUntilIdle();
  }

  std::unique_ptr<FastPairHandshake> CreateHandshake(
      scoped_refptr<Device> device,
      FastPairHandshake::OnCompleteCallback callback) {
    auto fake = std::make_unique<FakeFastPairHandshake>(
        adapter_, std::move(device), std::move(callback));
    return fake;
  }

 protected:
  bluetooth_config::FakeDiscoverySessionManager*
  fake_discovery_session_manager() {
    return ash_test_helper()
        ->bluetooth_config_test_helper()
        ->fake_discovery_session_manager();
  }

  scoped_refptr<Device> initial_device_;
  scoped_refptr<Device> initial_device2_;
  scoped_refptr<Device> subsequent_device_;
  scoped_refptr<Device> retroactive_device_;
  scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>> adapter_;
  raw_ptr<FakeFeatureStatusTracker, DanglingUntriaged> feature_status_tracker_;
  raw_ptr<MockScannerBroker, DanglingUntriaged> mock_scanner_broker_;
  raw_ptr<FakeRetroactivePairingDetector, DanglingUntriaged>
      fake_retroactive_pairing_detector_;
  raw_ptr<MockPairerBroker, DanglingUntriaged> mock_pairer_broker_;
  raw_ptr<MockUIBroker, DanglingUntriaged> mock_ui_broker_;
  raw_ptr<MockCompanionAppBroker, DanglingUntriaged> mock_companion_app_broker_;
  raw_ptr<MockFastPairRepository, DanglingUntriaged> mock_fast_pair_repository_;
  bluetooth_config::FakeAdapterStateController fake_adapter_state_controller_;
  std::unique_ptr<Mediator> mediator_;
};

TEST_F(MediatorTest, TogglesScanningWhenFastPairEnabledChanges) {
  EXPECT_CALL(*mock_scanner_broker_, StartScanning);
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_scanner_broker_, StopScanning);
  feature_status_tracker_->SetIsFastPairEnabled(false);
  EXPECT_CALL(*mock_scanner_broker_, StartScanning);
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_scanner_broker_, StopScanning);
  feature_status_tracker_->SetIsFastPairEnabled(false);

  // When one or more discovery sessions are active, don't toggle
  // scanning when fast pair enabled changes.
  EXPECT_CALL(*mock_scanner_broker_, StopScanning);
  SetHasAtLeastOneDiscoverySessionChanged(true);
  EXPECT_CALL(*mock_scanner_broker_, StopScanning);
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_scanner_broker_, StopScanning);
  feature_status_tracker_->SetIsFastPairEnabled(false);
  EXPECT_CALL(*mock_scanner_broker_, StopScanning);
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_scanner_broker_, StopScanning);
  feature_status_tracker_->SetIsFastPairEnabled(false);
}

TEST_F(MediatorTest, TogglesScanningWhenHasAtLeastOneDiscoverySessionChanges) {
  EXPECT_CALL(*mock_scanner_broker_, StartScanning);
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_scanner_broker_, StopScanning);
  SetHasAtLeastOneDiscoverySessionChanged(true);
  EXPECT_CALL(*mock_scanner_broker_, StartScanning);
  SetHasAtLeastOneDiscoverySessionChanged(false);
  EXPECT_CALL(*mock_scanner_broker_, StopScanning);
  SetHasAtLeastOneDiscoverySessionChanged(true);

  // When fast pair is disabled, don't toggle scanning when "we have at
  // least one discovery session" changes.
  EXPECT_CALL(*mock_scanner_broker_, StopScanning);
  feature_status_tracker_->SetIsFastPairEnabled(false);
  EXPECT_CALL(*mock_scanner_broker_, StopScanning);
  SetHasAtLeastOneDiscoverySessionChanged(false);
  EXPECT_CALL(*mock_scanner_broker_, StopScanning);
  SetHasAtLeastOneDiscoverySessionChanged(true);
  EXPECT_CALL(*mock_scanner_broker_, StopScanning);
  SetHasAtLeastOneDiscoverySessionChanged(false);
}

TEST_F(MediatorTest,
       CancelsPairingsWhenHasAtLeastOneDiscoverySessionChangesNotPairing) {
  // Start with fast pair enabled and one handshake in progress.
  feature_status_tracker_->SetIsFastPairEnabled(true);
  FastPairHandshakeLookup::GetInstance()->Create(adapter_, initial_device_,
                                                 base::DoNothing());
  EXPECT_TRUE(FastPairHandshakeLookup::GetInstance()->Get(initial_device_));

  // When one or more discovery sessions are active, stop scanning and dismiss
  // notifications. If we aren't actively pairing, dismiss all handshakes.
  EXPECT_CALL(*mock_pairer_broker_, IsPairing).WillOnce(Return(false));
  EXPECT_CALL(*mock_scanner_broker_, StopScanning);
  EXPECT_CALL(*mock_pairer_broker_, StopPairing);
  EXPECT_CALL(*mock_ui_broker_, RemoveNotifications);
  SetHasAtLeastOneDiscoverySessionChanged(true);
  EXPECT_FALSE(FastPairHandshakeLookup::GetInstance()->Get(initial_device_));

  // When no discovery sessions are active, resume scanning.
  EXPECT_CALL(*mock_scanner_broker_, StartScanning);
  SetHasAtLeastOneDiscoverySessionChanged(false);
}

TEST_F(MediatorTest,
       CancelsPairingsWhenHasAtLeastOneDiscoverySessionChangesIsPairing) {
  // Start with fast pair enabled and one handshake in progress.
  feature_status_tracker_->SetIsFastPairEnabled(true);
  FastPairHandshakeLookup::GetInstance()->Create(adapter_, initial_device_,
                                                 base::DoNothing());
  EXPECT_TRUE(FastPairHandshakeLookup::GetInstance()->Get(initial_device_));

  // When one or more discovery sessions are active, stop scanning and dismiss
  // notifications. Simulate the case where the user has already begun pairing
  // before opening Settings, or has initiated V1 device pair.
  EXPECT_CALL(*mock_pairer_broker_, IsPairing).WillOnce(Return(true));
  EXPECT_CALL(*mock_scanner_broker_, StopScanning);
  EXPECT_CALL(*mock_pairer_broker_, StopPairing).Times(0);
  EXPECT_CALL(*mock_ui_broker_, RemoveNotifications);
  SetHasAtLeastOneDiscoverySessionChanged(true);
  EXPECT_TRUE(FastPairHandshakeLookup::GetInstance()->Get(initial_device_));

  // When no discovery sessions are active, resume scanning.
  EXPECT_CALL(*mock_scanner_broker_, StartScanning);
  SetHasAtLeastOneDiscoverySessionChanged(false);
}

TEST_F(MediatorTest, CancelsPairingsWhenFastPairDisabled) {
  // Start with fast pair enabled and one handshake in progress.
  feature_status_tracker_->SetIsFastPairEnabled(true);
  FastPairHandshakeLookup::GetInstance()->Create(adapter_, initial_device_,
                                                 base::DoNothing());
  EXPECT_TRUE(FastPairHandshakeLookup::GetInstance()->Get(initial_device_));

  // When Fast Pair becomes disabled, stop scanning, dismiss
  // notifications, and dismiss all handshakes.
  EXPECT_CALL(*mock_scanner_broker_, StopScanning);
  EXPECT_CALL(*mock_pairer_broker_, StopPairing);
  EXPECT_CALL(*mock_ui_broker_, RemoveNotifications);
  feature_status_tracker_->SetIsFastPairEnabled(false);
  EXPECT_FALSE(FastPairHandshakeLookup::GetInstance()->Get(initial_device_));

  // When Fast Pair becomes enabled, resume scanning.
  EXPECT_CALL(*mock_scanner_broker_, StartScanning);
  feature_status_tracker_->SetIsFastPairEnabled(true);
}

TEST_F(MediatorTest, InvokesShowDiscoveryWhenDeviceFound) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);
}

TEST_F(MediatorTest,
       InvokesShowDiscovery_OnlyOneinitial_Device_DeviceFound_DifferentDevice) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1);
  EXPECT_CALL(*mock_ui_broker_, ExtendNotification).Times(0);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);
  mock_scanner_broker_->NotifyDeviceFound(initial_device2_);
}

TEST_F(MediatorTest,
       InvokesShowDiscovery_OnlyOneinitial_Device_DeviceFound_SameDevice) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1);
  EXPECT_CALL(*mock_ui_broker_, ExtendNotification).Times(1);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);
}

TEST_F(MediatorTest, InvokesShowPairing_V1) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  auto device = base::MakeRefCounted<Device>(kTestMetadataId, kTestAddress,
                                             Protocol::kFastPairInitial);
  initial_device_->set_version(DeviceFastPairVersion::kV1);
  EXPECT_CALL(*mock_ui_broker_, ShowPairing).Times(0);
  mock_ui_broker_->NotifyDiscoveryAction(initial_device_,
                                         DiscoveryAction::kPairToDevice);
}

TEST_F(MediatorTest, DoesNotInvokeShowPairing_DismissedByUser) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairing).Times(0);
  mock_ui_broker_->NotifyDiscoveryAction(initial_device_,
                                         DiscoveryAction::kDismissedByUser);
}

TEST_F(MediatorTest, DoesNotInvokeShowPairing_Dismissed) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairing).Times(0);
  mock_ui_broker_->NotifyDiscoveryAction(initial_device_,
                                         DiscoveryAction::kDismissedByOs);
}

TEST_F(MediatorTest, DoesNotInvokeShowPairing_LearnMore) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairing).Times(0);
  mock_ui_broker_->NotifyDiscoveryAction(initial_device_,
                                         DiscoveryAction::kLearnMore);
}

TEST_F(MediatorTest, NotifyPairFailure_KeyBasedPairingCharacteristicDiscovery) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(
      initial_device_, PairFailure::kKeyBasedPairingCharacteristicDiscovery);
}

TEST_F(MediatorTest, NotifyPairFailure_CreateGattConnection) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(initial_device_,
                                         PairFailure::kCreateGattConnection);
}

TEST_F(MediatorTest, NotifyPairFailure_GattServiceDiscovery) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(initial_device_,
                                         PairFailure::kGattServiceDiscovery);
}

TEST_F(MediatorTest, NotifyPairFailure_GattServiceDiscoveryTimeout) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(
      initial_device_, PairFailure::kGattServiceDiscoveryTimeout);
}

TEST_F(MediatorTest, NotifyPairFailure_DataEncryptorRetrieval) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(initial_device_,
                                         PairFailure::kDataEncryptorRetrieval);
}

TEST_F(MediatorTest, NotifyPairFailure_PasskeyCharacteristicDiscovery) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(
      initial_device_, PairFailure::kPasskeyCharacteristicDiscovery);
}

TEST_F(MediatorTest, NotifyPairFailure_AccountKeyCharacteristicDiscovery) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(
      initial_device_, PairFailure::kAccountKeyCharacteristicDiscovery);
}

TEST_F(MediatorTest,
       NotifyPairFailure_KeyBasedPairingCharacteristicNotifySession) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(
      initial_device_,
      PairFailure::kKeyBasedPairingCharacteristicNotifySession);
}

TEST_F(MediatorTest, NotifyPairFailure_PasskeyCharacteristicNotifySession) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(
      initial_device_, PairFailure::kPasskeyCharacteristicNotifySession);
}

TEST_F(MediatorTest,
       NotifyPairFailure_KeyBasedPairingCharacteristicNotifySessionTimeout) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(
      initial_device_,
      PairFailure::kKeyBasedPairingCharacteristicNotifySessionTimeout);
}

TEST_F(MediatorTest,
       NotifyPairFailure_PasskeyCharacteristicNotifySessionTimeout) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(
      initial_device_, PairFailure::kPasskeyCharacteristicNotifySessionTimeout);
}

TEST_F(MediatorTest, NotifyPairFailure_KeyBasedPairingCharacteristicWrite) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(
      initial_device_, PairFailure::kKeyBasedPairingCharacteristicWrite);
}

TEST_F(MediatorTest, NotifyPairFailure_PasskeyPairingCharacteristicWrite) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(
      initial_device_, PairFailure::kPasskeyPairingCharacteristicWrite);
}

TEST_F(MediatorTest, NotifyPairFailure_KeyBasedPairingResponseTimeout) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(
      initial_device_, PairFailure::kKeyBasedPairingResponseTimeout);
}

TEST_F(MediatorTest, NotifyPairFailure_PasskeyResponseTimeout) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(initial_device_,
                                         PairFailure::kPasskeyResponseTimeout);
}

TEST_F(MediatorTest, NotifyPairFailure_KeybasedPairingResponseDecryptFailure) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(
      initial_device_, PairFailure::kKeybasedPairingResponseDecryptFailure);
}

TEST_F(MediatorTest, NotifyPairFailure_IncorrectKeyBasedPairingResponseType) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(
      initial_device_, PairFailure::kIncorrectKeyBasedPairingResponseType);
}

TEST_F(MediatorTest, NotifyPairFailure_PasskeyDecryptFailure) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(initial_device_,
                                         PairFailure::kPasskeyDecryptFailure);
}

TEST_F(MediatorTest, NotifyPairFailure_IncorrectPasskeyResponseType) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(
      initial_device_, PairFailure::kIncorrectPasskeyResponseType);
}

TEST_F(MediatorTest, NotifyPairFailure_PasskeyMismatch) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(initial_device_,
                                         PairFailure::kPasskeyMismatch);
}

TEST_F(MediatorTest, NotifyPairFailure_PairingDeviceLost) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(initial_device_,
                                         PairFailure::kPairingDeviceLost);
}

TEST_F(MediatorTest, NotifyPairFailure_PairingConnect) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(initial_device_,
                                         PairFailure::kPairingConnect);
}

TEST_F(MediatorTest, NotifyPairFailure_AddressConnect) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed);
  mock_pairer_broker_->NotifyPairFailure(initial_device_,
                                         PairFailure::kAddressConnect);
}

TEST_F(MediatorTest, InvokesShowAssociateAccount) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_pairer_broker_, PairDevice);
  EXPECT_CALL(*mock_ui_broker_, ShowAssociateAccount);
  retroactive_device_->set_account_key(kAccountKey1);
  fake_retroactive_pairing_detector_->NotifyRetroactivePairFound(
      retroactive_device_);
  ASSERT_TRUE(retroactive_device_->version().value() ==
              DeviceFastPairVersion::kHigherThanV1);
}

TEST_F(MediatorTest,
       InvokesShowDiscovery_OnlyOneNotification_DifferentDeviceProtocols) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);
  mock_scanner_broker_->NotifyDeviceFound(subsequent_device_);
}

TEST_F(MediatorTest, InvokesShowDiscovery_OnlyOneNotification_DifferentDevice) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);
  mock_scanner_broker_->NotifyDeviceFound(initial_device2_);
}

TEST_F(MediatorTest, InvokesShowDiscovery_OnlyOneNotification_SameDevice) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);
}

TEST_F(MediatorTest, DoesntInvokeShowAssociateAccount_FastPairDisabled) {
  feature_status_tracker_->SetIsFastPairEnabled(false);
  EXPECT_CALL(*mock_ui_broker_, ShowAssociateAccount).Times(0);
  fake_retroactive_pairing_detector_->NotifyRetroactivePairFound(
      initial_device_);
}

TEST_F(
    MediatorTest,
    NoNotificationOnAccountKeyWriteFailure_AccountKeyCharacteristicDiscovery) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed).Times(0);
  mock_pairer_broker_->NotifyAccountKeyWrite(
      initial_device_, AccountKeyFailure::kAccountKeyCharacteristicDiscovery);
}

TEST_F(MediatorTest,
       NoNotificationOnAccountKeyWriteFailure_kAccountKeyCharacteristicWrite) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowPairingFailed).Times(0);
  mock_pairer_broker_->NotifyAccountKeyWrite(
      initial_device_, AccountKeyFailure::kAccountKeyCharacteristicDiscovery);
}

TEST_F(MediatorTest, AssociateAccountKeyAction_AssociateAccount) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_fast_pair_repository_, WriteAccountAssociationToFootprints);
  EXPECT_CALL(*mock_ui_broker_, RemoveNotifications);
  retroactive_device_->set_version(DeviceFastPairVersion::kHigherThanV1);
  retroactive_device_->set_account_key(kAccountKey1);
  mock_ui_broker_->NotifyAssociateAccountAction(
      retroactive_device_, AssociateAccountAction::kAssociateAccount);
}

TEST_F(MediatorTest, AssociateAccountKeyAction_LearnMore) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_pairer_broker_, PairDevice).Times(0);
  EXPECT_CALL(*mock_ui_broker_, RemoveNotifications).Times(0);
  mock_ui_broker_->NotifyAssociateAccountAction(
      initial_device_, AssociateAccountAction::kLearnMore);
}

TEST_F(MediatorTest, AssociateAccountKeyAction_DismissedByUser) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_pairer_broker_, PairDevice).Times(0);
  mock_ui_broker_->NotifyAssociateAccountAction(
      initial_device_, AssociateAccountAction::kDismissedByUser);
}

TEST_F(MediatorTest, AssociateAccountKeyAction_Dismissed) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_pairer_broker_, PairDevice).Times(0);
  mock_ui_broker_->NotifyAssociateAccountAction(
      initial_device_, AssociateAccountAction::kDismissedByUser);
}

TEST_F(MediatorTest, CompanionAppAction_DownloadApp_Disabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitWithFeatures(
      /*enabled_features=*/{},
      /*disabled_features=*/{ash::features::kFastPairPwaCompanion});

  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_DEATH_IF_SUPPORTED(
      {
        mock_ui_broker_->NotifyCompanionAppAction(
            initial_device_, CompanionAppAction::kDownloadAndLaunchApp);
      },
      "");
}

TEST_F(MediatorTest, CompanionAppAction_DownloadApp_Enabled) {
  base::test::ScopedFeatureList feature_list{
      ash::features::kFastPairPwaCompanion};

  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_companion_app_broker_, InstallCompanionApp).Times(1);
  mock_ui_broker_->NotifyCompanionAppAction(
      initial_device_, CompanionAppAction::kDownloadAndLaunchApp);
}

TEST_F(MediatorTest, CompanionAppAction_LaunchApp_Disabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitWithFeatures(
      /*enabled_features=*/{},
      /*disabled_features=*/{ash::features::kFastPairPwaCompanion});

  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_DEATH_IF_SUPPORTED(
      {
        mock_ui_broker_->NotifyCompanionAppAction(
            initial_device_, CompanionAppAction::kLaunchApp);
      },
      "");
}

TEST_F(MediatorTest, CompanionAppAction_LaunchApp_Enabled) {
  base::test::ScopedFeatureList feature_list{
      ash::features::kFastPairPwaCompanion};

  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_companion_app_broker_, LaunchCompanionApp).Times(1);
  mock_ui_broker_->NotifyCompanionAppAction(initial_device_,
                                            CompanionAppAction::kLaunchApp);
}

TEST_F(MediatorTest, CompanionAppAction_DismissedByUser_Disabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitWithFeatures(
      /*enabled_features=*/{},
      /*disabled_features=*/{ash::features::kFastPairPwaCompanion});

  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_DEATH_IF_SUPPORTED(
      {
        mock_ui_broker_->NotifyCompanionAppAction(
            initial_device_, CompanionAppAction::kDismissedByUser);
      },
      "");
}

TEST_F(MediatorTest, CompanionAppAction_DismissedByUser_Enabled) {
  base::test::ScopedFeatureList feature_list{
      ash::features::kFastPairPwaCompanion};

  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_pairer_broker_, PairDevice).Times(0);
  mock_ui_broker_->NotifyCompanionAppAction(
      initial_device_, CompanionAppAction::kDismissedByUser);
}

TEST_F(MediatorTest, CompanionAppAction_Dismissed_Disabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitWithFeatures(
      /*enabled_features=*/{},
      /*disabled_features=*/{ash::features::kFastPairPwaCompanion});

  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_DEATH_IF_SUPPORTED(
      {
        mock_ui_broker_->NotifyCompanionAppAction(
            initial_device_, CompanionAppAction::kDismissed);
      },
      "");
}

TEST_F(MediatorTest, CompanionAppAction_Dismissed_Enabled) {
  base::test::ScopedFeatureList feature_list{
      ash::features::kFastPairPwaCompanion};

  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_pairer_broker_, PairDevice).Times(0);
  mock_ui_broker_->NotifyCompanionAppAction(initial_device_,
                                            CompanionAppAction::kDismissed);
}

TEST_F(MediatorTest, PairingFailedAction_NavigateToSettings) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_pairer_broker_, PairDevice).Times(0);
  mock_ui_broker_->NotifyPairingFailedAction(
      initial_device_, PairingFailedAction::kNavigateToSettings);
}

TEST_F(MediatorTest, PairingFailedAction_DismissedByUser) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_pairer_broker_, PairDevice).Times(0);
  mock_ui_broker_->NotifyPairingFailedAction(
      initial_device_, PairingFailedAction::kDismissedByUser);
}

TEST_F(MediatorTest, PairingFailedAction_Dismissed) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_pairer_broker_, PairDevice).Times(0);
  mock_ui_broker_->NotifyPairingFailedAction(initial_device_,
                                             PairingFailedAction::kDismissed);
}

TEST_F(MediatorTest, FastPairBluetoothConfigDelegate) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  bluetooth_config::FastPairDelegate* delegate =
      mediator_->GetFastPairDelegate();
  delegate->SetDeviceNameManager(nullptr);
  delegate->SetAdapterStateController(nullptr);
  EXPECT_TRUE(delegate);
  EXPECT_EQ(delegate->GetDeviceImageInfo(kTestAddress), std::nullopt);
}

TEST_F(MediatorTest,
       FastPairBluetoothConfigDelegateNotifiesAdapterStateChanges) {
  EXPECT_CALL(*mock_scanner_broker_, StartScanning);
  feature_status_tracker_->SetIsFastPairEnabled(true);
  FastPairHandshakeLookup::GetInstance()->Create(adapter_, initial_device_,
                                                 base::DoNothing());

  bluetooth_config::FastPairDelegate* delegate =
      mediator_->GetFastPairDelegate();
  delegate->SetDeviceNameManager(nullptr);

  // Mediator should not observe changes to the adapter state before the
  // AdapterStateController is set and the observation is created.
  fake_adapter_state_controller_.SetSystemState(
      bluetooth_config::mojom::BluetoothSystemState::kDisabling);
  EXPECT_TRUE(FastPairHandshakeLookup::GetInstance()->Get(initial_device_));
  fake_adapter_state_controller_.SetSystemState(
      bluetooth_config::mojom::BluetoothSystemState::kEnabled);
  EXPECT_TRUE(FastPairHandshakeLookup::GetInstance()->Get(initial_device_));

  // After the AdapterStateController is set, we should be notified and
  // create an observation.
  delegate->SetAdapterStateController(&fake_adapter_state_controller_);

  // Simulate a call to toggling Bluetooth off via the UI. This should stop
  // scanning, clear existing handshakes, stop pairing, and dismiss
  // notifications.
  EXPECT_CALL(*mock_scanner_broker_, StopScanning);
  EXPECT_CALL(*mock_pairer_broker_, StopPairing);
  EXPECT_CALL(*mock_ui_broker_, RemoveNotifications);
  fake_adapter_state_controller_.SetSystemState(
      bluetooth_config::mojom::BluetoothSystemState::kDisabling);
  EXPECT_FALSE(FastPairHandshakeLookup::GetInstance()->Get(initial_device_));

  delegate->SetAdapterStateController(nullptr);
}

TEST_F(MediatorTest, DiscoveryBanLogic_InitialParing) {
  feature_status_tracker_->SetIsFastPairEnabled(true);

  // Simulate the device first found. When the user dismissed the notification,
  // if the device is found again, we do not expect the notification to be
  // shown.
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);
  mock_ui_broker_->NotifyDiscoveryAction(initial_device_,
                                         DiscoveryAction::kDismissedByUser);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(0);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);

  // After the 2 second timeout, when the device is found again, expect
  // the notification to be shown.
  task_environment()->FastForwardBy(kDismissedDiscoveryNotificationBanTime);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);

  // When the device is found again, we expect the notification to not be shown
  // since it's within the 5 minute short ban timeout period after it has been
  // dismissed by user again.
  mock_ui_broker_->NotifyDiscoveryAction(initial_device_,
                                         DiscoveryAction::kDismissedByUser);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(0);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);

  // After the 5 minute timeout, when the device is found again, expect
  // the notification to be shown.
  task_environment()->FastForwardBy(kShortBanDiscoveryNotificationBanTime);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);

  // When the device is found again, we expect the notification to not be shown
  // again since it's in the long ban state.
  mock_ui_broker_->NotifyDiscoveryAction(initial_device_,
                                         DiscoveryAction::kDismissedByUser);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(0);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);

  // Even after a long timeout period, we do not expect the notification to be
  // shown again under the long ban period.
  task_environment()->FastForwardBy(kLongBanDiscoveryNotificationBanTime);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(0);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);

  // We expect the notification to be shown again when the Fast Pair
  // state is reset. Simulate the Fast Pair toggle being turned off then on
  // again.
  feature_status_tracker_->SetIsFastPairEnabled(false);
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);

  // We also expect the notification to be shown again after a successful
  // pairing. Trigger the ban logic.
  mock_ui_broker_->NotifyDiscoveryAction(initial_device_,
                                         DiscoveryAction::kDismissedByUser);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(0);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);
  // Trigger a successful pairing
  mock_pairer_broker_->NotifyDevicePaired(initial_device_);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);
}

TEST_F(MediatorTest, DiscoveryBan_SubsequentParing) {
  feature_status_tracker_->SetIsFastPairEnabled(true);

  // Simulate the device first found. When the user dismissed the notification,
  // if the device is found again, we do not expect the notification to be
  // shown.
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1);
  mock_scanner_broker_->NotifyDeviceFound(subsequent_device_);
  mock_ui_broker_->NotifyDiscoveryAction(subsequent_device_,
                                         DiscoveryAction::kDismissedByUser);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(0);
  mock_scanner_broker_->NotifyDeviceFound(subsequent_device_);

  // After the 2 second timeout, when the device is found again, expect
  // the notification to be shown.
  task_environment()->FastForwardBy(kDismissedDiscoveryNotificationBanTime);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1);
  mock_scanner_broker_->NotifyDeviceFound(subsequent_device_);

  // When the device is found again, we expect the notification to not be shown
  // since it's within the 5 minute short ban timeout period after it has been
  // dismissed by user again.
  mock_ui_broker_->NotifyDiscoveryAction(subsequent_device_,
                                         DiscoveryAction::kDismissedByUser);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(0);
  mock_scanner_broker_->NotifyDeviceFound(subsequent_device_);

  // After the 5 minute timeout, when the device is found again, expect
  // the notification to be shown.
  task_environment()->FastForwardBy(kShortBanDiscoveryNotificationBanTime);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1);
  mock_scanner_broker_->NotifyDeviceFound(subsequent_device_);

  // When the device is found again, we expect the notification to not be shown
  // again since it's in the long ban state.
  mock_ui_broker_->NotifyDiscoveryAction(subsequent_device_,
                                         DiscoveryAction::kDismissedByUser);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(0);
  mock_scanner_broker_->NotifyDeviceFound(subsequent_device_);

  // Even after a long timeout period, we do not expect the notification to be
  // shown again under the long ban period.
  task_environment()->FastForwardBy(kLongBanDiscoveryNotificationBanTime);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(0);
  mock_scanner_broker_->NotifyDeviceFound(subsequent_device_);

  // We expect the notification to be shown again when the Fast Pair
  // state is reset. Simulate the Fast Pair toggle being turned off then on
  // again.
  feature_status_tracker_->SetIsFastPairEnabled(false);
  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1);
  mock_scanner_broker_->NotifyDeviceFound(subsequent_device_);

  // We also expect the notification to be shown again after a successful
  // pairing. Trigger the ban logic.
  mock_ui_broker_->NotifyDiscoveryAction(subsequent_device_,
                                         DiscoveryAction::kDismissedByUser);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(0);
  mock_scanner_broker_->NotifyDeviceFound(subsequent_device_);
  // Trigger a successful pairing
  mock_pairer_broker_->NotifyDevicePaired(subsequent_device_);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1);
  mock_scanner_broker_->NotifyDeviceFound(subsequent_device_);
}

TEST_F(MediatorTest, DiscoveryBan_MultipleDevices) {
  feature_status_tracker_->SetIsFastPairEnabled(true);

  // Simulate the device first found. When the user dismissed the notification,
  // if the device is found again, we do not expect the notification to be
  // shown.
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);

  mock_ui_broker_->NotifyDiscoveryAction(initial_device_,
                                         DiscoveryAction::kDismissedByUser);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(0);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);

  // However, at this point, a different device is able to be shown
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1);
  mock_scanner_broker_->NotifyDeviceFound(initial_device2_);

  // After the 2 second timeout, when the device is found again, don't expect
  // the notification to be shown since the second device is currently
  // being shown.
  task_environment()->FastForwardBy(kDismissedDiscoveryNotificationBanTime);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(0);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);

  // When the second device is dismissed, do not expect it be to shown
  // again since it is blocked.
  mock_ui_broker_->NotifyDiscoveryAction(initial_device2_,
                                         DiscoveryAction::kDismissedByUser);
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(0);
  mock_scanner_broker_->NotifyDeviceFound(initial_device2_);

  // Now the first device can be shown again since first come first serve
  // notifications no longer apply and it is no longer blocked.
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);
}

TEST_F(MediatorTest, DiscoveryBan_RetroactiveAvoidsBan) {
  feature_status_tracker_->SetIsFastPairEnabled(true);

  // Simulate the device first found.
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1);
  mock_scanner_broker_->NotifyDeviceFound(initial_device_);

  // Simulate another device being found. We expect no notification to be
  // shown for this device due to our existing first come first serve
  // notification logic.
  EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(0);
  mock_scanner_broker_->NotifyDeviceFound(initial_device2_);

  // However, there is an exception in the first come first serve notification
  // logic to show retroactive devices.
  EXPECT_CALL(*mock_ui_broker_, ShowAssociateAccount).Times(1);
  fake_retroactive_pairing_detector_->NotifyRetroactivePairFound(
      retroactive_device_);
}

TEST_F(MediatorTest, PersistsDeviceImages_AfterRetroactivePairFound) {
  feature_status_tracker_->SetIsFastPairEnabled(true);

  // We should save mac address to model ID mapping and persist images
  // once Retroactive Pair is found--in other words, a device was just
  // classic paired and we have images for that device we want to
  // display in Bluetooth Settings, even if the user is offline/logged
  // out/etc.
  EXPECT_CALL(*mock_fast_pair_repository_, FetchDeviceImages).Times(1);
  EXPECT_CALL(*mock_fast_pair_repository_, PersistDeviceImages).Times(1);
  fake_retroactive_pairing_detector_->NotifyRetroactivePairFound(
      retroactive_device_);
}

TEST_F(MediatorTest, PersistsDeviceImages_AfterDeviceInitialPaired) {
  feature_status_tracker_->SetIsFastPairEnabled(true);

  // We should save mac address to model ID mapping and persist images
  // once a device is paired. We have images for the paired device we want to
  // display in Bluetooth Settings, even if the user is offline/logged
  // out/etc.
  EXPECT_CALL(*mock_fast_pair_repository_, FetchDeviceImages).Times(1);
  EXPECT_CALL(*mock_fast_pair_repository_, PersistDeviceImages).Times(1);
  mock_pairer_broker_->NotifyDevicePaired(initial_device_);
}

TEST_F(MediatorTest, PersistsDeviceImages_AfterDeviceSubsequentPaired) {
  feature_status_tracker_->SetIsFastPairEnabled(true);

  // We should save mac address to model ID mapping and persist images
  // once a device is paired. We have images for the paired device we want to
  // display in Bluetooth Settings, even if the user is offline/logged
  // out/etc.
  EXPECT_CALL(*mock_fast_pair_repository_, FetchDeviceImages).Times(1);
  EXPECT_CALL(*mock_fast_pair_repository_, PersistDeviceImages).Times(1);
  mock_pairer_broker_->NotifyDevicePaired(subsequent_device_);
}

TEST_F(MediatorTest,
       ShowAssociateAccount_OnRetroactivePairSilentAccountKeyWrite) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  retroactive_device_->set_account_key(kAccountKey1);
  EXPECT_CALL(*mock_ui_broker_, ShowAssociateAccount);
  mock_pairer_broker_->NotifyAccountKeyWrite(retroactive_device_,
                                             /*error=*/std::nullopt);
}

TEST_F(MediatorTest, NoShowAssociateAccount_OnInitialPairAccountKeyWrite) {
  feature_status_tracker_->SetIsFastPairEnabled(true);
  initial_device_->set_account_key(kAccountKey1);
  EXPECT_CALL(*mock_ui_broker_, ShowAssociateAccount).Times(0);
  mock_pairer_broker_->NotifyAccountKeyWrite(initial_device_,
                                             /*error=*/std::nullopt);
}

TEST_F(MediatorTest, ShowCompanionApp_OnDevicePaired_Disabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitWithFeatures(
      /*enabled_features=*/{},
      /*disabled_features=*/{ash::features::kFastPairPwaCompanion});

  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_companion_app_broker_, MaybeShowCompanionAppActions)
      .Times(0);
  mock_pairer_broker_->NotifyDevicePaired(initial_device_);
}

TEST_F(MediatorTest, ShowCompanionApp_OnDevicePaired_Enabled) {
  base::test::ScopedFeatureList feature_list{
      ash::features::kFastPairPwaCompanion};

  feature_status_tracker_->SetIsFastPairEnabled(true);
  EXPECT_CALL(*mock_companion_app_broker_, MaybeShowCompanionAppActions)
      .Times(1);
  mock_pairer_broker_->NotifyDevicePaired(initial_device_);
}

}  // namespace quick_pair
}  // namespace ash