chromium/chromeos/ash/components/network/proxy/ui_proxy_config_service_unittest.cc

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

#include "chromeos/ash/components/network/proxy/ui_proxy_config_service.h"

#include <memory>
#include <utility>

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/strings/string_util.h"
#include "base/test/gtest_tags.h"
#include "base/test/task_environment.h"
#include "base/test/values_test_util.h"
#include "base/values.h"
#include "chromeos/ash/components/dbus/shill/shill_manager_client.h"
#include "chromeos/ash/components/network/network_handler.h"
#include "chromeos/ash/components/network/network_handler_test_helper.h"
#include "chromeos/ash/components/network/network_state.h"
#include "chromeos/components/onc/onc_utils.h"
#include "components/onc/onc_pref_names.h"
#include "components/prefs/testing_pref_service.h"
#include "components/proxy_config/pref_proxy_config_tracker_impl.h"
#include "components/proxy_config/proxy_config_dictionary.h"
#include "components/proxy_config/proxy_config_pref_names.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash {

namespace {

constexpr char kTestUserWifiGuid[] = "wifi1";
constexpr char kTestUserWifiConfig[] = R"({
    "GUID": "wifi1", "Type": "wifi", "Profile": "user_profile_path",
    "ProxySettings": {"Type": "WPAD"}})";

constexpr char kTestSharedWifiGuid[] = "wifi2";
constexpr char kTestSharedWifiConfig[] = R"({
    "GUID": "wifi2", "Type": "wifi", "Profile": "shared_profile_path"})";

constexpr char kTestUnconfiguredWifiGuid[] = "wifi2";
constexpr char kTestUnconfiguredWifiConfig[] = R"({
    "GUID": "wifi2", "Type": "wifi"})";

constexpr char kAugmentedOncValueTemplate[] =
    R"({"Active": $2, "Effective": "$1", "$1": $2, "UserEditable": $3})";

constexpr char kAugmentedOncValueWithUserSettingTemplate[] =
    R"({"Active": $2, "Effective": "$1", "$1": $2, "UserSetting": $3,
      "UserEditable": $4})";

std::unique_ptr<NetworkState> GetNetworkState(const std::string& guid) {
  auto network_state = std::make_unique<NetworkState>("path");
  network_state->PropertyChanged("Profile", base::Value("profile"));
  network_state->PropertyChanged("GUID", base::Value(guid));
  return network_state;
}

std::string UserSettingOncValue(const std::string& value) {
  return base::ReplaceStringPlaceholders(
      kAugmentedOncValueTemplate, {"UserSetting", value, "true"}, nullptr);
}

std::string UserPolicyOncValue(const std::string& value) {
  return base::ReplaceStringPlaceholders(
      kAugmentedOncValueTemplate, {"UserPolicy", value, "false"}, nullptr);
}

std::string DevicePolicyOncValue(const std::string& value) {
  return base::ReplaceStringPlaceholders(
      kAugmentedOncValueTemplate, {"DevicePolicy", value, "false"}, nullptr);
}

std::string ExtensionControlledOncValue(const std::string& value) {
  return base::ReplaceStringPlaceholders(
      R"({"Active": $1,"Effective": "ActiveExtension",
          "UserEditable": false})",
      {value}, nullptr);
}

std::string UserPolicyAndUserSettingOncValue(const std::string& policy,
                                             const std::string& user_setting) {
  return base::ReplaceStringPlaceholders(
      kAugmentedOncValueWithUserSettingTemplate,
      {
          "UserPolicy",
          policy,
          user_setting,
          "false",
      },
      nullptr);
}

std::string ExtensionControlledAndUserSettingOncValue(
    const std::string& extension,
    const std::string& user_setting) {
  return base::ReplaceStringPlaceholders(
      R"({"Active": $1,"Effective": "ActiveExtension", "UserSetting": $2,
          "UserEditable": false})",
      {extension, user_setting}, nullptr);
}

}  // namespace

class UIProxyConfigServiceTest : public testing::Test {
 public:
  UIProxyConfigServiceTest()
      : task_environment_(base::test::TaskEnvironment::MainThreadType::UI) {
    PrefProxyConfigTrackerImpl::RegisterProfilePrefs(user_prefs_.registry());
    PrefProxyConfigTrackerImpl::RegisterPrefs(local_state_.registry());
    ::onc::RegisterProfilePrefs(user_prefs_.registry());
    ::onc::RegisterPrefs(local_state_.registry());
  }

