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

#include "chrome/browser/net/profile_network_context_service.h"

#include <algorithm>
#include <atomic>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>

#include "base/check_op.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/run_until.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/platform_thread.h"  // For |Sleep()|.
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/net/profile_network_context_service_factory.h"
#include "chrome/browser/net/profile_network_context_service_test_utils.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/policy/chrome_browser_policy_connector.h"
#include "chrome/browser/policy/policy_test_utils.h"
#include "chrome/browser/privacy_sandbox/privacy_sandbox_settings_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_test_util.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_paths_internal.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/enterprise/connectors/core/connectors_prefs.h"
#include "components/enterprise/encryption/core/features.h"
#include "components/metrics/content/subprocess_metrics_provider.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/policy_constants.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
#include "components/privacy_sandbox/privacy_sandbox_features.h"
#include "components/privacy_sandbox/privacy_sandbox_settings.h"
#include "components/privacy_sandbox/privacy_sandbox_test_util.h"
#include "components/safe_browsing/core/common/features.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/user_prefs/user_prefs.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/storage_partition_config.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/simple_url_loader_test_helper.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/system/data_pipe_utils.h"
#include "net/base/features.h"
#include "net/base/load_flags.h"
#include "net/disk_cache/buildflags.h"
#include "net/disk_cache/cache_util.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/http_auth_preferences.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/test/embedded_test_server/request_handler_util.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom.h"
#include "services/network/public/cpp/cors/cors.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "services/network/test/trust_token_request_handler.h"
#include "services/network/test/trust_token_test_server_handler_registration.h"
#include "services/network/test/trust_token_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "chromeos/constants/chromeos_features.h"
#endif

constexpr char kHttpCacheFinchExperimentGroups[] =
    "profile_network_context_service.http_cache_finch_experiment_groups";

// Most tests for this class are in NetworkContextConfigurationBrowserTest.
class ProfileNetworkContextServiceBrowsertest : public InProcessBrowserTest {
 public:
  ProfileNetworkContextServiceBrowsertest() = default;

  ~ProfileNetworkContextServiceBrowsertest() override = default;

  // TODO(crbug.com/40285326): This fails with the field trial testing config.
  void SetUpCommandLine(base::CommandLine* command_line) override {
    InProcessBrowserTest::SetUpCommandLine(command_line);
    command_line->AppendSwitch("disable-field-trial-config");
  }

  void SetUpOnMainThread() override {
    EXPECT_TRUE(embedded_test_server()->Start());
    loader_factory_ = browser()
                          ->GetProfile()
                          ->GetDefaultStoragePartition()
                          ->GetURLLoaderFactoryForBrowserProcess()
                          .get();
  }

  network::mojom::URLLoaderFactory* loader_factory() const {
    return loader_factory_;
  }

  void CheckDiskCacheSizeHistogramRecorded() {
    std::string all_metrics;
    do {
      content::FetchHistogramsFromChildProcesses();
      metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
      base::PlatformThread::Sleep(base::Milliseconds(5));
      all_metrics = histograms_.GetAllHistogramsRecorded();
    } while (std::string::npos ==
             all_metrics.find("HttpCache.MaxFileSizeOnInit"));
  }

  base::HistogramTester histograms_;

 protected:
  // The HttpCache is only created when a request is issued, thus we perform a
  // navigation to ensure that the http cache is initialized.
  void NavigateToCreateHttpCache() {
    ASSERT_TRUE(ui_test_utils::NavigateToURL(
        browser(), embedded_test_server()->GetURL("/createbackend")));
  }

 private:
  raw_ptr<network::mojom::URLLoaderFactory> loader_factory_ = nullptr;
};

IN_PROC_BROWSER_TEST_F(ProfileNetworkContextServiceBrowsertest,
                       DiskCacheLocation) {
  // Run a request that caches the response, to give the network service time to
  // create a cache directory.
  std::unique_ptr<network::ResourceRequest> request =
      std::make_unique<network::ResourceRequest>();
  request->url = embedded_test_server()->GetURL("/cachetime");
  request->credentials_mode = network::mojom::CredentialsMode::kOmit;
  content::SimpleURLLoaderTestHelper simple_loader_helper;
  std::unique_ptr<network::SimpleURLLoader> simple_loader =
      network::SimpleURLLoader::Create(std::move(request),
                                       TRAFFIC_ANNOTATION_FOR_TESTS);

  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
      loader_factory(), simple_loader_helper.GetCallback());
  simple_loader_helper.WaitForCallback();
  ASSERT_TRUE(simple_loader_helper.response_body());

  base::FilePath expected_cache_path;
  chrome::GetUserCacheDirectory(browser()->GetProfile()->GetPath(),
                                &expected_cache_path);
  expected_cache_path = expected_cache_path.Append(chrome::kCacheDirname);
  base::ScopedAllowBlockingForTesting allow_blocking;
  EXPECT_TRUE(base::PathExists(expected_cache_path));
}

IN_PROC_BROWSER_TEST_F(ProfileNetworkContextServiceBrowsertest,
                       DefaultCacheSize) {
  // We don't have a great way of directly checking that the disk cache has the
  // correct max size, but we can make sure that we set up our network context
  // params correctly.
  ProfileNetworkContextService* profile_network_context_service =
      ProfileNetworkContextServiceFactory::GetForContext(
          browser()->GetProfile());
  base::FilePath empty_relative_partition_path;
  network::mojom::NetworkContextParams network_context_params;
  cert_verifier::mojom::CertVerifierCreationParams
      cert_verifier_creation_params;
  profile_network_context_service->ConfigureNetworkContextParams(
      /*in_memory=*/false, empty_relative_partition_path,
      &network_context_params, &cert_verifier_creation_params);
  EXPECT_EQ(0, network_context_params.http_cache_max_size);

  CheckDiskCacheSizeHistogramRecorded();
}

