chromium/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc

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

#include "components/autofill/core/browser/payments/credit_card_access_manager.h"

#include <stddef.h>

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

#include "base/base64.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/field_trial.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/time/clock.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/data_model/credit_card.h"
#include "components/autofill/core/browser/form_data_importer_test_api.h"
#include "components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h"
#include "components/autofill/core/browser/metrics/payments/better_auth_metrics.h"
#include "components/autofill/core/browser/metrics/payments/card_unmask_authentication_metrics.h"
#include "components/autofill/core/browser/metrics/payments/card_unmask_flow_metrics.h"
#include "components/autofill/core/browser/payments/autofill_error_dialog_context.h"
#include "components/autofill/core/browser/payments/card_unmask_challenge_option.h"
#include "components/autofill/core/browser/payments/credit_card_access_manager_test_api.h"
#include "components/autofill/core/browser/payments/credit_card_cvc_authenticator.h"
#include "components/autofill/core/browser/payments/credit_card_risk_based_authenticator.h"
#include "components/autofill/core/browser/payments/mandatory_reauth_manager.h"
#include "components/autofill/core/browser/payments/payments_autofill_client.h"
#include "components/autofill/core/browser/payments/test/mock_payments_window_manager.h"
#include "components/autofill/core/browser/payments/test/test_credit_card_otp_authenticator.h"
#include "components/autofill/core/browser/payments/test_payments_autofill_client.h"
#include "components/autofill/core/browser/payments/test_payments_network_interface.h"
#include "components/autofill/core/browser/payments_data_manager.h"
#include "components/autofill/core/browser/test_autofill_client.h"
#include "components/autofill/core/browser/test_autofill_driver.h"
#include "components/autofill/core/browser/test_browser_autofill_manager.h"
#include "components/autofill/core/browser/test_personal_data_manager.h"
#include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
#include "components/autofill/core/common/autofill_clock.h"
#include "components/autofill/core/common/autofill_payments_features.h"
#include "components/autofill/core/common/autofill_prefs.h"
#include "components/strings/grit/components_strings.h"
#include "components/sync/test/test_sync_service.h"
#include "components/variations/scoped_variations_ids_provider.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
#include "components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h"
#include "components/autofill/core/browser/payments/test_internal_authenticator.h"
#include "components/autofill/core/browser/strike_databases/payments/fido_authentication_strike_database.h"
#include "content/public/test/mock_navigation_handle.h"
#endif

#if BUILDFLAG(IS_ANDROID)
#include "base/android/build_info.h"
#endif

ASCIIToUTF16;
NiceMock;

namespace autofill {
namespace {

PaymentsRpcCardType;
PaymentsRpcResult;

constexpr char kTestGUID[] =;
constexpr char kTestGUID2[] =;
constexpr char kTestNumber[] =;  // Visa
constexpr char kTestNumber2[] =;
constexpr char16_t kTestNumber16[] =;
constexpr char16_t kTestCvc16[] =;
constexpr char kTestServerId[] =;
constexpr char kTestServerId2[] =;

CreditCardFormEventLogger;

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
constexpr char kTestCvc[] = "123";
// Base64 encoding of "This is a test challenge".
constexpr char kTestChallenge[] = "VGhpcyBpcyBhIHRlc3QgY2hhbGxlbmdl";
// Base64 encoding of "This is a test Credential ID".
constexpr char kCredentialId[] = "VGhpcyBpcyBhIHRlc3QgQ3JlZGVudGlhbCBJRC4=";
constexpr char kGooglePaymentsRpid[] = "google.com";

std::string BytesToBase64(const std::vector<uint8_t>& bytes) {
  return base::Base64Encode(bytes);
}
#endif

// Implements an `OnCreditCardFetched()` callback that stores the values it
// receives.
class TestAccessor {};

}  // namespace

class CreditCardAccessManagerTest : public testing::Test {};

#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID) || \
    BUILDFLAG(IS_IOS)

class CreditCardAccessManagerMandatoryReauthTest
    : public CreditCardAccessManagerTest {
 public:
  CreditCardAccessManagerMandatoryReauthTest() = default;
  ~CreditCardAccessManagerMandatoryReauthTest() override = default;

 protected:
  void SetUp() override {
    CreditCardAccessManagerTest::SetUp();
    feature_list_.InitAndEnableFeature(
        features::kAutofillEnableFpanRiskBasedAuthentication);
#if BUILDFLAG(IS_ANDROID)
    if (base::android::BuildInfo::GetInstance()->is_automotive()) {
      autofill_client_.GetPrefs()->SetBoolean(
          prefs::kAutofillPaymentMethodsMandatoryReauth,
          /*value=*/true);
      return;
    }
#endif  // BUILDFLAG(IS_ANDROID)
    autofill_client_.GetPrefs()->SetBoolean(
        prefs::kAutofillPaymentMethodsMandatoryReauth,
        /*value=*/PrefIsEnabled());
  }

  void SetUpDeviceAuthenticatorResponseMock() {
    ON_CALL(mandatory_reauth_manager(), GetAuthenticationMethod)
        .WillByDefault(testing::Return(GetAuthenticationMethod()));

    // We should only expect an AuthenticateWithMessage() call if the feature
    // flag is on and the pref is enabled, or if the device is automotive.
    if (IsMandatoryReauthEnabled()) {
      ON_CALL(mandatory_reauth_manager(),
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_IOS)
              AuthenticateWithMessage)
          .WillByDefault(testing::WithArg<1>(
#elif BUILDFLAG(IS_ANDROID)
              Authenticate)
          .WillByDefault(testing::WithArg<0>(
#endif
              testing::Invoke([mandatory_reauth_response_is_success =
                                   MandatoryReauthResponseIsSuccess()](
                                  base::OnceCallback<void(bool)> callback) {
                std::move(callback).Run(mandatory_reauth_response_is_success);
              })));
    } else {
      EXPECT_CALL(mandatory_reauth_manager(),
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_IOS)
                  AuthenticateWithMessage)
#elif BUILDFLAG(IS_ANDROID)
                  Authenticate)
#endif
          .Times(0);
    }
  }

  payments::MockMandatoryReauthManager& mandatory_reauth_manager() {
    return *static_cast<payments::MockMandatoryReauthManager*>(
        autofill_client_.GetPaymentsAutofillClient()
            ->GetOrCreatePaymentsMandatoryReauthManager());
  }

  virtual bool PrefIsEnabled() const = 0;

  virtual bool MandatoryReauthResponseIsSuccess() const = 0;

  virtual bool HasAuthenticator() const = 0;

  virtual payments::MandatoryReauthAuthenticationMethod
  GetAuthenticationMethod() const = 0;

  bool IsMandatoryReauthEnabled() {
#if BUILDFLAG(IS_ANDROID)
    if (base::android::BuildInfo::GetInstance()->is_automotive()) {
      return true;
    }
#endif
    return PrefIsEnabled();
  }

  base::test::ScopedFeatureList feature_list_;
};

// Parameters of the CreditCardAccessManagerMandatoryReauthFunctionalTest:
// - bool pref_is_enabled: Whether the mandatory re-auth pref is turned on or
// off.
// - bool mandatory_reauth_response_is_success: Whether the response from the
// mandatory re-auth is a success or failure.
// - bool authentication_method: The authentication method that is supported.
class CreditCardAccessManagerMandatoryReauthFunctionalTest
    : public CreditCardAccessManagerMandatoryReauthTest,
      public testing::WithParamInterface<
          std::tuple<bool,
                     bool,
                     payments::MandatoryReauthAuthenticationMethod>> {
 public:
  CreditCardAccessManagerMandatoryReauthFunctionalTest() = default;
  ~CreditCardAccessManagerMandatoryReauthFunctionalTest() override = default;

  bool PrefIsEnabled() const override { return std::get<0>(GetParam()); }

  bool MandatoryReauthResponseIsSuccess() const override {
    return std::get<1>(GetParam());
  }

  bool HasAuthenticator() const override {
    return std::get<2>(GetParam()) !=
           payments::MandatoryReauthAuthenticationMethod::kUnsupportedMethod;
  }

  payments::MandatoryReauthAuthenticationMethod GetAuthenticationMethod()
      const override {
    return std::get<2>(GetParam());
  }

  std::string GetStringForAuthenticationMethod() const {
    switch (GetAuthenticationMethod()) {
      case payments::MandatoryReauthAuthenticationMethod::kUnsupportedMethod:
        return ".UnsupportedMethod";
      case payments::MandatoryReauthAuthenticationMethod::kBiometric:
        return ".Biometric";
      case payments::MandatoryReauthAuthenticationMethod::kScreenLock:
        return ".ScreenLock";
      case payments::MandatoryReauthAuthenticationMethod::kUnknown:
        NOTIMPLEMENTED();
        return "";
    }
  }
};

// Tests that retrieving local cards works correctly in the context of the
// Mandatory Re-Auth feature.
TEST_P(CreditCardAccessManagerMandatoryReauthFunctionalTest,
       MandatoryReauth_FetchLocalCard) {
  base::HistogramTester histogram_tester;
  CreateLocalCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  card->set_cvc(kTestCvc16);

  credit_card_access_manager().PrepareToFetchCreditCard();
  WaitForCallbacks();

  // TODO(crbug.com/40935048): Extract shared boilerplate code out for
  // CreditCardAccessManagerMandatoryReauthFunctionalTest tests.
  SetUpDeviceAuthenticatorResponseMock();
  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));

  // The only time we should expect an error is if mandatory re-auth is
  // enabled, but the mandatory re-auth authentication was not successful.
  if (IsMandatoryReauthEnabled() && HasAuthenticator() &&
      !MandatoryReauthResponseIsSuccess()) {
    EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kTransientError);
    EXPECT_TRUE(accessor_->number().empty());
  } else {
    EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kSuccess);
    EXPECT_EQ(accessor_->number(), kTestNumber16);
    EXPECT_EQ(accessor_->cvc(), kTestCvc16);
  }
  std::string reauth_usage_histogram_name =
      "Autofill.PaymentMethods.CheckoutFlow.ReauthUsage.LocalCard";
  reauth_usage_histogram_name += GetStringForAuthenticationMethod();
  if (IsMandatoryReauthEnabled()) {
    if (HasAuthenticator()) {
      histogram_tester.ExpectBucketCount(
          reauth_usage_histogram_name,
          autofill_metrics::MandatoryReauthAuthenticationFlowEvent::
              kFlowStarted,
          1);
      histogram_tester.ExpectBucketCount(
          reauth_usage_histogram_name,
          MandatoryReauthResponseIsSuccess()
              ? autofill_metrics::MandatoryReauthAuthenticationFlowEvent::
                    kFlowSucceeded
              : autofill_metrics::MandatoryReauthAuthenticationFlowEvent::
                    kFlowFailed,
          1);
      histogram_tester.ExpectUniqueSample(
          "Autofill.ServerCardUnmask.LocalCard.Result.DeviceUnlock",
          MandatoryReauthResponseIsSuccess()
              ? autofill_metrics::ServerCardUnmaskResult::
                    kAuthenticationUnmasked
              : autofill_metrics::ServerCardUnmaskResult::kAuthenticationError,
          1);
    } else {
      histogram_tester.ExpectBucketCount(
          reauth_usage_histogram_name,
          autofill_metrics::MandatoryReauthAuthenticationFlowEvent::
              kFlowSkipped,
          1);
    }
  } else {
    histogram_tester.ExpectBucketCount(
        reauth_usage_histogram_name,
        autofill_metrics::MandatoryReauthAuthenticationFlowEvent::kFlowStarted,
        0);
  }
}

// Tests that retrieving virtual cards works correctly in the context of the
// Mandatory Re-Auth feature.
TEST_P(CreditCardAccessManagerMandatoryReauthFunctionalTest,
       MandatoryReauth_FetchVirtualCard) {
  base::HistogramTester histogram_tester;
  CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
  CreditCard* virtual_card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  virtual_card->set_record_type(CreditCard::RecordType::kVirtualCard);

  credit_card_access_manager().FetchCreditCard(
      virtual_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                                   accessor_->GetWeakPtr()));

  // This checks risk-based authentication flow is successfully invoked,
  // because it is always the very first authentication flow in a VCN
  // unmasking flow.
  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
                  ->risk_based_authentication_invoked());
  // Mock server response with valid card information.
  payments::PaymentsNetworkInterface::UnmaskResponseDetails response;
  response.real_pan = "4111111111111111";
  response.dcvv = "321";
  response.expiration_month = test::NextMonth();
  response.expiration_year = test::NextYear();
  response.card_type = PaymentsRpcCardType::kVirtualCard;

  // TODO(crbug.com/40935048): Extract shared boilerplate code out for
  // CreditCardAccessManagerMandatoryReauthFunctionalTest tests.
  SetUpDeviceAuthenticatorResponseMock();
  credit_card_access_manager()
      .OnVirtualCardRiskBasedAuthenticationResponseReceived(
          PaymentsRpcResult::kSuccess, response);

  // Ensure the accessor received the correct response.
  if (IsMandatoryReauthEnabled() && HasAuthenticator() &&
      !MandatoryReauthResponseIsSuccess()) {
    EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kTransientError);
  } else {
    EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kSuccess);
    EXPECT_EQ(accessor_->number(), u"4111111111111111");
    EXPECT_EQ(accessor_->cvc(), u"321");
    EXPECT_EQ(accessor_->expiry_month(), base::UTF8ToUTF16(test::NextMonth()));
    EXPECT_EQ(accessor_->expiry_year(), base::UTF8ToUTF16(test::NextYear()));
  }
  std::string reauth_usage_histogram_name =
      "Autofill.PaymentMethods.CheckoutFlow.ReauthUsage.VirtualCard";
  reauth_usage_histogram_name += GetStringForAuthenticationMethod();
  if (IsMandatoryReauthEnabled()) {
    if (HasAuthenticator()) {
      histogram_tester.ExpectBucketCount(
          reauth_usage_histogram_name,
          autofill_metrics::MandatoryReauthAuthenticationFlowEvent::
              kFlowStarted,
          1);
      histogram_tester.ExpectBucketCount(
          reauth_usage_histogram_name,
          MandatoryReauthResponseIsSuccess()
              ? autofill_metrics::MandatoryReauthAuthenticationFlowEvent::
                    kFlowSucceeded
              : autofill_metrics::MandatoryReauthAuthenticationFlowEvent::
                    kFlowFailed,
          1);
      histogram_tester.ExpectUniqueSample(
          "Autofill.CvcStorage.CvcFilling.VirtualCard",
          autofill_metrics::CvcFillingFlowType::kMandatoryReauth,
          MandatoryReauthResponseIsSuccess() ? 1 : 0);
      histogram_tester.ExpectUniqueSample(
          "Autofill.ServerCardUnmask.VirtualCard.Result.DeviceUnlock",
          MandatoryReauthResponseIsSuccess()
              ? autofill_metrics::ServerCardUnmaskResult::
                    kAuthenticationUnmasked
              : autofill_metrics::ServerCardUnmaskResult::kAuthenticationError,
          1);
    } else {
      histogram_tester.ExpectBucketCount(
          reauth_usage_histogram_name,
          autofill_metrics::MandatoryReauthAuthenticationFlowEvent::
              kFlowSkipped,
          1);
    }
  } else {
    histogram_tester.ExpectBucketCount(
        reauth_usage_histogram_name,
        autofill_metrics::MandatoryReauthAuthenticationFlowEvent::kFlowStarted,
        0);
  }
}