  void SetUp() override {
    base::AddTagToTestResult("feature_id",
                             "screenplay-bb5f01d1-9cf8-4aba-9aa6-925608747b02");
    ConfigureService(kTestUserWifiConfig);
    ConfigureService(kTestSharedWifiConfig);
    ConfigureService(kTestUnconfiguredWifiConfig);
  }

  ~UIProxyConfigServiceTest() override = default;

  void ConfigureService(const std::string& shill_json_string) {
    std::optional<base::Value::Dict> shill_json_dict =
        chromeos::onc::ReadDictionaryFromJson(shill_json_string);
    CHECK(shill_json_dict.has_value());
    ShillManagerClient::Get()->ConfigureService(
        *shill_json_dict, base::DoNothing(),
        base::BindOnce([](const std::string& name, const std::string& msg) {}));
    base::RunLoop().RunUntilIdle();
  }

  std::unique_ptr<UIProxyConfigService> CreateServiceOffLocalState() {
    return std::make_unique<UIProxyConfigService>(
        nullptr, &local_state_, NetworkHandler::Get()->network_state_handler(),
        NetworkHandler::Get()->network_profile_handler());
  }

  std::unique_ptr<UIProxyConfigService> CreateServiceForUser() {
    return std::make_unique<UIProxyConfigService>(
        &user_prefs_, &local_state_,
        NetworkHandler::Get()->network_state_handler(),
        NetworkHandler::Get()->network_profile_handler());
  }

 protected:
  sync_preferences::TestingPrefServiceSyncable user_prefs_;
  TestingPrefServiceSimple local_state_;

 private:
  base::test::TaskEnvironment task_environment_;
  NetworkHandlerTestHelper network_handler_test_helper_;
};

TEST_F(UIProxyConfigServiceTest, UnknownNetwork) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  base::Value::Dict config;
  EXPECT_FALSE(service->MergeEnforcedProxyConfig("unkown_network", &config));
  EXPECT_EQ(base::Value::Dict(), config);
}

TEST_F(UIProxyConfigServiceTest, UserConfigOnly) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  base::Value::Dict config;
  EXPECT_FALSE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));
  EXPECT_EQ(base::Value::Dict(), config);
}

TEST_F(UIProxyConfigServiceTest, LocalStatePrefIgnoredForUserService) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  local_state_.SetManagedPref(
      proxy_config::prefs::kProxy,
      ProxyConfigDictionary::CreatePacScript("http://pac/script.pac", true));

  base::Value::Dict config;
  EXPECT_FALSE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));
  EXPECT_EQ(base::Value::Dict(), config);
}

TEST_F(UIProxyConfigServiceTest, LocalStatePolicyPrefForDeviceService) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceOffLocalState();

  local_state_.SetManagedPref(
      proxy_config::prefs::kProxy,
      ProxyConfigDictionary::CreatePacScript("http://pac/script.pac", true));

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1, "PAC": $2})",
      {DevicePolicyOncValue(R"("PAC")"),
       DevicePolicyOncValue(R"("http://pac/script.pac")")},
      nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);
}

TEST_F(UIProxyConfigServiceTest,
       UserPolicyProxyNotMergedForUnconfiguredNetork) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceOffLocalState();

  user_prefs_.SetManagedPref(
      proxy_config::prefs::kProxy,
      ProxyConfigDictionary::CreatePacScript("http://pac/script.pac", true));

  base::Value::Dict config;
  EXPECT_FALSE(
      service->MergeEnforcedProxyConfig(kTestUnconfiguredWifiGuid, &config));
  EXPECT_EQ(base::Value::Dict(), config);
}

TEST_F(UIProxyConfigServiceTest, ExtensionProxyNotMergedForUnconfiguredNetork) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceOffLocalState();

  user_prefs_.SetExtensionPref(
      proxy_config::prefs::kProxy,
      ProxyConfigDictionary::CreatePacScript("http://pac/script.pac", true));

  base::Value::Dict config;
  EXPECT_FALSE(
      service->MergeEnforcedProxyConfig(kTestUnconfiguredWifiGuid, &config));
  EXPECT_EQ(base::Value::Dict(), config);
}