IN_PROC_BROWSER_TEST_F(ProfileNetworkContextServiceBrowsertest, CacheSize) {
  // We don't have a great way of directly checking that the disk cache has the
  // correct max size, but we can make sure that we set up our network context
  // params correctly and that the histogram is recorded.
  ProfileNetworkContextService* profile_network_context_service =
      ProfileNetworkContextServiceFactory::GetForContext(
          browser()->GetProfile());
  base::FilePath empty_relative_partition_path;
  network::mojom::NetworkContextParams network_context_params;
  cert_verifier::mojom::CertVerifierCreationParams
      cert_verifier_creation_params;
  profile_network_context_service->ConfigureNetworkContextParams(
      /*in_memory=*/false, empty_relative_partition_path,
      &network_context_params, &cert_verifier_creation_params);
  EXPECT_EQ(0, network_context_params.http_cache_max_size);

  CheckDiskCacheSizeHistogramRecorded();
}

IN_PROC_BROWSER_TEST_F(ProfileNetworkContextServiceBrowsertest, BrotliEnabled) {
  // Brotli is only used over encrypted connections.
  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
  https_server.AddDefaultHandlers(
      base::FilePath(FILE_PATH_LITERAL("content/test/data")));
  ASSERT_TRUE(https_server.Start());

  std::unique_ptr<network::ResourceRequest> request =
      std::make_unique<network::ResourceRequest>();
  request->url = https_server.GetURL("/echoheader?accept-encoding");

  content::SimpleURLLoaderTestHelper simple_loader_helper;
  std::unique_ptr<network::SimpleURLLoader> simple_loader =
      network::SimpleURLLoader::Create(std::move(request),
                                       TRAFFIC_ANNOTATION_FOR_TESTS);
  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
      loader_factory(), simple_loader_helper.GetCallback());
  simple_loader_helper.WaitForCallback();
  ASSERT_TRUE(simple_loader_helper.response_body());
  std::vector<std::string> encodings =
      base::SplitString(*simple_loader_helper.response_body(), ",",
                        base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
  EXPECT_TRUE(std::ranges::contains(encodings, "br"));
}

void CheckCacheResetStatus(base::HistogramTester* histograms, bool reset) {
  // TODO(crbug.com/40114587): The failure case, here, is to time out.  Since
  // Chrome doesn't synchronize cache loading, there's no guarantee that this is
  // complete and it's merely available at earliest convenience.  If shutdown
  // occurs prior to the cache being loaded, then nothing is reported.  This
  // should probably be fixed to avoid the use of the sleep function, but that
  // will require synchronizing in some meaningful way to guarantee the cache
  // has been loaded prior to testing the histograms.
  while (!histograms->GetBucketCount("HttpCache.HardReset", reset)) {
    content::FetchHistogramsFromChildProcesses();
    metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
    base::PlatformThread::Sleep(base::Milliseconds(5));
  }

  if (reset) {
    // Some tests load the cache multiple times, but should only be reset once.
    EXPECT_EQ(histograms->GetBucketCount("HttpCache.HardReset", true), 1);
  } else {
    // Make sure it's never reset.
    EXPECT_EQ(histograms->GetBucketCount("HttpCache.HardReset", true), 0);
  }
}

class ProfileNetworkContextServiceCacheSameBrowsertest
    : public ProfileNetworkContextServiceBrowsertest {
 public:
  ProfileNetworkContextServiceCacheSameBrowsertest() {
    // Override features that are enabled via the fieldtrial testing config.
    split_cache_disabled_feature_list_.InitAndDisableFeature(
        net::features::kSplitCacheByNetworkIsolationKey);
  }
  ~ProfileNetworkContextServiceCacheSameBrowsertest() override = default;

  base::HistogramTester histograms_;

 private:
  base::test::ScopedFeatureList split_cache_disabled_feature_list_;
};

IN_PROC_BROWSER_TEST_F(ProfileNetworkContextServiceCacheSameBrowsertest,
                       PRE_TestCacheResetParameter) {
  NavigateToCreateHttpCache();
  CheckCacheResetStatus(&histograms_, false);

  // At this point, we have already called the initialization.
  // Verify that we have the correct values in the profile preferences.
  PrefService* profile_prefs = browser()->GetProfile()->GetPrefs();
  DCHECK_EQ(profile_prefs->GetString(kHttpCacheFinchExperimentGroups),
            "None None None None");
}

IN_PROC_BROWSER_TEST_F(ProfileNetworkContextServiceCacheSameBrowsertest,
                       TestCacheResetParameter) {
  NavigateToCreateHttpCache();
  CheckCacheResetStatus(&histograms_, false);

  // At this point, we have already called the initialization.
  // Verify that we have the correct values in the profile preferences.
  PrefService* profile_prefs = browser()->GetProfile()->GetPrefs();
  DCHECK_EQ(profile_prefs->GetString(kHttpCacheFinchExperimentGroups),
            "None None None None");
}

class ProfileNetworkContextServiceCacheChangeBrowsertest
    : public ProfileNetworkContextServiceBrowsertest {
 public:
  ProfileNetworkContextServiceCacheChangeBrowsertest() {
    split_cache_always_enabled_feature_list_.InitAndEnableFeatureWithParameters(
        net::features::kSplitCacheByNetworkIsolationKey, {});
  }
  ~ProfileNetworkContextServiceCacheChangeBrowsertest() override = default;

  base::HistogramTester histograms_;

 private:
  base::test::ScopedFeatureList split_cache_always_enabled_feature_list_;
};

// The first time we load, even if we're in an experiment there's no reset
// from the unknown state (new profile).
IN_PROC_BROWSER_TEST_F(ProfileNetworkContextServiceCacheChangeBrowsertest,
                       PRE_TestCacheResetParameter) {
  NavigateToCreateHttpCache();
  CheckCacheResetStatus(&histograms_, false);

  // At this point, we have already called the initialization.
  // Verify that we have the correct values in the profile preferences.
  PrefService* profile_prefs = browser()->GetProfile()->GetPrefs();
  DCHECK_EQ(profile_prefs->GetString(kHttpCacheFinchExperimentGroups),
            "scoped_feature_list_trial_group None None None");
  // Set the local state for the next test.
  profile_prefs->SetString(kHttpCacheFinchExperimentGroups,
                           "None None None None");
}

