// Copyright 2017 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/signin/process_dice_header_delegate_impl.h"

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

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "chrome/browser/signin/dice_tab_helper.h"
#include "chrome/browser/signin/dice_web_signin_interceptor.h"
#include "chrome/browser/signin/dice_web_signin_interceptor_factory.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/webui/signin/signin_ui_error.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "components/metrics/profile_metrics_service.h"
#include "components/signin/public/base/account_consistency_method.h"
#include "components/signin/public/base/signin_metrics.h"
#include "components/signin/public/base/signin_prefs.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "components/sync/base/features.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/navigation_simulator.h"
#include "google_apis/gaia/core_account_id.h"
#include "google_apis/gaia/gaia_id.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using signin_metrics::Reason;

namespace {

signin_metrics::AccessPoint kTestAccessPoint =
    signin_metrics::AccessPoint::kSettings;

signin_metrics::PromoAction kTestPromoAction =
    signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO;

// Dummy delegate that declines all interceptions.
class TestDiceWebSigninInterceptorDelegate
    : public WebSigninInterceptor::Delegate {
 public:
  ~TestDiceWebSigninInterceptorDelegate() override = default;

  bool IsSigninInterceptionSupported(
      const content::WebContents& web_contents) override {
    return false;
  }

  std::unique_ptr<ScopedWebSigninInterceptionBubbleHandle>
  ShowSigninInterceptionBubble(
      content::WebContents* web_contents,
      const BubbleParameters& bubble_parameters,
      base::OnceCallback<void(SigninInterceptionResult)> callback) override {
    std::move(callback).Run(SigninInterceptionResult::kDeclined);
    return nullptr;
  }

  std::unique_ptr<ScopedWebSigninInterceptionBubbleHandle>
  ShowOidcInterceptionDialog(
      content::WebContents* web_contents,
      const BubbleParameters& bubble_parameters,
      signin::SigninChoiceWithConfirmAndRetryCallback callback,
      base::OnceClosure done_callback,
      base::RepeatingClosure retry_callback) override {
    std::move(callback)
        .Then(std::move(done_callback))
        .Run(signin::SIGNIN_CHOICE_CANCEL, base::DoNothing(),
             base::DoNothing());
    return nullptr;
  }

  void ShowFirstRunExperienceInNewProfile(
      BrowserWindowInterface* browser,
      const CoreAccountId& account_id,
      WebSigninInterceptor::SigninInterceptionType interception_type) override {
  }

  void ShowSigninError(content::WebContents* web_contents,
                       const SigninUIError& error) override {}
};

class MockDiceWebSigninInterceptor : public DiceWebSigninInterceptor {
 public:
  explicit MockDiceWebSigninInterceptor(Profile* profile)
      : DiceWebSigninInterceptor(
            profile,
            std::make_unique<TestDiceWebSigninInterceptorDelegate>(),
            &profile_metrics_service_) {}
  ~MockDiceWebSigninInterceptor() override = default;

  MOCK_METHOD(void,
              MaybeInterceptWebSignin,
              (content::WebContents * web_contents,
               CoreAccountId account_id,
               signin_metrics::AccessPoint access_point,
               bool is_new_account,
               bool is_sync_signin,
               signin::Tribool primary_is_connected),
              (override));

 private:
  metrics::ProfileMetricsService profile_metrics_service_;
};

std::unique_ptr<KeyedService> CreateMockDiceWebSigninInterceptor(
    content::BrowserContext* context) {
  return std::make_unique<MockDiceWebSigninInterceptor>(
      Profile::FromBrowserContext(context));
}

class ProcessDiceHeaderDelegateImplTest
    : public ChromeRenderViewHostTestHarness {
 public:
  ProcessDiceHeaderDelegateImplTest()
      : email_("foo@bar.com"),
        auth_error_(GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
            GoogleServiceAuthError::InvalidGaiaCredentialsReason::UNKNOWN)) {
    account_info_.gaia = GaiaId("12345");
    account_info_.account_id = CoreAccountId::FromGaiaId(account_info_.gaia);
    account_info_.email = "email@gmail.com";
  }

  ~ProcessDiceHeaderDelegateImplTest() override = default;