TEST_F(UIProxyConfigServiceTest, OncPolicyNotMergedForUnfongiuredNetork) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceOffLocalState();

  const std::string user_onc_config = base::ReplaceStringPlaceholders(
      R"([{"GUID": "$1", "Type": "WiFi", "ProxySettings": {"Type": "WPAD"}}])",
      {kTestUnconfiguredWifiGuid}, nullptr);
  user_prefs_.SetManagedPref(::onc::prefs::kOpenNetworkConfiguration,
                             base::test::ParseJsonList(user_onc_config));

  base::Value::Dict config;
  EXPECT_FALSE(
      service->MergeEnforcedProxyConfig(kTestUnconfiguredWifiGuid, &config));
  EXPECT_EQ(base::Value::Dict(), config);
}

TEST_F(UIProxyConfigServiceTest, PacPolicyPref) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  user_prefs_.SetManagedPref(
      proxy_config::prefs::kProxy,
      ProxyConfigDictionary::CreatePacScript("http://pac/script.pac", true));

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1, "PAC": $2})",
      {UserPolicyOncValue(R"("PAC")"),
       UserPolicyOncValue(R"("http://pac/script.pac")")},
      nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);
}

TEST_F(UIProxyConfigServiceTest, AutoDetectPolicyPref) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  user_prefs_.SetManagedPref(proxy_config::prefs::kProxy,
                             ProxyConfigDictionary::CreateAutoDetect());

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1})", {UserPolicyOncValue(R"("WPAD")")}, nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);
}

TEST_F(UIProxyConfigServiceTest, DirectPolicyPref) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  user_prefs_.SetManagedPref(proxy_config::prefs::kProxy,
                             ProxyConfigDictionary::CreateDirect());

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1})", {UserPolicyOncValue(R"("Direct")")}, nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);
}

TEST_F(UIProxyConfigServiceTest, ManualPolicyPref) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  user_prefs_.SetManagedPref(
      proxy_config::prefs::kProxy,
      ProxyConfigDictionary::CreateFixedServers(
          "http=proxy1:81;https=proxy2:81;socks=proxy3:81", "localhost"));

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1,
          "Manual": {
            "HTTPProxy": {"Host": $2, "Port": $5},
            "SecureHTTPProxy": {"Host": $3, "Port": $5},
            "SOCKS": {"Host": $4, "Port": $5}
          },
          "ExcludeDomains": $6
          })",
      {UserPolicyOncValue(R"("Manual")"), UserPolicyOncValue(R"("proxy1")"),
       UserPolicyOncValue(R"("proxy2")"), UserPolicyOncValue(R"("proxy3")"),
       UserPolicyOncValue("81"), UserPolicyOncValue(R"(["localhost"])")},
      nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);
}

TEST_F(UIProxyConfigServiceTest, PartialManualPolicyPref) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  user_prefs_.SetManagedPref(
      proxy_config::prefs::kProxy,
      ProxyConfigDictionary::CreateFixedServers("http=proxy1:81;", ""));

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1,
          "Manual": {
            "HTTPProxy": {"Host": $2, "Port": $3},
            "SecureHTTPProxy": {"Host": $4, "Port": $5},
            "SOCKS": {"Host": $4, "Port": $5}
          },
          "ExcludeDomains": $6
          })",
      {UserPolicyOncValue(R"("Manual")"), UserPolicyOncValue(R"("proxy1")"),
       UserPolicyOncValue("81"), UserPolicyOncValue(R"("")"),
       UserPolicyOncValue("0"), UserPolicyOncValue("[]")},
      nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);
}