// The second time we load we know the state, which was "None None None None"
// for the previous test, so we should see a reset being in an experiment.
IN_PROC_BROWSER_TEST_F(ProfileNetworkContextServiceCacheChangeBrowsertest,
                       TestCacheResetParameter) {
  NavigateToCreateHttpCache();
  CheckCacheResetStatus(&histograms_, true);

  // At this point, we have already called the initialization once.
  // Verify that we have the correct values in the profile preferences.
  PrefService* profile_prefs = browser()->GetProfile()->GetPrefs();
  DCHECK_EQ(profile_prefs->GetString(kHttpCacheFinchExperimentGroups),
            "scoped_feature_list_trial_group None None None");
}

// This subclass adds the "SplitCacheByIncludeCredentials" feature.
class ProfileNetworkContextServiceCacheCredentialsBrowserTest
    : public ProfileNetworkContextServiceBrowsertest {
 public:
  ProfileNetworkContextServiceCacheCredentialsBrowserTest() {
    split_cache_always_enabled_feature_list_.InitAndEnableFeatureWithParameters(
        net::features::kSplitCacheByIncludeCredentials, {});
  }
  ~ProfileNetworkContextServiceCacheCredentialsBrowserTest() override = default;

  base::HistogramTester histograms_;

 private:
  base::test::ScopedFeatureList split_cache_always_enabled_feature_list_;
};

IN_PROC_BROWSER_TEST_F(ProfileNetworkContextServiceCacheCredentialsBrowserTest,
                       PRE_TestCacheResetParameter) {
  NavigateToCreateHttpCache();
  // Even if we're in an experiment there's no reset from the unknown state
  // (new profile).
  CheckCacheResetStatus(&histograms_, false);

  // At this point, we have already called the initialization.
  // Verify that we have the correct values in the profile preferences.
  PrefService* profile_prefs = browser()->GetProfile()->GetPrefs();
  DCHECK_EQ(profile_prefs->GetString(kHttpCacheFinchExperimentGroups),
            "None None None scoped_feature_list_trial_group");
  // Set the local state for the next test.
  profile_prefs->SetString(kHttpCacheFinchExperimentGroups,
                           "None None None None");
}

// The second time we load we know the state, which was "None None None None"
// for the previous test, so we should see a reset being in an experiment.
IN_PROC_BROWSER_TEST_F(ProfileNetworkContextServiceCacheCredentialsBrowserTest,
                       TestCacheResetParameter) {
  NavigateToCreateHttpCache();
  CheckCacheResetStatus(&histograms_, true);

  // At this point, we have already called the initialization once.
  // Verify that we have the correct values in the profile preferences.
  PrefService* profile_prefs = browser()->GetProfile()->GetPrefs();
  DCHECK_EQ(profile_prefs->GetString(kHttpCacheFinchExperimentGroups),
            "None None None scoped_feature_list_trial_group");
}

class ProfileNetworkContextServiceDiskCacheBackendExperimentBrowserTest
    : public ProfileNetworkContextServiceBrowsertest,
      public ::testing::WithParamInterface<net::features::DiskCacheBackend> {
 public:
  ProfileNetworkContextServiceDiskCacheBackendExperimentBrowserTest() {
    feature_list_.InitAndEnableFeatureWithParameters(
        net::features::kDiskCacheBackendExperiment,
        {{"backend", GetBackendParamValue()},
         {"DiskCacheBackendResetCacheOnGroupChange", "true"}});
  }
  ~ProfileNetworkContextServiceDiskCacheBackendExperimentBrowserTest()
      override = default;

  const char* GetBackendParamValue() {
    switch (GetParam()) {
      case net::features::DiskCacheBackend::kDefault:
        return "default";
      case net::features::DiskCacheBackend::kSimple:
        return "simple";
      case net::features::DiskCacheBackend::kBlockfile:
        return "blockfile";
#if BUILDFLAG(ENABLE_DISK_CACHE_SQL_BACKEND)
      case net::features::DiskCacheBackend::kSql:
        return "sql";
#endif  // ENABLE_DISK_CACHE_SQL_BACKEND
    }
  }

  base::HistogramTester histograms_;

 private:
  base::test::ScopedFeatureList feature_list_;
};

IN_PROC_BROWSER_TEST_P(
    ProfileNetworkContextServiceDiskCacheBackendExperimentBrowserTest,
    PRE_TestCacheResetParameter) {
  NavigateToCreateHttpCache();
  // Even if we're in an experiment there's no reset from the unknown state
  // (new profile).
  CheckCacheResetStatus(&histograms_, false);

  // At this point, we have already called the initialization.
  // Verify that we have the correct values in the profile preferences.
  PrefService* profile_prefs = browser()->GetProfile()->GetPrefs();
  DCHECK_EQ(profile_prefs->GetString(kHttpCacheFinchExperimentGroups),
            "None None None None scoped_feature_list_trial_group");

  // Set the local state for the next test.
  profile_prefs->SetString(kHttpCacheFinchExperimentGroups,
                           "None None None None");
}

// The second time we load we know the state, which was "None None None None"
// for the previous test, so we should see a reset being in an experiment.
IN_PROC_BROWSER_TEST_P(
    ProfileNetworkContextServiceDiskCacheBackendExperimentBrowserTest,
    TestCacheResetParameter) {
  NavigateToCreateHttpCache();
  CheckCacheResetStatus(&histograms_, true);

  // At this point, we have already called the initialization.
  // Verify that we have the correct values in the profile preferences.
  PrefService* profile_prefs = browser()->GetProfile()->GetPrefs();
  DCHECK_EQ(profile_prefs->GetString(kHttpCacheFinchExperimentGroups),
            "None None None None scoped_feature_list_trial_group");
}

INSTANTIATE_TEST_SUITE_P(
    All,
    ProfileNetworkContextServiceDiskCacheBackendExperimentBrowserTest,
    testing::ValuesIn({net::features::DiskCacheBackend::kSimple,
                       net::features::DiskCacheBackend::kBlockfile
#if BUILDFLAG(ENABLE_DISK_CACHE_SQL_BACKEND)
                       ,
                       net::features::DiskCacheBackend::kSql
#endif  // ENABLE_DISK_CACHE_SQL_BACKEND
    }));

class ProfileNetworkContextServiceDiskCacheBackendExperimentNoResetBrowserTest
    : public ProfileNetworkContextServiceBrowsertest {
 public:
  ProfileNetworkContextServiceDiskCacheBackendExperimentNoResetBrowserTest() {
    feature_list_.InitAndEnableFeatureWithParameters(
        net::features::kDiskCacheBackendExperiment, {{"backend", "simple"}});
  }

 private:
  base::test::ScopedFeatureList feature_list_;
};

