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

// Copyright 2023 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/iban_access_manager.h"

#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/form_data_importer_test_api.h"
#include "components/autofill/core/browser/payments/mock_test_payments_network_interface.h"
#include "components/autofill/core/browser/payments/payments_autofill_client.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_personal_data_manager.h"
#include "components/autofill/core/browser/ui/suggestion.h"
#include "components/autofill/core/browser/ui/suggestion_type.h"
#include "components/autofill/core/common/autofill_prefs.h"
#include "components/sync/test/test_sync_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

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

namespace autofill {

namespace {

constexpr char16_t kFullIbanValue[] =;
constexpr int64_t kInstrumentId =;
constexpr int kDaysSinceLastUsed =;
constexpr int kDefaultUnmaskIbanLatencyMs =;
constexpr size_t kDefaultUseCount =;

}  // namespace

class IbanAccessManagerTest : public testing::Test {};

// Verify that `FetchValue` returns the correct value for an existing local
// IBAN.
TEST_F(IbanAccessManagerTest, FetchValue_ExistingLocalIban) {}

// Verify that `FetchValue` does not trigger callback if local IBAN does not
// exist.
TEST_F(IbanAccessManagerTest, FetchValue_NonExistingLocalIban) {}

// Verify that an UnmaskIban call won't be triggered if no server IBAN with the
// same `instrument_id` as BackendId is found.
TEST_F(IbanAccessManagerTest, NoServerIbanWithBackendId_DoesNotUnmask) {}

// Verify that a successful `UnmaskIban` call results in the `FetchValue`
// returning the complete server IBAN value.
TEST_F(IbanAccessManagerTest, ServerIban_BackendId_Success) {}

// Verify that a failed `UnmaskIban` call results in the method `OnIbanFetched`
// not being called.
TEST_F(IbanAccessManagerTest, ServerIban_BackendId_Failure) {}

// Verify that there will be no progress dialog when unmasking a local IBAN.
TEST_F(IbanAccessManagerTest, FetchValue_LocalIbanNoProgressDialog) {}

// Verify that there will be a progress dialog when unmasking a server IBAN.
TEST_F(IbanAccessManagerTest, FetchValue_ServerIban_ProgressDialog_Success) {}

// Verify that there will be a progress dialog when unmasking a server IBAN,
// followed by an error dialog if it fails to be unmasked.
TEST_F(IbanAccessManagerTest, FetchValue_ServerIban_ProgressDialog_Failure) {}

// Verify that local IBAN metadata has been recorded correctly.
TEST_F(IbanAccessManagerTest, LocalIban_LogUsageMetric) {}

// Verify that server IBAN metadata has been recorded correctly.
TEST_F(IbanAccessManagerTest, ServerIban_LogUsageMetric) {}

// Verify that the duration of successful `UnmaskIban` call is logged correctly.
TEST_F(IbanAccessManagerTest, UnmaskServerIban_Success_Metric) {}

// Verify that duration of failed `UnmaskIban` call is logged correctly.
TEST_F(IbanAccessManagerTest, UnmaskServerIban_Failure_Metric) {}

// Verify that UnmaskIbanResult records true for a successful call.
TEST_F(IbanAccessManagerTest, UnmaskIbanResult_Metric_Success) {}

// Verify that UnmaskIbanResult records false for a failed call.
TEST_F(IbanAccessManagerTest, UnmaskIbanResult_Metric_Failure) {}

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

class IbanAccessManagerMandatoryReauthTest : public IbanAccessManagerTest {
 public:
  IbanAccessManagerMandatoryReauthTest() = default;
  ~IbanAccessManagerMandatoryReauthTest() override = default;

 protected:
  void SetUp() override {
    IbanAccessManagerTest::SetUp();
    autofill_client_.GetPrefs()->SetBoolean(
        prefs::kAutofillPaymentMethodsMandatoryReauth, true);
  }