TEST_F(UIProxyConfigServiceTest, ManualPolicyPrefWithPacPreset) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  user_prefs_.SetManagedPref(
      proxy_config::prefs::kProxy,
      ProxyConfigDictionary::CreateFixedServers(
          "http=proxy:80;https=proxy:80;socks=proxy:80", "localhost"));

  std::string config_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1, "PAC": $2})",
      {UserSettingOncValue(R"("PAC")"),
       UserSettingOncValue(R"("http://pac/test.script.pac")")},
      nullptr);
  base::Value::Dict config = base::test::ParseJsonDict(config_json);

  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1,
          "Manual": {
            "HTTPProxy": {"Host": $2, "Port": $3},
            "SecureHTTPProxy": {"Host": $2, "Port": $3},
            "SOCKS": {"Host": $2, "Port": $3}
          },
          "PAC": $4,
          "ExcludeDomains": $5
         })",
      {UserPolicyAndUserSettingOncValue(R"("Manual")", R"("PAC")"),
       UserPolicyOncValue(R"("proxy")"), UserPolicyOncValue("80"),
       UserSettingOncValue(R"("http://pac/test.script.pac")"),
       UserPolicyOncValue(R"(["localhost"])")},
      nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);
}

TEST_F(UIProxyConfigServiceTest, PacExtensionPref) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  user_prefs_.SetExtensionPref(
      proxy_config::prefs::kProxy,
      ProxyConfigDictionary::CreatePacScript("http://pac/script.pac", true));

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1, "PAC": $2})",
      {ExtensionControlledOncValue(R"("PAC")"),
       ExtensionControlledOncValue(R"("http://pac/script.pac")")},
      nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);
}

TEST_F(UIProxyConfigServiceTest, AutoDetectExtensionPref) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  user_prefs_.SetExtensionPref(proxy_config::prefs::kProxy,
                               ProxyConfigDictionary::CreateAutoDetect());

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1})", {ExtensionControlledOncValue(R"("WPAD")")}, nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);
}

TEST_F(UIProxyConfigServiceTest, DirectExtensionPref) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  user_prefs_.SetExtensionPref(proxy_config::prefs::kProxy,
                               ProxyConfigDictionary::CreateDirect());

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1})", {ExtensionControlledOncValue(R"("Direct")")}, nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);
}

TEST_F(UIProxyConfigServiceTest, ManualExtensionPref) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  user_prefs_.SetExtensionPref(
      proxy_config::prefs::kProxy,
      ProxyConfigDictionary::CreateFixedServers(
          "http=proxy1:81;https=proxy2:82;socks=proxy3:81", "localhost"));

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1,
          "Manual": {
            "HTTPProxy": {"Host": $2, "Port": $3},
            "SecureHTTPProxy": {"Host": $4, "Port": $5},
            "SOCKS": {"Host": $6, "Port": $3}
          },
          "ExcludeDomains": $7
          })",
      {ExtensionControlledOncValue(R"("Manual")"),
       ExtensionControlledOncValue(R"("proxy1")"),
       ExtensionControlledOncValue("81"),
       ExtensionControlledOncValue(R"("proxy2")"),
       ExtensionControlledOncValue("82"),
       ExtensionControlledOncValue(R"("proxy3")"),
       ExtensionControlledOncValue(R"(["localhost"])")},
      nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);
}

TEST_F(UIProxyConfigServiceTest, ExtensionProxyOverridesDefault) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  user_prefs_.SetExtensionPref(proxy_config::prefs::kProxy,
                               ProxyConfigDictionary::CreatePacScript(
                                   "http://extension/script.pac", true));

  std::string config_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1, "PAC": $2})",
      {UserSettingOncValue(R"("PAC")"),
       UserSettingOncValue(R"("http://default/script.pac")")},
      nullptr);
  base::Value::Dict config = base::test::ParseJsonDict(config_json);

  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1, "PAC": $2})",
      {ExtensionControlledAndUserSettingOncValue(R"("PAC")", R"("PAC")"),
       ExtensionControlledAndUserSettingOncValue(
           R"("http://extension/script.pac")",
           R"("http://default/script.pac")")},
      nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);
}

TEST_F(UIProxyConfigServiceTest, PolicyPrefOverridesExtensionPref) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  user_prefs_.SetManagedPref(proxy_config::prefs::kProxy,
                             ProxyConfigDictionary::CreatePacScript(
                                 "http://managed/script.pac", true));

  user_prefs_.SetExtensionPref(proxy_config::prefs::kProxy,
                               ProxyConfigDictionary::CreateAutoDetect());

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1, "PAC": $2})",
      {UserPolicyOncValue(R"("PAC")"),
       UserPolicyOncValue(R"("http://managed/script.pac")")},
      nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);

  auto network_state = GetNetworkState(kTestUserWifiGuid);
  EXPECT_EQ(service->ProxyModeForNetwork(network_state.get()),
            ProxyPrefs::MODE_PAC_SCRIPT);
}

