// 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 "content/browser/webid/config_fetcher.h"

#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "content/browser/webid/test/mock_idp_network_request_manager.h"
#include "content/public/common/content_features.h"
#include "content/test/test_render_frame_host.h"
#include "content/test/test_web_contents.h"
#include "net/http/http_status_code.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/origin.h"

namespace content::webid {

using ::testing::_;
using ::testing::StrictMock;
using ::testing::WithArg;

class ConfigFetcherTest : public RenderViewHostImplTestHarness {
 protected:
  ConfigFetcherTest() = default;
  ~ConfigFetcherTest() override = default;

  void SetUp() override { RenderViewHostImplTestHarness::SetUp(); }

  base::test::ScopedFeatureList feature_list_;
};

TEST_F(ConfigFetcherTest, FailedToFetchWellKnown) {
  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  EXPECT_CALL(*network_manager, FetchConfig)
      .WillOnce(WithArg<3>(
          [](IdpNetworkRequestManager::FetchConfigCallback callback) {
            IdpNetworkRequestManager::Endpoints endpoints;
            endpoints.token = GURL("https://idp.example/token.php");
            endpoints.accounts = GURL("https://idp.example/accounts.php");

            IdentityProviderMetadata metadata;
            metadata.idp_login_url =
                GURL("https://idp.example/idp_login_url.php");
            std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK},
                                    endpoints, metadata);
          }));

  // Returns a 404 for the fetch of the well-known file.
  EXPECT_CALL(*network_manager, FetchWellKnown)
      .WillOnce(WithArg<1>(
          [](IdpNetworkRequestManager::FetchWellKnownCallback callback) {
            IdpNetworkRequestManager::WellKnown well_known;
            std::move(callback).Run(
                {ParseStatus::kHttpNotFoundError, net::HTTP_NOT_FOUND},
                well_known);
          }));

  base::RunLoop loop;

  // Asserts that we get a kWellKnownHttpNotFound.
  fetcher.Start(
      {{GURL("https://idp.example/fedcm.json"),
        /*force_skip_well_known_enforcement=*/false}},
      /*icon_ideal_size=*/0,
      /*icon_minimum_size=*/0,
      base::BindLambdaForTesting([&loop](std::vector<ConfigFetcher::FetchResult>
                                             result) {
        EXPECT_EQ(result.size(), 1ul);
        EXPECT_TRUE(result[0].error);
        EXPECT_EQ(result[0].error->result,
                  blink::mojom::FederatedRequestResult::kWellKnownHttpNotFound);
        loop.Quit();
      }));

  loop.Run();
}

TEST_F(ConfigFetcherTest, FailedToFetchWellKnownButNoEnforcement) {
  feature_list_.InitAndEnableFeature(
      features::kFedCmWithoutWellKnownEnforcement);

  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  EXPECT_CALL(*network_manager, FetchConfig)
      .WillOnce(WithArg<3>(
          [](IdpNetworkRequestManager::FetchConfigCallback callback) {
            IdpNetworkRequestManager::Endpoints endpoints;
            endpoints.token = GURL("https://idp.example/token.php");
            endpoints.accounts = GURL("https://idp.example/accounts.php");

            IdentityProviderMetadata metadata;
            metadata.idp_login_url =
                GURL("https://idp.example/idp_login_url.php");
            std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK},
                                    endpoints, metadata);
          }));

  // Returns a 404 for the fetch of the well-known file.
  EXPECT_CALL(*network_manager, FetchWellKnown)
      .WillOnce(WithArg<1>(
          [](IdpNetworkRequestManager::FetchWellKnownCallback callback) {
            IdpNetworkRequestManager::WellKnown well_known;
            std::move(callback).Run(
                {ParseStatus::kHttpNotFoundError, net::HTTP_NOT_FOUND},
                well_known);
          }));

  base::RunLoop loop;

  // Asserts that we get no error in the result.
  fetcher.Start({{GURL("https://idp.example/fedcm.json"),
                  /*force_skip_well_known_enforcement=*/false}},
                /*icon_ideal_size=*/0,
                /*icon_minimum_size=*/0,
                base::BindLambdaForTesting(
                    [&loop](std::vector<ConfigFetcher::FetchResult> result) {
                      EXPECT_EQ(result.size(), 1ul);
                      EXPECT_FALSE(result[0].error);
                      EXPECT_TRUE(result[0].wellknown.provider_urls.empty());
                      EXPECT_EQ(result[0].endpoints.token,
                                GURL("https://idp.example/token.php"));
                      loop.Quit();
                    }));

  loop.Run();
}