IN_PROC_BROWSER_TEST_F(
    ProfileNetworkContextServiceDiskCacheBackendExperimentNoResetBrowserTest,
    TestNoCacheResetOnGroupChangeByDefault) {
  NavigateToCreateHttpCache();
  PrefService* profile_prefs = browser()->GetProfile()->GetPrefs();
  EXPECT_EQ(profile_prefs->GetString(kHttpCacheFinchExperimentGroups),
            "None None None None");
}

class ProfileNetworkContextServiceCacheResetBrowserTestBase
    : public ProfileNetworkContextServiceBrowsertest {
 public:
  ProfileNetworkContextServiceCacheResetBrowserTestBase() = default;

  void SetUpOnMainThread() override {
    embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
        &ProfileNetworkContextServiceCacheResetBrowserTestBase::
            HandleCacheResetTest,
        base::Unretained(this)));
    ProfileNetworkContextServiceBrowsertest::SetUpOnMainThread();
  }

 protected:
  void FetchUrl(const GURL& url,
                content::StoragePartition* partition = nullptr) {
    std::unique_ptr<network::ResourceRequest> request =
        std::make_unique<network::ResourceRequest>();
    request->url = url;
    request->credentials_mode = network::mojom::CredentialsMode::kOmit;

    content::SimpleURLLoaderTestHelper simple_loader_helper;
    std::unique_ptr<network::SimpleURLLoader> simple_loader =
        network::SimpleURLLoader::Create(std::move(request),
                                         TRAFFIC_ANNOTATION_FOR_TESTS);
    network::mojom::URLLoaderFactory* factory =
        partition ? partition->GetURLLoaderFactoryForBrowserProcess().get()
                  : loader_factory();
    simple_loader->DownloadToString(factory, simple_loader_helper.GetCallback(),
                                    /*max_body_size=*/1024 * 1024);
    simple_loader_helper.WaitForCallback();
    EXPECT_TRUE(simple_loader_helper.response_body());
  }

  std::atomic<int> cache_reset_test_request_count_{0};

 private:
  std::unique_ptr<net::test_server::HttpResponse> HandleCacheResetTest(
      const net::test_server::HttpRequest& request) {
    if (request.relative_url != "/cache_reset_test") {
      return nullptr;
    }
    cache_reset_test_request_count_++;
    auto http_response =
        std::make_unique<net::test_server::BasicHttpResponse>();
    http_response->set_content("data");
    http_response->set_content_type("text/plain");
    http_response->AddCustomHeader("Cache-Control", "max-age=3600");
    return http_response;
  }
};

class ProfileNetworkContextServiceCacheResetOnUpgradeBrowserTest
    : public ProfileNetworkContextServiceCacheResetBrowserTestBase {
 public:
  ProfileNetworkContextServiceCacheResetOnUpgradeBrowserTest() {
    const ::testing::TestInfo* const test_info =
        ::testing::UnitTest::GetInstance()->current_test_info();
    if (std::string_view(test_info->name()).starts_with("PRE_")) {
      // PRE_ test: no experiment (default)
      feature_list_.InitAndDisableFeature(
          net::features::kDiskCacheBackendExperiment);
    } else {
      // non-PRE test: experiment active (use simple backend)
      feature_list_.InitAndEnableFeatureWithParameters(
          net::features::kDiskCacheBackendExperiment,
          {{"backend", "simple"},
           {"DiskCacheBackendResetCacheOnGroupChange", "true"}});
    }
  }

 private:
  base::test::ScopedFeatureList feature_list_;
};

class ProfileNetworkContextServiceCacheResetSameBackendBrowserTest
    : public ProfileNetworkContextServiceCacheResetBrowserTestBase {
 public:
  ProfileNetworkContextServiceCacheResetSameBackendBrowserTest() {
    // Enable the experiment in both PRE and non-PRE runs to keep the backend
    // type same, avoiding forced cache recreation.
    feature_list_.InitAndEnableFeatureWithParameters(
        net::features::kDiskCacheBackendExperiment,
        {{"backend", "simple"},
         {"DiskCacheBackendResetCacheOnGroupChange", "true"}});
  }

 private:
  base::test::ScopedFeatureList feature_list_;
};

IN_PROC_BROWSER_TEST_F(
    ProfileNetworkContextServiceCacheResetOnUpgradeBrowserTest,
    PRE_TestCacheResetOnUpgrade) {
  GURL url = embedded_test_server()->GetURL("/cache_reset_test");

  // Populate cache.
  FetchUrl(url);
  EXPECT_EQ(1, cache_reset_test_request_count_);

  // Request again to make sure it is cached.
  FetchUrl(url);
  EXPECT_EQ(1, cache_reset_test_request_count_);

  // Simulate upgrade by clearing the pref.
  PrefService* profile_prefs = browser()->GetProfile()->GetPrefs();
  profile_prefs->ClearPref(kHttpCacheFinchExperimentGroups);
}

IN_PROC_BROWSER_TEST_F(
    ProfileNetworkContextServiceCacheResetOnUpgradeBrowserTest,
    TestCacheResetOnUpgrade) {
  GURL url = embedded_test_server()->GetURL("/cache_reset_test");

  // cache_reset_test_request_count_ is reset to 0 in this new process.
  EXPECT_EQ(0, cache_reset_test_request_count_);

  // Request the cached resource.
  FetchUrl(url);

  // Since we upgraded and are now in an experiment, the cache should have
  // been reset. Thus, the request should have gone to the server.
  EXPECT_EQ(1, cache_reset_test_request_count_);
}