TEST_F(UIProxyConfigServiceTest, PolicyPrefForSharedNetwork) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  user_prefs_.SetManagedPref(proxy_config::prefs::kProxy,
                             ProxyConfigDictionary::CreateAutoDetect());

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestSharedWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1})", {UserPolicyOncValue(R"("WPAD")")}, nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);

  auto network_state = GetNetworkState(kTestSharedWifiGuid);
  EXPECT_EQ(service->ProxyModeForNetwork(network_state.get()),
            ProxyPrefs::MODE_AUTO_DETECT);
}

TEST_F(UIProxyConfigServiceTest, ExtensionPrefForSharedNetwork) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  user_prefs_.SetExtensionPref(proxy_config::prefs::kProxy,
                               ProxyConfigDictionary::CreateAutoDetect());

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestSharedWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1})", {ExtensionControlledOncValue(R"("WPAD")")}, nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);

  auto network_state = GetNetworkState(kTestSharedWifiGuid);
  EXPECT_EQ(service->ProxyModeForNetwork(network_state.get()),
            ProxyPrefs::MODE_AUTO_DETECT);
}

TEST_F(UIProxyConfigServiceTest, PacOncUserPolicy) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  const std::string onc_config = base::ReplaceStringPlaceholders(
      R"([{"GUID": "$1", "Type": "WiFi", "ProxySettings": {"Type": "PAC",
           "PAC": "http://onc/script.pac"}}])",
      {kTestUserWifiGuid}, nullptr);
  user_prefs_.SetManagedPref(::onc::prefs::kOpenNetworkConfiguration,
                             base::test::ParseJsonList(onc_config));

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1, "PAC": $2})",
      {UserPolicyOncValue(R"("PAC")"),
       UserPolicyOncValue(R"("http://onc/script.pac")")},
      nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);

  auto network_state = GetNetworkState(kTestUserWifiGuid);
  EXPECT_EQ(service->ProxyModeForNetwork(network_state.get()),
            ProxyPrefs::MODE_PAC_SCRIPT);
}

TEST_F(UIProxyConfigServiceTest, AutoDetectOncUserPolicy) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  const std::string onc_config = base::ReplaceStringPlaceholders(
      R"([{"GUID": "$1", "Type": "WiFi", "ProxySettings": {"Type": "WPAD"}}])",
      {kTestUserWifiGuid}, nullptr);
  user_prefs_.SetManagedPref(::onc::prefs::kOpenNetworkConfiguration,
                             base::test::ParseJsonList(onc_config));

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1})", {UserPolicyOncValue(R"("WPAD")")}, nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);

  auto network_state = GetNetworkState(kTestUserWifiGuid);
  EXPECT_EQ(service->ProxyModeForNetwork(network_state.get()),
            ProxyPrefs::MODE_AUTO_DETECT);
}

// Tests that ONC policy configured networks without proxy settings force Direct
// connection.
TEST_F(UIProxyConfigServiceTest, OncUserPolicyWithoutProxySettings) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  const std::string onc_config = base::ReplaceStringPlaceholders(
      R"([{"GUID": "$1", "Type": "WiFi", "AutoConnect": false}])",
      {kTestUserWifiGuid}, nullptr);
  user_prefs_.SetManagedPref(::onc::prefs::kOpenNetworkConfiguration,
                             base::test::ParseJsonList(onc_config));

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1})", {UserPolicyOncValue(R"("Direct")")}, nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);

  auto network_state = GetNetworkState(kTestUserWifiGuid);
  EXPECT_EQ(service->ProxyModeForNetwork(network_state.get()),
            ProxyPrefs::MODE_DIRECT);
}

TEST_F(UIProxyConfigServiceTest, DirectOncUserPolicy) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  const std::string onc_config = base::ReplaceStringPlaceholders(
      R"([{"GUID": "$1", "Type": "WiFi",
           "ProxySettings": {"Type": "Direct"}}])",
      {kTestUserWifiGuid}, nullptr);
  user_prefs_.SetManagedPref(::onc::prefs::kOpenNetworkConfiguration,
                             base::test::ParseJsonList(onc_config));

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1})", {UserPolicyOncValue(R"("Direct")")}, nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);
}