TEST_F(ConfigFetcherTest, FailedToFetchConfig) {
  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  // Returns a 404 for the fetch of the config file.
  EXPECT_CALL(*network_manager, FetchConfig)
      .WillOnce(WithArg<3>(
          [](IdpNetworkRequestManager::FetchConfigCallback callback) {
            std::move(callback).Run(
                {ParseStatus::kHttpNotFoundError, net::HTTP_NOT_FOUND},
                /*endpoints=*/{}, /*metadata=*/{});
          }));

  EXPECT_CALL(*network_manager, FetchWellKnown)
      .WillOnce(WithArg<1>(
          [](IdpNetworkRequestManager::FetchWellKnownCallback callback) {
            IdpNetworkRequestManager::WellKnown well_known;
            well_known.provider_urls = {GURL("https://idp.example/fedcm.json")};
            std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK},
                                    well_known);
          }));

  base::RunLoop loop;

  // Asserts that we get a kConfigHttpNotFound.
  fetcher.Start(
      {{GURL("https://idp.example/fedcm.json"),
        /*force_skip_well_known_enforcement=*/false}},
      /*icon_ideal_size=*/0,
      /*icon_minimum_size=*/0,
      base::BindLambdaForTesting(
          [&loop](std::vector<ConfigFetcher::FetchResult> result) {
            EXPECT_EQ(result.size(), 1ul);
            EXPECT_TRUE(result[0].error);
            EXPECT_EQ(
                result[0].error->result,
                blink::mojom::FederatedRequestResult::kConfigHttpNotFound);
            loop.Quit();
          }));

  loop.Run();
}

TEST_F(ConfigFetcherTest, SucceedsToFetchConfigButInvalidResponse) {
  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  // Returns a 200 but with an empty and invalid response.
  EXPECT_CALL(*network_manager, FetchConfig)
      .WillOnce(WithArg<3>(
          [](IdpNetworkRequestManager::FetchConfigCallback callback) {
            std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK},
                                    /*endpoints=*/{}, /*metadata=*/{});
          }));

  EXPECT_CALL(*network_manager, FetchWellKnown)
      .WillOnce(WithArg<1>(
          [](IdpNetworkRequestManager::FetchWellKnownCallback callback) {
            IdpNetworkRequestManager::WellKnown well_known;
            well_known.provider_urls = {GURL("https://idp.example/fedcm.json")};
            std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK},
                                    well_known);
          }));

  base::RunLoop loop;

  // Asserts that we get a kConfigHttpNotFound.
  fetcher.Start(
      {{GURL("https://idp.example/fedcm.json"),
        /*force_skip_well_known_enforcement=*/false}},
      /*icon_ideal_size=*/0,
      /*icon_minimum_size=*/0,
      base::BindLambdaForTesting([&loop](std::vector<ConfigFetcher::FetchResult>
                                             result) {
        EXPECT_EQ(result.size(), 1ul);
        EXPECT_TRUE(result[0].error);
        EXPECT_EQ(result[0].error->result,
                  blink::mojom::FederatedRequestResult::kConfigInvalidResponse);
        loop.Quit();
      }));

  loop.Run();
}