  void AddAccount(bool is_primary) {
    if (!identity_test_environment_profile_adaptor_) {
      InitializeIdentityTestEnvironment();
    }
    if (is_primary) {
      identity_test_environment_profile_adaptor_->identity_test_env()
          ->SetPrimaryAccount(email_, signin::ConsentLevel::kSignin);
    } else {
      identity_test_environment_profile_adaptor_->identity_test_env()
          ->MakeAccountAvailable(email_);
    }
  }

  void InitializeIdentityTestEnvironment() {
    DCHECK(profile());
    identity_test_environment_profile_adaptor_ =
        std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile());
  }

  // Creates a ProcessDiceHeaderDelegateImpl instance.
  std::unique_ptr<ProcessDiceHeaderDelegateImpl>
  CreateDelegateAndNavigateToSignin(
      bool is_sync_signin_tab,
      const GURL& redirect_url,
      Reason reason = Reason::kSigninPrimaryAccount,
      signin_metrics::AccessPoint access_point = kTestAccessPoint) {
    signin_reason_ = reason;
    if (!identity_test_environment_profile_adaptor_) {
      InitializeIdentityTestEnvironment();
    }
    // Load the signin page.
    std::unique_ptr<content::NavigationSimulator> simulator =
        content::NavigationSimulator::CreateRendererInitiated(signin_url_,
                                                              main_rfh());
    simulator->Start();
    if (is_sync_signin_tab) {
      DiceTabHelper::CreateForWebContents(web_contents());
      DiceTabHelper* dice_tab_helper =
          DiceTabHelper::FromWebContents(web_contents());
      dice_tab_helper->InitializeSigninFlow(
          signin_url_, access_point, signin_reason_, kTestPromoAction,
          redirect_url,
          /*record_signin_started_metrics=*/true,
          base::BindRepeating(
              &ProcessDiceHeaderDelegateImplTest::StartSyncCallback,
              base::Unretained(this)),
          base::BindRepeating(
              &ProcessDiceHeaderDelegateImplTest::OnHistorySyncOptinStarted,
              base::Unretained(this)),
          base::BindRepeating(
              &ProcessDiceHeaderDelegateImplTest::OnSigninHeaderReceived,
              base::Unretained(this)),
          base::BindRepeating(
              &ProcessDiceHeaderDelegateImplTest::ShowSigninErrorCallback,
              base::Unretained(this)));
    }
    simulator->Commit();
    DCHECK_EQ(signin_url_, web_contents()->GetVisibleURL());

    if (is_sync_signin_tab) {
      return ProcessDiceHeaderDelegateImpl::Create(web_contents());
    } else {
      // Use the constructor rather than the `Create()` method, to specify the
      // error callback.
      return std::make_unique<ProcessDiceHeaderDelegateImpl>(
          web_contents(), /*is_sync_signin_tab=*/false,
          signin_metrics::AccessPoint::kWebSignin, kTestPromoAction, GURL(),
          ProcessDiceHeaderDelegateImpl::EnableSyncCallback(),
          ProcessDiceHeaderDelegateImpl::EnableHistorySyncOptinCallback(),
          base::BindRepeating(
              &ProcessDiceHeaderDelegateImplTest::OnSigninHeaderReceived,
              base::Unretained(this)),
          base::BindOnce(
              &ProcessDiceHeaderDelegateImplTest::ShowSigninErrorCallback,
              base::Unretained(this)));
    }
  }

  // ChromeRenderViewHostTestHarness:
  TestingProfile::TestingFactories GetTestingFactories() const override {
    return IdentityTestEnvironmentProfileAdaptor::
        GetIdentityTestEnvironmentFactoriesWithAppendedFactories(
            {TestingProfile::TestingFactory{
                DiceWebSigninInterceptorFactory::GetInstance(),
                base::BindRepeating(&CreateMockDiceWebSigninInterceptor)}});
  }

  void TearDown() override {
    identity_test_environment_profile_adaptor_.reset();
    ChromeRenderViewHostTestHarness::TearDown();
  }

  // Callback for the ProcessDiceHeaderDelegateImpl.
  void StartSyncCallback(Profile* profile,
                         signin_metrics::AccessPoint access_point,
                         signin_metrics::PromoAction promo_action,
                         content::WebContents* contents,
                         const CoreAccountInfo& account_info) {
    EXPECT_EQ(profile, this->profile());
    EXPECT_EQ(access_point, kTestAccessPoint);
    EXPECT_EQ(promo_action, kTestPromoAction);
    EXPECT_EQ(web_contents(), contents);
    EXPECT_EQ(account_info_, account_info);
    enable_sync_called_ = true;
  }

  void OnSigninHeaderReceived() { signin_header_received_ = true; }

  void OnHistorySyncOptinStarted(Profile* profile,
                                 content::WebContents* contents,
                                 const CoreAccountInfo& account_info,
                                 signin_metrics::AccessPoint access_point) {
    EXPECT_EQ(profile, this->profile());
    EXPECT_EQ(access_point, kTestAccessPoint);
    EXPECT_EQ(web_contents(), contents);
    EXPECT_EQ(account_info_, account_info);
    history_sync_optin_started_ = true;
  }

  // Callback for the ProcessDiceHeaderDelegateImpl.
  void ShowSigninErrorCallback(Profile* profile,
                               content::WebContents* contents,
                               const SigninUIError& error) {
    EXPECT_EQ(profile, this->profile());
    EXPECT_EQ(web_contents(), contents);
    EXPECT_EQ(base::UTF8ToUTF16(auth_error_.ToString()), error.message());
    EXPECT_EQ(base::UTF8ToUTF16(email_), error.email());
    show_error_called_ = true;
  }

  MockDiceWebSigninInterceptor* mock_interceptor() {
    return static_cast<MockDiceWebSigninInterceptor*>(
        DiceWebSigninInterceptorFactory::GetForProfile(profile()));
  }

  std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
      identity_test_environment_profile_adaptor_;

  const GURL signin_url_ = GURL("https://accounts.google.com");
  bool enable_sync_called_ = false;
  bool signin_header_received_ = false;
  bool show_error_called_ = false;
  bool history_sync_optin_started_ = false;
  CoreAccountInfo account_info_;
  std::string email_;
  GoogleServiceAuthError auth_error_;
  Reason signin_reason_ = Reason::kSigninPrimaryAccount;
};