IN_PROC_BROWSER_TEST_F(
    ProfileNetworkContextServiceCacheResetSameBackendBrowserTest,
    PRE_TestCacheResetOnUpgradeMultiPartition) {
  GURL url = embedded_test_server()->GetURL("/cache_reset_test");

  // Populate cache for default partition.
  FetchUrl(url);
  EXPECT_EQ(1, cache_reset_test_request_count_);
  FetchUrl(url);
  EXPECT_EQ(1, cache_reset_test_request_count_);

  // Populate cache for non-default partition.
  content::StoragePartition* partition =
      browser()->GetProfile()->GetStoragePartition(
          content::StoragePartitionConfig::Create(browser()->GetProfile(),
                                                  "testdomain", "testpartition",
                                                  /*in_memory=*/false));

  FetchUrl(url, partition);
  EXPECT_EQ(2, cache_reset_test_request_count_);

  // Request again for non-default partition to make sure it is cached.
  FetchUrl(url, partition);
  EXPECT_EQ(2, cache_reset_test_request_count_);

  // Simulate upgrade by clearing the pref.
  PrefService* profile_prefs = browser()->GetProfile()->GetPrefs();
  profile_prefs->ClearPref(kHttpCacheFinchExperimentGroups);
}

IN_PROC_BROWSER_TEST_F(
    ProfileNetworkContextServiceCacheResetSameBackendBrowserTest,
    TestCacheResetOnUpgradeMultiPartition) {
  GURL url = embedded_test_server()->GetURL("/cache_reset_test");

  // On ChromeOS, the sign-in profile is also initialized on startup.
  // In the PRE_ test, both the main profile and the sign-in profile default
  // partitions initialize. Since the experiment is active, both will reset
  // their caches and update their respective preferences.
  // In the non-PRE test, we only clear the preference for the main profile.
  // Therefore, upon restart, the main profile's default partition will see
  // the empty preference and reset its cache (logging `true`).
  // However, the sign-in profile will see its persisted preference from the
  // PRE_ run and will NOT reset its cache (logging `false`).
  // This results in an additional `false` sample on ChromeOS.
  const int count_for_chrome_os_signin_profile =
#if BUILDFLAG(IS_CHROMEOS)
      1;
#else
      0;
#endif  // BUILDFLAG(IS_CHROMEOS)

  // Default partition should have initialized on browser startup.
  // Wait for it to record its cache initialization status.
  // Since we cleared the pref in PRE_ test, it should have reset the cache.
  ASSERT_TRUE(base::test::RunUntil([&]() {
    content::FetchHistogramsFromChildProcesses();
    metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
    return histograms_.GetBucketCount("HttpCache.HardReset", true) +
               histograms_.GetBucketCount("HttpCache.HardReset", false) ==
           1 + count_for_chrome_os_signin_profile;
  }));

  EXPECT_EQ(histograms_.GetBucketCount("HttpCache.HardReset", true), 1);
  EXPECT_EQ(histograms_.GetBucketCount("HttpCache.HardReset", false),
            count_for_chrome_os_signin_profile);

  // Clear the pref again. If the buggy code runs, the non-default partition
  // initialization will see the empty pref, call GetHttpCacheBackendResetParam,
  // return true (since we are in experiment), and update the pref.
  // If the correct code runs, it will short-circuit and not touch the pref.
  PrefService* profile_prefs = browser()->GetProfile()->GetPrefs();
  const std::string pref_name = kHttpCacheFinchExperimentGroups;
  profile_prefs->ClearPref(pref_name);

  // Request the cached resource for non-default partition.
  content::StoragePartition* partition =
      browser()->GetProfile()->GetStoragePartition(
          content::StoragePartitionConfig::Create(browser()->GetProfile(),
                                                  "testdomain", "testpartition",
                                                  /*in_memory=*/false));
  FetchUrl(url, partition);

  // Wait for the non-default partition to record its cache initialization
  // status. It should NOT reset the cache.
  ASSERT_TRUE(base::test::RunUntil([&]() {
    content::FetchHistogramsFromChildProcesses();
    metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
    return histograms_.GetBucketCount("HttpCache.HardReset", true) +
               histograms_.GetBucketCount("HttpCache.HardReset", false) ==
           count_for_chrome_os_signin_profile + 2;
  }));

  // Total counts: 1 true (from default startup), 1 false (from non-default).
  EXPECT_EQ(histograms_.GetBucketCount("HttpCache.HardReset", true), 1);
  EXPECT_EQ(histograms_.GetBucketCount("HttpCache.HardReset", false),
            count_for_chrome_os_signin_profile + 1);

  // The pref should STILL be empty, because the non-default partition
  // should not have updated it.
  EXPECT_TRUE(profile_prefs->GetString(pref_name).empty());
}

class AmbientAuthenticationTestWithPolicy : public policy::PolicyTest {
 public:
  AmbientAuthenticationTestWithPolicy() {
    policy::PolicyTest::SetUpInProcessBrowserTestFixture();
  }

  void IsAmbientAuthAllowedForProfilesTest() {
    PrefService* service = g_browser_process->local_state();
    int policy_value =
        service->GetInteger(prefs::kAmbientAuthenticationInPrivateModesEnabled);

    Profile* regular_profile = browser()->GetProfile();
    Profile* incognito_profile =
        regular_profile->GetPrimaryOTRProfile(/*create_if_needed=*/true);
    Profile* non_primary_otr_profile = regular_profile->GetOffTheRecordProfile(
        Profile::OTRProfileID::CreateUniqueForTesting(),
        /*create_if_needed=*/true);

    EXPECT_TRUE(AmbientAuthenticationTestHelper::IsAmbientAuthAllowedForProfile(
        regular_profile));
    EXPECT_TRUE(AmbientAuthenticationTestHelper::IsAmbientAuthAllowedForProfile(
        non_primary_otr_profile));
    EXPECT_EQ(AmbientAuthenticationTestHelper::IsAmbientAuthAllowedForProfile(
                  incognito_profile),
              AmbientAuthenticationTestHelper::IsIncognitoAllowedInPolicy(
                  policy_value));
// ChromeOS guest sessions don't have the capability to
// do ambient authentications.
#if !BUILDFLAG(IS_CHROMEOS)
    EXPECT_EQ(
        AmbientAuthenticationTestHelper::IsAmbientAuthAllowedForProfile(
            CreateGuestBrowser()->GetProfile()),
        AmbientAuthenticationTestHelper::IsGuestAllowedInPolicy(policy_value));
#endif
  }