TEST_F(ConfigFetcherTest, SuccessfullAndValidResponse) {
  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  // Returns a 200 but with an empty and invalid response.
  EXPECT_CALL(*network_manager, FetchConfig)
      .WillOnce(WithArg<3>(
          [](IdpNetworkRequestManager::FetchConfigCallback callback) {
            IdpNetworkRequestManager::Endpoints endpoints;
            endpoints.token = GURL("https://idp.example/token.php");
            endpoints.accounts = GURL("https://idp.example/accounts.php");

            IdentityProviderMetadata metadata;
            metadata.idp_login_url =
                GURL("https://idp.example/idp_login_url.php");
            std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK},
                                    endpoints, metadata);
          }));

  EXPECT_CALL(*network_manager, FetchWellKnown)
      .WillOnce(WithArg<1>(
          [](IdpNetworkRequestManager::FetchWellKnownCallback callback) {
            IdpNetworkRequestManager::WellKnown well_known;
            well_known.provider_urls = {GURL("https://idp.example/fedcm.json")};
            std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK},
                                    well_known);
          }));

  base::RunLoop loop;

  // Asserts that we get a kConfigHttpNotFound.
  fetcher.Start({{GURL("https://idp.example/fedcm.json"),
                  /*force_skip_well_known_enforcement=*/false}},
                /*icon_ideal_size=*/0,
                /*icon_minimum_size=*/0,
                base::BindLambdaForTesting(
                    [&loop](std::vector<ConfigFetcher::FetchResult> result) {
                      EXPECT_EQ(result.size(), 1ul);
                      EXPECT_FALSE(result[0].error);
                      loop.Quit();
                    }));

  loop.Run();
}

TEST_F(ConfigFetcherTest,
       TooManyProvidersInWellKnownLeadsToErrorWellKnownTooBig) {
  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  // Returns a 200 but with an empty and invalid response.
  EXPECT_CALL(*network_manager, FetchConfig)
      .WillOnce(WithArg<3>(
          [](IdpNetworkRequestManager::FetchConfigCallback callback) {
            IdpNetworkRequestManager::Endpoints endpoints;
            endpoints.token = GURL("https://idp.example/token.php");
            endpoints.accounts = GURL("https://idp.example/accounts.php");

            IdentityProviderMetadata metadata;
            metadata.idp_login_url =
                GURL("https://idp.example/idp_login_url.php");
            std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK},
                                    endpoints, metadata);
          }));

  EXPECT_CALL(*network_manager, FetchWellKnown)
      .WillOnce(WithArg<1>(
          [](IdpNetworkRequestManager::FetchWellKnownCallback callback) {
            IdpNetworkRequestManager::WellKnown well_known;
            well_known.provider_urls = {
                GURL("https://idp.example/fedcm.json"),
                GURL("https://idp.example/one-too-many.json")};
            std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK},
                                    well_known);
          }));

  base::RunLoop loop;

  // Asserts that we get a kConfigHttpNotFound.
  fetcher.Start(
      {{GURL("https://idp.example/fedcm.json"),
        /*force_skip_well_known_enforcement=*/false}},
      /*icon_ideal_size=*/0,
      /*icon_minimum_size=*/0,
      base::BindLambdaForTesting(
          [&loop](std::vector<ConfigFetcher::FetchResult> result) {
            EXPECT_EQ(result.size(), 1ul);
            EXPECT_TRUE(result[0].error);
            EXPECT_EQ(result[0].error->result,
                      blink::mojom::FederatedRequestResult::kWellKnownTooBig);
            loop.Quit();
          }));

  loop.Run();
}