// Tests that retrieving masked server cards triggers mandatory reauth (if
// applicable) when risk-based auth returned the card.
TEST_P(CreditCardAccessManagerMandatoryReauthFunctionalTest,
       MandatoryReauth_FetchMaskedServerCard) {
  std::string test_number = "4444333322221111";
  base::HistogramTester histogram_tester;
  CreateServerCard(kTestGUID, test_number, kTestServerId);
  CreditCard* masked_server_card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);

  credit_card_access_manager().FetchCreditCard(
      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                                         accessor_->GetWeakPtr()));

  // Ensures CreditCardRiskBasedAuthenticator::Authenticate is successfully
  // invoked.
  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
                  ->risk_based_authentication_invoked());

  // Mock CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse to
  // successfully return the valid card number.
  CreditCard card = *masked_server_card;
  card.set_record_type(CreditCard::RecordType::kFullServerCard);

  // TODO(crbug.com/40935048): Extract shared boilerplate code out for
  // CreditCardAccessManagerMandatoryReauthFunctionalTest tests.
  SetUpDeviceAuthenticatorResponseMock();
  credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
      CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
          .with_result(CreditCardRiskBasedAuthenticator::
                           RiskBasedAuthenticationResponse::Result::
                               kNoAuthenticationRequired)
          .with_card(card));

  // Ensure the accessor received the correct response.
  if (IsMandatoryReauthEnabled() && HasAuthenticator() &&
      !MandatoryReauthResponseIsSuccess()) {
    EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kTransientError);
  } else {
    EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kSuccess);
    EXPECT_EQ(accessor_->number(), base::UTF8ToUTF16(test_number));
  }
  std::string reauth_usage_histogram_name =
      "Autofill.PaymentMethods.CheckoutFlow.ReauthUsage.ServerCard";
  reauth_usage_histogram_name += GetStringForAuthenticationMethod();
  if (IsMandatoryReauthEnabled()) {
    if (HasAuthenticator()) {
      histogram_tester.ExpectBucketCount(
          reauth_usage_histogram_name,
          autofill_metrics::MandatoryReauthAuthenticationFlowEvent::
              kFlowStarted,
          1);
      histogram_tester.ExpectBucketCount(
          reauth_usage_histogram_name,
          MandatoryReauthResponseIsSuccess()
              ? autofill_metrics::MandatoryReauthAuthenticationFlowEvent::
                    kFlowSucceeded
              : autofill_metrics::MandatoryReauthAuthenticationFlowEvent::
                    kFlowFailed,
          1);
      histogram_tester.ExpectUniqueSample(
          "Autofill.ServerCardUnmask.ServerCard.Result.DeviceUnlock",
          MandatoryReauthResponseIsSuccess()
              ? autofill_metrics::ServerCardUnmaskResult::
                    kAuthenticationUnmasked
              : autofill_metrics::ServerCardUnmaskResult::kAuthenticationError,
          1);
    } else {
      histogram_tester.ExpectBucketCount(
          reauth_usage_histogram_name,
          autofill_metrics::MandatoryReauthAuthenticationFlowEvent::
              kFlowSkipped,
          1);
    }
  } else {
    histogram_tester.ExpectBucketCount(
        reauth_usage_histogram_name,
        autofill_metrics::MandatoryReauthAuthenticationFlowEvent::kFlowStarted,
        0);
  }
}

INSTANTIATE_TEST_SUITE_P(
    ,
    CreditCardAccessManagerMandatoryReauthFunctionalTest,
    testing::Combine(
        testing::Bool(),
        testing::Bool(),
        testing::Values(
            payments::MandatoryReauthAuthenticationMethod::kUnsupportedMethod,
            payments::MandatoryReauthAuthenticationMethod::kBiometric,
            payments::MandatoryReauthAuthenticationMethod::kScreenLock)));

// Test suite built for testing mandatory re-auth's functionality as an
// integration with other projects.
// -- bool mandatory_reauth_response_is_success: Whether or not the re-auth
// authentication was successful.
class CreditCardAccessManagerMandatoryReauthIntegrationTest
    : public CreditCardAccessManagerMandatoryReauthTest,
      public testing::WithParamInterface<bool> {
 public:
  CreditCardAccessManagerMandatoryReauthIntegrationTest() = default;
  ~CreditCardAccessManagerMandatoryReauthIntegrationTest() override = default;

 protected:

  bool PrefIsEnabled() const override { return true; }

  bool MandatoryReauthResponseIsSuccess() const override { return GetParam(); }

  bool HasAuthenticator() const override { return true; }

  payments::MandatoryReauthAuthenticationMethod GetAuthenticationMethod()
      const override {
    return payments::MandatoryReauthAuthenticationMethod::kBiometric;
  }
};

// Tests that when retrieving local cards with a CVC stored, the CVC is filled.
// This test is in the context of the Mandatory Re-Auth feature.
TEST_P(CreditCardAccessManagerMandatoryReauthIntegrationTest,
       MandatoryReauth_FetchLocalCard_CvcFillWorksCorrectly) {
  base::HistogramTester histogram_tester;
  CreateLocalCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);

  credit_card_access_manager().PrepareToFetchCreditCard();
  WaitForCallbacks();

  // TODO(crbug.com/40935048): Extract shared boilerplate code out for
  // CreditCardAccessManagerMandatoryReauthTest tests.
  SetUpDeviceAuthenticatorResponseMock();
  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));

  EXPECT_EQ(accessor_->cvc(),
            MandatoryReauthResponseIsSuccess() ? kTestCvc16 : u"");
  histogram_tester.ExpectBucketCount(
      "Autofill.CvcStorage.CvcFilling.LocalCard",
      autofill_metrics::CvcFillingFlowType::kMandatoryReauth,
      MandatoryReauthResponseIsSuccess() ? 1 : 0);
}

// Tests that when retrieving local cards without a CVC stored, the CVC is not
// filled. This test is in the context of the Mandatory Re-Auth feature.
TEST_P(CreditCardAccessManagerMandatoryReauthIntegrationTest,
       MandatoryReauth_FetchLocalCard_NoCvcFillWorksCorrectly) {
  base::HistogramTester histogram_tester;
  CreateLocalCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  card->set_cvc(u"");

  credit_card_access_manager().PrepareToFetchCreditCard();
  WaitForCallbacks();

  // TODO(crbug.com/40935048): Extract shared boilerplate code out for
  // CreditCardAccessManagerMandatoryReauthTest tests.
  SetUpDeviceAuthenticatorResponseMock();
  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));

  EXPECT_EQ(accessor_->cvc(), u"");
  histogram_tester.ExpectBucketCount(
      "Autofill.CvcStorage.CvcFilling.LocalCard",
      autofill_metrics::CvcFillingFlowType::kMandatoryReauth, 0);
}

// Tests that when retrieving masked server cards with a CVC stored, the CVC is
// filled.  This test is in the context of the Mandatory Re-Auth feature.
TEST_P(CreditCardAccessManagerMandatoryReauthIntegrationTest,
       MandatoryReauth_FetchMaskedServerCard_CvcFillWorksCorrectly) {
  base::HistogramTester histogram_tester;
  std::string test_number = "4444333322221111";
  CreateServerCard(kTestGUID, test_number, kTestServerId);
  CreditCard* masked_server_card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);

  credit_card_access_manager().PrepareToFetchCreditCard();
  WaitForCallbacks();

  credit_card_access_manager().FetchCreditCard(
      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                                         accessor_->GetWeakPtr()));

  // TODO(crbug.com/40935048): Extract shared boilerplate code out for
  // CreditCardAccessManagerMandatoryReauthTest tests.
  SetUpDeviceAuthenticatorResponseMock();
  credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
      CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
          .with_result(CreditCardRiskBasedAuthenticator::
                           RiskBasedAuthenticationResponse::Result::
                               kNoAuthenticationRequired)
          .with_card(*masked_server_card));

  EXPECT_EQ(accessor_->cvc(),
            MandatoryReauthResponseIsSuccess() ? kTestCvc16 : u"");
  histogram_tester.ExpectBucketCount(
      "Autofill.CvcStorage.CvcFilling.ServerCard",
      autofill_metrics::CvcFillingFlowType::kMandatoryReauth,
      MandatoryReauthResponseIsSuccess() ? 1 : 0);
}

// Tests that when retrieving masked server cards without a CVC stored, the CVC
// is not filled. This test is in the context of the Mandatory Re-Auth feature.
TEST_P(CreditCardAccessManagerMandatoryReauthIntegrationTest,
       MandatoryReauth_FetchMaskedServerCard_NoCvcFillWorksCorrectly) {
  base::HistogramTester histogram_tester;
  std::string test_number = "4444333322221111";
  CreateServerCard(kTestGUID, test_number, kTestServerId);
  CreditCard* masked_server_card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  masked_server_card->set_cvc(u"");

  credit_card_access_manager().PrepareToFetchCreditCard();
  WaitForCallbacks();

  credit_card_access_manager().FetchCreditCard(
      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                                         accessor_->GetWeakPtr()));

  // TODO(crbug.com/40935048): Extract shared boilerplate code out for
  // CreditCardAccessManagerMandatoryReauthTest tests.
  SetUpDeviceAuthenticatorResponseMock();
  credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
      CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
          .with_result(CreditCardRiskBasedAuthenticator::
                           RiskBasedAuthenticationResponse::Result::
                               kNoAuthenticationRequired)
          .with_card(*masked_server_card));

  EXPECT_EQ(accessor_->cvc(), u"");
  histogram_tester.ExpectBucketCount(
      "Autofill.CvcStorage.CvcFilling.ServerCard",
      autofill_metrics::CvcFillingFlowType::kMandatoryReauth, 0);
}

INSTANTIATE_TEST_SUITE_P(,
                         CreditCardAccessManagerMandatoryReauthIntegrationTest,
                         testing::Bool());

#endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID) ||
        // BUILDFLAG(IS_IOS)

// Tests retrieving local cards.
TEST_F(CreditCardAccessManagerTest, FetchLocalCardSuccess) {}

// Ensures that FetchCreditCard() reports a failure when a card does not exist.
TEST_F(CreditCardAccessManagerTest, FetchNullptrFailure) {}

// Ensures that FetchCreditCard() returns the full PAN upon a successful
// response from payments.
TEST_F(CreditCardAccessManagerTest, FetchServerCardCVCSuccess) {}

// Ensures that FetchCreditCard() returns a failure upon a negative response
// from the server.
TEST_F(CreditCardAccessManagerTest, FetchServerCardCVCNetworkError) {}

// Ensures that FetchCreditCard() returns a failure upon a negative response
// from the server.
TEST_F(CreditCardAccessManagerTest, FetchServerCardCVCPermanentFailure) {}

// Ensures that a "try again" response from payments does not end the flow.
TEST_F(CreditCardAccessManagerTest, FetchServerCardCVCTryAgainFailure) {}

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
// Params of the CreditCardAccessManagerBetterAuthLogTest:
// -- bool has_server_card;
// -- bool is_user_opted_in;
class CreditCardAccessManagerBetterAuthLogTest
    : public CreditCardAccessManagerTest,
      public testing::WithParamInterface<std::tuple<bool, bool>> {
 public:
  CreditCardAccessManagerBetterAuthLogTest() = default;
  ~CreditCardAccessManagerBetterAuthLogTest() override = default;

  bool HasServerCard() { return std::get<0>(GetParam()); }
  bool IsUserOptedIn() { return std::get<1>(GetParam()); }

  void SetUp() override {
    ClearCards();
    if (HasServerCard()) {
      CreateServerCard(kTestGUID, kTestNumber);
    } else {
      CreateLocalCard(kTestGUID, kTestNumber);
    }
    CreditCardAccessManagerTest::SetUp();
  }

  const std::string kVerifiabilityCheckDurationMetrics =
      "Autofill.BetterAuth.UserVerifiabilityCheckDuration";
  const std::string kPreflightCallMetrics =
      "Autofill.BetterAuth.CardUnmaskPreflightCalledWithFidoOptInStatus";
  const std::string kPreflightLatencyMetrics =
      "Autofill.BetterAuth.CardUnmaskPreflightDuration";
  const std::string kPreflightFlowInitiatedMetrics =
      "Autofill.BetterAuth.CardUnmaskPreflightInitiated";
};

TEST_P(CreditCardAccessManagerBetterAuthLogTest,
       CardUnmaskPreflightCalledMetric_FidoEligible) {
  base::HistogramTester histogram_tester;
  auto* fido_authenticator = GetFIDOAuthenticator();
  fido_authenticator->SetUserVerifiable(/*is_user_verifiable=*/true);
  fido_authenticator->set_is_user_opted_in(IsUserOptedIn());
  ResetFetchCreditCard();
  credit_card_access_manager().PrepareToFetchCreditCard();
  InvokeUnmaskDetailsTimeout();
  WaitForCallbacks();
  histogram_tester.ExpectTotalCount(/*name=*/kVerifiabilityCheckDurationMetrics,
                                    /*expected_count=*/HasServerCard() ? 1 : 0);
  if (HasServerCard()) {
    histogram_tester.ExpectUniqueSample(kPreflightCallMetrics,
                                        /*sample=*/IsUserOptedIn(),
                                        /*expected_bucket_count=*/1);
  } else {
    histogram_tester.ExpectTotalCount(/*name=*/kPreflightCallMetrics,
                                      /*expected_count=*/0);
  }
  histogram_tester.ExpectTotalCount(/*name=*/kPreflightCallMetrics,
                                    /*expected_count=*/HasServerCard() ? 1 : 0);
  histogram_tester.ExpectTotalCount(/*name=*/kPreflightLatencyMetrics,
                                    /*expected_count=*/HasServerCard() ? 1 : 0);
  histogram_tester.ExpectTotalCount(/*name=*/kPreflightFlowInitiatedMetrics,
                                    /*expected_count=*/HasServerCard() ? 1 : 0);
}