  void EnablePolicyWithValue(net::AmbientAuthAllowedProfileTypes value) {
    SetPolicy(&policies_,
              policy::key::kAmbientAuthenticationInPrivateModesEnabled,
              base::Value(static_cast<int>(value)));
    UpdateProviderPolicy(policies_);
  }

 private:
  policy::PolicyMap policies_;
};

IN_PROC_BROWSER_TEST_F(AmbientAuthenticationTestWithPolicy, RegularOnly) {
  EnablePolicyWithValue(net::AmbientAuthAllowedProfileTypes::kRegularOnly);
  IsAmbientAuthAllowedForProfilesTest();
}

IN_PROC_BROWSER_TEST_F(AmbientAuthenticationTestWithPolicy,
                       IncognitoAndRegular) {
  EnablePolicyWithValue(
      net::AmbientAuthAllowedProfileTypes::kIncognitoAndRegular);
  IsAmbientAuthAllowedForProfilesTest();
}

IN_PROC_BROWSER_TEST_F(AmbientAuthenticationTestWithPolicy, GuestAndRegular) {
  EnablePolicyWithValue(net::AmbientAuthAllowedProfileTypes::kGuestAndRegular);
  IsAmbientAuthAllowedForProfilesTest();
}

IN_PROC_BROWSER_TEST_F(AmbientAuthenticationTestWithPolicy, All) {
  EnablePolicyWithValue(net::AmbientAuthAllowedProfileTypes::kAll);
  IsAmbientAuthAllowedForProfilesTest();
}

// Test subclass that adds switches::kDiskCacheDir and switches::kDiskCacheSize
// to the command line, to make sure they're respected.
class ProfileNetworkContextServiceDiskCacheBrowsertest
    : public ProfileNetworkContextServiceBrowsertest {
 public:
  const int64_t kCacheSize = 7;

  ProfileNetworkContextServiceDiskCacheBrowsertest() {
    EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
  }

  ~ProfileNetworkContextServiceDiskCacheBrowsertest() override = default;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    command_line->AppendSwitchPath(switches::kDiskCacheDir,
                                   temp_dir_.GetPath());
    command_line->AppendSwitchASCII(switches::kDiskCacheSize,
                                    base::NumberToString(kCacheSize));
  }

  const base::FilePath& TempPath() { return temp_dir_.GetPath(); }

 private:
  base::ScopedTempDir temp_dir_;
};

// Makes sure switches::kDiskCacheDir is hooked up correctly.
IN_PROC_BROWSER_TEST_F(ProfileNetworkContextServiceDiskCacheBrowsertest,
                       DiskCacheLocation) {
  // Make sure command line switch is hooked up to the pref.
  ASSERT_EQ(TempPath(), g_browser_process->local_state()->GetFilePath(
                            prefs::kDiskCacheDir));

  // Run a request that caches the response, to give the network service time to
  // create a cache directory.
  std::unique_ptr<network::ResourceRequest> request =
      std::make_unique<network::ResourceRequest>();
  request->url = embedded_test_server()->GetURL("/cachetime");
  request->credentials_mode = network::mojom::CredentialsMode::kOmit;
  content::SimpleURLLoaderTestHelper simple_loader_helper;
  std::unique_ptr<network::SimpleURLLoader> simple_loader =
      network::SimpleURLLoader::Create(std::move(request),
                                       TRAFFIC_ANNOTATION_FOR_TESTS);

  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
      loader_factory(), simple_loader_helper.GetCallback());
  simple_loader_helper.WaitForCallback();
  ASSERT_TRUE(simple_loader_helper.response_body());

  // Cache directory should now exist.
  base::FilePath expected_cache_path =
      TempPath()
          .Append(browser()->GetProfile()->GetBaseName())
          .Append(chrome::kCacheDirname);
  base::ScopedAllowBlockingForTesting allow_blocking;
  EXPECT_TRUE(base::PathExists(expected_cache_path));
}

// Makes sure switches::kDiskCacheSize is hooked up correctly.
IN_PROC_BROWSER_TEST_F(ProfileNetworkContextServiceDiskCacheBrowsertest,
                       DiskCacheSize) {
  // Make sure command line switch is hooked up to the pref.
  ASSERT_EQ(kCacheSize, g_browser_process->local_state()->GetInteger(
                            prefs::kDiskCacheSize));

  // We don't have a great way of directly checking that the disk cache has the
  // correct max size, but we can make sure that we set up our network context
  // params correctly.
  ProfileNetworkContextService* profile_network_context_service =
      ProfileNetworkContextServiceFactory::GetForContext(
          browser()->GetProfile());
  base::FilePath empty_relative_partition_path;
  network::mojom::NetworkContextParams network_context_params;
  cert_verifier::mojom::CertVerifierCreationParams
      cert_verifier_creation_params;
  profile_network_context_service->ConfigureNetworkContextParams(
      /*in_memory=*/false, empty_relative_partition_path,
      &network_context_params, &cert_verifier_creation_params);
  EXPECT_EQ(kCacheSize, network_context_params.http_cache_max_size);
}

class ProfileNetworkContextTrustTokensBrowsertest
    : public ProfileNetworkContextServiceBrowsertest {
 public:
  ProfileNetworkContextTrustTokensBrowsertest() = default;
  ~ProfileNetworkContextTrustTokensBrowsertest() override = default;

  void SetUpOnMainThread() override {
    host_resolver()->AddRule("*", "127.0.0.1");
    https_server_ = std::make_unique<net::EmbeddedTestServer>(
        net::test_server::EmbeddedTestServer::TYPE_HTTPS);
    https_server_->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
    https_server_->AddDefaultHandlers(
        base::FilePath(FILE_PATH_LITERAL("content/test/data")));
    network::test::RegisterTrustTokenTestHandlers(https_server_.get(),
                                                  &request_handler_);
    ASSERT_TRUE(https_server_->Start());
  }

  void ProvideRequestHandlerKeyCommitmentsToNetworkService(
      std::string_view host) {
    base::flat_map<url::Origin, std::string_view> origins_and_commitments;
    std::string key_commitments = request_handler_.GetKeyCommitmentRecord();

    GURL::Replacements replacements;
    replacements.SetHostStr(host);
    origins_and_commitments.insert_or_assign(
        url::Origin::Create(
            https_server_->base_url().ReplaceComponents(replacements)),
        key_commitments);

    base::RunLoop run_loop;
    content::GetNetworkService()->SetTrustTokenKeyCommitments(
        network::WrapKeyCommitmentsForIssuers(
            std::move(origins_and_commitments)),
        run_loop.QuitClosure());
    run_loop.Run();
  }

  content::WebContents* GetActiveWebContents() {
    return browser()->tab_strip_model()->GetActiveWebContents();
  }

  void Flush() {
    browser()
        ->GetProfile()
        ->GetDefaultStoragePartition()
        ->FlushNetworkInterfaceForTesting();
  }

 protected:
  net::EmbeddedTestServer* https_test_server() { return https_server_.get(); }

 private:
  std::unique_ptr<net::EmbeddedTestServer> https_server_;
  network::test::TrustTokenRequestHandler request_handler_;
};