TEST_F(UIProxyConfigServiceTest, ManualOncUserPolicy) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  const std::string onc_config = base::ReplaceStringPlaceholders(
      R"([{"GUID": "$1", "Type": "WiFi", "ProxySettings": {
           "Type": "Manual",
           "ExcludeDomains": ["foo.test", "localhost"],
           "Manual": {
             "HTTPProxy": {"Host": "proxy1", "Port": 81},
             "SecureHTTPProxy": {"Host": "proxy2", "Port": 82},
             "SOCKS": {"Host": "proxy3", "Port": 83}}}}])",
      {kTestUserWifiGuid}, nullptr);
  user_prefs_.SetManagedPref(::onc::prefs::kOpenNetworkConfiguration,
                             base::test::ParseJsonList(onc_config));

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1,
          "Manual": {
            "HTTPProxy": {"Host": $2, "Port": $3},
            "SecureHTTPProxy": {"Host": $4, "Port": $5},
            "SOCKS": {"Host": $7, "Port": $6}
          },
          "ExcludeDomains": $8
          })",
      {UserPolicyOncValue(R"("Manual")"), UserPolicyOncValue(R"("proxy1")"),
       UserPolicyOncValue("81"), UserPolicyOncValue(R"("proxy2")"),
       UserPolicyOncValue("82"), UserPolicyOncValue("83"),
       UserPolicyOncValue(R"("proxy3")"),
       UserPolicyOncValue(R"(["foo.test", "localhost"])")},
      nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);

  auto network_state = GetNetworkState(kTestUserWifiGuid);
  EXPECT_EQ(service->ProxyModeForNetwork(network_state.get()),
            ProxyPrefs::MODE_FIXED_SERVERS);
}

TEST_F(UIProxyConfigServiceTest, PartialManualOncUserPolicy) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  const std::string onc_config = base::ReplaceStringPlaceholders(
      R"([{"GUID": "$1", "Type": "WiFi", "ProxySettings": {
           "Type": "Manual",
           "Manual": {
             "HTTPProxy": {"Host": "proxy1", "Port": 81},
             "SOCKS": {"Host": "proxy4", "Port": 84}}}}])",
      {kTestUserWifiGuid}, nullptr);
  user_prefs_.SetManagedPref(::onc::prefs::kOpenNetworkConfiguration,
                             base::test::ParseJsonList(onc_config));

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1,
          "Manual": {
            "HTTPProxy": {"Host": $2, "Port": $3},
            "SOCKS": {"Host": $4, "Port": $5},
            "SecureHTTPProxy": {"Host": $6, "Port": $7}
          },
          "ExcludeDomains": $8
          })",
      {UserPolicyOncValue(R"("Manual")"), UserPolicyOncValue(R"("proxy1")"),
       UserPolicyOncValue("81"), UserPolicyOncValue(R"("proxy4")"),
       UserPolicyOncValue("84"), UserPolicyOncValue(R"("")"),
       UserPolicyOncValue("0"), UserPolicyOncValue(R"([])")},
      nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);

  auto network_state = GetNetworkState(kTestUserWifiGuid);
  EXPECT_EQ(service->ProxyModeForNetwork(network_state.get()),
            ProxyPrefs::MODE_FIXED_SERVERS);
}

TEST_F(UIProxyConfigServiceTest, OncDevicePolicy) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  const std::string onc_config = base::ReplaceStringPlaceholders(
      R"([{"GUID": "$1", "Type": "WiFi", "ProxySettings": {"Type": "PAC",
           "PAC": "http://onc/script.pac"}}])",
      {kTestUserWifiGuid}, nullptr);
  local_state_.SetManagedPref(::onc::prefs::kDeviceOpenNetworkConfiguration,
                              base::test::ParseJsonList(onc_config));

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1, "PAC": $2})",
      {DevicePolicyOncValue(R"("PAC")"),
       DevicePolicyOncValue(R"("http://onc/script.pac")")},
      nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);

  auto network_state = GetNetworkState(kTestUserWifiGuid);
  EXPECT_EQ(service->ProxyModeForNetwork(network_state.get()),
            ProxyPrefs::MODE_PAC_SCRIPT);
}