TEST_P(CreditCardAccessManagerBetterAuthLogTest,
       CardUnmaskPreflightCalledMetric_NotFidoEligible) {
  base::HistogramTester histogram_tester;
  GetFIDOAuthenticator()->SetUserVerifiable(/*is_user_verifiable=*/false);
  ResetFetchCreditCard();
  credit_card_access_manager().PrepareToFetchCreditCard();
  InvokeUnmaskDetailsTimeout();
  WaitForCallbacks();
  if (HasServerCard()) {
    histogram_tester.ExpectUniqueSample(
        /*name=*/kPreflightFlowInitiatedMetrics, /*sample=*/true,
        /*expected_bucket_count=*/1);
    histogram_tester.ExpectTotalCount(
        /*name=*/kVerifiabilityCheckDurationMetrics,
        /*expected_count=*/1);
  } else {
    histogram_tester.ExpectTotalCount(/*name=*/kPreflightFlowInitiatedMetrics,
                                      /*expected_count=*/0);
    histogram_tester.ExpectTotalCount(
        /*name=*/kVerifiabilityCheckDurationMetrics,
        /*expected_count=*/0);
  }
  histogram_tester.ExpectTotalCount(
      /*name=*/kPreflightCallMetrics,
      /*expected_count=*/0);
  histogram_tester.ExpectTotalCount(/*name=*/kPreflightLatencyMetrics,
                                    /*expected_count=*/0);
}

INSTANTIATE_TEST_SUITE_P(,
                         CreditCardAccessManagerBetterAuthLogTest,
                         testing::Combine(testing::Bool(), testing::Bool()));

// Ensures that FetchCreditCard() returns the full PAN upon a successful
// WebAuthn verification and response from payments.
TEST_F(CreditCardAccessManagerTest, FetchServerCardFIDOSuccess) {
  base::HistogramTester histogram_tester;
  std::string unmask_decision_histogram_name =
      "Autofill.BetterAuth.CardUnmaskTypeDecision";
  std::string webauthn_result_histogram_name =
      "Autofill.BetterAuth.WebauthnResult.ImmediateAuthentication";
  std::string flow_events_histogram_name =
      "Autofill.BetterAuth.FlowEvents.Fido";

  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(true);
  payments_network_interface().AddFidoEligibleCard(
      card->server_id(), kCredentialId, kGooglePaymentsRpid);

  credit_card_access_manager().PrepareToFetchCreditCard();
  WaitForCallbacks();

  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  WaitForCallbacks();
  histogram_tester.ExpectUniqueSample(
      flow_events_histogram_name,
      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);

  // FIDO Success.
  EXPECT_EQ(CreditCardFidoAuthenticator::Flow::AUTHENTICATION_FLOW,
            GetFIDOAuthenticator()->current_flow());
  TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
                                                /*did_succeed=*/true);
  EXPECT_TRUE(GetRealPanForFIDOAuth(PaymentsRpcResult::kSuccess, kTestNumber));
  EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kSuccess);

  EXPECT_EQ(kCredentialId,
            BytesToBase64(GetFIDOAuthenticator()->GetCredentialId()));
  EXPECT_EQ(kTestNumber16, accessor_->number());
  EXPECT_EQ(kTestCvc16, accessor_->cvc());

  histogram_tester.ExpectUniqueSample(
      unmask_decision_histogram_name,
      autofill_metrics::CardUnmaskTypeDecisionMetric::kFidoOnly, 1);
  histogram_tester.ExpectUniqueSample(
      webauthn_result_histogram_name,
      autofill_metrics::WebauthnResultMetric::kSuccess, 1);
  histogram_tester.ExpectTotalCount(
      "Autofill.BetterAuth.CardUnmaskDuration.Fido", 1);
  histogram_tester.ExpectTotalCount(
      "Autofill.BetterAuth.CardUnmaskDuration.Fido.ServerCard.Success", 1);
  histogram_tester.ExpectBucketCount(
      flow_events_histogram_name,
      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptCompleted, 1);
}

// Ensures that CVC filling gets logged after FIDO success if the card has CVC.
TEST_F(CreditCardAccessManagerTest, LogCvcFillingFIDOSuccess) {
  base::HistogramTester histogram_tester;

  CreditCard server_card = test::WithCvc(test::GetMaskedServerCard());
  personal_data().test_payments_data_manager().AddServerCreditCard(server_card);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByInstrumentId(
          server_card.instrument_id());
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(true);
  payments_network_interface().AddFidoEligibleCard(
      card->server_id(), kCredentialId, kGooglePaymentsRpid);

  credit_card_access_manager().PrepareToFetchCreditCard();
  WaitForCallbacks();

  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  WaitForCallbacks();

  // FIDO Success.
  TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
                                                /*did_succeed=*/true);
  EXPECT_TRUE(GetRealPanForFIDOAuth(PaymentsRpcResult::kSuccess, kTestNumber));

  histogram_tester.ExpectUniqueSample(
      "Autofill.CvcStorage.CvcFilling.ServerCard",
      autofill_metrics::CvcFillingFlowType::kFido, 1);
}

// Ensures that CVC filling doesn't get logged after FIDO success if the card
// doesn't have CVC.
TEST_F(CreditCardAccessManagerTest, DoNotLogCvcFillingFIDOSuccess) {
  base::HistogramTester histogram_tester;

  CreditCard server_card = test::GetMaskedServerCard();
  server_card.set_cvc(u"");
  personal_data().test_payments_data_manager().AddServerCreditCard(server_card);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByInstrumentId(
          server_card.instrument_id());
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(true);
  payments_network_interface().AddFidoEligibleCard(
      card->server_id(), kCredentialId, kGooglePaymentsRpid);

  credit_card_access_manager().PrepareToFetchCreditCard();
  WaitForCallbacks();

  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  WaitForCallbacks();

  // FIDO Success.
  TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
                                                /*did_succeed=*/true);
  EXPECT_TRUE(GetRealPanForFIDOAuth(PaymentsRpcResult::kSuccess, kTestNumber));

  histogram_tester.ExpectUniqueSample(
      "Autofill.CvcStorage.CvcFilling.ServerCard",
      autofill_metrics::CvcFillingFlowType::kFido, 0);
}

// Ensures that CVC filling gets logged if a card with CVC is retrieved with
// non-interactive authentication.
TEST_F(CreditCardAccessManagerTest,
       LogCvcFillingWithoutInteractiveAuthentication) {
  base::HistogramTester histogram_tester;
  CreditCard local_card = test::WithCvc(test::GetCreditCard());
  personal_data().payments_data_manager().AddCreditCard(local_card);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(
          local_card.guid());

  credit_card_access_manager().PrepareToFetchCreditCard();
  WaitForCallbacks();

  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));

  histogram_tester.ExpectUniqueSample(
      "Autofill.CvcStorage.CvcFilling.LocalCard",
      autofill_metrics::CvcFillingFlowType::kNoInteractiveAuthentication, 1);
}

// Ensures that CVC filling doesn't get logged if a card without CVC is
// retrieved with non-interactive authentication
TEST_F(CreditCardAccessManagerTest,
       DoNotLogCvcFillingWithoutInteractiveAuthentication) {
  base::HistogramTester histogram_tester;
  CreditCard local_card = test::GetCreditCard();
  personal_data().payments_data_manager().AddCreditCard(local_card);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(
          local_card.guid());

  credit_card_access_manager().PrepareToFetchCreditCard();
  WaitForCallbacks();

  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));

  histogram_tester.ExpectUniqueSample(
      "Autofill.CvcStorage.CvcFilling.LocalCard",
      autofill_metrics::CvcFillingFlowType::kNoInteractiveAuthentication, 0);
}

// Ensures that accessor retrieve empty CVC upon a successful
// WebAuthn verification and response from payments using masked server card
// without CVC.
TEST_F(CreditCardAccessManagerTest, FetchServerCardWithoutCvcFIDOSuccess) {
  CreditCard server_card = CreditCard();
  test::SetCreditCardInfo(&server_card, "Elvis Presley", kTestNumber,
                          test::NextMonth().c_str(), test::NextYear().c_str(),
                          "1", u"");
  server_card.set_guid(kTestGUID);
  server_card.set_record_type(CreditCard::RecordType::kMaskedServerCard);
  personal_data().test_payments_data_manager().AddServerCreditCard(server_card);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(true);
  payments_network_interface().AddFidoEligibleCard(
      card->server_id(), kCredentialId, kGooglePaymentsRpid);

  credit_card_access_manager().PrepareToFetchCreditCard();
  WaitForCallbacks();

  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  WaitForCallbacks();

  // FIDO Success.
  EXPECT_EQ(CreditCardFidoAuthenticator::Flow::AUTHENTICATION_FLOW,
            GetFIDOAuthenticator()->current_flow());
  TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
                                                /*did_succeed=*/true);
  EXPECT_TRUE(GetRealPanForFIDOAuth(PaymentsRpcResult::kSuccess, kTestNumber));
  EXPECT_EQ(u"", accessor_->cvc());
}

// Ensures that FetchCreditCard() returns the full PAN upon a successful
// WebAuthn verification and response from payments.
TEST_F(CreditCardAccessManagerTest, FetchServerCardFIDOSuccessWithDcvv) {
  // Opt user in for FIDO auth.
  prefs::SetCreditCardFIDOAuthEnabled(autofill_client_.GetPrefs(), true);

  // General setup.
  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  payments_network_interface().AddFidoEligibleCard(
      card->server_id(), kCredentialId, kGooglePaymentsRpid);

  credit_card_access_manager().PrepareToFetchCreditCard();
  WaitForCallbacks();

  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  WaitForCallbacks();

  // FIDO Success.
  TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
                                                /*did_succeed=*/true);

  // Mock Payments response that includes DCVV along with Full PAN.
  EXPECT_TRUE(GetRealPanForFIDOAuth(PaymentsRpcResult::kSuccess, kTestNumber,
                                    kTestCvc));

  // Expect accessor to successfully retrieve the DCVV.
  EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kSuccess);
  EXPECT_EQ(kTestNumber16, accessor_->number());
  EXPECT_EQ(kTestCvc16, accessor_->cvc());
}

// Ensures that CVC prompt is invoked after WebAuthn fails.
TEST_F(CreditCardAccessManagerTest,
       FetchServerCardFIDOVerificationFailureCVCFallback) {
  base::HistogramTester histogram_tester;
  std::string webauthn_result_histogram_name =
      "Autofill.BetterAuth.WebauthnResult.ImmediateAuthentication";
  std::string flow_events_fido_histogram_name =
      "Autofill.BetterAuth.FlowEvents.Fido";
  std::string flow_events_cvc_fallback_histogram_name =
      "Autofill.BetterAuth.FlowEvents.CvcFallbackFromFido";

  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(true);
  payments_network_interface().AddFidoEligibleCard(
      card->server_id(), kCredentialId, kGooglePaymentsRpid);

  credit_card_access_manager().PrepareToFetchCreditCard();
  WaitForCallbacks();

  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  WaitForCallbacks();
  histogram_tester.ExpectUniqueSample(
      flow_events_fido_histogram_name,
      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);

  // FIDO Failure.
  EXPECT_EQ(CreditCardFidoAuthenticator::Flow::AUTHENTICATION_FLOW,
            GetFIDOAuthenticator()->current_flow());
  TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
                                                /*did_succeed=*/false);

  histogram_tester.ExpectBucketCount(
      flow_events_cvc_fallback_histogram_name,
      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);

  EXPECT_FALSE(GetRealPanForFIDOAuth(PaymentsRpcResult::kSuccess, kTestNumber));
  EXPECT_NE(accessor_->result(), CreditCardFetchResult::kSuccess);

  // Followed by a fallback to CVC.
  EXPECT_EQ(CreditCardFidoAuthenticator::Flow::NONE_FLOW,
            GetFIDOAuthenticator()->current_flow());
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
  EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kSuccess);
  EXPECT_EQ(kTestNumber16, accessor_->number());
  EXPECT_EQ(kTestCvc16, accessor_->cvc());

  histogram_tester.ExpectUniqueSample(
      webauthn_result_histogram_name,
      autofill_metrics::WebauthnResultMetric::kNotAllowedError, 1);
  histogram_tester.ExpectBucketCount(
      flow_events_fido_histogram_name,
      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptCompleted, 0);
  histogram_tester.ExpectBucketCount(
      flow_events_cvc_fallback_histogram_name,
      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptCompleted, 1);
}

// Ensures that CVC prompt is invoked after payments returns an error from
// GetRealPan via FIDO.
TEST_F(CreditCardAccessManagerTest,
       FetchServerCardFIDOServerFailureCVCFallback) {
  base::HistogramTester histogram_tester;
  std::string histogram_name =
      "Autofill.BetterAuth.WebauthnResult.ImmediateAuthentication";

  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(true);
  payments_network_interface().AddFidoEligibleCard(
      card->server_id(), kCredentialId, kGooglePaymentsRpid);

  credit_card_access_manager().PrepareToFetchCreditCard();
  WaitForCallbacks();

  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  WaitForCallbacks();

  // FIDO Failure.
  EXPECT_EQ(CreditCardFidoAuthenticator::Flow::AUTHENTICATION_FLOW,
            GetFIDOAuthenticator()->current_flow());
  TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
                                                /*did_succeed=*/true);
  EXPECT_TRUE(
      GetRealPanForFIDOAuth(PaymentsRpcResult::kPermanentFailure, kTestNumber));
  EXPECT_NE(accessor_->result(), CreditCardFetchResult::kSuccess);

  // Followed by a fallback to CVC.
  EXPECT_EQ(CreditCardFidoAuthenticator::Flow::NONE_FLOW,
            GetFIDOAuthenticator()->current_flow());
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
  EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kSuccess);
  EXPECT_EQ(kTestNumber16, accessor_->number());
  EXPECT_EQ(kTestCvc16, accessor_->cvc());

  histogram_tester.ExpectUniqueSample(
      histogram_name, autofill_metrics::WebauthnResultMetric::kSuccess, 1);
  histogram_tester.ExpectTotalCount(
      "Autofill.BetterAuth.CardUnmaskDuration.Fido", 1);
  histogram_tester.ExpectTotalCount(
      "Autofill.BetterAuth.CardUnmaskDuration.Fido.ServerCard.Failure", 1);
}

// Ensures WebAuthn call is not made if Request Options is missing a Credential
// ID, and falls back to CVC.
TEST_F(CreditCardAccessManagerTest,
       FetchServerCardBadRequestOptionsCVCFallback) {
  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(true);
  // Don't set Credential ID.
  payments_network_interface().AddFidoEligibleCard(
      card->server_id(), /*credential_id=*/"", kGooglePaymentsRpid);

  credit_card_access_manager().PrepareToFetchCreditCard();
  WaitForCallbacks();

  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  WaitForCallbacks();

  // FIDO Failure.
  EXPECT_FALSE(GetRealPanForFIDOAuth(PaymentsRpcResult::kSuccess, kTestNumber));
  EXPECT_NE(accessor_->result(), CreditCardFetchResult::kSuccess);

  // Followed by a fallback to CVC.
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
  EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kSuccess);
  EXPECT_EQ(kTestNumber16, accessor_->number());
  EXPECT_EQ(kTestCvc16, accessor_->cvc());
}