TEST_F(ConfigFetcherTest, ProvidersUrlsIgnoredWhenAccountEndpointsMatch) {
  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  // Returns a 200 but with an empty and invalid response.
  EXPECT_CALL(*network_manager, FetchConfig)
      .WillOnce(WithArg<3>(
          [](IdpNetworkRequestManager::FetchConfigCallback callback) {
            IdpNetworkRequestManager::Endpoints endpoints;
            endpoints.token = GURL("https://idp.example/token.php");
            endpoints.accounts = GURL("https://idp.example/accounts.php");

            IdentityProviderMetadata metadata;
            metadata.idp_login_url =
                GURL("https://idp.example/idp_login_url.php");
            std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK},
                                    endpoints, metadata);
          }));

  EXPECT_CALL(*network_manager, FetchWellKnown)
      .WillOnce(WithArg<1>(
          [](IdpNetworkRequestManager::FetchWellKnownCallback callback) {
            IdpNetworkRequestManager::WellKnown well_known;
            well_known.provider_urls = {GURL("https://idp.example/fedcm.json")};
            well_known.accounts = GURL("https://idp.example/accounts.php");
            well_known.login_url =
                GURL("https://idp.example/idp_login_url.php");
            std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK},
                                    well_known);
          }));

  base::RunLoop loop;

  // Asserts that we get no error in the result.
  fetcher.Start({{GURL("https://idp.example/fedcm.json"),
                  /*force_skip_well_known_enforcement=*/false}},
                /*icon_ideal_size=*/0,
                /*icon_minimum_size=*/0,
                base::BindLambdaForTesting(
                    [&loop](std::vector<ConfigFetcher::FetchResult> result) {
                      EXPECT_EQ(result.size(), 1ul);
                      EXPECT_FALSE(result[0].error);
                      loop.Quit();
                    }));

  loop.Run();
}

TEST_F(ConfigFetcherTest, ProvidersUrlsCanbeEmptyWhenAccountEndpointsMatch) {
  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  // Returns a 200 but with an empty and invalid response.
  EXPECT_CALL(*network_manager, FetchConfig)
      .WillOnce(WithArg<3>(
          [](IdpNetworkRequestManager::FetchConfigCallback callback) {
            IdpNetworkRequestManager::Endpoints endpoints;
            endpoints.token = GURL("https://idp.example/token.php");
            endpoints.accounts = GURL("https://idp.example/accounts.php");

            IdentityProviderMetadata metadata;
            metadata.idp_login_url =
                GURL("https://idp.example/idp_login_url.php");
            std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK},
                                    endpoints, metadata);
          }));

  EXPECT_CALL(*network_manager, FetchWellKnown)
      .WillOnce(WithArg<1>(
          [](IdpNetworkRequestManager::FetchWellKnownCallback callback) {
            IdpNetworkRequestManager::WellKnown well_known;
            well_known.accounts = GURL("https://idp.example/accounts.php");
            well_known.login_url =
                GURL("https://idp.example/idp_login_url.php");
            std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK},
                                    well_known);
          }));

  base::RunLoop loop;

  // Asserts that we get no error in the result.
  fetcher.Start({{GURL("https://idp.example/fedcm.json"),
                  /*force_skip_well_known_enforcement=*/false}},
                /*icon_ideal_size=*/0,
                /*icon_minimum_size=*/0,
                base::BindLambdaForTesting(
                    [&loop](std::vector<ConfigFetcher::FetchResult> result) {
                      EXPECT_EQ(result.size(), 1ul);
                      EXPECT_FALSE(result[0].error);
                      EXPECT_TRUE(result[0].wellknown.provider_urls.empty());
                      loop.Quit();
                    }));

  loop.Run();
}

TEST_F(ConfigFetcherTest, ValidFetchResult) {
  // The most basic valid fetch result is one where:
  // (a) both the well-known and the config files were loaded successfully
  // (b) there is an accounts and token endpoint in the config file
  // (c) the well-known file contains the configURL
  IdentityProviderMetadata metadata;
  metadata.idp_login_url = GURL("https://idp.example/sign-in");
  ConfigFetcher::FetchResult result;
  result.endpoints.accounts = GURL("https://idp.example/accounts");
  result.endpoints.token = GURL("https://idp.example/token");
  result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")};
  result.identity_provider_config_url = GURL("https://idp.example/fedcm.json");
  result.metadata = std::move(metadata);

  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  fetcher.ValidateAndMaybeSetError(result);
  EXPECT_FALSE(result.error);
}

TEST_F(ConfigFetcherTest, InvalidMissingAcccountsEndpoint) {
  ConfigFetcher::FetchResult result;
  result.endpoints.token = GURL("https://idp.example/token");
  result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")};
  result.identity_provider_config_url = GURL("https://idp.example/fedcm.json");

  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  fetcher.ValidateAndMaybeSetError(result);
  EXPECT_TRUE(result.error);
  EXPECT_EQ(result.error->result,
            blink::mojom::FederatedRequestResult::kConfigInvalidResponse);
}