TEST_F(UIProxyConfigServiceTest, OncUserPolicyForSharedNetwork) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  const std::string onc_config = base::ReplaceStringPlaceholders(
      R"([{"GUID": "$1", "Type": "WiFi", "ProxySettings": {"Type": "WPAD"}}])",
      {kTestSharedWifiGuid}, nullptr);
  user_prefs_.SetManagedPref(::onc::prefs::kOpenNetworkConfiguration,
                             base::test::ParseJsonList(onc_config));

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestSharedWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1})", {UserPolicyOncValue(R"("WPAD")")}, nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);

  auto network_state = GetNetworkState(kTestSharedWifiGuid);
  EXPECT_EQ(service->ProxyModeForNetwork(network_state.get()),
            ProxyPrefs::MODE_AUTO_DETECT);
}

TEST_F(UIProxyConfigServiceTest, OncDevicePolicyForSharedNetwork) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  const std::string onc_config = base::ReplaceStringPlaceholders(
      R"([{"GUID": "$1", "Type": "WiFi", "ProxySettings": {"Type": "PAC",
           "PAC": "http://onc/script.pac"}}])",
      {kTestSharedWifiGuid}, nullptr);
  local_state_.SetManagedPref(::onc::prefs::kDeviceOpenNetworkConfiguration,
                              base::test::ParseJsonList(onc_config));

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestSharedWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1, "PAC": $2})",
      {DevicePolicyOncValue(R"("PAC")"),
       DevicePolicyOncValue(R"("http://onc/script.pac")")},
      nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);

  auto network_state = GetNetworkState(kTestSharedWifiGuid);
  EXPECT_EQ(service->ProxyModeForNetwork(network_state.get()),
            ProxyPrefs::MODE_PAC_SCRIPT);
}

TEST_F(UIProxyConfigServiceTest, OncUserAndDevicePolicy) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  const std::string user_onc_config = base::ReplaceStringPlaceholders(
      R"([{"GUID": "$1", "Type": "WiFi", "ProxySettings": {"Type": "WPAD"}}])",
      {kTestUserWifiGuid}, nullptr);
  user_prefs_.SetManagedPref(::onc::prefs::kOpenNetworkConfiguration,
                             base::test::ParseJsonList(user_onc_config));

  const std::string device_onc_config = base::ReplaceStringPlaceholders(
      R"([{"GUID": "$1", "Type": "WiFi", "ProxySettings": {"Type": "PAC",
           "PAC": "http://onc/script.pac"}}])",
      {kTestUserWifiGuid}, nullptr);
  local_state_.SetManagedPref(::onc::prefs::kDeviceOpenNetworkConfiguration,
                              base::test::ParseJsonList(device_onc_config));

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1})", {UserPolicyOncValue(R"("WPAD")")}, nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);

  auto network_state = GetNetworkState(kTestUserWifiGuid);
  EXPECT_EQ(service->ProxyModeForNetwork(network_state.get()),
            ProxyPrefs::MODE_AUTO_DETECT);
}

TEST_F(UIProxyConfigServiceTest, OncUserAndDevicePolicyBuiltOffLocalState) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceOffLocalState();

  const std::string user_onc_config = base::ReplaceStringPlaceholders(
      R"([{"GUID": "$1", "Type": "WiFi", "ProxySettings": {"Type": "WPAD"}}])",
      {kTestUserWifiGuid}, nullptr);
  user_prefs_.SetManagedPref(::onc::prefs::kOpenNetworkConfiguration,
                             base::test::ParseJsonList(user_onc_config));

  const std::string device_onc_config = base::ReplaceStringPlaceholders(
      R"([{"GUID": "$1", "Type": "WiFi", "ProxySettings": {"Type": "PAC",
           "PAC": "http://onc/script.pac"}}])",
      {kTestUserWifiGuid}, nullptr);
  local_state_.SetManagedPref(::onc::prefs::kDeviceOpenNetworkConfiguration,
                              base::test::ParseJsonList(device_onc_config));

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1, "PAC": $2})",
      {DevicePolicyOncValue(R"("PAC")"),
       DevicePolicyOncValue(R"("http://onc/script.pac")")},
      nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);

  auto network_state = GetNetworkState(kTestUserWifiGuid);
  EXPECT_EQ(service->ProxyModeForNetwork(network_state.get()),
            ProxyPrefs::MODE_PAC_SCRIPT);
}