// Ensures that CVC prompt is invoked when the pre-flight call to Google
// Payments times out.
TEST_F(CreditCardAccessManagerTest, FetchServerCardFIDOTimeoutCVCFallback) {
  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(true);

  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  InvokeUnmaskDetailsTimeout();
  WaitForCallbacks();

  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
  EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kSuccess);
  EXPECT_EQ(kTestNumber16, accessor_->number());
  EXPECT_EQ(kTestCvc16, accessor_->cvc());
}

// Ensures the existence of user-perceived latency during the preflight call is
// correctly logged.
TEST_F(CreditCardAccessManagerTest,
       Metrics_LoggingExistenceOfUserPerceivedLatency) {
  // Setting up a FIDO-enabled user with a local card and a server card.
  std::string server_guid = "00000000-0000-0000-0000-000000000001";
  std::string local_guid = "00000000-0000-0000-0000-000000000003";
  CreateServerCard(server_guid, "4594299181086168");
  CreateLocalCard(local_guid, "4409763681177079");
  CreditCard* server_card =
      personal_data().payments_data_manager().GetCreditCardByGUID(server_guid);
  CreditCard* local_card =
      personal_data().payments_data_manager().GetCreditCardByGUID(local_guid);
  GetFIDOAuthenticator()->SetUserVerifiable(true);

  for (bool user_is_opted_in : {true, false}) {
    std::string histogram_name =
        "Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection.";
    histogram_name += user_is_opted_in ? "OptedIn" : "OptedOut";
    SetCreditCardFIDOAuthEnabled(user_is_opted_in);

    {
      // Preflight call ignored because local card was chosen.
      base::HistogramTester histogram_tester;

      ResetFetchCreditCard();
      credit_card_access_manager().PrepareToFetchCreditCard();
      task_environment_.FastForwardBy(base::Seconds(4));
      WaitForCallbacks();

      credit_card_access_manager().FetchCreditCard(
          local_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                                     accessor_->GetWeakPtr()));
      WaitForCallbacks();

      histogram_tester.ExpectUniqueSample(
          histogram_name,
          autofill_metrics::PreflightCallEvent::kDidNotChooseMaskedCard, 1);
    }

    {
      // Preflight call returned after card was chosen.
      base::HistogramTester histogram_tester;
      payments_network_interface().ShouldReturnUnmaskDetailsImmediately(false);

      ResetFetchCreditCard();
      credit_card_access_manager().PrepareToFetchCreditCard();
      credit_card_access_manager().FetchCreditCard(
          server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                                      accessor_->GetWeakPtr()));
      task_environment_.FastForwardBy(base::Seconds(4));
      WaitForCallbacks();

      histogram_tester.ExpectUniqueSample(
          histogram_name,
          autofill_metrics::PreflightCallEvent::
              kCardChosenBeforePreflightCallReturned,
          1);
      histogram_tester.ExpectTotalCount(
          "Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection.OptedIn."
          "Duration",
          static_cast<int>(user_is_opted_in));
      histogram_tester.ExpectTotalCount(
          "Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection.OptedIn."
          "TimedOutCvcFallback",
          static_cast<int>(user_is_opted_in));
    }

    {
      // Preflight call returned before card was chosen.
      base::HistogramTester histogram_tester;
      // This is important because CreditCardFidoAuthenticator will update the
      // opted-in pref according to GetDetailsForGetRealPan response.
      payments_network_interface().AllowFidoRegistration(!user_is_opted_in);

      ResetFetchCreditCard();
      credit_card_access_manager().PrepareToFetchCreditCard();
      WaitForCallbacks();

      credit_card_access_manager().FetchCreditCard(
          server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                                      accessor_->GetWeakPtr()));
      WaitForCallbacks();

      histogram_tester.ExpectUniqueSample(
          histogram_name,
          autofill_metrics::PreflightCallEvent::
              kPreflightCallReturnedBeforeCardChosen,
          1);
    }
  }
}

// Ensures that falling back to CVC because of preflight timeout is correctly
// logged.
TEST_F(CreditCardAccessManagerTest, Metrics_LoggingTimedOutCvcFallback) {
  // Setting up a FIDO-enabled user with a local card and a server card.
  std::string server_guid = "00000000-0000-0000-0000-000000000001";
  CreateServerCard(server_guid, "4594299181086168");
  CreditCard* server_card =
      personal_data().payments_data_manager().GetCreditCardByGUID(server_guid);
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(true);
  payments_network_interface().ShouldReturnUnmaskDetailsImmediately(false);

  std::string existence_perceived_latency_histogram_name =
      "Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection.OptedIn";
  std::string perceived_latency_duration_histogram_name =
      "Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection.OptedIn."
      "Duration";
  std::string timeout_cvc_fallback_histogram_name =
      "Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection.OptedIn."
      "TimedOutCvcFallback";

  // Preflight call arrived before timeout, after card was chosen.
  {
    base::HistogramTester histogram_tester;

    ResetFetchCreditCard();
    credit_card_access_manager().PrepareToFetchCreditCard();
    credit_card_access_manager().FetchCreditCard(
        server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                                    accessor_->GetWeakPtr()));

    // Mock a delayed response.
    InvokeDelayedGetUnmaskDetailsResponse();

    task_environment_.FastForwardBy(base::Seconds(4));
    WaitForCallbacks();

    histogram_tester.ExpectUniqueSample(
        existence_perceived_latency_histogram_name,
        autofill_metrics::PreflightCallEvent::
            kCardChosenBeforePreflightCallReturned,
        1);
    histogram_tester.ExpectTotalCount(perceived_latency_duration_histogram_name,
                                      1);
    histogram_tester.ExpectBucketCount(timeout_cvc_fallback_histogram_name,
                                       false, 1);
  }

  // Preflight call timed out and CVC fallback was invoked.
  {
    base::HistogramTester histogram_tester;

    ResetFetchCreditCard();
    credit_card_access_manager().PrepareToFetchCreditCard();
    credit_card_access_manager().FetchCreditCard(
        server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                                    accessor_->GetWeakPtr()));
    task_environment_.FastForwardBy(base::Seconds(4));
    WaitForCallbacks();

    histogram_tester.ExpectUniqueSample(
        existence_perceived_latency_histogram_name,
        autofill_metrics::PreflightCallEvent::
            kCardChosenBeforePreflightCallReturned,
        1);
    histogram_tester.ExpectTotalCount(perceived_latency_duration_histogram_name,
                                      1);
    histogram_tester.ExpectBucketCount(timeout_cvc_fallback_histogram_name,
                                       true, 1);
  }
}

// Ensures that use of a server card that is not enrolled into FIDO invokes
// authorization flow when user is opted-in.
TEST_F(CreditCardAccessManagerTest, FIDONewCardAuthorization) {
  base::HistogramTester histogram_tester;
  std::string unmask_decision_histogram_name =
      "Autofill.BetterAuth.CardUnmaskTypeDecision";
  std::string webauthn_result_histogram_name =
      "Autofill.BetterAuth.WebauthnResult.AuthenticationAfterCVC";
  std::string flow_events_histogram_name =
      "Autofill.BetterAuth.FlowEvents.CvcThenFido";

  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  OptUserInToFido();

  payments_network_interface().ShouldReturnUnmaskDetailsImmediately(true);
  payments_network_interface().SetFidoRequestOptionsInUnmaskDetails(
      kCredentialId, kGooglePaymentsRpid);
  credit_card_access_manager().PrepareToFetchCreditCard();
  WaitForCallbacks();

  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  InvokeUnmaskDetailsTimeout();
  WaitForCallbacks();
  histogram_tester.ExpectUniqueSample(
      flow_events_histogram_name,
      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);

  // Do not return any RequestOptions or CreationOptions in GetRealPan.
  // RequestOptions should have been returned in unmask details response.
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber,
                                   TestFidoRequestOptionsType::kNotPresent));
  // Ensure that the form is not filled yet (OnCreditCardFetched is not called).
  EXPECT_EQ(accessor_->number(), std::u16string());
  EXPECT_EQ(accessor_->cvc(), std::u16string());

  // Mock user response.
  EXPECT_EQ(CreditCardFidoAuthenticator::Flow::FOLLOWUP_AFTER_CVC_AUTH_FLOW,
            GetFIDOAuthenticator()->current_flow());
  TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
                                                /*did_succeed=*/true);
  // Ensure that the form is filled after user verification (OnCreditCardFetched
  // is called).
  EXPECT_EQ(kTestNumber16, accessor_->number());
  EXPECT_EQ(kTestCvc16, accessor_->cvc());

  // Mock OptChange payments call.
  OptChange(PaymentsRpcResult::kSuccess, true);

  histogram_tester.ExpectUniqueSample(
      unmask_decision_histogram_name,
      autofill_metrics::CardUnmaskTypeDecisionMetric::kCvcThenFido, 1);
  histogram_tester.ExpectUniqueSample(
      webauthn_result_histogram_name,
      autofill_metrics::WebauthnResultMetric::kSuccess, 1);
  histogram_tester.ExpectBucketCount(
      flow_events_histogram_name,
      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptCompleted, 1);
}

// Ensures that the use of a server card that is not enrolled into FIDO fills
// the form if the user is opted-in to FIDO but no request options are present.
TEST_F(CreditCardAccessManagerTest,
       FIDONewCardAuthorization_NoRequestOptions_FormFilled) {
  CreditCard* card = CreateServerCard(kTestGUID, kTestNumber);
  OptUserInToFido();

  // Clear the FIDO request options that were set.
  payments_network_interface().unmask_details()->fido_request_options.clear();

  payments_network_interface().ShouldReturnUnmaskDetailsImmediately(true);
  credit_card_access_manager().PrepareToFetchCreditCard();
  WaitForCallbacks();

  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  InvokeUnmaskDetailsTimeout();
  WaitForCallbacks();

  // Do not return any RequestOptions or CreationOptions in GetRealPan.
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber,
                                   TestFidoRequestOptionsType::kNotPresent));
  // Ensure that the form is filled as there are no FIDO request options
  // present.
  EXPECT_EQ(accessor_->number(), kTestNumber16);
  EXPECT_EQ(accessor_->cvc(), kTestCvc16);
}

// Ensures that use of a server card that is not enrolled into FIDO fills the
// form if the user is opted-in to FIDO but the request options are invalid.
TEST_F(CreditCardAccessManagerTest,
       FIDONewCardAuthorization_InvalidRequestOptions_FormFilled) {
  CreditCard* card = CreateServerCard(kTestGUID, kTestNumber);
  OptUserInToFido();

  payments_network_interface().ShouldReturnUnmaskDetailsImmediately(true);

  // Set invalid FIDO request options.
  payments_network_interface().SetFidoRequestOptionsInUnmaskDetails(
      /*credential_id=*/"", /*relying_party_id=*/"");

  credit_card_access_manager().PrepareToFetchCreditCard();
  WaitForCallbacks();

  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  InvokeUnmaskDetailsTimeout();
  WaitForCallbacks();

  // Do not return any RequestOptions in GetRealPan. RequestOptions should have
  // been returned in unmask details response.
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber,
                                   TestFidoRequestOptionsType::kNotPresent));
  // Ensure that the form is filled as the only FIDO request options present are
  // invalid.
  EXPECT_EQ(accessor_->number(), kTestNumber16);
  EXPECT_EQ(accessor_->cvc(), kTestCvc16);
}

// Ensures expired cards always invoke a CVC prompt instead of WebAuthn.
TEST_F(CreditCardAccessManagerTest, FetchExpiredServerCardInvokesCvcPrompt) {
  // Creating an expired server card and opting the user in with authorized
  // card.
  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  card->SetExpirationYearFromString(u"2010");
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(true);
  payments_network_interface().AddFidoEligibleCard(
      card->server_id(), kCredentialId, kGooglePaymentsRpid);

  credit_card_access_manager().PrepareToFetchCreditCard();
  WaitForCallbacks();

  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  WaitForCallbacks();

  // Expect CVC prompt to be invoked.
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
  EXPECT_EQ(kTestNumber16, accessor_->number());
  EXPECT_EQ(kTestCvc16, accessor_->cvc());
}

#if BUILDFLAG(IS_ANDROID)
// Ensures that the WebAuthn verification prompt is invoked after user opts in
// on unmask card checkbox.
TEST_F(CreditCardAccessManagerTest, FIDOOptInSuccess_Android) {
  base::HistogramTester histogram_tester;
  std::string histogram_name =
      "Autofill.BetterAuth.WebauthnResult.CheckoutOptIn";

  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(false);

  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  InvokeUnmaskDetailsTimeout();
  WaitForCallbacks();

  // For Android, set `test_fido_request_options_type` to valid to mock user
  // checking the opt-in checkbox and ensuring GetRealPan returns
  // RequestOptions.
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber,
                                   TestFidoRequestOptionsType::kValid));
  WaitForCallbacks();

  // Check current flow to ensure CreditCardFidoAuthenticator::Authorize is
  // called and correct flow is set.
  EXPECT_EQ(CreditCardFidoAuthenticator::Flow::OPT_IN_WITH_CHALLENGE_FLOW,
            GetFIDOAuthenticator()->current_flow());
  // Ensure that the form is not filled yet (OnCreditCardFetched is not called).
  EXPECT_EQ(accessor_->number(), std::u16string());
  EXPECT_EQ(accessor_->cvc(), std::u16string());

  // Mock user response.
  TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
                                                /*did_succeed=*/true);
  // Ensure that the form is filled after user verification (OnCreditCardFetched
  // is called).
  EXPECT_EQ(kTestNumber16, accessor_->number());
  EXPECT_EQ(kTestCvc16, accessor_->cvc());

  // Mock OptChange payments call.
  OptChange(PaymentsRpcResult::kSuccess,
            /*user_is_opted_in=*/true);

  EXPECT_EQ(kGooglePaymentsRpid, GetFIDOAuthenticator()->GetRelyingPartyId());
  EXPECT_EQ(kTestChallenge,
            BytesToBase64(GetFIDOAuthenticator()->GetChallenge()));
  EXPECT_TRUE(GetFIDOAuthenticator()->IsUserOptedIn());

  histogram_tester.ExpectUniqueSample(
      histogram_name, autofill_metrics::WebauthnResultMetric::kSuccess, 1);
}

// Ensures that the card is filled into the form if the request options returned
// are invalid when the user opts in through the checkbox.
TEST_F(CreditCardAccessManagerTest,
       FIDOOptInFailure_InvalidResponseRequestOptions) {
  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(false);

  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  InvokeUnmaskDetailsTimeout();
  WaitForCallbacks();

  // Set the test request options returned to invalid to mock the user checking
  // the checkbox, but invalid request options are returned from the server.
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber,
                                   TestFidoRequestOptionsType::kInvalid));
  WaitForCallbacks();

  // Ensure that the form is filled because the request options returned from
  // the response were invalid.
  EXPECT_EQ(accessor_->number(), kTestNumber16);
  EXPECT_EQ(accessor_->cvc(), kTestCvc16);
}