TEST_F(ConfigFetcherTest, InvalidCrossOriginAcccountsEndpoint) {
  ConfigFetcher::FetchResult result;
  result.endpoints.accounts = GURL("https://cross-origin.example/accounts");
  result.endpoints.token = GURL("https://idp.example/token");
  result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")};
  result.identity_provider_config_url = GURL("https://idp.example/fedcm.json");

  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  fetcher.ValidateAndMaybeSetError(result);
  EXPECT_TRUE(result.error);
  EXPECT_EQ(result.error->result,
            blink::mojom::FederatedRequestResult::kConfigInvalidResponse);
}

TEST_F(ConfigFetcherTest, InvalidMissingTokenEndpoint) {
  ConfigFetcher::FetchResult result;
  result.endpoints.accounts = GURL("https://idp.example/accounts");
  result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")};
  result.identity_provider_config_url = GURL("https://idp.example/fedcm.json");

  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  fetcher.ValidateAndMaybeSetError(result);
  EXPECT_TRUE(result.error);
  EXPECT_EQ(result.error->result,
            blink::mojom::FederatedRequestResult::kConfigInvalidResponse);
}

TEST_F(ConfigFetcherTest, InvalidCrossOriginTokenEndpoint) {
  ConfigFetcher::FetchResult result;
  result.endpoints.accounts = GURL("https://idp.example/accounts");
  result.endpoints.token = GURL("https://cross-origin.example/token");
  result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")};
  result.identity_provider_config_url = GURL("https://idp.example/fedcm.json");

  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  fetcher.ValidateAndMaybeSetError(result);

  EXPECT_TRUE(result.error);
  EXPECT_EQ(result.error->result,
            blink::mojom::FederatedRequestResult::kConfigInvalidResponse);
}

TEST_F(ConfigFetcherTest, InvalidCrossOriginSigninUrl) {
  ConfigFetcher::FetchResult result;
  result.endpoints.accounts = GURL("https://idp.example/accounts");
  result.endpoints.token = GURL("https://idp.example/token");
  IdentityProviderMetadata metadata;
  metadata.idp_login_url = GURL("https://cross-origin.example/sign-in");
  result.metadata = metadata;
  result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")};
  result.identity_provider_config_url = GURL("https://idp.example/fedcm.json");

  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  fetcher.ValidateAndMaybeSetError(result);

  EXPECT_TRUE(result.error);
  EXPECT_EQ(result.error->result,
            blink::mojom::FederatedRequestResult::kConfigInvalidResponse);
}

TEST_F(ConfigFetcherTest, InvalidConfigUrlNotInProviders) {
  IdentityProviderMetadata metadata;
  metadata.idp_login_url = GURL("https://idp.example/idp_login_url.php");

  ConfigFetcher::FetchResult result;
  result.endpoints.accounts = GURL("https://idp.example/accounts");
  result.endpoints.token = GURL("https://idp.example/token");
  result.wellknown.provider_urls = {
      GURL("https://another-idp.example/fedcm.json")};
  result.identity_provider_config_url = GURL("https://idp.example/fedcm.json");
  result.metadata = std::move(metadata);

  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  fetcher.ValidateAndMaybeSetError(result);

  EXPECT_TRUE(result.error);
  EXPECT_EQ(result.error->result,
            blink::mojom::FederatedRequestResult::kConfigNotInWellKnown);
}

TEST_F(ConfigFetcherTest, InvalidConfigUrlNotInWellKnown) {
  IdentityProviderMetadata metadata;
  metadata.idp_login_url = GURL("https://idp.example/idp_login_url.php");

  ConfigFetcher::FetchResult result;
  result.endpoints.accounts = GURL("https://idp.example/accounts");
  result.endpoints.token = GURL("https://idp.example/token");
  result.wellknown.provider_urls = {
      GURL("https://idp.example/another-file.json")};
  result.identity_provider_config_url = GURL("https://idp.example/fedcm.json");
  result.metadata = std::move(metadata);

  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  fetcher.ValidateAndMaybeSetError(result);

  EXPECT_TRUE(result.error);
  EXPECT_EQ(result.error->result,
            blink::mojom::FederatedRequestResult::kConfigNotInWellKnown);
}