  void SetUpDeviceAuthenticatorResponseMock(bool success) {
    ON_CALL(mandatory_reauth_manager(), StartDeviceAuthentication)
        .WillByDefault(testing::WithArg<1>(
            testing::Invoke([success](base::OnceCallback<void(bool)> callback) {
              std::move(callback).Run(success);
            })));
  }

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

// Tests that retrieving local IBANs works correctly in the context of the
// Mandatory Re-Auth feature.
TEST_F(IbanAccessManagerMandatoryReauthTest, FetchValue_Local_Reauth_Success) {
  base::HistogramTester histogram_tester;
  SetUpDeviceAuthenticatorResponseMock(/*success=*/true);

  Suggestion suggestion(SuggestionType::kIbanEntry);
  Iban local_iban = test::GetLocalIban();
  local_iban.set_value(kFullIbanValue);
  personal_data().test_payments_data_manager().AddIbanForTest(
      std::make_unique<Iban>(local_iban));
  suggestion.payload =
      Suggestion::BackendId(Suggestion::Guid(local_iban.guid()));

  base::MockCallback<IbanAccessManager::OnIbanFetchedCallback> callback;
  EXPECT_CALL(callback, Run(std::u16string(kFullIbanValue)));
  iban_access_manager_->FetchValue(
      suggestion.GetPayload<Suggestion::BackendId>(), callback.Get());
}

// Tests that retrieving local IBANs does not return the full IBAN value if
// Mandatory Re-Auth fails.
TEST_F(IbanAccessManagerMandatoryReauthTest, FetchValue_Local_Reauth_Fail) {
  SetUpDeviceAuthenticatorResponseMock(/*success=*/false);

  Suggestion suggestion(SuggestionType::kIbanEntry);
  Iban local_iban = test::GetLocalIban();
  local_iban.set_value(kFullIbanValue);
  personal_data().test_payments_data_manager().AddIbanForTest(
      std::make_unique<Iban>(local_iban));
  suggestion.payload =
      Suggestion::BackendId(Suggestion::Guid(local_iban.guid()));

  base::MockCallback<IbanAccessManager::OnIbanFetchedCallback> callback;
  EXPECT_CALL(callback, Run(std::u16string(kFullIbanValue))).Times(0);
  iban_access_manager_->FetchValue(
      suggestion.GetPayload<Suggestion::BackendId>(), callback.Get());
}

// Tests that retrieving server IBANs works correctly in the context of the
// Mandatory Re-Auth feature.
TEST_F(IbanAccessManagerMandatoryReauthTest, FetchValue_Server_Reauth_Success) {
  SetUpDeviceAuthenticatorResponseMock(/*success=*/true);
  SetUpUnmaskIbanCall(/*is_successful=*/true, /*value=*/kFullIbanValue);

  Iban server_iban = test::GetServerIban();
  server_iban.set_identifier(Iban::InstrumentId(kInstrumentId));
  personal_data().test_payments_data_manager().AddServerIban(server_iban);
  Suggestion suggestion(SuggestionType::kIbanEntry);
  suggestion.payload = Suggestion::InstrumentId(kInstrumentId);

  base::MockCallback<IbanAccessManager::OnIbanFetchedCallback> callback;
  EXPECT_CALL(callback, Run(std::u16string(kFullIbanValue)));
  iban_access_manager_->FetchValue(
      suggestion.GetPayload<Suggestion::BackendId>(), callback.Get());
}

// Tests that retrieving server IBANs does not return the full IBAN value if
// Mandatory Re-Auth fails.
TEST_F(IbanAccessManagerMandatoryReauthTest, FetchValue_Server_Reauth_Fail) {
  SetUpDeviceAuthenticatorResponseMock(/*success=*/false);
  SetUpUnmaskIbanCall(/*is_successful=*/true, /*value=*/kFullIbanValue);

  Iban server_iban = test::GetServerIban();
  server_iban.set_identifier(Iban::InstrumentId(kInstrumentId));
  personal_data().test_payments_data_manager().AddServerIban(server_iban);
  Suggestion suggestion(SuggestionType::kIbanEntry);
  suggestion.payload = Suggestion::InstrumentId(kInstrumentId);

  base::MockCallback<IbanAccessManager::OnIbanFetchedCallback> callback;
  EXPECT_CALL(callback, Run(std::u16string(kFullIbanValue))).Times(0);
  iban_access_manager_->FetchValue(
      suggestion.GetPayload<Suggestion::BackendId>(), callback.Get());
}

// Tests that `NonInteractivePaymentMethodType` is set to `kLocalIban` on
// local IBAN retrieval flow.
TEST_F(IbanAccessManagerMandatoryReauthTest,
       NonInteractivePaymentMethodType_Local) {
  autofill_client_.GetPrefs()->SetBoolean(
      prefs::kAutofillPaymentMethodsMandatoryReauth, false);
  SetUpDeviceAuthenticatorResponseMock(/*success=*/true);

  Iban local_iban = test::GetLocalIban();
  personal_data().test_payments_data_manager().AddIbanForTest(
      std::make_unique<Iban>(local_iban));
  Suggestion suggestion(SuggestionType::kIbanEntry);
  suggestion.payload =
      Suggestion::BackendId(Suggestion::Guid(local_iban.guid()));

  iban_access_manager_->FetchValue(
      suggestion.GetPayload<Suggestion::BackendId>(), base::DoNothing());

  EXPECT_EQ(
      test_api(*autofill_client_.GetFormDataImporter())
          .payment_method_type_if_non_interactive_authentication_flow_completed(),
      NonInteractivePaymentMethodType::kLocalIban);
}

// Tests that `NonInteractivePaymentMethodType` is set to `kServerIban` on
// server IBAN retrieval flow.
TEST_F(IbanAccessManagerMandatoryReauthTest,
       NonInteractivePaymentMethodType_Server) {
  autofill_client_.GetPrefs()->SetBoolean(
      prefs::kAutofillPaymentMethodsMandatoryReauth, false);
  SetUpDeviceAuthenticatorResponseMock(/*success=*/true);
  SetUpUnmaskIbanCall(/*is_successful=*/true, /*value=*/kFullIbanValue);

  Iban server_iban = test::GetServerIban();
  personal_data().test_payments_data_manager().AddServerIban(server_iban);
  Suggestion suggestion(SuggestionType::kIbanEntry);
  suggestion.payload = Suggestion::InstrumentId(server_iban.instrument_id());

  iban_access_manager_->FetchValue(
      suggestion.GetPayload<Suggestion::BackendId>(), base::DoNothing());

  EXPECT_EQ(
      test_api(*autofill_client_.GetFormDataImporter())
          .payment_method_type_if_non_interactive_authentication_flow_completed(),
      NonInteractivePaymentMethodType::kServerIban);
}

// Tests that ReauthUsage is logged as `LocalIban` and `kFlowSucceeded` when
// mandatory re-auth succeeds when fetching a local IBAN.
TEST_F(IbanAccessManagerMandatoryReauthTest, ReauthUsage_LocalIban_Succcess) {
  base::HistogramTester histogram_tester;
  SetUpDeviceAuthenticatorResponseMock(
      /*success=*/true);

  Suggestion suggestion(SuggestionType::kIbanEntry);
  Iban local_iban = test::GetLocalIban();
  local_iban.set_value(kFullIbanValue);
  personal_data().test_payments_data_manager().AddIbanForTest(
      std::make_unique<Iban>(local_iban));
  suggestion.payload =
      Suggestion::BackendId(Suggestion::Guid(local_iban.guid()));

  ON_CALL(mandatory_reauth_manager(), StartDeviceAuthentication)
      .WillByDefault([this](NonInteractivePaymentMethodType
                                non_interactive_payment_method_type,
                            base::OnceCallback<void(bool)> callback) {
        base::MockCallback<IbanAccessManager::OnIbanFetchedCallback>
            on_iban_fetched_callback;
        iban_access_manager_
            ->OnDeviceAuthenticationResponseForFillingForTesting(
                on_iban_fetched_callback.Get(), std::u16string(kFullIbanValue),
                NonInteractivePaymentMethodType::kLocalIban, /*success=*/true);
      });
  iban_access_manager_->FetchValue(
      suggestion.GetPayload<Suggestion::BackendId>(), base::DoNothing());

  histogram_tester.ExpectBucketCount(
      "Autofill.PaymentMethods.CheckoutFlow.ReauthUsage.LocalIban.Biometric",
      autofill_metrics::MandatoryReauthAuthenticationFlowEvent::kFlowSucceeded,
      1);
}

// Tests that ReauthUsage is logged as `LocalIban` and `kFlowFailed` when
// mandatory re-auth fails when fetching a local IBAN.
TEST_F(IbanAccessManagerMandatoryReauthTest, ReauthUsage_LocalIban_Fail) {
  base::HistogramTester histogram_tester;
  SetUpDeviceAuthenticatorResponseMock(/*success=*/false);

  Suggestion suggestion(SuggestionType::kIbanEntry);
  Iban local_iban = test::GetLocalIban();
  local_iban.set_value(kFullIbanValue);
  personal_data().test_payments_data_manager().AddIbanForTest(
      std::make_unique<Iban>(local_iban));
  suggestion.payload =
      Suggestion::BackendId(Suggestion::Guid(local_iban.guid()));

  ON_CALL(mandatory_reauth_manager(), StartDeviceAuthentication)
      .WillByDefault([this](NonInteractivePaymentMethodType
                                non_interactive_payment_method_type,
                            base::OnceCallback<void(bool)> callback) {
        base::MockCallback<IbanAccessManager::OnIbanFetchedCallback>
            on_iban_fetched_callback;
        iban_access_manager_
            ->OnDeviceAuthenticationResponseForFillingForTesting(
                on_iban_fetched_callback.Get(), std::u16string(kFullIbanValue),
                NonInteractivePaymentMethodType::kLocalIban, /*success=*/false);
      });
  iban_access_manager_->FetchValue(
      suggestion.GetPayload<Suggestion::BackendId>(), base::DoNothing());

  histogram_tester.ExpectBucketCount(
      "Autofill.PaymentMethods.CheckoutFlow.ReauthUsage.LocalIban.Biometric",
      autofill_metrics::MandatoryReauthAuthenticationFlowEvent::kFlowFailed, 1);
}

// Tests that ReauthUsage is logged as `kServerIban` and `kFlowSucceeded` when
// mandatory re-auth succeeds when fetching a server IBAN.
TEST_F(IbanAccessManagerMandatoryReauthTest, ReauthUsage_ServerIban_Succcess) {
  base::HistogramTester histogram_tester;
  SetUpDeviceAuthenticatorResponseMock(/*success=*/true);
  SetUpUnmaskIbanCall(/*is_successful=*/true, /*value=*/kFullIbanValue);

  Iban server_iban = test::GetServerIban();
  server_iban.set_identifier(Iban::InstrumentId(kInstrumentId));
  personal_data().test_payments_data_manager().AddServerIban(server_iban);
  Suggestion suggestion(SuggestionType::kIbanEntry);
  suggestion.payload = Suggestion::InstrumentId(kInstrumentId);

  ON_CALL(mandatory_reauth_manager(), StartDeviceAuthentication)
      .WillByDefault([this](NonInteractivePaymentMethodType
                                non_interactive_payment_method_type,
                            base::OnceCallback<void(bool)> callback) {
        base::MockCallback<IbanAccessManager::OnIbanFetchedCallback>
            on_iban_fetched_callback;
        iban_access_manager_
            ->OnDeviceAuthenticationResponseForFillingForTesting(
                on_iban_fetched_callback.Get(), std::u16string(kFullIbanValue),
                NonInteractivePaymentMethodType::kServerIban, /*success=*/true);
      });
  iban_access_manager_->FetchValue(
      suggestion.GetPayload<Suggestion::BackendId>(), base::DoNothing());

  histogram_tester.ExpectBucketCount(
      "Autofill.PaymentMethods.CheckoutFlow.ReauthUsage.ServerIban.Biometric",
      autofill_metrics::MandatoryReauthAuthenticationFlowEvent::kFlowSucceeded,
      1);
}

// Tests that ReauthUsage is logged as `ServerIban` and `kFlowFailed` when
// mandatory re-auth fails when fetching a server IBAN.
TEST_F(IbanAccessManagerMandatoryReauthTest, ReauthUsage_ServerIban_Fail) {
  base::HistogramTester histogram_tester;
  SetUpDeviceAuthenticatorResponseMock(/*success=*/false);
  SetUpUnmaskIbanCall(/*is_successful=*/true, /*value=*/kFullIbanValue);

  Iban server_iban = test::GetServerIban();
  server_iban.set_identifier(Iban::InstrumentId(kInstrumentId));
  personal_data().test_payments_data_manager().AddServerIban(server_iban);
  Suggestion suggestion(SuggestionType::kIbanEntry);
  suggestion.payload = Suggestion::InstrumentId(kInstrumentId);

  ON_CALL(mandatory_reauth_manager(), StartDeviceAuthentication)
      .WillByDefault([this](NonInteractivePaymentMethodType
                                non_interactive_payment_method_type,
                            base::OnceCallback<void(bool)> callback) {
        base::MockCallback<IbanAccessManager::OnIbanFetchedCallback>
            on_iban_fetched_callback;
        iban_access_manager_
            ->OnDeviceAuthenticationResponseForFillingForTesting(
                on_iban_fetched_callback.Get(), std::u16string(kFullIbanValue),
                NonInteractivePaymentMethodType::kServerIban,
                /*success=*/false);
      });
  iban_access_manager_->FetchValue(
      suggestion.GetPayload<Suggestion::BackendId>(), base::DoNothing());

  histogram_tester.ExpectBucketCount(
      "Autofill.PaymentMethods.CheckoutFlow.ReauthUsage.ServerIban.Biometric",
      autofill_metrics::MandatoryReauthAuthenticationFlowEvent::kFlowFailed, 1);
}

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

}  // namespace autofill