IN_PROC_BROWSER_TEST_F(ProfileNetworkContextTrustTokensBrowsertest,
                       TrustTokenBlocked) {
  ProvideRequestHandlerKeyCommitmentsToNetworkService("a.test");
  auto* host_content_settings_map =
      HostContentSettingsMapFactory::GetForProfile(browser()->GetProfile());
  Flush();

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), https_test_server()->GetURL("a.test", "/title1.html")));

  std::string issuance_origin =
      url::Origin::Create(https_test_server()->GetURL("a.test", "/"))
          .Serialize();

  std::string command = content::JsReplace(R"(
  (async () => {
    try {
      await fetch("/issue", {privateToken: {version: 1,
                                          operation: 'token-request'}});
      return await document.hasPrivateToken($1);
    } catch {
      return false;
    }
  })();)",
                                           issuance_origin);

  EXPECT_EQ(true, EvalJs(GetActiveWebContents(), command));

  host_content_settings_map->SetDefaultContentSetting(
      ContentSettingsType::ANTI_ABUSE, CONTENT_SETTING_BLOCK);
  Flush();

  chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
  EXPECT_TRUE(content::WaitForLoadStop(GetActiveWebContents()));
  EXPECT_EQ(false, EvalJs(GetActiveWebContents(), command));

  host_content_settings_map->SetDefaultContentSetting(
      ContentSettingsType::ANTI_ABUSE, CONTENT_SETTING_ALLOW);
  Flush();

  chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
  EXPECT_TRUE(content::WaitForLoadStop(GetActiveWebContents()));
  EXPECT_EQ(true, EvalJs(GetActiveWebContents(), command));

  // Trust Tokens are blocked when the top level origin cookie content setting
  // is blocked
  GURL top_level_origin = https_test_server()->GetURL("a.test", "/");
  host_content_settings_map->SetContentSettingDefaultScope(
      top_level_origin, top_level_origin, ContentSettingsType::COOKIES,
      CONTENT_SETTING_BLOCK);

  chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
  EXPECT_TRUE(content::WaitForLoadStop(GetActiveWebContents()));
  EXPECT_EQ(false, EvalJs(GetActiveWebContents(), command));
}

// Base class for testing Cache Encryption with policy.
// Subclasses must implement GetCacheEncryptionPolicyValue().
class CacheEncryptionPolicyTestBase : public InProcessBrowserTest {
 public:
  CacheEncryptionPolicyTestBase() {
#if BUILDFLAG(ENTERPRISE_CACHE_ENCRYPTION)
    scoped_feature_list_.InitAndEnableFeature(
        enterprise_encryption::kEnableCacheEncryption);
#endif
  }

  // Determine whether the policy should be enabled or disabled for this
  // fixture.
  virtual bool GetCacheEncryptionPolicyValue() const = 0;

  void SetUp() override {
    // Configure the mock policy provider to report that it's initialized.
    EXPECT_CALL(provider_, IsInitializationComplete(testing::_))
        .WillRepeatedly(testing::Return(true));
    EXPECT_CALL(provider_, IsFirstPolicyLoadComplete(testing::_))
        .WillRepeatedly(testing::Return(true));

    // Set the mock provider for the Chrome policy connector.
    // This MUST be done before InProcessBrowserTest::SetUp() initializes the
    // browser.
    policy::ChromeBrowserPolicyConnector::SetPolicyProviderForTesting(
        &provider_);

    // Set the policy value *before* the browser fully starts, based on
    // subclass.
#if BUILDFLAG(ENTERPRISE_CACHE_ENCRYPTION)
    policy::PolicyMap policies;
    policies.Set(policy::key::kCacheEncryptionEnabled,
                 policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
                 policy::POLICY_SOURCE_CLOUD,  // Or other appropriate source
                 base::Value(GetCacheEncryptionPolicyValue()), nullptr);
    provider_.UpdateChromePolicy(policies);
#endif

    InProcessBrowserTest::SetUp();
  }

  void TearDown() override {
    // Clean up the testing provider.
    policy::ChromeBrowserPolicyConnector::SetPolicyProviderForTesting(nullptr);
    InProcessBrowserTest::TearDown();
  }

  void SetUpOnMainThread() override {
    InProcessBrowserTest::SetUpOnMainThread();
    ASSERT_TRUE(embedded_test_server()->Start());
  }

  // Helper function to call ComputeHttpCacheSize synchronously
  int64_t ComputeHttpCacheSizeSync() {
    network::mojom::NetworkContext* network_context =
        browser()
            ->GetProfile()
            ->GetDefaultStoragePartition()
            ->GetNetworkContext();

    base::RunLoop run_loop;
    int64_t result_size_or_error =
        net::ERR_UNEXPECTED;  // Initialize with an error

    network_context->ComputeHttpCacheSize(
        base::Time(), base::Time::Max(),
        base::BindLambdaForTesting(
            [&](bool is_upper_bound, int64_t size_or_error) {
              result_size_or_error = size_or_error;
              run_loop.Quit();
            }));
    run_loop.Run();
    return result_size_or_error;
  }