TEST_F(ConfigFetcherTest, InvalidWellKnownTooManyProviders) {
  IdentityProviderMetadata metadata;
  metadata.idp_login_url = GURL("https://idp.example/idp_login_url.php");

  ConfigFetcher::FetchResult result;
  result.endpoints.accounts = GURL("https://idp.example/accounts");
  result.endpoints.token = GURL("https://idp.example/token");
  result.wellknown.provider_urls = {
      GURL("https://idp.example/fedcm.json"),
      GURL("https://idp.example/another-one.json")};
  result.identity_provider_config_url = GURL("https://idp.example/fedcm.json");
  result.metadata = std::move(metadata);

  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  fetcher.ValidateAndMaybeSetError(result);

  EXPECT_TRUE(result.error);
  EXPECT_EQ(result.error->result,
            blink::mojom::FederatedRequestResult::kWellKnownTooBig);
}

TEST_F(ConfigFetcherTest, SkippingTheChecksWithTheWellKnownFlag) {
  feature_list_.InitAndEnableFeature(
      features::kFedCmWithoutWellKnownEnforcement);

  IdentityProviderMetadata metadata;
  metadata.idp_login_url = GURL("https://idp.example/idp_login_url.php");

  ConfigFetcher::FetchResult result;
  result.endpoints.accounts = GURL("https://idp.example/accounts");
  result.endpoints.token = GURL("https://idp.example/token");
  result.wellknown.provider_urls = {
      GURL("https://idp.example/another-file.json")};
  result.identity_provider_config_url = GURL("https://idp.example/fedcm.json");
  result.metadata = std::move(metadata);

  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  fetcher.ValidateAndMaybeSetError(result);

  EXPECT_FALSE(result.error);
}

TEST_F(ConfigFetcherTest, InvalidWellKnownWithoutSignInUrl) {
  ConfigFetcher::FetchResult result;
  result.endpoints.accounts = GURL("https://idp.example/accounts");
  result.endpoints.token = GURL("https://idp.example/token");
  result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")};
  result.wellknown.accounts = GURL("https://idp.example/accounts");
  result.identity_provider_config_url =
      GURL("https://idp.example/another-file.json");

  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  fetcher.ValidateAndMaybeSetError(result);

  EXPECT_TRUE(result.error);
}

TEST_F(ConfigFetcherTest, InvalidWellKnownWithoutAccountsEndpoint) {
  ConfigFetcher::FetchResult result;
  result.endpoints.accounts = GURL("https://idp.example/accounts");
  result.endpoints.token = GURL("https://idp.example/token");
  result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")};
  result.wellknown.login_url = GURL("https://idp.example/signin");
  result.identity_provider_config_url =
      GURL("https://idp.example/another-file.json");

  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  fetcher.ValidateAndMaybeSetError(result);

  EXPECT_TRUE(result.error);
}

TEST_F(ConfigFetcherTest, ValidWellKnownWithMatchingAccountsAndSignInUrl) {
  ConfigFetcher::FetchResult result;
  IdentityProviderMetadata metadata;
  metadata.idp_login_url = GURL("https://idp.example/sign-in");
  result.metadata = metadata;
  result.endpoints.accounts = GURL("https://idp.example/accounts");
  result.endpoints.token = GURL("https://idp.example/token");
  result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")};
  result.wellknown.accounts = GURL("https://idp.example/accounts");
  result.wellknown.login_url = GURL("https://idp.example/sign-in");
  result.identity_provider_config_url =
      GURL("https://idp.example/another-file.json");

  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  fetcher.ValidateAndMaybeSetError(result);

  EXPECT_FALSE(result.error);
}