TEST_F(UIProxyConfigServiceTest, OncUserPolicyOverridesUserSettings) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  const std::string user_onc_config = base::ReplaceStringPlaceholders(
      R"([{"GUID": "$1", "Type": "WiFi", "ProxySettings": {"Type": "WPAD"}}])",
      {kTestUserWifiGuid}, nullptr);
  user_prefs_.SetManagedPref(::onc::prefs::kOpenNetworkConfiguration,
                             base::test::ParseJsonList(user_onc_config));

  std::string config_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1, "PAC": $2})",
      {UserSettingOncValue(R"("PAC")"),
       UserSettingOncValue(R"("http://pac/test.script.pac")")},
      nullptr);
  base::Value::Dict config = base::test::ParseJsonDict(config_json);

  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1, "PAC": $2})",
      {UserPolicyAndUserSettingOncValue(R"("WPAD")", R"("PAC")"),
       UserSettingOncValue(R"("http://pac/test.script.pac")")},
      nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);

  auto network_state = GetNetworkState(kTestUserWifiGuid);
  EXPECT_EQ(service->ProxyModeForNetwork(network_state.get()),
            ProxyPrefs::MODE_AUTO_DETECT);
}

TEST_F(UIProxyConfigServiceTest, PolicyPrefOverridesOncPolicy) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  user_prefs_.SetManagedPref(
      proxy_config::prefs::kProxy,
      ProxyConfigDictionary::CreatePacScript("http://pac/script.pac", true));

  const std::string user_onc_config = base::ReplaceStringPlaceholders(
      R"([{"GUID": "$1", "Type": "WiFi", "ProxySettings": {"Type": "WPAD"}}])",
      {kTestUserWifiGuid}, nullptr);
  user_prefs_.SetManagedPref(::onc::prefs::kOpenNetworkConfiguration,
                             base::test::ParseJsonList(user_onc_config));

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1, "PAC": $2})",
      {UserPolicyOncValue(R"("PAC")"),
       UserPolicyOncValue(R"("http://pac/script.pac")")},
      nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);

  auto network_state = GetNetworkState(kTestUserWifiGuid);
  EXPECT_EQ(service->ProxyModeForNetwork(network_state.get()),
            ProxyPrefs::MODE_PAC_SCRIPT);
}

TEST_F(UIProxyConfigServiceTest, ExtensionPrefOverridesOncPolicy) {
  std::unique_ptr<UIProxyConfigService> service = CreateServiceForUser();

  user_prefs_.SetExtensionPref(
      proxy_config::prefs::kProxy,
      ProxyConfigDictionary::CreatePacScript("http://pac/script.pac", true));

  const std::string user_onc_config = base::ReplaceStringPlaceholders(
      R"([{"GUID": "$1", "Type": "WiFi", "ProxySettings": {"Type": "WPAD"}}])",
      {kTestUserWifiGuid}, nullptr);
  user_prefs_.SetManagedPref(::onc::prefs::kOpenNetworkConfiguration,
                             base::test::ParseJsonList(user_onc_config));

  base::Value::Dict config;
  EXPECT_TRUE(service->MergeEnforcedProxyConfig(kTestUserWifiGuid, &config));

  std::string expected_json = base::ReplaceStringPlaceholders(
      R"({"Type": $1, "PAC": $2})",
      {ExtensionControlledOncValue(R"("PAC")"),
       ExtensionControlledOncValue(R"("http://pac/script.pac")")},
      nullptr);
  base::Value::Dict expected = base::test::ParseJsonDict(expected_json);
  EXPECT_EQ(expected, config);

  auto network_state = GetNetworkState(kTestUserWifiGuid);
  EXPECT_EQ(service->ProxyModeForNetwork(network_state.get()),
            ProxyPrefs::MODE_PAC_SCRIPT);
}

}  // namespace ash