// Check that sync is enabled if the tab is closed during signin.
TEST_F(ProcessDiceHeaderDelegateImplTest,
       CloseTabWhileCompletingProfileSignIn) {
  std::unique_ptr<ProcessDiceHeaderDelegateImpl> delegate =
      CreateDelegateAndNavigateToSignin(/*is_sync_signin_tab=*/true,
                                        /*redirect_url=*/GURL());

  // Close the tab.
  DeleteContents();

  // Check expectations.
  delegate->CompleteChromeSignInAfterGaiaSignin(account_info_);
  EXPECT_NE(enable_sync_called_,
            syncer::IsReplaceSyncPromosWithSignInPromosEnabled());
  EXPECT_EQ(history_sync_optin_started_,
            syncer::IsReplaceSyncPromosWithSignInPromosEnabled());
  EXPECT_FALSE(show_error_called_);
}

TEST_F(ProcessDiceHeaderDelegateImplTest,
       UnsupportedAccessPointForHistorySync) {
  base::test::ScopedFeatureList scoped_feature_list_{
      syncer::kReplaceSyncPromosWithSignInPromos};

  std::unique_ptr<ProcessDiceHeaderDelegateImpl> delegate =
      CreateDelegateAndNavigateToSignin(
          /*is_sync_signin_tab=*/true,
          /*redirect_url=*/GURL(), Reason::kSigninPrimaryAccount,
          signin_metrics::AccessPoint::kBookmarkBubble);

  // Check expectations.
  delegate->CompleteChromeSignInAfterGaiaSignin(account_info_);

  // History sync opt-in is not started because the access point is not
  // supported.
  EXPECT_FALSE(history_sync_optin_started_);
  EXPECT_FALSE(show_error_called_);
}

// Check that the error is still shown if the tab is closed before the error is
// received.
TEST_F(ProcessDiceHeaderDelegateImplTest, CloseTabWhileFailingSignin) {
  std::unique_ptr<ProcessDiceHeaderDelegateImpl> delegate =
      CreateDelegateAndNavigateToSignin(/*is_sync_signin_tab=*/true,
                                        /*redirect_url=*/GURL());

  // Close the tab.
  DeleteContents();

  // Check expectations.
  delegate->HandleTokenExchangeFailure(email_, auth_error_);
  EXPECT_FALSE(enable_sync_called_);
  EXPECT_TRUE(show_error_called_);
}