TEST_F(ConfigFetcherTest,
       FailResultEvenIfConfigUrlMatchesWhenAccountsEndpointIsAvailable) {
  // In this test, we verify that when the accounts endpoint is available and
  // not matching the one provided in the configURL, we do to allow falling
  // back to checking based on the fact that the provider_url contains the
  // configURL.
  ConfigFetcher::FetchResult result;
  IdentityProviderMetadata metadata;
  metadata.idp_login_url = GURL("https://idp.example/sign-in");
  result.metadata = metadata;
  result.endpoints.accounts = GURL("https://idp.example/accounts");
  result.endpoints.token = GURL("https://idp.example/token");
  result.wellknown.accounts = GURL("https://idp.example/another-accounts");
  result.wellknown.login_url = GURL("https://idp.example/sign-in");

  // Even if the configURL is present in the provider_urls, the presence of
  // the accounts_endpoint disqualifies this configuration.
  result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")};
  result.identity_provider_config_url = GURL("https://idp.example/fedcm.json");

  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  fetcher.ValidateAndMaybeSetError(result);

  EXPECT_TRUE(result.error);
  // EXPECT_EQ(result.error->result, blink::mojom::FederatedRequestResult::
  //                                     kConfigInvalidResponse);
}

TEST_F(ConfigFetcherTest,
       SuccessResultEvenWithEmptyAccountsEndpointWithLightweightFedCm) {
  // Validate that when LightweightFedCM is enabled, it's permissible to have an
  // empty accounts_endpoint set.
  feature_list_.InitAndEnableFeature(features::kFedCmLightweightMode);
  ConfigFetcher::FetchResult result;
  IdentityProviderMetadata metadata;
  metadata.idp_login_url = GURL("https://idp.example/sign-in");
  result.metadata = metadata;
  result.endpoints.accounts = GURL();
  result.endpoints.token = GURL("https://idp.example/token");
  result.wellknown.login_url = GURL("https://idp.example/sign-in");
  result.wellknown.accounts = GURL();

  result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")};
  result.identity_provider_config_url = GURL("https://idp.example/fedcm.json");
  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  fetcher.ValidateAndMaybeSetError(result);

  EXPECT_FALSE(result.error);
}

TEST_F(ConfigFetcherTest, ProvidersUrlsCanbeEmptyWhenLightweightIsEnabled) {
  // Validate that when LightweightFedCM is enabled,
  // it's permissible to have an empty accounts_endpoint set and
  // no provider_config_urls, so long as the accounts url is empty in both the
  // wellknown and config.
  feature_list_.InitAndEnableFeature(features::kFedCmLightweightMode);
  ConfigFetcher::FetchResult result;
  IdentityProviderMetadata metadata;
  metadata.idp_login_url = GURL("https://idp.example/sign-in");
  result.metadata = metadata;
  result.endpoints.accounts = GURL();
  result.endpoints.token = GURL("https://idp.example/token");
  result.wellknown.login_url = GURL("https://idp.example/sign-in");
  result.wellknown.accounts = GURL();

  result.wellknown.provider_urls = {};
  result.identity_provider_config_url = GURL("https://idp.example/fedcm.json");
  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  fetcher.ValidateAndMaybeSetError(result);

  EXPECT_FALSE(result.error);
}

TEST_F(ConfigFetcherTest,
       FailureResultWithMismatchingAccountsEndpointWithLightweightFedCm) {
  // Validate that when LightweightFedCM is enabled, it's still an error to have
  // a non-same-origin accounts endpoint.
  feature_list_.InitAndEnableFeature(features::kFedCmLightweightMode);
  ConfigFetcher::FetchResult result;
  IdentityProviderMetadata metadata;
  metadata.idp_login_url = GURL("https://idp.example/sign-in");
  result.metadata = metadata;
  result.endpoints.accounts = GURL("https://not-the-idp.example/accounts");
  result.endpoints.token = GURL("https://idp.example/token");
  result.wellknown.login_url = GURL();
  result.wellknown.accounts = GURL();

  result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")};
  result.identity_provider_config_url = GURL("https://idp.example/fedcm.json");
  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  fetcher.ValidateAndMaybeSetError(result);

  EXPECT_TRUE(result.error);
}

