[SPC] Add SecurePaymentConfirmationAppFactory tests for handling icons

This CL adds a new test class,
SecurePaymentConfirmationAppFactoryNetworkAndIssuerIconsTest,
to secure_payment_confirmation_app_factory_unittest.cc, which
triggers creation of a SecureConfirmationPaymentApp and verifies
that the networkInfo and issuerInfo fields are correctly handled.

A future CL will add testing for the new paymentEntitiesLogos field.

Bug: 417683819
Change-Id: I33353ff94b196362fefa208c3571b3fc693f5191
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6575767
Commit-Queue: Stephen McGruer <smcgruer@chromium.org>
Reviewed-by: Slobodan Pejic <slobodan@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1467090}
diff --git a/components/payments/content/mock_payment_manifest_web_data_service.h b/components/payments/content/mock_payment_manifest_web_data_service.h
index 608d7c9..c3283eb8 100644
--- a/components/payments/content/mock_payment_manifest_web_data_service.h
+++ b/components/payments/content/mock_payment_manifest_web_data_service.h
@@ -18,6 +18,13 @@
 class MockPaymentManifestWebDataService : public PaymentManifestWebDataService {
  public:
   MockPaymentManifestWebDataService();
+
+  MOCK_METHOD(WebDataServiceBase::Handle,
+              GetSecurePaymentConfirmationCredentials,
+              (std::vector<std::vector<uint8_t>> credential_ids,
+               const std::string& relying_party_id,
+               WebDataServiceConsumer* consumer),
+              (override));
   MOCK_METHOD(void,
               ClearSecurePaymentConfirmationCredentials,
               (base::Time begin, base::Time end, base::OnceClosure callback),
diff --git a/components/payments/content/payment_manifest_web_data_service.h b/components/payments/content/payment_manifest_web_data_service.h
index 5ac52ee5..e731480 100644
--- a/components/payments/content/payment_manifest_web_data_service.h
+++ b/components/payments/content/payment_manifest_web_data_service.h
@@ -71,7 +71,7 @@
   // `credential_ids` and returns it to the `consumer`, which must outlive the
   // DB operation, because DB tasks cannot be cancelled. Please use
   // `std::move()` for `credential_ids` parameter to avoid extra copies.
-  WebDataServiceBase::Handle GetSecurePaymentConfirmationCredentials(
+  virtual WebDataServiceBase::Handle GetSecurePaymentConfirmationCredentials(
       std::vector<std::vector<uint8_t>> credential_ids,
       const std::string& relying_party_id,
       WebDataServiceConsumer* consumer);
diff --git a/components/payments/content/secure_payment_confirmation_app_factory.cc b/components/payments/content/secure_payment_confirmation_app_factory.cc
index 12bfcb7dd..587905b 100644
--- a/components/payments/content/secure_payment_confirmation_app_factory.cc
+++ b/components/payments/content/secure_payment_confirmation_app_factory.cc
@@ -413,13 +413,6 @@
   delegate->OnDoneCreatingPaymentApps();
 }
 
-#if BUILDFLAG(IS_ANDROID)
-void SecurePaymentConfirmationAppFactory::SetBrowserBoundKeyStoreForTesting(
-    scoped_refptr<BrowserBoundKeyStore> key_store) {
-  browser_bound_key_store_for_testing_ = std::move(key_store);
-}
-#endif  // BUILDFLAG(IS_ANDROID)
-
 void SecurePaymentConfirmationAppFactory::OnWebDataServiceRequestDone(
     WebDataServiceBase::Handle handle,
     std::unique_ptr<WDTypedResult> result) {
@@ -446,6 +439,13 @@
   }
 }
 
+#if BUILDFLAG(IS_ANDROID)
+void SecurePaymentConfirmationAppFactory::SetBrowserBoundKeyStoreForTesting(
+    scoped_refptr<BrowserBoundKeyStore> key_store) {
+  browser_bound_key_store_for_testing_ = std::move(key_store);
+}
+#endif  // BUILDFLAG(IS_ANDROID)
+
 void SecurePaymentConfirmationAppFactory::OnGetMatchingCredentialIdsFromStore(
     std::unique_ptr<Request> request,
     std::string relying_party_id,
diff --git a/components/payments/content/secure_payment_confirmation_app_factory.h b/components/payments/content/secure_payment_confirmation_app_factory.h
index ec18bd4..337bd5d 100644
--- a/components/payments/content/secure_payment_confirmation_app_factory.h
+++ b/components/payments/content/secure_payment_confirmation_app_factory.h
@@ -34,6 +34,11 @@
   // PaymentAppFactory:
   void Create(base::WeakPtr<Delegate> delegate) override;
 
+  // WebDataServiceConsumer:
+  void OnWebDataServiceRequestDone(
+      WebDataServiceBase::Handle handle,
+      std::unique_ptr<WDTypedResult> result) override;
+
 #if BUILDFLAG(IS_ANDROID)
   void SetBrowserBoundKeyStoreForTesting(
       scoped_refptr<BrowserBoundKeyStore> key_store);
@@ -42,11 +47,6 @@
  private:
   struct Request;
 
-  // WebDataServiceConsumer:
-  void OnWebDataServiceRequestDone(
-      WebDataServiceBase::Handle handle,
-      std::unique_ptr<WDTypedResult> result) override;
-
   void OnIsUserVerifyingPlatformAuthenticatorAvailable(
       std::unique_ptr<Request> request,
       bool is_available);
diff --git a/components/payments/content/secure_payment_confirmation_app_factory_unittest.cc b/components/payments/content/secure_payment_confirmation_app_factory_unittest.cc
index 3bb0a6b..fb49fb2 100644
--- a/components/payments/content/secure_payment_confirmation_app_factory_unittest.cc
+++ b/components/payments/content/secure_payment_confirmation_app_factory_unittest.cc
@@ -18,7 +18,9 @@
 #include "components/payments/content/mock_payment_manifest_web_data_service.h"
 #include "components/payments/core/features.h"
 #include "components/payments/core/native_error_strings.h"
+#include "components/payments/core/secure_payment_confirmation_credential.h"
 #include "components/webauthn/core/browser/mock_internal_authenticator.h"
+#include "components/webdata/common/web_data_results.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_web_contents_factory.h"
@@ -46,11 +48,14 @@
 using ::testing::Return;
 using ::testing::ReturnRef;
 
-static constexpr char kChallengeBase64[] = "aaaa";
-static constexpr char kCredentialIdBase64[] = "cccc";
+constexpr std::string kRpId = "rp.example";
+constexpr char kChallengeBase64[] = "aaaa";
+constexpr char kCredentialIdBase64[] = "cccc";
 
 class SecurePaymentConfirmationAppFactoryTest : public testing::Test {
  protected:
+  const GURL kInstrumentIconUrl = GURL("https://site.example/icon.png");
+
   SecurePaymentConfirmationAppFactoryTest()
       : os_crypt_(os_crypt_async::GetTestOSCryptAsyncForTesting(
             /*is_sync_for_unittests=*/true)),
@@ -78,13 +83,14 @@
         std::vector<uint8_t>(challenge_bytes_.begin(), challenge_bytes_.end());
     spc_request->instrument = blink::mojom::PaymentCredentialInstrument::New();
     spc_request->instrument->display_name = "1234";
-    spc_request->instrument->icon = GURL("https://site.example/icon.png");
+    spc_request->instrument->icon = kInstrumentIconUrl;
     spc_request->payee_origin =
         url::Origin::Create(GURL("https://merchant.example"));
-    spc_request->rp_id = "rp.example";
+    spc_request->rp_id = kRpId;
 
     return spc_request;
   }
+
   content::BrowserTaskEnvironment task_environment_;
   std::unique_ptr<os_crypt_async::OSCryptAsync> os_crypt_;
   content::TestBrowserContext context_;
@@ -521,6 +527,244 @@
   secure_payment_confirmation_app_factory_->Create(mock_delegate->GetWeakPtr());
 }
 
+// Class wrapping tests relating to the network and issuer icons support in
+// SecurePaymentConfirmationAppFactory.
+class SecurePaymentConfirmationAppFactoryNetworkAndIssuerIconsTest
+    : public SecurePaymentConfirmationAppFactoryTest {
+ protected:
+  const GURL kIssuerIconUrl = GURL("https://issuer.example/icon.png");
+  const GURL kNetworkIconUrl = GURL("https://network.example/icon.png");
+  const WebDataServiceBase::Handle kWebDataServiceHandle = 1234;
+
+  SecurePaymentConfirmationAppFactoryNetworkAndIssuerIconsTest() {
+    // For test setup simplicity, force tests in this fixture to use the (mocked
+    // out) database storage path.
+    feature_list_.InitAndDisableFeature(
+        features::kSecurePaymentConfirmationUseCredentialStoreAPIs);
+  }
+
+  void SetUp() override {
+    SecurePaymentConfirmationAppFactoryTest::SetUp();
+
+    mock_authenticator_ =
+        std::make_unique<webauthn::MockInternalAuthenticator>(web_contents_);
+    EXPECT_CALL(*mock_authenticator_,
+                IsUserVerifyingPlatformAuthenticatorAvailable(_))
+        .WillOnce(RunOnceCallback<0>(true));
+
+    mock_service_ = base::MakeRefCounted<MockPaymentManifestWebDataService>();
+    EXPECT_CALL(*mock_service_,
+                GetSecurePaymentConfirmationCredentials(_, _, _))
+        .WillOnce(Return(kWebDataServiceHandle));
+  }
+
+  std::unique_ptr<MockPaymentAppFactoryDelegate> CreateMockDelegate(
+      mojom::PaymentMethodDataPtr method_data) {
+    auto mock_delegate = std::make_unique<MockPaymentAppFactoryDelegate>(
+        web_contents_, std::move(method_data));
+    EXPECT_CALL(*mock_delegate, CreateInternalAuthenticator())
+        .WillOnce(Return(ByMove(std::move(mock_authenticator_))));
+    EXPECT_CALL(*mock_delegate, GetPaymentManifestWebDataService())
+        .WillRepeatedly(Return(mock_service_));
+
+    return mock_delegate;
+  }
+
+  void FakeCredentialFetchedFromDatabase(std::string credential_id_bytes) {
+    std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>>
+        credentials;
+    std::vector<uint8_t> credential_id(credential_id_bytes.begin(),
+                                       credential_id_bytes.end());
+    credentials.emplace_back(
+        std::make_unique<SecurePaymentConfirmationCredential>(
+            std::move(credential_id), kRpId,
+            /*user_id=*/std::vector<uint8_t>()));
+
+    std::unique_ptr<WDTypedResult> result = std::make_unique<WDResult<
+        std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>>>>(
+        SECURE_PAYMENT_CONFIRMATION, std::move(credentials));
+
+    secure_payment_confirmation_app_factory_->OnWebDataServiceRequestDone(
+        kWebDataServiceHandle, std::move(result));
+  }
+
+  void FakeImageDownloaded(GURL image_url, bool succeeded = true) {
+    std::vector<gfx::Size> icon_sizes({{32, 32}});
+    std::vector<SkBitmap> icon_bitmaps;
+    if (succeeded) {
+      icon_bitmaps.emplace_back();
+      icon_bitmaps[0].allocN32Pixels(/*width=*/32, /*height=*/32);
+    }
+
+    ASSERT_TRUE(static_cast<content::TestWebContents*>(web_contents_.get())
+                    ->TestDidDownloadImage(image_url, /*https_status_code=*/200,
+                                           std::move(icon_bitmaps),
+                                           std::move(icon_sizes)));
+  }
+
+  std::unique_ptr<webauthn::MockInternalAuthenticator> mock_authenticator_;
+  scoped_refptr<MockPaymentManifestWebDataService> mock_service_;
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Tests that when neither the network nor issuer icons are specified, they are
+// not present on the final PaymentApp.
+TEST_F(SecurePaymentConfirmationAppFactoryNetworkAndIssuerIconsTest,
+       NoIconsSpecified) {
+  auto method_data = mojom::PaymentMethodData::New();
+  method_data->supported_method = "secure-payment-confirmation";
+  method_data->secure_payment_confirmation =
+      CreateSecurePaymentConfirmationRequest();
+
+  std::unique_ptr<MockPaymentAppFactoryDelegate> mock_delegate =
+      CreateMockDelegate(std::move(method_data));
+
+  std::unique_ptr<PaymentApp> created_payment_app;
+  EXPECT_CALL(*mock_delegate, OnPaymentAppCreated(_))
+      .WillOnce(MoveArg<0>(&created_payment_app));
+
+  secure_payment_confirmation_app_factory_->Create(mock_delegate->GetWeakPtr());
+
+  FakeCredentialFetchedFromDatabase(credential_id_bytes_);
+  FakeImageDownloaded(kInstrumentIconUrl);
+
+  ASSERT_TRUE(created_payment_app);
+  EXPECT_FALSE(created_payment_app->issuer_bitmap());
+  EXPECT_FALSE(created_payment_app->network_bitmap());
+}
+
+TEST_F(SecurePaymentConfirmationAppFactoryNetworkAndIssuerIconsTest,
+       NetworkIcon) {
+  auto method_data = mojom::PaymentMethodData::New();
+  method_data->supported_method = "secure-payment-confirmation";
+  mojom::SecurePaymentConfirmationRequestPtr spc_request =
+      CreateSecurePaymentConfirmationRequest();
+  spc_request->network_info = mojom::NetworkOrIssuerInformation::New();
+  spc_request->network_info->name = "Network Name";
+  spc_request->network_info->icon = kNetworkIconUrl;
+  method_data->secure_payment_confirmation = std::move(spc_request);
+
+  std::unique_ptr<MockPaymentAppFactoryDelegate> mock_delegate =
+      CreateMockDelegate(std::move(method_data));
+
+  std::unique_ptr<PaymentApp> created_payment_app;
+  EXPECT_CALL(*mock_delegate, OnPaymentAppCreated(_))
+      .WillOnce(MoveArg<0>(&created_payment_app));
+
+  secure_payment_confirmation_app_factory_->Create(mock_delegate->GetWeakPtr());
+
+  FakeCredentialFetchedFromDatabase(credential_id_bytes_);
+  FakeImageDownloaded(kInstrumentIconUrl);
+  FakeImageDownloaded(kNetworkIconUrl);
+
+  // This payment app should have been created with a network icon but not an
+  // issuer one.
+  ASSERT_TRUE(created_payment_app);
+  EXPECT_TRUE(created_payment_app->network_bitmap());
+  EXPECT_FALSE(created_payment_app->issuer_bitmap());
+}
+
+TEST_F(SecurePaymentConfirmationAppFactoryNetworkAndIssuerIconsTest,
+       IssuerIcon) {
+  auto method_data = mojom::PaymentMethodData::New();
+  method_data->supported_method = "secure-payment-confirmation";
+  mojom::SecurePaymentConfirmationRequestPtr spc_request =
+      CreateSecurePaymentConfirmationRequest();
+  spc_request->issuer_info = mojom::NetworkOrIssuerInformation::New();
+  spc_request->issuer_info->name = "Issuer Name";
+  spc_request->issuer_info->icon = kIssuerIconUrl;
+  method_data->secure_payment_confirmation = std::move(spc_request);
+
+  std::unique_ptr<MockPaymentAppFactoryDelegate> mock_delegate =
+      CreateMockDelegate(std::move(method_data));
+
+  std::unique_ptr<PaymentApp> created_payment_app;
+  EXPECT_CALL(*mock_delegate, OnPaymentAppCreated(_))
+      .WillOnce(MoveArg<0>(&created_payment_app));
+
+  secure_payment_confirmation_app_factory_->Create(mock_delegate->GetWeakPtr());
+
+  FakeCredentialFetchedFromDatabase(credential_id_bytes_);
+  FakeImageDownloaded(kInstrumentIconUrl);
+  FakeImageDownloaded(kIssuerIconUrl);
+
+  // This payment app should have been created with an issuer icon but not a
+  // network one.
+  ASSERT_TRUE(created_payment_app);
+  EXPECT_FALSE(created_payment_app->network_bitmap());
+  EXPECT_TRUE(created_payment_app->issuer_bitmap());
+}
+
+TEST_F(SecurePaymentConfirmationAppFactoryNetworkAndIssuerIconsTest,
+       NetworkAndIssuerIcon) {
+  auto method_data = mojom::PaymentMethodData::New();
+  method_data->supported_method = "secure-payment-confirmation";
+  mojom::SecurePaymentConfirmationRequestPtr spc_request =
+      CreateSecurePaymentConfirmationRequest();
+  spc_request->network_info = mojom::NetworkOrIssuerInformation::New();
+  spc_request->network_info->name = "Network Name";
+  spc_request->network_info->icon = kNetworkIconUrl;
+  spc_request->issuer_info = mojom::NetworkOrIssuerInformation::New();
+  spc_request->issuer_info->name = "Issuer Name";
+  spc_request->issuer_info->icon = kIssuerIconUrl;
+  method_data->secure_payment_confirmation = std::move(spc_request);
+
+  std::unique_ptr<MockPaymentAppFactoryDelegate> mock_delegate =
+      CreateMockDelegate(std::move(method_data));
+
+  std::unique_ptr<PaymentApp> created_payment_app;
+  EXPECT_CALL(*mock_delegate, OnPaymentAppCreated(_))
+      .WillOnce(MoveArg<0>(&created_payment_app));
+
+  secure_payment_confirmation_app_factory_->Create(mock_delegate->GetWeakPtr());
+
+  FakeCredentialFetchedFromDatabase(credential_id_bytes_);
+  FakeImageDownloaded(kInstrumentIconUrl);
+  FakeImageDownloaded(kNetworkIconUrl);
+  FakeImageDownloaded(kIssuerIconUrl);
+
+  // This payment app should have been created with both a network and issuer
+  // icon.
+  ASSERT_TRUE(created_payment_app);
+  EXPECT_TRUE(created_payment_app->network_bitmap());
+  EXPECT_TRUE(created_payment_app->issuer_bitmap());
+}
+
+TEST_F(SecurePaymentConfirmationAppFactoryNetworkAndIssuerIconsTest,
+       NetworkAndIssuerIcon_DownloadFails) {
+  auto method_data = mojom::PaymentMethodData::New();
+  method_data->supported_method = "secure-payment-confirmation";
+  mojom::SecurePaymentConfirmationRequestPtr spc_request =
+      CreateSecurePaymentConfirmationRequest();
+  spc_request->network_info = mojom::NetworkOrIssuerInformation::New();
+  spc_request->network_info->name = "Network Name";
+  spc_request->network_info->icon = kNetworkIconUrl;
+  spc_request->issuer_info = mojom::NetworkOrIssuerInformation::New();
+  spc_request->issuer_info->name = "Issuer Name";
+  spc_request->issuer_info->icon = kIssuerIconUrl;
+  method_data->secure_payment_confirmation = std::move(spc_request);
+
+  std::unique_ptr<MockPaymentAppFactoryDelegate> mock_delegate =
+      CreateMockDelegate(std::move(method_data));
+
+  std::unique_ptr<PaymentApp> created_payment_app;
+  EXPECT_CALL(*mock_delegate, OnPaymentAppCreated(_))
+      .WillOnce(MoveArg<0>(&created_payment_app));
+
+  secure_payment_confirmation_app_factory_->Create(mock_delegate->GetWeakPtr());
+
+  FakeCredentialFetchedFromDatabase(credential_id_bytes_);
+  FakeImageDownloaded(kInstrumentIconUrl);
+  FakeImageDownloaded(kNetworkIconUrl, /*succeeded=*/false);
+  FakeImageDownloaded(kIssuerIconUrl);
+
+  // The resultant payment app should have been created with only an issuer
+  // icon.
+  ASSERT_TRUE(created_payment_app);
+  EXPECT_FALSE(created_payment_app->network_bitmap());
+  EXPECT_TRUE(created_payment_app->issuer_bitmap());
+}
+
 // TODO(crbug.com/417683819): Add tests verifying that paymentEntitiesLogos is
 // correctly converted into icons to be downloaded.