// Tests that there is no redirect when `redirect_url` is empty.
TEST_F(ProcessDiceHeaderDelegateImplTest, NoRedirect) {
  std::unique_ptr<ProcessDiceHeaderDelegateImpl> delegate =
      CreateDelegateAndNavigateToSignin(/*is_sync_signin_tab=*/true,
                                        /*redirect_url=*/GURL());
  delegate->CompleteChromeSignInAfterGaiaSignin(account_info_);
  EXPECT_NE(enable_sync_called_,
            syncer::IsReplaceSyncPromosWithSignInPromosEnabled());
  EXPECT_EQ(history_sync_optin_started_,
            syncer::IsReplaceSyncPromosWithSignInPromosEnabled());

  // There was no redirect.
  EXPECT_EQ(signin_url_, web_contents()->GetVisibleURL());
  EXPECT_FALSE(show_error_called_);
  // Check that the sync signin flow is complete.
  DiceTabHelper* dice_tab_helper =
      DiceTabHelper::FromWebContents(web_contents());
  ASSERT_TRUE(dice_tab_helper);
  EXPECT_FALSE(dice_tab_helper->IsSyncSigninInProgress());
}

// Check that a Dice header can still be processed in a reused tab.
// Regression test for https://crbug.com/40069069
TEST_F(ProcessDiceHeaderDelegateImplTest, TabReuse) {
  // Complete a first signin flow.
  std::unique_ptr<ProcessDiceHeaderDelegateImpl> delegate =
      CreateDelegateAndNavigateToSignin(/*is_sync_signin_tab=*/true,
                                        /*redirect_url=*/GURL());
  delegate->CompleteChromeSignInAfterGaiaSignin(account_info_);
  EXPECT_NE(enable_sync_called_,
            syncer::IsReplaceSyncPromosWithSignInPromosEnabled());
  EXPECT_EQ(history_sync_optin_started_,
            syncer::IsReplaceSyncPromosWithSignInPromosEnabled());
  EXPECT_FALSE(show_error_called_);

  // Receive another Dice header in the same tab.
  enable_sync_called_ = false;
  history_sync_optin_started_ = false;
  ProcessDiceHeaderDelegateImpl::Create(web_contents());
  // Calling `EnableSync()` does nothing because the tab has already been used.
  delegate->CompleteChromeSignInAfterGaiaSignin(account_info_);
  EXPECT_FALSE(enable_sync_called_);
  EXPECT_FALSE(history_sync_optin_started_);
  EXPECT_FALSE(show_error_called_);
}

TEST_F(ProcessDiceHeaderDelegateImplTest, SigninHeaderReceived) {
  // Complete a first signin flow.
  std::unique_ptr<ProcessDiceHeaderDelegateImpl> delegate =
      CreateDelegateAndNavigateToSignin(/*is_sync_signin_tab=*/true,
                                        /*redirect_url=*/GURL());
  ASSERT_FALSE(signin_header_received_);

  delegate->OnDiceSigninHeaderReceived();
  EXPECT_TRUE(signin_header_received_);

  // Delete content and reset the received value.
  DeleteContents();
  signin_header_received_ = false;

  delegate->OnDiceSigninHeaderReceived();
  // Make sure the message is not propagated after the content (and the attached
  // DiceTabHelper as well) is deleted.
  EXPECT_FALSE(signin_header_received_);
}

TEST_F(ProcessDiceHeaderDelegateImplTest, SigninHeaderReceived_SyncingTabOff) {
  // Complete a first signin flow.
  std::unique_ptr<ProcessDiceHeaderDelegateImpl> delegate =
      CreateDelegateAndNavigateToSignin(/*is_sync_signin_tab=*/false,
                                        /*redirect_url=*/GURL());
  ASSERT_FALSE(signin_header_received_);

  delegate->OnDiceSigninHeaderReceived();

  // Since there is DiceTabHelper created, we do not expect the message to be
  // redirected.
  EXPECT_FALSE(signin_header_received_);
}