// Ensures that the failed user verification disallows enrollment.
TEST_F(CreditCardAccessManagerTest, FIDOOptInUserVerificationFailure) {
  base::HistogramTester histogram_tester;
  std::string histogram_name =
      "Autofill.BetterAuth.WebauthnResult.CheckoutOptIn";

  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(false);

  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  InvokeUnmaskDetailsTimeout();
  WaitForCallbacks();

  // For Android, set `test_fido_request_options_type` to valid to mock user
  // checking the opt-in checkbox and ensuring GetRealPan returns
  // RequestOptions.
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber,
                                   TestFidoRequestOptionsType::kValid));
  // Check current flow to ensure CreditCardFidoAuthenticator::Authorize is
  // called and correct flow is set.
  EXPECT_EQ(CreditCardFidoAuthenticator::Flow::OPT_IN_WITH_CHALLENGE_FLOW,
            GetFIDOAuthenticator()->current_flow());
  // Ensure that the form is not filled yet (OnCreditCardFetched is not called).
  EXPECT_EQ(accessor_->number(), std::u16string());
  EXPECT_EQ(accessor_->cvc(), std::u16string());

  // Mock GetAssertion failure.
  TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
                                                /*did_succeed=*/false);
  // Ensure that form is still filled even if user verification fails
  // (OnCreditCardFetched is called). Note that this is different behavior than
  // registering a new card.
  EXPECT_EQ(kTestNumber16, accessor_->number());
  EXPECT_EQ(kTestCvc16, accessor_->cvc());

  EXPECT_FALSE(GetFIDOAuthenticator()->IsUserOptedIn());

  histogram_tester.ExpectUniqueSample(
      histogram_name, autofill_metrics::WebauthnResultMetric::kNotAllowedError,
      1);
}

// Ensures that enrollment does not happen if the server returns a failure.
TEST_F(CreditCardAccessManagerTest, FIDOOptInServerFailure) {
  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(false);

  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  InvokeUnmaskDetailsTimeout();
  WaitForCallbacks();

  // For Android, set `test_fido_request_options_type` to valid to mock user
  // checking the opt-in checkbox and ensuring GetRealPan returns
  // RequestOptions.
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber,
                                   TestFidoRequestOptionsType::kValid));
  // Check current flow to ensure CreditCardFidoAuthenticator::Authorize is
  // called and correct flow is set.
  EXPECT_EQ(CreditCardFidoAuthenticator::Flow::OPT_IN_WITH_CHALLENGE_FLOW,
            GetFIDOAuthenticator()->current_flow());
  // Ensure that the form is not filled yet (OnCreditCardFetched is not called).
  EXPECT_EQ(accessor_->number(), std::u16string());
  EXPECT_EQ(accessor_->cvc(), std::u16string());

  // Mock user response and OptChange payments call.
  TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
                                                /*did_succeed=*/true);
  // Ensure that the form is filled after user verification (OnCreditCardFetched
  // is called).
  EXPECT_EQ(kTestNumber16, accessor_->number());
  EXPECT_EQ(kTestCvc16, accessor_->cvc());
  OptChange(PaymentsRpcResult::kPermanentFailure, false);

  EXPECT_FALSE(GetFIDOAuthenticator()->IsUserOptedIn());
}

// Ensures that enrollment does not happen if user unchecking the opt-in
// checkbox.
TEST_F(CreditCardAccessManagerTest, FIDOOptIn_CheckboxDeclined) {
  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(false);

  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  InvokeUnmaskDetailsTimeout();
  WaitForCallbacks();

  // For Android, set `test_fido_request_options_type` to not present to mock
  // user unchecking the opt-in checkbox resulting in GetRealPan not returning
  // request options.
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber,
                                   TestFidoRequestOptionsType::kNotPresent));
  // Ensure that form is filled (OnCreditCardFetched is called).
  EXPECT_EQ(kTestNumber16, accessor_->number());
  EXPECT_EQ(kTestCvc16, accessor_->cvc());
  // Check current flow to ensure CreditCardFidoAuthenticator::Authorize is
  // never called.
  EXPECT_EQ(CreditCardFidoAuthenticator::Flow::NONE_FLOW,
            GetFIDOAuthenticator()->current_flow());
  EXPECT_FALSE(GetFIDOAuthenticator()->IsUserOptedIn());
}

// Ensures that opting-in through settings page on Android successfully sends an
// opt-in request the next time the user downstreams a card.
TEST_F(CreditCardAccessManagerTest, FIDOSettingsPageOptInSuccess_Android) {
  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  GetFIDOAuthenticator()->SetUserVerifiable(true);

  // Setting the local opt-in state as true and implying that Payments servers
  // has the opt-in state to false - this shows the user opted-in through the
  // settings page.
  SetCreditCardFIDOAuthEnabled(true);
  payments_network_interface().AllowFidoRegistration(true);
  payments_network_interface().ShouldReturnUnmaskDetailsImmediately(true);

  credit_card_access_manager().PrepareToFetchCreditCard();
  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  InvokeUnmaskDetailsTimeout();
  WaitForCallbacks();

  MockUserResponseForCvcAuth(kTestCvc16, /*enable_fido=*/false);

  // Although the checkbox was hidden and |enable_fido_auth| was set to false in
  // the user request, because of the previous opt-in intention, the client must
  // request to opt-in.
  EXPECT_TRUE(payments_network_interface()
                  .unmask_request()
                  ->user_response.enable_fido_auth);
}

#else   // BUILDFLAG(IS_ANDROID)
// Ensures that the WebAuthn enrollment prompt is invoked after user opts in. In
// this case, the user is not yet enrolled server-side, and thus receives
// |creation_options|.
TEST_F(CreditCardAccessManagerTest,
       FIDOEnrollmentSuccess_CreationOptions_Desktop) {
  base::HistogramTester histogram_tester;
  std::string webauthn_result_histogram_name =
      "Autofill.BetterAuth.WebauthnResult.CheckoutOptIn";
  std::string opt_in_histogram_name =
      "Autofill.BetterAuth.OptInCalled.FromCheckoutFlow";
  std::string promo_shown_histogram_name =
      "Autofill.BetterAuth.OptInPromoShown.FromCheckoutFlow";
  std::string promo_user_decision_histogram_name =
      "Autofill.BetterAuth.OptInPromoUserDecision.FromCheckoutFlow";

  ClearStrikes();
  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(false);
  payments_network_interface().AllowFidoRegistration(true);

  credit_card_access_manager().PrepareToFetchCreditCard();
  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  WaitForCallbacks();

  // Mock user and payments response.
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
  AcceptWebauthnOfferDialog(/*did_accept=*/true);

  OptChange(PaymentsRpcResult::kSuccess,
            /*user_is_opted_in=*/false,
            /*include_creation_options=*/true);

  // Mock user response and OptChange payments call.
  EXPECT_EQ(CreditCardFidoAuthenticator::Flow::OPT_IN_WITH_CHALLENGE_FLOW,
            GetFIDOAuthenticator()->current_flow());
  TestCreditCardFidoAuthenticator::MakeCredential(GetFIDOAuthenticator(),
                                                  /*did_succeed=*/true);
  OptChange(PaymentsRpcResult::kSuccess,
            /*user_is_opted_in=*/true);

  EXPECT_EQ(kGooglePaymentsRpid, GetFIDOAuthenticator()->GetRelyingPartyId());
  EXPECT_EQ(kTestChallenge,
            BytesToBase64(GetFIDOAuthenticator()->GetChallenge()));
  EXPECT_TRUE(GetFIDOAuthenticator()->IsUserOptedIn());
  EXPECT_EQ(0, GetStrikes());
  histogram_tester.ExpectUniqueSample(
      webauthn_result_histogram_name,
      autofill_metrics::WebauthnResultMetric::kSuccess, 1);
  histogram_tester.ExpectTotalCount(opt_in_histogram_name, 2);
  histogram_tester.ExpectBucketCount(
      opt_in_histogram_name,
      autofill_metrics::WebauthnOptInParameters::kFetchingChallenge, 1);
  histogram_tester.ExpectBucketCount(
      opt_in_histogram_name,
      autofill_metrics::WebauthnOptInParameters::kWithCreationChallenge, 1);
  histogram_tester.ExpectTotalCount(promo_shown_histogram_name, 1);
  histogram_tester.ExpectUniqueSample(
      promo_user_decision_histogram_name,
      autofill_metrics::WebauthnOptInPromoUserDecisionMetric::kAccepted, 1);
}

// Ensures that the correct number of strikes are added when the user declines
// the WebAuthn offer.
TEST_F(CreditCardAccessManagerTest, FIDOEnrollment_OfferDeclined_Desktop) {
  base::HistogramTester histogram_tester;
  std::string promo_shown_histogram_name =
      "Autofill.BetterAuth.OptInPromoShown.FromCheckoutFlow";
  std::string promo_user_decision_histogram_name =
      "Autofill.BetterAuth.OptInPromoUserDecision.FromCheckoutFlow";

  ClearStrikes();
  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(false);
  payments_network_interface().AllowFidoRegistration(true);

  credit_card_access_manager().PrepareToFetchCreditCard();
  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  WaitForCallbacks();

  // Mock user and payments response.
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
  AcceptWebauthnOfferDialog(/*did_accept=*/false);
  EXPECT_EQ(
      FidoAuthenticationStrikeDatabase::kStrikesToAddWhenOptInOfferDeclined,
      GetStrikes());
  histogram_tester.ExpectTotalCount(promo_shown_histogram_name, 1);
  histogram_tester.ExpectUniqueSample(
      promo_user_decision_histogram_name,
      autofill_metrics::WebauthnOptInPromoUserDecisionMetric::
          kDeclinedImmediately,
      1);
}

// Ensures that the correct number of strikes are added when the user declines
// the WebAuthn offer.
TEST_F(CreditCardAccessManagerTest,
       FIDOEnrollment_OfferDeclinedAfterAccepting_Desktop) {
  base::HistogramTester histogram_tester;
  std::string promo_shown_histogram_name =
      "Autofill.BetterAuth.OptInPromoShown.FromCheckoutFlow";
  std::string promo_user_decision_histogram_name =
      "Autofill.BetterAuth.OptInPromoUserDecision.FromCheckoutFlow";

  ClearStrikes();
  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(false);
  payments_network_interface().AllowFidoRegistration(true);

  credit_card_access_manager().PrepareToFetchCreditCard();
  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  WaitForCallbacks();

  // Mock user and payments response.
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
  AcceptWebauthnOfferDialog(/*did_accept=*/true);
  AcceptWebauthnOfferDialog(/*did_accept=*/false);
  EXPECT_EQ(
      FidoAuthenticationStrikeDatabase::kStrikesToAddWhenOptInOfferDeclined,
      GetStrikes());
  histogram_tester.ExpectTotalCount(promo_shown_histogram_name, 1);
  histogram_tester.ExpectUniqueSample(
      promo_user_decision_histogram_name,
      autofill_metrics::WebauthnOptInPromoUserDecisionMetric::
          kDeclinedAfterAccepting,
      1);
}

// Ensures that the correct number of strikes are added when the user fails to
// complete user-verification for an opt-in attempt.
TEST_F(CreditCardAccessManagerTest,
       FIDOEnrollment_UserVerificationFailed_Desktop) {
  base::HistogramTester histogram_tester;
  std::string webauthn_result_histogram_name =
      "Autofill.BetterAuth.WebauthnResult.CheckoutOptIn";
  std::string opt_in_histogram_name =
      "Autofill.BetterAuth.OptInCalled.FromCheckoutFlow";

  ClearStrikes();
  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(false);
  payments_network_interface().AllowFidoRegistration(true);

  credit_card_access_manager().PrepareToFetchCreditCard();
  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  InvokeUnmaskDetailsTimeout();
  WaitForCallbacks();

  // Mock user and payments response.
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
  WaitForCallbacks();
  AcceptWebauthnOfferDialog(/*did_accept=*/true);

  OptChange(PaymentsRpcResult::kSuccess,
            /*user_is_opted_in=*/false,
            /*include_creation_options=*/true);

  // Mock user response.
  TestCreditCardFidoAuthenticator::MakeCredential(GetFIDOAuthenticator(),
                                                  /*did_succeed=*/false);
  EXPECT_EQ(FidoAuthenticationStrikeDatabase::
                kStrikesToAddWhenUserVerificationFailsOnOptInAttempt,
            GetStrikes());
  histogram_tester.ExpectUniqueSample(
      webauthn_result_histogram_name,
      autofill_metrics::WebauthnResultMetric::kNotAllowedError, 1);
  histogram_tester.ExpectUniqueSample(
      opt_in_histogram_name,
      autofill_metrics::WebauthnOptInParameters::kFetchingChallenge, 1);
}

// Ensures that the WebAuthn enrollment prompt is invoked after user opts in. In
// this case, the user is already enrolled server-side, and thus receives
// |request_options|.
TEST_F(CreditCardAccessManagerTest,
       FIDOEnrollmentSuccess_RequestOptions_Desktop) {
  base::HistogramTester histogram_tester;
  std::string webauthn_result_histogram_name =
      "Autofill.BetterAuth.WebauthnResult.CheckoutOptIn";
  std::string opt_in_histogram_name =
      "Autofill.BetterAuth.OptInCalled.FromCheckoutFlow";

  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(false);
  payments_network_interface().AllowFidoRegistration(true);

  credit_card_access_manager().PrepareToFetchCreditCard();
  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));
  WaitForCallbacks();

  // Mock user and payments response.
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
  WaitForCallbacks();
  AcceptWebauthnOfferDialog(/*did_accept=*/true);

  OptChange(PaymentsRpcResult::kSuccess,
            /*user_is_opted_in=*/false,
            /*include_creation_options=*/false,
            /*include_request_options=*/true);

  // Mock user response and OptChange payments call.
  EXPECT_EQ(CreditCardFidoAuthenticator::Flow::OPT_IN_WITH_CHALLENGE_FLOW,
            GetFIDOAuthenticator()->current_flow());
  TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
                                                /*did_succeed=*/true);
  OptChange(PaymentsRpcResult::kSuccess,
            /*user_is_opted_in=*/true);

  EXPECT_EQ(kGooglePaymentsRpid, GetFIDOAuthenticator()->GetRelyingPartyId());
  EXPECT_EQ(kTestChallenge,
            BytesToBase64(GetFIDOAuthenticator()->GetChallenge()));
  EXPECT_TRUE(GetFIDOAuthenticator()->IsUserOptedIn());

  histogram_tester.ExpectUniqueSample(
      webauthn_result_histogram_name,
      autofill_metrics::WebauthnResultMetric::kSuccess, 1);
  histogram_tester.ExpectTotalCount(opt_in_histogram_name, 2);
  histogram_tester.ExpectBucketCount(
      opt_in_histogram_name,
      autofill_metrics::WebauthnOptInParameters::kFetchingChallenge, 1);
  histogram_tester.ExpectBucketCount(
      opt_in_histogram_name,
      autofill_metrics::WebauthnOptInParameters::kWithRequestChallenge, 1);
}