TEST_F(ConfigFetcherTest,
       FailureResultWithEmptyAccountsEndpointWithoutLightweightFedCm) {
  // Validate that when LightweightFedCM is disabled, it's still an error to not
  // define an accounts endpoint.
  ConfigFetcher::FetchResult result;
  IdentityProviderMetadata metadata;
  metadata.idp_login_url = GURL("https://idp.example/sign-in");
  result.metadata = metadata;
  result.endpoints.accounts = GURL();
  result.endpoints.token = GURL("https://idp.example/token");
  result.wellknown.login_url = GURL();
  result.wellknown.accounts = GURL();

  result.wellknown.provider_urls = {GURL("https://idp.example/fedcm.json")};
  result.identity_provider_config_url = GURL("https://idp.example/fedcm.json");
  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  fetcher.ValidateAndMaybeSetError(result);

  EXPECT_TRUE(result.error);
}

TEST_F(ConfigFetcherTest, InvalidEmptyConfig) {
  ConfigFetcher::FetchResult result;

  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  fetcher.ValidateAndMaybeSetError(result);

  EXPECT_TRUE(result.error);
  EXPECT_EQ(result.error->result,
            blink::mojom::FederatedRequestResult::kConfigInvalidResponse);
}

TEST_F(ConfigFetcherTest, InvalidNetworkError) {
  ConfigFetcher::FetchResult result;
  result.error = ConfigFetcher::FetchError(
      blink::mojom::FederatedRequestResult::kConfigHttpNotFound,
      RequestIdTokenStatus::kConfigHttpNotFound,
      /*additional_console_error_message=*/std::nullopt);

  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  fetcher.ValidateAndMaybeSetError(result);

  EXPECT_TRUE(result.error);
  EXPECT_EQ(result.error->result,
            blink::mojom::FederatedRequestResult::kConfigHttpNotFound);
}

TEST_F(ConfigFetcherTest, RegisteredIdpSkipsWellKnownCheck) {
  base::test::ScopedFeatureList list;
  list.InitAndEnableFeature(features::kFedCmIdPRegistration);
  auto network_manager =
      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
  ConfigFetcher fetcher(*main_rfh(), network_manager.get());

  EXPECT_CALL(*network_manager, FetchConfig)
      .WillOnce(WithArg<3>(
          [](IdpNetworkRequestManager::FetchConfigCallback callback) {
            IdpNetworkRequestManager::Endpoints endpoints;
            endpoints.token = GURL("https://idp.example/token.php");
            endpoints.accounts = GURL("https://idp.example/accounts.php");

            IdentityProviderMetadata metadata;
            metadata.idp_login_url =
                GURL("https://idp.example/idp_login_url.php");
            std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK},
                                    endpoints, metadata);
          }));

  // Returns a 404 for the fetch of the well-known file.
  EXPECT_CALL(*network_manager, FetchWellKnown)
      .WillOnce(WithArg<1>(
          [](IdpNetworkRequestManager::FetchWellKnownCallback callback) {
            IdpNetworkRequestManager::WellKnown well_known;
            std::move(callback).Run(
                {ParseStatus::kHttpNotFoundError, net::HTTP_NOT_FOUND},
                well_known);
          }));

  base::RunLoop loop;

  // Asserts that we get success despite well-known failing.
  fetcher.Start({{GURL("https://idp.example/fedcm.json"),
                  /*force_skip_well_known_enforcement=*/true}},
                /*icon_ideal_size=*/0,
                /*icon_minimum_size=*/0,
                base::BindLambdaForTesting(
                    [&loop](std::vector<ConfigFetcher::FetchResult> result) {
                      EXPECT_EQ(result.size(), 1ul);
                      EXPECT_FALSE(result[0].error);
                      loop.Quit();
                    }));

  loop.Run();
}

}  // namespace content::webid
