chromium/ios/chrome/browser/ui/settings/password/password_settings/password_settings_mediator_unittest.mm

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

#import "ios/chrome/browser/ui/settings/password/password_settings/password_settings_mediator.h"

#import "base/test/task_environment.h"
#import "components/affiliations/core/browser/fake_affiliation_service.h"
#import "components/keyed_service/core/service_access_type.h"
#import "components/password_manager/core/browser/password_manager_test_utils.h"
#import "components/password_manager/core/browser/password_store/test_password_store.h"
#import "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
#import "components/password_manager/core/common/password_manager_features.h"
#import "components/signin/public/identity_manager/objc/identity_manager_observer_bridge.h"
#import "components/sync/base/data_type.h"
#import "components/sync/base/passphrase_enums.h"
#import "components/sync/test/mock_sync_service.h"
#import "ios/chrome/browser/passwords/model/ios_chrome_profile_password_store_factory.h"
#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
#import "ios/chrome/browser/signin/model/identity_manager_factory.h"
#import "ios/chrome/browser/sync/model/sync_observer_bridge.h"
#import "ios/chrome/browser/sync/model/sync_service_factory.h"
#import "ios/chrome/common/ui/reauthentication/reauthentication_protocol.h"
#import "ios/web/public/test/web_task_environment.h"
#import "testing/gmock/include/gmock/gmock.h"
#import "testing/gtest/include/gtest/gtest.h"
#import "testing/gtest_mac.h"
#import "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"

using password_manager::SavedPasswordsPresenter;
using password_manager::TestPasswordStore;

using SyncServiceForPasswordTests = testing::NiceMock<syncer::MockSyncService>;

// Sets up the provided mock sync service to respond with the given passphrase
// type and transport active/disabled state.
void SetSyncStatus(SyncServiceForPasswordTests* sync_service,
                   syncer::PassphraseType passphrase_type,
                   bool sync_transport_active = true) {
  ON_CALL(*sync_service, GetTransportState())
      .WillByDefault(
          testing::Return(sync_transport_active
                              ? syncer::SyncService::TransportState::ACTIVE
                              : syncer::SyncService::TransportState::DISABLED));
  ON_CALL(*sync_service, GetActiveDataTypes())
      .WillByDefault(testing::Return(syncer::DataTypeSet({syncer::PASSWORDS})));
  ON_CALL(*(sync_service->GetMockUserSettings()), GetAllEncryptedDataTypes())
      .WillByDefault(testing::Return(syncer::DataTypeSet({syncer::PASSWORDS})));
  ON_CALL(*(sync_service->GetMockUserSettings()), GetPassphraseType())
      .WillByDefault(testing::Return(passphrase_type));
}

class PasswordSettingsMediatorTest : public PlatformTest {
 protected:
  void SetUp() override {
    TestChromeBrowserState::Builder builder;
    builder.AddTestingFactory(
        IOSChromeProfilePasswordStoreFactory::GetInstance(),
        base::BindRepeating(
            &password_manager::BuildPasswordStore<web::BrowserState,
                                                  TestPasswordStore>));
    browser_state_ = std::move(builder).Build();

    store_ =
        base::WrapRefCounted(static_cast<password_manager::TestPasswordStore*>(
            IOSChromeProfilePasswordStoreFactory::GetForBrowserState(
                browser_state_.get(), ServiceAccessType::EXPLICIT_ACCESS)
                .get()));
    presenter_ = std::make_unique<SavedPasswordsPresenter>(
        &affiliation_service_, store_, /*accont_store=*/nullptr);

    mediator_ = [[PasswordSettingsMediator alloc]
           initWithReauthenticationModule:reauth_module_
                  savedPasswordsPresenter:presenter_.get()
        bulkMovePasswordsToAccountHandler:
            bulk_move_passwords_to_account_handler_
                            exportHandler:export_handler_
                              prefService:browser_state_->GetPrefs()
                          identityManager:IdentityManagerFactory::
                                              GetForBrowserState(
                                                  browser_state_.get())
                              syncService:&sync_service_];
    mediator_.consumer = consumer_;
  }