TEST_F(CreditCardAccessManagerTest, SettingsPage_OptOut) {
  base::HistogramTester histogram_tester;
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(true);

  EXPECT_TRUE(IsCreditCardFIDOAuthEnabled());
  credit_card_access_manager().OnSettingsPageFIDOAuthToggled(false);
  EXPECT_TRUE(GetFIDOAuthenticator()->IsOptOutCalled());
  OptChange(PaymentsRpcResult::kSuccess,
            /*user_is_opted_in=*/false);

  EXPECT_FALSE(IsCreditCardFIDOAuthEnabled());
}
#endif  // BUILDFLAG(IS_ANDROID)

// Params of the CreditCardAccessManagerBetterAuthOptInLogTest:
// -- bool is_virtual_card;
// -- bool unmask_details_offer_fido_opt_in;
// -- bool card_authorization_token_present;
// -- bool max_strikes_limit_reached;
// -- bool has_opted_in_from_android_settings;
// -- bool is_opted_in_for_fido;
class CreditCardAccessManagerBetterAuthOptInLogTest
    : public CreditCardAccessManagerTest,
      public testing::WithParamInterface<
          std::tuple<bool, bool, bool, bool, bool, bool>> {
 public:
  CreditCardAccessManagerBetterAuthOptInLogTest() = default;
  ~CreditCardAccessManagerBetterAuthOptInLogTest() override = default;

  void SetUp() override {
    CreditCardAccessManagerTest::SetUp();

    if (MaxStrikesLimitReached()) {
      AddMaxStrikes();
    } else {
      ClearStrikes();
    }

    CreateServerCard(kTestGUID, kTestNumber);
    GetFIDOAuthenticator()->SetUserVerifiable(true);
#if BUILDFLAG(IS_ANDROID)
    SetCreditCardFIDOAuthEnabled(HasOptedInFromAndroidSettings());
#else
    SetCreditCardFIDOAuthEnabled(false);
#endif  // BUILDFLAG(OS_ANDROID)
    payments_network_interface().AllowFidoRegistration(
        /*offer_fido_opt_in=*/UnmaskDetailsOfferFidoOptIn());
    if (IsVirtualCard()) {
      GetCreditCard()->set_record_type(CreditCard::RecordType::kVirtualCard);
    }
    if (IsOptedIntoFido()) {
      // If user and device are already opted into FIDO, then add an eligible
      // card to ensure that the `unmask_details_` contains fido request
      // options.
      payments_network_interface().AddFidoEligibleCard(
          "random_id", kCredentialId, kGooglePaymentsRpid);
    }

    credit_card_access_manager().PrepareToFetchCreditCard();
    credit_card_access_manager().FetchCreditCard(
        GetCreditCard(), base::BindOnce(&TestAccessor::OnCreditCardFetched,
                                        accessor_->GetWeakPtr()));
  }

  bool IsVirtualCard() { return std::get<0>(GetParam()); }
  bool UnmaskDetailsOfferFidoOptIn() { return std::get<1>(GetParam()); }
  bool CardAuthorizationTokenPresent() { return std::get<2>(GetParam()); }
  bool MaxStrikesLimitReached() { return std::get<3>(GetParam()); }
  bool HasOptedInFromAndroidSettings() { return std::get<4>(GetParam()); }
  bool IsOptedIntoFido() { return std::get<5>(GetParam()); }

  bool ShouldOfferFidoOptIn() {
    return !IsOptedIntoFido() && !IsVirtualCard() &&
           UnmaskDetailsOfferFidoOptIn() && CardAuthorizationTokenPresent() &&
           !MaxStrikesLimitReached();
  }

  bool ShouldOfferFidoOptInAndroid() {
    return !IsOptedIntoFido() && !IsVirtualCard() &&
           UnmaskDetailsOfferFidoOptIn() && !HasOptedInFromAndroidSettings();
  }

  const std::string GetFidoOptInNotOfferedHistogram() {
    return fido_opt_in_not_offered_histogram;
  }

  CreditCard* GetCreditCard() {
    return personal_data().payments_data_manager().GetCreditCardByGUID(
        kTestGUID);
  }

 private:
  const std::string fido_opt_in_not_offered_histogram =
      "Autofill.BetterAuth.OptInPromoNotOfferedReason";
};

#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
// Ensures that the correct metrics are logged when the FIDO opt-in dialog is
// not shown on Desktop.
TEST_P(CreditCardAccessManagerBetterAuthOptInLogTest,
       FidoOptInNotShown_Desktop) {
  base::HistogramTester histogram_tester;

  EXPECT_EQ(test_api(credit_card_access_manager())
                .ShouldOfferFidoOptInDialog(
                    CreditCardCvcAuthenticator::CvcAuthenticationResponse()
                        .with_did_succeed(true)
                        .with_card(GetCreditCard())
                        .with_card_authorization_token(
                            CardAuthorizationTokenPresent()
                                ? "card_authorization_token"
                                : "")
                        .with_cvc(u"123")),
            ShouldOfferFidoOptIn());

  if (IsOptedIntoFido()) {
    histogram_tester.ExpectUniqueSample(
        GetFidoOptInNotOfferedHistogram(),
        /*sample=*/
        autofill_metrics::WebauthnOptInPromoNotOfferedReason::kAlreadyOptedIn,
        /*expected_bucket_count=*/1);
  } else if (!UnmaskDetailsOfferFidoOptIn()) {
    histogram_tester.ExpectUniqueSample(
        GetFidoOptInNotOfferedHistogram(),
        /*sample=*/
        autofill_metrics::WebauthnOptInPromoNotOfferedReason::
            kUnmaskDetailsOfferFidoOptInFalse,
        /*expected_bucket_count=*/1);
  } else if (!CardAuthorizationTokenPresent()) {
    histogram_tester.ExpectUniqueSample(
        GetFidoOptInNotOfferedHistogram(),
        /*sample=*/
        autofill_metrics::WebauthnOptInPromoNotOfferedReason::
            kCardAuthorizationTokenEmpty,
        /*expected_bucket_count=*/1);
  } else if (MaxStrikesLimitReached()) {
    histogram_tester.ExpectUniqueSample(
        GetFidoOptInNotOfferedHistogram(),
        /*sample=*/
        autofill_metrics::WebauthnOptInPromoNotOfferedReason::
            kBlockedByStrikeDatabase,
        /*expected_bucket_count=*/1);
  } else if (IsVirtualCard()) {
    histogram_tester.ExpectUniqueSample(
        GetFidoOptInNotOfferedHistogram(),
        /*sample=*/
        autofill_metrics::WebauthnOptInPromoNotOfferedReason::kVirtualCard,
        /*expected_bucket_count=*/1);
  } else {
    histogram_tester.ExpectTotalCount(GetFidoOptInNotOfferedHistogram(),
                                      /*expected_count=*/0);
  }
}
#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)

#if BUILDFLAG(IS_ANDROID)
// Ensures that the correct metrics are logged when the FIDO opt-in checkbox is
// not shown on Android.
TEST_P(CreditCardAccessManagerBetterAuthOptInLogTest,
       FidoOptInNotShown_Android) {
  base::HistogramTester histogram_tester;

  EXPECT_EQ(test_api(credit_card_access_manager()).ShouldOfferFidoAuth(),
            ShouldOfferFidoOptInAndroid());

  if (IsOptedIntoFido()) {
    histogram_tester.ExpectUniqueSample(
        GetFidoOptInNotOfferedHistogram(),
        /*sample=*/
        autofill_metrics::WebauthnOptInPromoNotOfferedReason::kAlreadyOptedIn,
        /*expected_bucket_count=*/1);
  } else if (!UnmaskDetailsOfferFidoOptIn()) {
    histogram_tester.ExpectUniqueSample(
        GetFidoOptInNotOfferedHistogram(),
        /*sample=*/
        autofill_metrics::WebauthnOptInPromoNotOfferedReason::
            kUnmaskDetailsOfferFidoOptInFalse,
        /*expected_bucket_count=*/1);
  } else if (HasOptedInFromAndroidSettings()) {
    histogram_tester.ExpectUniqueSample(
        GetFidoOptInNotOfferedHistogram(),
        /*sample=*/
        autofill_metrics::WebauthnOptInPromoNotOfferedReason::
            kOptedInFromSettings,
        /*expected_bucket_count=*/1);
  } else if (IsVirtualCard()) {
    histogram_tester.ExpectUniqueSample(
        GetFidoOptInNotOfferedHistogram(),
        /*sample=*/
        autofill_metrics::WebauthnOptInPromoNotOfferedReason::kVirtualCard,
        /*expected_bucket_count=*/1);
  } else {
    histogram_tester.ExpectTotalCount(GetFidoOptInNotOfferedHistogram(),
                                      /*expected_count=*/0);
  }
}
#endif  // BUILDFLAG(IS_ANDROID)
INSTANTIATE_TEST_SUITE_P(,
                         CreditCardAccessManagerBetterAuthOptInLogTest,
                         testing::Combine(testing::Bool(),
                                          testing::Bool(),
                                          testing::Bool(),
                                          testing::Bool(),
                                          testing::Bool(),
                                          testing::Bool()));

// Ensure that when unmask detail response is delayed, we will automatically
// fall back to CVC even if local pref and Payments mismatch.
TEST_F(CreditCardAccessManagerTest,
       IntentToOptOut_DelayedUnmaskDetailsResponse) {
  base::HistogramTester histogram_tester;
  // Setting up a FIDO-enabled user with a server card.
  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  // The user is FIDO-enabled from Payments.
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(true);
  payments_network_interface().AddFidoEligibleCard(
      card->server_id(), kCredentialId, kGooglePaymentsRpid);
  // Mock the user manually opt-out from Settings page, and Payments did not
  // update user status in time. The mismatch will set user INTENT_TO_OPT_OUT.
  SetCreditCardFIDOAuthEnabled(/*enabled=*/false);
  // Delay the UnmaskDetailsResponse so that we can't discover the mismatch,
  // which will use local pref and fall back to CVC.
  payments_network_interface().ShouldReturnUnmaskDetailsImmediately(false);

  credit_card_access_manager().PrepareToFetchCreditCard();
  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));

  // Ensure the auth flow type is CVC because no unmask detail response is
  // returned and local pref denotes that user is opted out.
  EXPECT_EQ(GetUnmaskAuthFlowType(), UnmaskAuthFlowType::kCvc);
  // Also ensure that since local pref is disabled, we will directly fall back
  // to CVC instead of falling back after time out. Ensure that
  // CardChosenBeforePreflightCallReturned is logged to opted-out histogram.
  histogram_tester.ExpectUniqueSample(
      "Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection.OptedOut",
      autofill_metrics::PreflightCallEvent::
          kCardChosenBeforePreflightCallReturned,
      1);
  // No bucket count for OptIn TimedOutCvcFallback.
  histogram_tester.ExpectTotalCount(
      "Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection.OptedIn."
      "TimedOutCvcFallback",
      0);

  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
  // Since no unmask detail returned, we can't discover the pref mismatch, we
  // won't call opt out and local pref is unchanged.
  EXPECT_FALSE(GetFIDOAuthenticator()->IsOptOutCalled());
  EXPECT_FALSE(IsCreditCardFIDOAuthEnabled());
}

TEST_F(CreditCardAccessManagerTest, IntentToOptOut_OptOutAfterUnmaskSucceeds) {
  // Setting up a FIDO-enabled user with a server card.
  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  // The user is FIDO-enabled from Payments.
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(true);
  payments_network_interface().AddFidoEligibleCard(
      card->server_id(), kCredentialId, kGooglePaymentsRpid);
  // Mock the user manually opt-out from Settings page, and Payments did not
  // update user status in time. The mismatch will set user INTENT_TO_OPT_OUT.
  SetCreditCardFIDOAuthEnabled(/*enabled=*/false);

  credit_card_access_manager().PrepareToFetchCreditCard();
  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));

  // Ensure that the local pref is still unchanged after unmask detail returns.
  EXPECT_FALSE(IsCreditCardFIDOAuthEnabled());
  // Also ensure the auth flow type is CVC because the local pref and payments
  // mismatch indicates that user intended to opt out.
  EXPECT_EQ(GetUnmaskAuthFlowType(), UnmaskAuthFlowType::kCvc);

  // Mock cvc auth success.
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
  WaitForCallbacks();

  // Ensure calling opt out after a successful cvc auth.
  EXPECT_TRUE(GetFIDOAuthenticator()->IsOptOutCalled());
  // Mock opt out success response. Local pref is consistent with payments.
  OptChange(PaymentsRpcResult::kSuccess,
            /*user_is_opted_in=*/false);
  EXPECT_FALSE(IsCreditCardFIDOAuthEnabled());
}

TEST_F(CreditCardAccessManagerTest, IntentToOptOut_OptOutAfterUnmaskFails) {
  // Setting up a FIDO-enabled user with a server card.
  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  // The user is FIDO-enabled from Payments.
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(true);
  payments_network_interface().AddFidoEligibleCard(
      card->server_id(), kCredentialId, kGooglePaymentsRpid);
  // Mock the user manually opt-out from Settings page, and Payments did not
  // update user status in time. The mismatch will set user INTENT_TO_OPT_OUT.
  SetCreditCardFIDOAuthEnabled(/*enabled=*/false);

  credit_card_access_manager().PrepareToFetchCreditCard();
  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));

  // Ensure that the local pref is still unchanged after unmask detail returns.
  EXPECT_FALSE(IsCreditCardFIDOAuthEnabled());
  // Ensure the auth flow type is CVC because the local pref and payments
  // mismatch indicates that user intended to opt out.
  EXPECT_EQ(GetUnmaskAuthFlowType(), UnmaskAuthFlowType::kCvc);

  // Mock cvc auth failure.
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kPermanentFailure,
                                   std::string()));
  WaitForCallbacks();

  // Ensure calling opt out after cvc auth failure.
  EXPECT_TRUE(GetFIDOAuthenticator()->IsOptOutCalled());
  // Mock opt out success. Local pref is consistent with payments.
  OptChange(PaymentsRpcResult::kSuccess,
            /*user_is_opted_in=*/false);
  EXPECT_FALSE(IsCreditCardFIDOAuthEnabled());
}