TEST_F(ProcessDiceHeaderDelegateImplTest, AttemptChromeSigninChoiceRemembered) {
  base::HistogramTester histogram_tester;

  if (!identity_test_environment_profile_adaptor_) {
    InitializeIdentityTestEnvironment();
  }
  identity_test_environment_profile_adaptor_->identity_test_env()
      ->MakeAccountAvailable(account_info_.email,
                             {.gaia_id = account_info_.gaia});

  SigninPrefs signin_prefs(*profile()->GetPrefs());
  signin_prefs.SetChromeSigninInterceptionUserChoice(
      account_info_.gaia, ChromeSigninUserChoice::kSignin);

  std::unique_ptr<ProcessDiceHeaderDelegateImpl> delegate =
      CreateDelegateAndNavigateToSignin(/*is_sync_signin_tab=*/false,
                                        /*redirect_url=*/GURL());

  delegate->HandleTokenExchangeSuccess(
      account_info_.account_id,
      /*is_new_account=*/true,
      /*primary_is_connected=*/signin::Tribool::kUnknown);

  histogram_tester.ExpectUniqueSample(
      "Signin.SignIn.Offered",
      signin_metrics::AccessPoint::kSigninChoiceRemembered, 1);

  histogram_tester.ExpectUniqueSample(
      "Signin.SignIn.Started",
      signin_metrics::AccessPoint::kSigninChoiceRemembered, 1);
}

struct TestConfiguration {
  // Test setup.
  bool signed_in;   // User was already signed in at the start of the flow.
  bool signin_tab;  // The tab is marked as a Sync signin tab.

  // Test expectations.
  bool callback_called;  // The relevant callback was called.
  bool show_ntp;         // The NTP was shown.
};

TestConfiguration kEnableSyncTestCases[] = {
    // clang-format off
    // signed_in | signin_tab | callback_called | show_ntp
    {  false,      false,       false,            false},
    {  false,      true,        true,             true},
    {  true,       false,       false,            false},
    // If the user is already syncing, the callback is called, but the flow
    // aborts before actually showing the dialog.
    {  true,       true,        true,             true},
    // clang-format on
};

// Parameterized version of ProcessDiceHeaderDelegateImplTest.
class ProcessDiceHeaderDelegateImplTestEnableSync
    : public ProcessDiceHeaderDelegateImplTest,
      public ::testing::WithParamInterface<TestConfiguration> {
 private:
  base::test::ScopedFeatureList scoped_feature_list_{
      syncer::kReplaceSyncPromosWithSignInPromos};
};

// Test the EnableSync() method in all configurations.
TEST_P(ProcessDiceHeaderDelegateImplTestEnableSync, EnableSync) {
  if (GetParam().signed_in) {
    AddAccount(/*is_primary=*/true);
  }
  const GURL& kNtpUrl = chrome::ChromeUINewTabURLAsGURL();
  std::unique_ptr<ProcessDiceHeaderDelegateImpl> delegate =
      CreateDelegateAndNavigateToSignin(GetParam().signin_tab,
                                        /*redirect_url=*/kNtpUrl);
  delegate->CompleteChromeSignInAfterGaiaSignin(account_info_);

  EXPECT_EQ(GetParam().callback_called, history_sync_optin_started_);
  EXPECT_FALSE(enable_sync_called_);

  GURL expected_url = GetParam().show_ntp ? kNtpUrl : signin_url_;
  EXPECT_EQ(expected_url, web_contents()->GetVisibleURL());
  EXPECT_FALSE(show_error_called_);
  // Check that the sync signin flow is complete.
  if (GetParam().signin_tab) {
    DiceTabHelper* dice_tab_helper =
        DiceTabHelper::FromWebContents(web_contents());
    ASSERT_TRUE(dice_tab_helper);
    EXPECT_FALSE(dice_tab_helper->IsSyncSigninInProgress());
  }
}

INSTANTIATE_TEST_SUITE_P(All,
                         ProcessDiceHeaderDelegateImplTestEnableSync,
                         ::testing::ValuesIn(kEnableSyncTestCases));

TestConfiguration kHandleTokenExchangeFailureTestCases[] = {
    // clang-format off
    // signed_in | signin_tab | callback_called | show_ntp
    {  false,      false,       true,             false},
    {  false,      true,        true,             true},
    {  true,       false,       true,             false},
    {  true,       true,        true,             true},
    // clang-format on
};

// Parameterized version of ProcessDiceHeaderDelegateImplTest.
class ProcessDiceHeaderDelegateImplTestHandleTokenExchangeFailure
    : public ProcessDiceHeaderDelegateImplTest,
      public ::testing::WithParamInterface<TestConfiguration> {
 private:
  base::test::ScopedFeatureList scoped_feature_list_{
      syncer::kReplaceSyncPromosWithSignInPromos};
};