  void TearDown() override { [mediator_ disconnect]; }

  web::WebTaskEnvironment task_env_;
  SyncServiceForPasswordTests sync_service_;
  affiliations::FakeAffiliationService affiliation_service_;
  scoped_refptr<TestPasswordStore> store_;
  std::unique_ptr<SavedPasswordsPresenter> presenter_;
  std::unique_ptr<TestChromeBrowserState> browser_state_;
  id consumer_ = OCMProtocolMock(@protocol(PasswordSettingsConsumer));
  id export_handler_ = OCMProtocolMock(@protocol(PasswordExportHandler));
  id bulk_move_passwords_to_account_handler_ =
      OCMProtocolMock(@protocol(BulkMoveLocalPasswordsToAccountHandler));
  id reauth_module_ = OCMProtocolMock(@protocol(ReauthenticationProtocol));
  PasswordSettingsMediator* mediator_;
};

TEST_F(PasswordSettingsMediatorTest,
       SyncChangeTriggersChangeOnDeviceEncryption) {
  // This was populated when the consumer was initially set.
  [[consumer_ verify] setOnDeviceEncryptionState:
                          PasswordSettingsOnDeviceEncryptionStateNotShown];

  ASSERT_TRUE(
      [mediator_ conformsToProtocol:@protocol(SyncObserverModelBridge)]);
  PasswordSettingsMediator<SyncObserverModelBridge>* syncObserver =
      static_cast<PasswordSettingsMediator<SyncObserverModelBridge>*>(
          mediator_);

  // Set the passphrase type to "Keystore," indicating that we are not yet
  // using on-device encryption.
  SetSyncStatus(&sync_service_, syncer::PassphraseType::kKeystorePassphrase);
  [syncObserver onSyncStateChanged];
  [[consumer_ verify] setOnDeviceEncryptionState:
                          PasswordSettingsOnDeviceEncryptionStateOfferOptIn];

  // Set the passphrase type to "Trusted Vault," meaning on-device encryption is
  // already enabled.
  SetSyncStatus(&sync_service_,
                syncer::PassphraseType::kTrustedVaultPassphrase);
  [syncObserver onSyncStateChanged];
  [[consumer_ verify] setOnDeviceEncryptionState:
                          PasswordSettingsOnDeviceEncryptionStateOptedIn];

  // Turn sync trasnport off.
  SetSyncStatus(&sync_service_, syncer::PassphraseType::kKeystorePassphrase,
                /* sync_transport_active= */ false);
  [syncObserver onSyncStateChanged];
  [[consumer_ verify] setOnDeviceEncryptionState:
                          PasswordSettingsOnDeviceEncryptionStateNotShown];
}

TEST_F(PasswordSettingsMediatorTest,
       IdentityChangeTriggersChangeOnDeviceEncryption) {
  ASSERT_TRUE(
      [mediator_ conformsToProtocol:@protocol(SyncObserverModelBridge)]);
  PasswordSettingsMediator<IdentityManagerObserverBridgeDelegate>*
      syncObserver = static_cast<
          PasswordSettingsMediator<IdentityManagerObserverBridgeDelegate>*>(
          mediator_);
  const signin::PrimaryAccountChangeEvent event;
  [syncObserver onPrimaryAccountChanged:event];
  [[consumer_ verify] setOnDeviceEncryptionState:
                          PasswordSettingsOnDeviceEncryptionStateNotShown];
}

// Tests that sync state changes trigger updates to showing the move local
// passwords to account module.
TEST_F(PasswordSettingsMediatorTest,
       SyncChangeTriggersBulkMovePasswordsToAccountChange) {
  ASSERT_TRUE(
      [mediator_ conformsToProtocol:@protocol(SyncObserverModelBridge)]);

  PasswordSettingsMediator<SyncObserverModelBridge>* syncObserver =
      static_cast<PasswordSettingsMediator<SyncObserverModelBridge>*>(
          mediator_);

  [syncObserver onSyncStateChanged];
  [[consumer_ verify] setLocalPasswordsCount:0 withUserEligibility:NO];
}