TEST_F(CreditCardAccessManagerTest, IntentToOptOut_OptOutFailure) {
  // Setting up a FIDO-enabled user with a server card.
  CreateServerCard(kTestGUID, kTestNumber);
  CreditCard* card =
      personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
  // The user is FIDO-enabled from Payments.
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  SetCreditCardFIDOAuthEnabled(true);
  payments_network_interface().AddFidoEligibleCard(
      card->server_id(), kCredentialId, kGooglePaymentsRpid);
  // Mock the user manually opt-out from Settings page, and Payments did not
  // update user status in time. The mismatch will set user INTENT_TO_OPT_OUT.
  SetCreditCardFIDOAuthEnabled(/*enabled=*/false);

  credit_card_access_manager().PrepareToFetchCreditCard();
  credit_card_access_manager().FetchCreditCard(
      card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                           accessor_->GetWeakPtr()));

  // Ensure that the local pref is still unchanged after unmask detail returns.
  EXPECT_FALSE(IsCreditCardFIDOAuthEnabled());
  // Also ensure the auth flow type is CVC because the local pref and payments
  // mismatch indicates that user intended to opt out.
  EXPECT_EQ(GetUnmaskAuthFlowType(), UnmaskAuthFlowType::kCvc);

  // Mock cvc auth success.
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
  WaitForCallbacks();

  // Mock payments opt out failure. Local pref should be unchanged.
  OptChange(PaymentsRpcResult::kPermanentFailure, false);
  EXPECT_FALSE(IsCreditCardFIDOAuthEnabled());
}

// TODO(crbug.com/40253866): Extend the FIDOAuthOptChange tests to more
// use-cases.
TEST_F(CreditCardAccessManagerTest, FIDOAuthOptChange_OptOut) {
  credit_card_access_manager().FIDOAuthOptChange(/*opt_in=*/false);
  ASSERT_TRUE(fido_authenticator().IsOptOutCalled());
}

TEST_F(CreditCardAccessManagerTest, FIDOAuthOptChange_OptOut_OffTheRecord) {
  autofill_client_.set_is_off_the_record(true);
  credit_card_access_manager().FIDOAuthOptChange(/*opt_in=*/false);
  ASSERT_FALSE(fido_authenticator().IsOptOutCalled());
}

// TODO(crbug.com/40707930) Debug issues and re-enable this test on MacOS.
#if !BUILDFLAG(IS_APPLE)
// Ensures that PrepareToFetchCreditCard() is properly rate limited.
TEST_F(CreditCardAccessManagerTest, PreflightCallRateLimited) {
  // Create server card and set user as eligible for FIDO auth.
  base::HistogramTester histogram_tester;
  std::string preflight_call_metric =
      "Autofill.BetterAuth.CardUnmaskPreflightCalledWithFidoOptInStatus";

  ClearCards();
  CreateServerCard(kTestGUID, kTestNumber);
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  ResetFetchCreditCard();

  // First call to PrepareToFetchCreditCard() should make RPC.
  credit_card_access_manager().PrepareToFetchCreditCard();
  histogram_tester.ExpectTotalCount(preflight_call_metric, 1);

  // Calling PrepareToFetchCreditCard() without a prior preflight call should
  // have set |can_fetch_unmask_details_| to false to prevent further ones.
  EXPECT_FALSE(
      test_api(credit_card_access_manager()).can_fetch_unmask_details());

  // Any subsequent calls should not make a RPC.
  credit_card_access_manager().PrepareToFetchCreditCard();
  histogram_tester.ExpectTotalCount(preflight_call_metric, 1);
}
#endif  // !BUILDFLAG(IS_APPLE)
#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)

// Ensures that UnmaskAuthFlowEvents also log to a ".ServerCard" subhistogram
// when a masked server card is selected.
TEST_F(CreditCardAccessManagerTest,
       UnmaskAuthFlowEvent_AlsoLogsServerCardSubhistogram) {}

// Ensures that |is_authentication_in_progress_| is set correctly.
TEST_F(CreditCardAccessManagerTest, AuthenticationInProgress) {}

// Ensures that the use of |unmasked_card_cache_| is set and logged correctly.
TEST_F(CreditCardAccessManagerTest, FetchCreditCardUsesUnmaskedCardCache) {}

TEST_F(CreditCardAccessManagerTest, GetCachedUnmaskedCards) {}

TEST_F(CreditCardAccessManagerTest, IsCardPresentInUnmaskedCache) {}

class CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest
    : public CreditCardAccessManagerTest {};

// Test the flow when the masked server card is successfully returned from
// the server during a risk-based retrieval.
TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
       RiskBasedMaskedServerCardUnmasking_Success) {}

// Ensures that the masked server card risk-based unmasking response is
// handled correctly if the retrieval failed.
TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
       RiskBasedMaskedServerCardUnmasking_RetrievalError) {}

// Ensures that the masked server card risk-based unmasking response is
// handled correctly if the flow is cancelled.
TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
       RiskBasedMaskedServerCardUnmasking_FlowCancelled) {}

// Ensures that the masked server card risk-based authentication is not invoked
// when the feature is disabled.
TEST_F(
    CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
    RiskBasedMaskedServerCardUnmasking_RiskBasedAuthenticationNotInvoked_FeatureDisabled) {}

TEST_F(
    CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
    RiskBasedMaskedServerCardUnmasking_CvcAuthenticationRequired_ContextTokenSetCorrectly) {}

// Ensures the authentication is delegated to the CVC authenticator when
// `fido_request_options` is not returned.
TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
       RiskBasedMaskedServerCardUnmasking_AuthenticationRequired_CvcOnly) {}

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
// Ensures the masked server card risk-based unmasking response is handled
// correctly and authentication is delegated to the FIDO authenticator, when
// `fido_request_options` is returned.
TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
       RiskBasedMaskedServerCardUnmasking_AuthenticationRequired_FidoOnly) {
  base::HistogramTester histogram_tester;

  std::string test_number = "4444333322221111";
  CreditCard* masked_server_card =
      CreateServerCard(kTestGUID, test_number, kTestServerId);
  test_api(credit_card_access_manager()).set_is_user_verifiable(true);
  fido_authenticator().set_is_user_opted_in(true);

  credit_card_access_manager().FetchCreditCard(
      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                                         accessor_->GetWeakPtr()));

  // Ensures CreditCardRiskBasedAuthenticator::Authenticate is successfully
  // invoked.
  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
                  ->risk_based_authentication_invoked());
  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
                  ->autofill_progress_dialog_shown());

  // Mock that CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse
  // indicates a yellow path when `fido_request_options` and `context_token` are
  // returned.
  credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
      CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
          .with_result(CreditCardRiskBasedAuthenticator::
                           RiskBasedAuthenticationResponse::Result::
                               kAuthenticationRequired)
          .with_fido_request_options(GetTestRequestOptions())
          .with_context_token("fake_context_token"));

  histogram_tester.ExpectUniqueSample(
      "Autofill.BetterAuth.FlowEvents.Fido",
      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);

  // Expect the CreditCardAccessManager invokes the FIDO authenticator.
  ASSERT_TRUE(fido_authenticator().authenticate_invoked());
  EXPECT_EQ(fido_authenticator().card().number(),
            base::UTF8ToUTF16(test_number));
  EXPECT_EQ(fido_authenticator().card().record_type(),
            CreditCard::RecordType::kMaskedServerCard);
  ASSERT_TRUE(fido_authenticator().context_token().has_value());
  EXPECT_EQ(fido_authenticator().context_token().value(), "fake_context_token");

  // Expect that we did not signal that there was no interactive authentication.
  EXPECT_FALSE(
      test_api(*autofill_client_.GetFormDataImporter())
          .payment_method_type_if_non_interactive_authentication_flow_completed()
          .has_value());

  histogram_tester.ExpectUniqueSample(
      "Autofill.BetterAuth.CardUnmaskTypeDecision",
      autofill_metrics::CardUnmaskTypeDecisionMetric::kFidoOnly, 1);
}

// Ensures that use of new card invokes authorization flow when user is
// opted-in to FIDO.
TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
       RiskBasedMaskedServerCardUnmasking_AuthenticationRequired_CvcThenFido) {
  base::HistogramTester histogram_tester;

  OptUserInToFido();
  std::string test_number = "4444333322221111";
  CreditCard* masked_server_card =
      CreateServerCard(kTestGUID, test_number, kTestServerId);

  payments_network_interface().ShouldReturnUnmaskDetailsImmediately(true);
  payments_network_interface().SetFidoRequestOptionsInUnmaskDetails(
      kCredentialId, kGooglePaymentsRpid);
  credit_card_access_manager().PrepareToFetchCreditCard();
  WaitForCallbacks();

  MockRiskBasedAuthSucceedsWithoutPanReturned(masked_server_card);

  histogram_tester.ExpectUniqueSample(
      "Autofill.BetterAuth.FlowEvents.CvcThenFido",
      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);

  // Expect CVC prompt to be invoked.
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, test_number,
                                   TestFidoRequestOptionsType::kNotPresent));
  // Ensure that the form is not filled yet (OnCreditCardFetched is not called).
  EXPECT_EQ(accessor_->number(), std::u16string());
  EXPECT_EQ(accessor_->cvc(), std::u16string());

  // Mock user response.
  EXPECT_EQ(CreditCardFidoAuthenticator::Flow::FOLLOWUP_AFTER_CVC_AUTH_FLOW,
            GetFIDOAuthenticator()->current_flow());
  TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
                                                /*did_succeed=*/true);
  // Ensure that the form is filled after user verification (OnCreditCardFetched
  // is called).
  EXPECT_EQ(base::UTF8ToUTF16(test_number), accessor_->number());
  EXPECT_EQ(kTestCvc16, accessor_->cvc());

  // Mock OptChange payments call.
  OptChange(PaymentsRpcResult::kSuccess, true);

  histogram_tester.ExpectUniqueSample(
      "Autofill.BetterAuth.CardUnmaskTypeDecision",
      autofill_metrics::CardUnmaskTypeDecisionMetric::kCvcThenFido, 1);
  histogram_tester.ExpectUniqueSample(
      "Autofill.BetterAuth.WebauthnResult.AuthenticationAfterCVC",
      autofill_metrics::WebauthnResultMetric::kSuccess, 1);
  histogram_tester.ExpectBucketCount(
      "Autofill.BetterAuth.FlowEvents.CvcThenFido",
      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptCompleted, 1);
}

// Ensures that the kCvc instead of kCvcThenFido flow is invoked if
// GetUnmaskDetails preflight call is not finished.
TEST_F(
    CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
    RiskBasedMaskedServerCardUnmasking_AuthenticationRequired_PreflightCallNotFinished) {
  base::HistogramTester histogram_tester;

  OptUserInToFido();
  std::string test_number = "4444333322221111";
  CreditCard* masked_server_card =
      CreateServerCard(kTestGUID, test_number, kTestServerId);

  payments_network_interface().ShouldReturnUnmaskDetailsImmediately(false);
  credit_card_access_manager().PrepareToFetchCreditCard();

  MockRiskBasedAuthSucceedsWithoutPanReturned(masked_server_card);

  histogram_tester.ExpectUniqueSample(
      "Autofill.BetterAuth.FlowEvents.Cvc",
      CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);

  // Expect CVC prompt to be invoked.
  EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, test_number));
  // Ensure that the form is filled.
  EXPECT_EQ(base::UTF8ToUTF16(test_number), accessor_->number());
  EXPECT_EQ(kTestCvc16, accessor_->cvc());

  // Expect that we did not signal that there was no interactive authentication.
  EXPECT_FALSE(
      test_api(*autofill_client_.GetFormDataImporter())
          .payment_method_type_if_non_interactive_authentication_flow_completed()
          .has_value());
}

#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)

// Ensures that CVC filling gets logged after masked server card risk-based
// unmasking success if the card has CVC.
TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
       LogCvcFilling_RiskBasedMaskedServerCardUnmaskingSuccess) {}

// Ensures that CVC filling doesn't get logged after after masked server card
// risk-based unmasking success if the card doesn't have CVC.
TEST_F(CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
       DoNotLogCvcFilling_RiskBasedMaskedServerCardUnmaskingSuccess) {}

// Ensures that the masked server card risk-based authentication is not invoked
// when the card is expired.
TEST_F(
    CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
    RiskBasedMaskedServerCardUnmasking_RiskBasedAuthenticationNotInvoked_CardExpired) {}

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
// Params of the
// CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingPreflightCallReturnedTest:
// -- bool fido_opted_in;
// -- bool preflight_call_returned;
class
    CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingPreflightCallReturnedTest
    : public CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingTest,
      public testing::WithParamInterface<std::tuple<bool, bool>> {
 public:
  CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingPreflightCallReturnedTest() =
      default;
  ~CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingPreflightCallReturnedTest()
      override = default;

  bool FidoOptedIn() { return std::get<0>(GetParam()); }
  bool PreflightCallReturned() { return std::get<1>(GetParam()); }
};

INSTANTIATE_TEST_SUITE_P(
    ,
    CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingPreflightCallReturnedTest,
    testing::Combine(testing::Bool(), testing::Bool()));

// Ensures that the metric for if the preflight call's response is received
// before card selection is logged correctly.
TEST_P(
    CreditCardAccessManagerRiskBasedMaskedServerCardUnmaskingPreflightCallReturnedTest,
    Metrics_LogPreflightCallResponseReceivedOnCardSelection) {
  base::HistogramTester histogram_tester;
  std::string test_number = "4444333322221111";
  CreditCard* masked_server_card = CreateServerCard(kTestGUID, test_number);
  GetFIDOAuthenticator()->SetUserVerifiable(true);
  if (FidoOptedIn()) {
    OptUserInToFido();
  }
  payments_network_interface().ShouldReturnUnmaskDetailsImmediately(
      PreflightCallReturned());

  std::string histogram_name =
      "Autofill.BetterAuth.PreflightCallResponseReceivedOnCardSelection.";
  histogram_name += FidoOptedIn() ? "OptedIn." : "OptedOut.";
  histogram_name += "ServerCard";

  credit_card_access_manager().PrepareToFetchCreditCard();
  credit_card_access_manager().FetchCreditCard(
      masked_server_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                                         accessor_->GetWeakPtr()));
  WaitForCallbacks();

  histogram_tester.ExpectUniqueSample(
      histogram_name,
      PreflightCallReturned() ? autofill_metrics::PreflightCallEvent::
                                    kPreflightCallReturnedBeforeCardChosen
                              : autofill_metrics::PreflightCallEvent::
                                    kCardChosenBeforePreflightCallReturned,
      1);
}

#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)

TEST_F(CreditCardAccessManagerTest, IsVirtualCardPresentInUnmaskedCache) {}

TEST_F(CreditCardAccessManagerTest,
       RiskBasedVirtualCardUnmasking_Success_VirtualCardsEnabled) {}