// Test the HandleTokenExchangeFailure() method in all configurations.
TEST_P(ProcessDiceHeaderDelegateImplTestHandleTokenExchangeFailure,
       HandleTokenExchangeFailure) {
  if (GetParam().signed_in) {
    AddAccount(/*is_primary=*/true);
  }
  const GURL& kNtpUrl = chrome::ChromeUINewTabURLAsGURL();
  std::unique_ptr<ProcessDiceHeaderDelegateImpl> delegate =
      CreateDelegateAndNavigateToSignin(GetParam().signin_tab,
                                        /*redirect_url=*/kNtpUrl);
  delegate->HandleTokenExchangeFailure(email_, auth_error_);
  EXPECT_FALSE(enable_sync_called_);
  EXPECT_EQ(GetParam().callback_called, show_error_called_);
  GURL expected_url = GetParam().show_ntp ? kNtpUrl : signin_url_;
  EXPECT_EQ(expected_url, web_contents()->GetVisibleURL());
  // Check that the sync signin flow is complete.
  if (GetParam().signin_tab) {
    DiceTabHelper* dice_tab_helper =
        DiceTabHelper::FromWebContents(web_contents());
    ASSERT_TRUE(dice_tab_helper);
    EXPECT_FALSE(dice_tab_helper->IsSyncSigninInProgress());
  }
}

INSTANTIATE_TEST_SUITE_P(
    All,
    ProcessDiceHeaderDelegateImplTestHandleTokenExchangeFailure,
    ::testing::ValuesIn(kHandleTokenExchangeFailureTestCases));

struct TokenExchangeSuccessConfiguration {
  bool is_reauth = false;   // User was already signed in with the account.
  bool signin_tab = false;  // A DiceTabHelper is attached to the tab.
  Reason reason = Reason::kSigninPrimaryAccount;
  // Expected value for the MaybeInterceptWebSigin call.
  bool sync_signin = false;
  signin_metrics::AccessPoint access_point =
      signin_metrics::AccessPoint::kWebSignin;
};

TokenExchangeSuccessConfiguration kHandleTokenExchangeSuccessTestCases[] = {
    {},
    {.signin_tab = true, .sync_signin = true, .access_point = kTestAccessPoint},
    {.signin_tab = true,
     .reason = Reason::kAddSecondaryAccount,
     .access_point = kTestAccessPoint},
    {.is_reauth = true},
    {.is_reauth = true,
     .signin_tab = true,
     .sync_signin = true,
     .access_point = kTestAccessPoint},
};

// Parameterized version of ProcessDiceHeaderDelegateImplTest.
class ProcessDiceHeaderDelegateImplTestHandleTokenExchangeSuccess
    : public ProcessDiceHeaderDelegateImplTest,
      public ::testing::WithParamInterface<TokenExchangeSuccessConfiguration> {
};

// Test the HandleTokenExchangeSuccess() method in all configurations.
TEST_P(ProcessDiceHeaderDelegateImplTestHandleTokenExchangeSuccess,
       HandleTokenExchangeSuccess) {
  if (GetParam().is_reauth) {
    AddAccount(/*is_primary=*/false);
  }
  std::unique_ptr<ProcessDiceHeaderDelegateImpl> delegate =
      CreateDelegateAndNavigateToSignin(
          GetParam().signin_tab,
          /*redirect_url=*/chrome::ChromeUINewTabURLAsGURL(),
          GetParam().reason);

  EXPECT_CALL(
      *mock_interceptor(),
      MaybeInterceptWebSignin(
          web_contents(), account_info_.account_id, GetParam().access_point,
          !GetParam().is_reauth, GetParam().sync_signin,
          /*primary_is_connected=*/signin::Tribool::kUnknown));
  delegate->HandleTokenExchangeSuccess(
      account_info_.account_id, !GetParam().is_reauth,
      /*primary_is_connected=*/signin::Tribool::kUnknown);

  // Check that the sync signin flow is complete.
  if (GetParam().signin_tab) {
    DiceTabHelper* dice_tab_helper =
        DiceTabHelper::FromWebContents(web_contents());
    ASSERT_TRUE(dice_tab_helper);
    EXPECT_EQ(GetParam().sync_signin,
              dice_tab_helper->IsSyncSigninInProgress());
  }
}

INSTANTIATE_TEST_SUITE_P(
    All,
    ProcessDiceHeaderDelegateImplTestHandleTokenExchangeSuccess,
    ::testing::ValuesIn(kHandleTokenExchangeSuccessTestCases));

}  // namespace
