chromium/ios/chrome/browser/ui/authentication/history_sync/history_sync_capabilities_fetcher_unittest.mm

// Copyright 2024 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/authentication/history_sync/history_sync_capabilities_fetcher.h"

#import "base/run_loop.h"
#import "base/test/bind.h"
#import "base/test/metrics/histogram_tester.h"
#import "base/test/scoped_feature_list.h"
#import "base/test/scoped_mock_clock_override.h"
#import "base/time/time.h"
#import "components/signin/public/base/signin_switches.h"
#import "components/signin/public/identity_manager/account_capabilities_test_mutator.h"
#import "components/signin/public/identity_manager/account_info.h"
#import "components/signin/public/identity_manager/identity_test_environment.h"
#import "components/signin/public/identity_manager/objc/identity_manager_observer_bridge.h"
#import "components/signin/public/identity_manager/tribool.h"
#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
#import "ios/web/public/test/web_task_environment.h"
#import "testing/platform_test.h"

namespace {

const char kTestEmail[] = "[email protected]";

class HistorySyncCapabilitiesFetcherTest
    : public PlatformTest,
      public ::testing::WithParamInterface<signin::Tribool> {
 public:
  signin::Tribool ExpectedCapabilityValue() const { return GetParam(); }

  void SetUp() override {
    PlatformTest::SetUp();
    TestChromeBrowserState::Builder builder;
    browser_state_ = std::move(builder).Build();
    feature_list_.InitAndEnableFeature(
        switches::kMinorModeRestrictionsForHistorySyncOptIn);
  }

  void TearDown() override {
    ASSERT_TRUE(fetcher_);
    [fetcher_ shutdown];
    PlatformTest::TearDown();
  }

  signin::IdentityManager* identity_manager() {
    return identity_test_env_.identity_manager();
  }

  HistorySyncCapabilitiesFetcher* BuildHistorySyncCapabilitiesFetcher() {
    HistorySyncCapabilitiesFetcher* fetcher =
        [[HistorySyncCapabilitiesFetcher alloc]
            initWithIdentityManager:identity_manager()];
    return fetcher;
  }

  AccountInfo SignInPrimaryAccount() {
    // Sign in AccountInfo.
    AccountInfo account = identity_test_env_.MakePrimaryAccountAvailable(
        kTestEmail, signin::ConsentLevel::kSignin);
    return account;
  }

  void SetAccountInfoCanShowUnrestrictedOptInsCapability(
      AccountInfo account,
      signin::Tribool capability) {
    AccountCapabilitiesTestMutator mutator(&account.capabilities);
    mutator.set_can_show_history_sync_opt_ins_without_minor_mode_restrictions(
        capability == signin::Tribool::kTrue);
    identity_test_env_.UpdateAccountInfoForAccount(account);
  }

 protected:
  web::WebTaskEnvironment task_environment_;
  signin::IdentityTestEnvironment identity_test_env_;
  base::test::ScopedFeatureList feature_list_;
  std::unique_ptr<TestChromeBrowserState> browser_state_;
  HistorySyncCapabilitiesFetcher* fetcher_ = nil;
};

// Tests that startFetchingRestrictionCapability will process the AccountInfo
// capability CanShowHistorySyncOptInsWithoutMinorModeRestrictions if its value
// is already available.
TEST_P(HistorySyncCapabilitiesFetcherTest,
       TestFetchingAvailableAccountInfoCapabilities) {
  base::HistogramTester histogram_tester;
  signin::Tribool expected_capability = ExpectedCapabilityValue();

  // Make account capabilities available before the fetcher is created.
  AccountInfo account = SignInPrimaryAccount();
  SetAccountInfoCanShowUnrestrictedOptInsCapability(account,
                                                    expected_capability);

  base::RunLoop run_loop;
  CapabilityFetchCompletionCallback callback = base::BindLambdaForTesting(
      [&run_loop, expected_capability](signin::Tribool capability) {
        EXPECT_EQ(capability, expected_capability);
        run_loop.Quit();
      });

  // Create the fetcher and attempt to fetch existing capabilities.
  fetcher_ = BuildHistorySyncCapabilitiesFetcher();
  EXPECT_EQ([fetcher_ canShowUnrestrictedOptInsCapability],
            expected_capability);

  [fetcher_ startFetchingRestrictionCapabilityWithCallback:std::move(callback)];
  run_loop.Run();

  histogram_tester.ExpectUniqueSample(
      "Signin.AccountCapabilities.ImmediatelyAvailable", true, 1);
  histogram_tester.ExpectTotalCount(
      "Signin.AccountCapabilities.ImmediatelyAvailable", 1);
  histogram_tester.ExpectUniqueSample(
      "Signin.AccountCapabilities.UserVisibleLatency", 0, 1);
  histogram_tester.ExpectTotalCount("Signin.AccountCapabilities.FetchLatency",
                                    0);
}

// Tests that the account capability is processed on AccountInfo received.
TEST_P(HistorySyncCapabilitiesFetcherTest,
       TestAccountInfoReceivedWithCapability) {
  base::HistogramTester histogram_tester;
  signin::Tribool expected_capability = ExpectedCapabilityValue();
  fetcher_ = BuildHistorySyncCapabilitiesFetcher();

  // Sign in AccountInfo without capabilities setup.
  AccountInfo account = SignInPrimaryAccount();
  EXPECT_EQ([fetcher_ canShowUnrestrictedOptInsCapability],
            signin::Tribool::kUnknown);

  // Set up the callback.
  base::RunLoop run_loop;
  CapabilityFetchCompletionCallback callback = base::BindLambdaForTesting(
      [&run_loop, expected_capability](signin::Tribool capability) {
        EXPECT_EQ(capability, expected_capability);
        run_loop.Quit();
      });

  // Test that onExtendedAccountInfoUpdated can be executed during async
  // capability fetching.
  [fetcher_ startFetchingRestrictionCapabilityWithCallback:std::move(callback)];

  // Set up AccountInfo capabilities.
  SetAccountInfoCanShowUnrestrictedOptInsCapability(account,
                                                    expected_capability);
  EXPECT_EQ([fetcher_ canShowUnrestrictedOptInsCapability],
            expected_capability);

  // Trigger onExtendedAccountInfoUpdated
  identity_test_env_.SimulateSuccessfulFetchOfAccountInfo(
      account.account_id, account.email, account.gaia,
      /*hosted_domain=*/"", "full_name", "given_name", "locale",
      /*picture_url=*/"");

  run_loop.Run();

  histogram_tester.ExpectUniqueSample(
      "Signin.AccountCapabilities.ImmediatelyAvailable", false, 1);
  histogram_tester.ExpectTotalCount(
      "Signin.AccountCapabilities.ImmediatelyAvailable", 1);
  histogram_tester.ExpectTotalCount(
      "Signin.AccountCapabilities.UserVisibleLatency", 1);
  histogram_tester.ExpectTotalCount("Signin.AccountCapabilities.FetchLatency",
                                    1);
}

// Tests that the fallback capability value is processed on fetch deadline.
TEST_P(HistorySyncCapabilitiesFetcherTest, TestCapabilityFetchDeadline) {
  base::HistogramTester histogram_tester;
  base::ScopedMockClockOverride scoped_clock;
  base::RunLoop run_loop;

  // Create the fetcher and wait for capability fetch timeout.
  CapabilityFetchCompletionCallback callback =
      base::BindLambdaForTesting([&run_loop](signin::Tribool capability) {
        EXPECT_EQ(capability, signin::Tribool::kUnknown);
        run_loop.Quit();
      });

  fetcher_ = BuildHistorySyncCapabilitiesFetcher();

  [fetcher_ startFetchingRestrictionCapabilityWithCallback:std::move(callback)];
  scoped_clock.Advance(base::Milliseconds(
      switches::kMinorModeRestrictionsFetchDeadlineMs.Get()));
  run_loop.Run();

  histogram_tester.ExpectUniqueSample(
      "Signin.AccountCapabilities.ImmediatelyAvailable", false, 1);
  histogram_tester.ExpectTotalCount(
      "Signin.AccountCapabilities.ImmediatelyAvailable", 1);
  histogram_tester.ExpectTotalCount(
      "Signin.AccountCapabilities.UserVisibleLatency", 1);
  histogram_tester.ExpectTotalCount("Signin.AccountCapabilities.FetchLatency",
                                    1);
}

INSTANTIATE_TEST_SUITE_P(,
                         HistorySyncCapabilitiesFetcherTest,
                         /*ExpectedCapabilityValue*/
                         ::testing::Values(signin::Tribool::kFalse,
                                           signin::Tribool::kTrue));

}  // namespace