TEST_F(CreditCardAccessManagerTest,
       RiskBasedVirtualCardUnmasking_Success_VirtualCardsDisabled) {}

// Ensures the virtual card risk-based unmasking response is handled correctly
// and authentication is delegated to the OTP authenticator, when only the OTP
// challenge option is returned.
TEST_F(CreditCardAccessManagerTest,
       RiskBasedVirtualCardUnmasking_AuthenticationRequired_OtpOnly) {}

// Ensures the virtual card risk-based unmasking response is handled correctly
// and authentication is delegated to the CVC authenticator, when only the CVC
// challenge option is returned.
TEST_F(CreditCardAccessManagerTest,
       RiskBasedVirtualCardUnmasking_AuthenticationRequired_CvcOnly) {}

// Ensures the virtual card risk-based unmasking flow type is set to
// kThreeDomainSecure when only the 3DS challenge option is returned.
TEST_F(CreditCardAccessManagerTest,
       RiskBasedVirtualCardUnmasking_Only3dsChallengeReturned) {}

// Ensures the virtual card risk-based unmasking response is handled correctly
// and authentication is delegated to the correct authenticator when multiple
// challenge options are returned.
TEST_F(CreditCardAccessManagerTest,
       RiskBasedVirtualCardUnmasking_AuthenticationRequired_OtpAndCvcAnd3ds) {}

#if !BUILDFLAG(IS_IOS)
TEST_F(
    CreditCardAccessManagerTest,
    RiskBasedVirtualCardUnmasking_CreditCardAccessManagerReset_TriggersOtpAuthenticatorResetOnFlowCancelled) {}

// Test that a success response for a VCN 3DS authentication is handled
// correctly and notifies the caller with the proper fields set.
TEST_F(CreditCardAccessManagerTest,
       VirtualCardUnmasking_3dsResponseReceived_Success) {}

// Test that a failure response for a VCN 3DS authentication is handled
// correctly and notifies the caller with the proper fields set.
TEST_F(CreditCardAccessManagerTest,
       VirtualCardUnmasking_3dsResponseReceived_Error) {}

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
// Ensures that the virtual card risk-based unmasking response is handled
// correctly and authentication is delegated to the FIDO authenticator, when
// only the FIDO challenge options is returned.
TEST_F(CreditCardAccessManagerTest,
       RiskBasedVirtualCardUnmasking_AuthenticationRequired_FidoOnly) {
  base::HistogramTester histogram_tester;
  CreditCard* masked_server_card =
      CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
  CreditCard virtual_card = *masked_server_card;
  virtual_card.set_record_type(CreditCard::RecordType::kVirtualCard);
  // TODO(crbug.com/40197696): Switch to SetUserVerifiable after moving all
  // is_user_veriable_ related logic from CreditCardAccessManager to
  // CreditCardFidoAuthenticator.
  test_api(credit_card_access_manager()).set_is_user_verifiable(true);
  fido_authenticator().set_is_user_opted_in(true);

  credit_card_access_manager().FetchCreditCard(
      &virtual_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                                    accessor_->GetWeakPtr()));

  // This checks risk-based authentication flow is successfully invoked,
  // because it is always the very first authentication flow in a VCN
  // unmasking flow.
  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
                  ->risk_based_authentication_invoked());
  // Mock server response with information regarding FIDO auth.
  payments::PaymentsNetworkInterface::UnmaskResponseDetails response;
  response.context_token = "fake_context_token";
  response.fido_request_options = GetTestRequestOptions();
  credit_card_access_manager()
      .OnVirtualCardRiskBasedAuthenticationResponseReceived(
          PaymentsRpcResult::kSuccess, response);

  // Expect the CreditCardAccessManager invokes the FIDO authenticator.
  ASSERT_TRUE(fido_authenticator().authenticate_invoked());
  EXPECT_EQ(fido_authenticator().card().number(),
            base::UTF8ToUTF16(std::string(kTestNumber)));
  EXPECT_EQ(fido_authenticator().card().record_type(),
            CreditCard::RecordType::kVirtualCard);
  ASSERT_TRUE(fido_authenticator().context_token().has_value());
  EXPECT_EQ(fido_authenticator().context_token().value(), "fake_context_token");

  // Mock FIDO authentication completed.
  CreditCardFidoAuthenticator::FidoAuthenticationResponse fido_response;
  fido_response.did_succeed = true;
  CreditCard card = test::WithCvc(test::GetVirtualCard(), u"234");
  fido_response.card = &card;
  test_api(credit_card_access_manager())
      .OnFIDOAuthenticationComplete(fido_response);

  // Expect accessor to successfully retrieve the virtual card CVC.
  EXPECT_EQ(u"234", accessor_->cvc());

  // Expect the metrics are logged correctly.
  histogram_tester.ExpectUniqueSample(
      "Autofill.ServerCardUnmask.VirtualCard.Attempt", true, 1);
  histogram_tester.ExpectUniqueSample(
      "Autofill.ServerCardUnmask.VirtualCard.Result.Fido",
      autofill_metrics::ServerCardUnmaskResult::kAuthenticationUnmasked, 1);
}

// Ensures that the virtual card risk-based unmasking response is handled
// correctly and authentication is delegated to the FIDO authenticator, when
// both FIDO and OTP challenge options are returned.
TEST_F(
    CreditCardAccessManagerTest,
    RiskBasedVirtualCardUnmasking_AuthenticationRequired_FidoAndOtp_PrefersFido) {
  base::HistogramTester histogram_tester;
  CreditCard* masked_server_card =
      CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
  CreditCard virtual_card = *masked_server_card;
  virtual_card.set_record_type(CreditCard::RecordType::kVirtualCard);
  // TODO(crbug.com/40197696): Switch to SetUserVerifiable after moving all
  // is_user_veriable_ related logic from CreditCardAccessManager to
  // CreditCardFidoAuthenticator.
  test_api(credit_card_access_manager()).set_is_user_verifiable(true);
  fido_authenticator().set_is_user_opted_in(true);

  credit_card_access_manager().FetchCreditCard(
      &virtual_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                                    accessor_->GetWeakPtr()));

  // This checks risk-based authentication flow is successfully invoked,
  // because it is always the very first authentication flow in a VCN
  // unmasking flow.
  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
                  ->risk_based_authentication_invoked());
  // Mock server response with information regarding both FIDO and OTP auth.
  payments::PaymentsNetworkInterface::UnmaskResponseDetails response;
  response.context_token = "fake_context_token";
  CardUnmaskChallengeOption challenge_option =
      test::GetCardUnmaskChallengeOptions(
          {CardUnmaskChallengeOptionType::kSmsOtp})[0];
  response.card_unmask_challenge_options.emplace_back(challenge_option);
  response.fido_request_options = GetTestRequestOptions();
  credit_card_access_manager()
      .OnVirtualCardRiskBasedAuthenticationResponseReceived(
          PaymentsRpcResult::kSuccess, response);

  // Expect the CreditCardAccessManager invokes the FIDO authenticator.
  ASSERT_TRUE(fido_authenticator().authenticate_invoked());
  EXPECT_EQ(fido_authenticator().card().number(),
            base::UTF8ToUTF16(std::string(kTestNumber)));
  EXPECT_EQ(fido_authenticator().card().record_type(),
            CreditCard::RecordType::kVirtualCard);
  ASSERT_TRUE(fido_authenticator().context_token().has_value());
  EXPECT_EQ(fido_authenticator().context_token().value(), "fake_context_token");

  // Mock FIDO authentication completed.
  CreditCardFidoAuthenticator::FidoAuthenticationResponse fido_response;
  fido_response.did_succeed = true;
  CreditCard card = test::GetVirtualCard();
  fido_response.card = &card;
  fido_response.cvc = u"123";
  test_api(credit_card_access_manager())
      .OnFIDOAuthenticationComplete(fido_response);

  // Expect the metrics are logged correctly.
  histogram_tester.ExpectUniqueSample(
      "Autofill.ServerCardUnmask.VirtualCard.Attempt", true, 1);
  histogram_tester.ExpectUniqueSample(
      "Autofill.ServerCardUnmask.VirtualCard.Result.Fido",
      autofill_metrics::ServerCardUnmaskResult::kAuthenticationUnmasked, 1);
}

// Ensures that the virtual card risk-based unmasking response is handled
// correctly and authentication is delegated to the OTP authenticator, when both
// FIDO and OTP challenge options are returned but FIDO local preference is not
// opted in.
TEST_F(
    CreditCardAccessManagerTest,
    RiskBasedVirtualCardUnmasking_AuthenticationRequired_FidoAndOtp_FidoNotOptedIn) {
  base::HistogramTester histogram_tester;
  std::vector<CardUnmaskChallengeOption> challenge_options =
      test::GetCardUnmaskChallengeOptions(
          {CardUnmaskChallengeOptionType::kSmsOtp});
  MockCardUnmaskFlowUpToAuthenticationSelectionDialogAccepted(
      /*fido_authenticator_is_user_opted_in=*/false,
      /*is_user_verifiable=*/true, challenge_options, /*selected_index=*/0);

  CreditCardOtpAuthenticator::OtpAuthenticationResponse otp_response;
  otp_response.result =
      CreditCardOtpAuthenticator::OtpAuthenticationResponse::Result::kSuccess;
  CreditCard card = test::GetCreditCard();
  otp_response.card = &card;
  otp_response.cvc = u"123";
  credit_card_access_manager().OnOtpAuthenticationComplete(otp_response);

  // Expect the metrics are logged correctly.
  histogram_tester.ExpectUniqueSample(
      "Autofill.ServerCardUnmask.VirtualCard.Attempt", true, 1);
  histogram_tester.ExpectUniqueSample(
      "Autofill.ServerCardUnmask.VirtualCard.Result.Otp",
      autofill_metrics::ServerCardUnmaskResult::kAuthenticationUnmasked, 1);
}

// Ensures that the virtual card risk-based unmasking response is handled
// correctly and authentication is delegated first to the FIDO authenticator,
// when both FIDO and OTP challenge options are returned, but fall back to OTP
// authentication if FIDO failed.
TEST_F(
    CreditCardAccessManagerTest,
    RiskBasedVirtualCardUnmasking_AuthenticationRequired_FidoAndOtp_FidoFailedFallBackToOtp) {
  base::HistogramTester histogram_tester;
  std::vector<CardUnmaskChallengeOption> challenge_options =
      test::GetCardUnmaskChallengeOptions(
          {CardUnmaskChallengeOptionType::kSmsOtp});
  MockCardUnmaskFlowUpToAuthenticationSelectionDialogAccepted(
      /*fido_authenticator_is_user_opted_in=*/true,
      /*is_user_verifiable=*/true, challenge_options, /*selected_index=*/0);

  CreditCardOtpAuthenticator::OtpAuthenticationResponse otp_response;
  otp_response.result =
      CreditCardOtpAuthenticator::OtpAuthenticationResponse::Result::kSuccess;
  CreditCard card = test::GetCreditCard();
  otp_response.card = &card;
  otp_response.cvc = u"123";
  credit_card_access_manager().OnOtpAuthenticationComplete(otp_response);

  // Expect the metrics are logged correctly.
  histogram_tester.ExpectUniqueSample(
      "Autofill.ServerCardUnmask.VirtualCard.Attempt", true, 1);
  histogram_tester.ExpectUniqueSample(
      "Autofill.ServerCardUnmask.VirtualCard.Result.OtpFallbackFromFido",
      autofill_metrics::ServerCardUnmaskResult::kAuthenticationUnmasked, 1);
}

// Ensures that the virtual card risk-based unmasking response is handled
// correctly if there is only FIDO option returned by the server but the user
// is not opted in.
TEST_F(
    CreditCardAccessManagerTest,
    RiskBasedVirtualCardUnmasking_AuthenticationRequired_FidoOnly_FidoNotOptedIn) {
  base::HistogramTester histogram_tester;
  CreditCard* masked_server_card =
      CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
  CreditCard virtual_card = *masked_server_card;
  virtual_card.set_record_type(CreditCard::RecordType::kVirtualCard);
  // TODO(crbug.com/40197696): Switch to SetUserVerifiable after moving all
  // is_user_veriable_ related logic from CreditCardAccessManager to
  // CreditCardFidoAuthenticator.
  test_api(credit_card_access_manager()).set_is_user_verifiable(true);
  fido_authenticator().set_is_user_opted_in(false);

  credit_card_access_manager().FetchCreditCard(
      &virtual_card, base::BindOnce(&TestAccessor::OnCreditCardFetched,
                                    accessor_->GetWeakPtr()));

  // This checks risk-based authentication flow is successfully invoked,
  // because it is always the very first authentication flow in a VCN
  // unmasking flow.
  EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
                  ->risk_based_authentication_invoked());
  // Mock server response with information regarding FIDO auth.
  payments::PaymentsNetworkInterface::UnmaskResponseDetails response;
  response.context_token = "fake_context_token";
  response.fido_request_options = GetTestRequestOptions();
  credit_card_access_manager()
      .OnVirtualCardRiskBasedAuthenticationResponseReceived(
          PaymentsRpcResult::kSuccess, response);

  // Expect the CreditCardAccessManager to end the session.
  EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kTransientError);
  EXPECT_FALSE(otp_authenticator_->on_challenge_option_selected_invoked());
  EXPECT_FALSE(fido_authenticator().authenticate_invoked());

  // Expect the metrics are logged correctly.
  histogram_tester.ExpectUniqueSample(
      "Autofill.ServerCardUnmask.VirtualCard.Attempt", true, 1);
  histogram_tester.ExpectUniqueSample(
      "Autofill.ServerCardUnmask.VirtualCard.Result.Fido",
      autofill_metrics::ServerCardUnmaskResult::kOnlyFidoAvailableButNotOptedIn,
      1);
}

#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
#endif  // !BUILDFLAG(IS_IOS)

// Ensures that the virtual card risk-based unmasking response is handled
// correctly if there is no challenge option returned by the server.
TEST_F(CreditCardAccessManagerTest,
       RiskBasedVirtualCardUnmasking_Failure_NoOptionReturned) {}

// Ensures that the virtual card risk-based unmasking response is handled
// correctly if there is virtual card retrieval error.
TEST_F(CreditCardAccessManagerTest,
       RiskBasedVirtualCardUnmasking_Failure_VirtualCardRetrievalError) {}

TEST_F(CreditCardAccessManagerTest,
       RiskBasedVirtualCardUnmasking_Failure_MerchantOptedOut) {}

TEST_F(CreditCardAccessManagerTest,
       RiskBasedVirtualCardUnmasking_FlowCancelled) {}

// Test that the CreditCardAccessManager's destructor resets the record type of
// the card that had no interactive authentication flows completed in the
// associated FormDataImporter.
TEST_F(CreditCardAccessManagerTest, DestructorResetsCardIdentifier) {}

}  // namespace autofill