  void VerifyCacheBackendInitialized() {
    // Navigate to a page to ensure the network stack is initialized.
    GURL url = embedded_test_server()->GetURL("/empty.html");
    ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
    content::RunAllTasksUntilIdle();

    browser()
        ->GetProfile()
        ->GetDefaultStoragePartition()
        ->FlushNetworkInterfaceForTesting();
    content::RunAllTasksUntilIdle();

    int64_t cache_size_or_error = ComputeHttpCacheSizeSync();
    LOG(INFO) << "ComputeHttpCacheSize result: " << cache_size_or_error;

    EXPECT_GE(cache_size_or_error, 0)
        << "Failed to compute cache size, backend might not be initialized. "
           "Result: "
        << net::ErrorToString(static_cast<int>(cache_size_or_error));

    histogram_tester_.ExpectBucketCount(
        "Enterprise.CacheEncryptionPolicyEnabled",
        !GetCacheEncryptionPolicyValue(), 0);
    EXPECT_GE(histogram_tester_.GetBucketCount(
                  "Enterprise.CacheEncryptionPolicyEnabled",
                  GetCacheEncryptionPolicyValue()),
              1);
  }

 protected:
  testing::NiceMock<policy::MockConfigurationPolicyProvider> provider_;
  base::HistogramTester histogram_tester_;
#if BUILDFLAG(ENTERPRISE_CACHE_ENCRYPTION)
  base::test::ScopedFeatureList scoped_feature_list_;
#endif
};

// Subclass where CacheEncryptionEnabled policy is TRUE.
class CacheEncryptionEnabledByPolicyTest
    : public CacheEncryptionPolicyTestBase {
 public:
  bool GetCacheEncryptionPolicyValue() const override { return true; }
};

// Subclass where CacheEncryptionEnabled policy is FALSE.
class CacheEncryptionDisabledByPolicyTest
    : public CacheEncryptionPolicyTestBase {
 public:
  bool GetCacheEncryptionPolicyValue() const override { return false; }
};

IN_PROC_BROWSER_TEST_F(CacheEncryptionEnabledByPolicyTest,
                       BackendInitializesWithPolicyEnabled) {
  // This test verifies that for the initial, default profile, the cache is
  // initialized correctly on startup.
  VerifyCacheBackendInitialized();
  PrefService* prefs = browser()->GetProfile()->GetPrefs();
  ASSERT_TRUE(prefs);
  EXPECT_FALSE(
      prefs->GetString(enterprise_connectors::kEncryptedCachePrimaryKey)
          .empty());
  EXPECT_TRUE(
      prefs->GetBoolean(enterprise_connectors::kCacheEncryptionEnabledPref));
  EXPECT_TRUE(prefs->IsManagedPreference(
      enterprise_connectors::kCacheEncryptionEnabledPref));
}

#if !BUILDFLAG(IS_CHROMEOS)
IN_PROC_BROWSER_TEST_F(CacheEncryptionEnabledByPolicyTest,
                       InitializesAndSetsKeyOnFirstUse) {
  // This test creates a new profile to ensure that the cache initialization
  // happens within the test body, which is required for code coverage.
  ProfileManager* profile_manager = g_browser_process->profile_manager();
  base::FilePath new_profile_path = profile_manager->user_data_dir().Append(
      FILE_PATH_LITERAL("NewTestProfile"));

  base::HistogramTester profile_histogram_tester;

  // Create the profile.
  Profile& new_profile =
      profiles::testing::CreateProfileSync(profile_manager, new_profile_path);

  PrefService* prefs = new_profile.GetPrefs();
  ASSERT_TRUE(prefs);
  // The key should not exist before the cache is initialized.
  EXPECT_TRUE(prefs->GetString(enterprise_connectors::kEncryptedCachePrimaryKey)
                  .empty());

  // Create a browser for the new profile and navigate to trigger cache init.
  BrowserWindowInterface* new_browser = CreateBrowser(&new_profile);
  GURL url = embedded_test_server()->GetURL("/empty.html");
  ASSERT_TRUE(ui_test_utils::NavigateToURL(new_browser, url));
  content::RunAllTasksUntilIdle();
  new_profile.GetDefaultStoragePartition()->FlushNetworkInterfaceForTesting();
  content::RunAllTasksUntilIdle();

  // After initialization, the key should have been created and stored.
  EXPECT_FALSE(
      prefs->GetString(enterprise_connectors::kEncryptedCachePrimaryKey)
          .empty());

  profile_histogram_tester.ExpectBucketCount(
      "Enterprise.CacheEncryptionPolicyEnabled",
      !GetCacheEncryptionPolicyValue(), 0);
  EXPECT_GE(profile_histogram_tester.GetBucketCount(
                "Enterprise.CacheEncryptionPolicyEnabled",
                GetCacheEncryptionPolicyValue()),
            1);
}
#endif  // !BUILDFLAG(IS_CHROMEOS)

IN_PROC_BROWSER_TEST_F(CacheEncryptionDisabledByPolicyTest,
                       BackendInitializesWithPolicyDisabled) {
  PrefService* prefs = browser()->GetProfile()->GetPrefs();
  ASSERT_TRUE(prefs);
  // The key pref should not exist before the cache is initialized.
  EXPECT_FALSE(
      prefs->HasPrefPath(enterprise_connectors::kEncryptedCachePrimaryKey));

  VerifyCacheBackendInitialized();

  // The key pref should still not exist if encryption is disabled.
  EXPECT_FALSE(
      prefs->HasPrefPath(enterprise_connectors::kEncryptedCachePrimaryKey));

  EXPECT_FALSE(
      prefs->GetBoolean(enterprise_connectors::kCacheEncryptionEnabledPref));
  EXPECT_TRUE(prefs->IsManagedPreference(
      enterprise_connectors::kCacheEncryptionEnabledPref));
}

IN_PROC_BROWSER_TEST_F(CacheEncryptionDisabledByPolicyTest,
                       KeyPrefIsNotStoredWhenPolicyIsDisabled) {
  // The pref should not be stored at all, if the policy is disabled.

  PrefService* prefs = browser()->GetProfile()->GetPrefs();
  ASSERT_TRUE(prefs);
  EXPECT_FALSE(
      prefs->GetBoolean(enterprise_connectors::kCacheEncryptionEnabledPref));
  EXPECT_TRUE((prefs->IsManagedPreference(
      enterprise_connectors::kCacheEncryptionEnabledPref)));
  EXPECT_FALSE(
      prefs->HasPrefPath(enterprise_connectors::kEncryptedCachePrimaryKey));
}
