// Copyright 2014 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/extensions/extension_management.h"

#include <algorithm>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

#include "base/feature_list.h"
#include "base/json/json_reader.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "chrome/browser/enterprise/browser_management/management_service_factory.h"
#include "chrome/browser/extensions/extension_management_internal.h"
#include "chrome/browser/extensions/extension_management_test_util.h"
#include "chrome/browser/extensions/external_policy_loader.h"
#include "chrome/browser/extensions/standard_management_policy_provider.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
#include "components/enterprise/browser/reporting/common_pref_names.h"
#include "components/policy/core/common/management/scoped_management_service_override_for_testing.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/test/browser_task_environment.h"
#include "extensions/browser/blocklist_extension_prefs.h"
#include "extensions/browser/cws_info_service.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/managed_installation_mode.h"
#include "extensions/browser/pref_names.h"
#include "extensions/buildflags/buildflags.h"
#include "extensions/common/extension_features.h"
#include "extensions/common/extension_urls.h"
#include "extensions/common/manifest.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/permissions/api_permission_set.h"
#include "extensions/common/url_pattern.h"
#include "extensions/common/url_pattern_set.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));

using extensions::mojom::APIPermissionID;
using extensions::mojom::ManifestLocation;

namespace extensions {

namespace {

const char kTargetExtension[] = "abcdefghijklmnopabcdefghijklmnop";
const char kTargetExtension2[] = "bcdefghijklmnopabcdefghijklmnopa";
const char kTargetExtension3[] = "cdefghijklmnopabcdefghijklmnopab";
const char kTargetExtension4[] = "defghijklmnopabcdefghijklmnopabc";
const char kTargetExtension5[] = "efghijklmnopabcdefghijklmnopabcd";
const char kTargetExtension6[] = "fghijklmnopabcdefghijklmnopabcde";
const char kTargetExtension7[] = "ghijklmnopabcdefghijklmnopabcdef";
const char kTargetExtension8[] = "hijklmnopabcdefghijklmnopabcdefg";
const char kTargetExtension9[] = "ijklmnopabcdefghijklmnopabcdefgh";
const char kExampleUpdateUrl[] = "http://example.com/update_url";

const char kNonExistingExtension[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const char kNonExistingUpdateUrl[] = "http://example.net/update.xml";

const char kExampleForceInstalledDictPreference[] = R"({
  "abcdefghijklmnopabcdefghijklmnop" : {
    "installation_mode" : "force_installed",
    "update_url" : "http://example.com/update_url",
    "override_update_url": true,
  },
  "bcdefghijklmnopabcdefghijklmnopa" : {
    "installation_mode" : "force_installed",
    "update_url" : "http://example.com/update_url"
  }
})";

const char kExampleDictPreferenceWithoutInstallationMode[] = R"({
  "abcdefghijklmnopabcdefghijklmnop" : {
    "override_update_url": true,
  },
  "bcdefghijklmnopabcdefghijklmnopa" : {
    "minimum_version_required": "1.1.0"
  }
})";

const char kExampleDictPreferenceWithMultipleEntries[] = R"({
  "abcdefghijklmnopabcdefghijklmnop,bcdefghijklmnopabcdefghijklmnopa" : {
    "installation_mode": "blocked",
  },
  "bcdefghijklmnopabcdefghijklmnopa,cdefghijklmnopabcdefghijklmnopab" : {
    "minimum_version_required": "2.0"
  }
})";

const char kExampleDictPreference[] =
    R"(
{
  "abcdefghijklmnopabcdefghijklmnop": {
    "installation_mode": "allowed",
    "blocked_permissions": ["fileSystem", "bookmarks", "downloads"],
    "minimum_version_required": "1.1.0",
    "runtime_allowed_hosts": ["<all_urls>"],
  },
  "bcdefghijklmnopabcdefghijklmnopa": {
    "installation_mode": "force_installed",
    "update_url": "http://example.com/update_url",
    "blocked_permissions": ["downloads"],
  },
  "cdefghijklmnopabcdefghijklmnopab": {
    "installation_mode": "normal_installed",
    "update_url": "http://example.com/update_url",
    "blocked_permissions": ["fileSystem", "history"],
  },
  "defghijklmnopabcdefghijklmnopabc": {
    "installation_mode": "blocked",
    "runtime_blocked_hosts": ["*://*.foo.com", "https://bar.org/test"],
    "blocked_install_message": "Custom Error Extension4",
  },
  "efghijklmnopabcdefghijklmnopabcd,fghijklmnopabcdefghijklmnopabcde": {
    "installation_mode": "allowed",
  },
  "ghijklmnopabcdefghijklmnopabcdef,hijklmnopabcdefghijklmnopabcdefg,": {
    "installation_mode": "allowed",
  },
  "ijklmnopabcdefghijklmnopabcdefgh": {
    "installation_mode": "removed",
  },
  "update_url:http://example.com/update_url": {
    "installation_mode": "allowed",
    "blocked_permissions": ["fileSystem", "bookmarks"],
  },
  "*": {
    "installation_mode": "blocked",
    "install_sources": ["*://foo.com/*"],
    "allowed_types": ["theme", "user_script"],
    "blocked_permissions": ["fileSystem", "downloads"],
    "runtime_blocked_hosts": ["*://*.example.com"],
    "blocked_install_message": "Custom Error Default",
  },
})";

const char kExampleDictNoCustomError[] =
    "{"
    "  \"*\": {"
    "    \"installation_mode\": \"blocked\","
    "  },"
    "}";

}  // namespace

class ExtensionManagementServiceTest : public testing::Test {
 public:
  typedef ExtensionManagementPrefUpdater<
      sync_preferences::TestingPrefServiceSyncable>
      PrefUpdater;

  ExtensionManagementServiceTest() = default;
  ~ExtensionManagementServiceTest() override = default;

  // testing::Test:
  void SetUp() override { InitPrefService(); }

  void InitPrefService() {
    extension_management_.reset();
    profile_ = std::make_unique<TestingProfile>();
    pref_service_ = profile_->GetTestingPrefService();
    extension_management_ =
        std::make_unique<ExtensionManagement>(profile_.get());
  }

  void SetPref(bool managed,
               const char* path,
               std::unique_ptr<base::Value> value) {
    if (managed) {
      pref_service_->SetManagedPref(path, std::move(value));
    } else {
      pref_service_->SetUserPref(path, std::move(value));
    }
  }

  void SetPref(bool managed, const char* path, base::Value value) {
    SetPref(managed, path, base::Value::ToUniquePtrValue(std::move(value)));
  }

  void SetPref(bool managed, const char* path, base::DictValue dict) {
    SetPref(managed, path, base::Value(std::move(dict)));
  }

  void RemovePref(bool managed, const char* path) {
    if (managed) {
      pref_service_->RemoveManagedPref(path);
    } else {
      pref_service_->RemoveUserPref(path);
    }
  }

  const internal::GlobalSettings* ReadGlobalSettings() {
    return extension_management_->global_settings_.get();
  }

  ManagedInstallationMode GetInstallationModeById(const std::string& id) {
    return GetInstallationMode(id, kNonExistingUpdateUrl);
  }

  ManagedInstallationMode GetInstallationModeByUpdateUrl(
      const std::string& update_url) {
    return GetInstallationMode(kNonExistingExtension, update_url);
  }

  void CheckAutomaticallyInstalledUpdateUrl(const std::string& id,
                                            const std::string& update_url) {
    auto iter = extension_management_->settings_by_id_.find(id);
    ASSERT_TRUE(iter != extension_management_->settings_by_id_.end());
    ASSERT_TRUE(
        (iter->second->installation_mode == ManagedInstallationMode::kForced) ||
        (iter->second->installation_mode ==
         ManagedInstallationMode::kRecommended));
    EXPECT_EQ(iter->second->update_url, update_url);
  }

  APIPermissionSet GetBlockedAPIPermissionsById(const std::string& id) {
    return GetBlockedAPIPermissions(id, kNonExistingUpdateUrl);
  }

  APIPermissionSet GetBlockedAPIPermissionsByUpdateUrl(
      const std::string& update_url) {
    return GetBlockedAPIPermissions(kNonExistingExtension, update_url);
  }

  void SetExampleDictPref(std::string_view example_dict_preference) {
    auto result = base::JSONReader::ReadAndReturnValueWithError(
        example_dict_preference,
        base::JSONParserOptions::JSON_ALLOW_TRAILING_COMMAS);
    ASSERT_TRUE(result.has_value()) << result.error().message;
    ASSERT_TRUE(result->is_dict());
    SetPref(true, pref_names::kExtensionManagement, std::move(*result));
  }

  // Wrapper of ExtensionManagement::GetInstallationMode, |id| and
  // |update_url| are used to construct an Extension for testing.
  ManagedInstallationMode GetInstallationMode(const std::string& id,
                                              const std::string& update_url) {
    scoped_refptr<const Extension> extension =
        CreateExtension(ManifestLocation::kUnpacked, "0.1", id, update_url);
    return extension_management_->GetInstallationMode(extension.get());
  }

  // Wrapper of ExtensionManagement::GetPolicyBlockedHosts, |id| is used
  // to construct an Extension for testing.
  URLPatternSet GetPolicyBlockedHosts(const std::string& id) {
    scoped_refptr<const Extension> extension = CreateExtension(
        ManifestLocation::kUnpacked, "0.1", id, kNonExistingUpdateUrl);
    return extension_management_->GetPolicyBlockedHosts(extension.get())
        .Clone();
  }

  // Wrapper of ExtensionManagement::GetPolicyAllowedHosts, |id| is used
  // to construct an Extension for testing.
  URLPatternSet GetPolicyAllowedHosts(const std::string& id) {
    scoped_refptr<const Extension> extension = CreateExtension(
        ManifestLocation::kUnpacked, "0.1", id, kNonExistingUpdateUrl);
    return extension_management_->GetPolicyAllowedHosts(extension.get())
        .Clone();
  }

  // Wrapper of ExtensionManagement::BlockedInstallMessage, |id| is used
  // in case the message is extension specific.
  const std::string GetBlockedInstallMessage(const std::string& id) {
    return extension_management_->BlockedInstallMessage(id);
  }

  // Wrapper of ExtensionManagement::GetBlockedAPIPermissions, |id| and
  // |update_url| are used to construct an Extension for testing.
  APIPermissionSet GetBlockedAPIPermissions(const std::string& id,
                                            const std::string& update_url) {
    scoped_refptr<const Extension> extension =
        CreateExtension(ManifestLocation::kUnpacked, "0.1", id, update_url);
    return extension_management_->GetBlockedAPIPermissions(extension.get());
  }

  // Wrapper of ExtensionManagement::CheckMinimumVersion, |id| and
  // |version| are used to construct an Extension for testing.
  bool CheckMinimumVersion(const std::string& id, const std::string& version) {
    scoped_refptr<const Extension> extension = CreateExtension(
        ManifestLocation::kUnpacked, version, id, kNonExistingUpdateUrl);
    std::string minimum_version_required;
    bool ret = extension_management_->CheckMinimumVersion(
        extension.get(), &minimum_version_required);
    EXPECT_EQ(ret, minimum_version_required.empty());
    EXPECT_EQ(ret, extension_management_->CheckMinimumVersion(extension.get(),
                                                              nullptr));
    return ret;
  }

 protected:
  scoped_refptr<const Extension> CreateExtensionHelper(
      ManifestLocation location,
      const std::string& version,
      const std::string& id,
      const std::string& update_url,
      int flags) {
    base::DictValue manifest_dict;
    manifest_dict.Set(manifest_keys::kName, "test");
    manifest_dict.Set(manifest_keys::kVersion, version);
    manifest_dict.Set(manifest_keys::kManifestVersion, 2);
    manifest_dict.Set(manifest_keys::kUpdateURL, update_url);
    std::u16string error;
    scoped_refptr<const Extension> extension =
        Extension::Create(base::FilePath(), location, std::move(manifest_dict),
                          flags, id, &error);
    CHECK(extension.get()) << error;
    return extension;
  }

  // Create an extension with specified |location|, |version|, |id| and
  // |update_url|. The extension created is NOT marked as originating from CWS.
  scoped_refptr<const Extension> CreateExtension(
      ManifestLocation location,
      const std::string& version,
      const std::string& id,
      const std::string& update_url) {
    return CreateExtensionHelper(location, version, id, update_url,
                                 Extension::NO_FLAGS);
  }

  scoped_refptr<const Extension> CreateOffstoreExtension(
      const std::string& id) {
    return CreateExtensionHelper(ManifestLocation::kUnpacked, "0.1", id,
                                 kNonExistingUpdateUrl, Extension::NO_FLAGS);
  }
  scoped_refptr<const Extension> CreateNormalExtension(const std::string& id) {
    return CreateExtensionHelper(ManifestLocation::kInternal, "0.1", id,
                                 extension_urls::kChromeWebstoreUpdateURL,
                                 Extension::FROM_WEBSTORE);
  }

  scoped_refptr<const Extension> CreateForcedExtension(const std::string& id) {
    return CreateForcedExtension(id, Extension::FROM_WEBSTORE);
  }

  scoped_refptr<const Extension> CreateForcedExtension(const std::string& id,
                                                       int flags) {
    scoped_refptr<const Extension> extension = CreateExtensionHelper(
        ManifestLocation::kExternalPolicy, "0.1", id, kExampleUpdateUrl, flags);
    base::DictValue forced_list_pref;
    ExternalPolicyLoader::AddExtension(forced_list_pref, id, kExampleUpdateUrl);
    SetPref(true, pref_names::kInstallForceList, forced_list_pref.Clone());
    return extension;
  }

  void SetExtensionLastUpdateTime(const std::string& id,
                                  base::Time update_time) {
    auto* extension_prefs = ExtensionPrefs::Get(profile_.get());
    extension_prefs->SetTimePref(
        id,
        {"last_update_time", PrefType::kTime, PrefScope::kExtensionSpecific},
        update_time);
  }

  bool IsUpdateUrlOverridden(const ExtensionId& extension_id) {
    return extension_management_->IsUpdateUrlOverridden(extension_id);
  }

  void SetCWSInfoService(CWSInfoServiceInterface* cws_info_service) {
    extension_management_->cws_info_service_ = cws_info_service;
  }

  bool IsFileUrlNavigationAllowed(const ExtensionId& extension_id) {
    return extension_management_->IsFileUrlNavigationAllowed(extension_id);
  }

  extensions::ManagedToolbarPinMode GetToolbarPinMode(const ExtensionId& id) {
    return extension_management_->GetToolbarPinMode(id);
  }

  content::BrowserTaskEnvironment task_environment_;
  std::unique_ptr<TestingProfile> profile_;
  raw_ptr<sync_preferences::TestingPrefServiceSyncable> pref_service_;
  std::unique_ptr<ExtensionManagement> extension_management_;
};

class MockCWSInfoService : public CWSInfoServiceInterface {
 public:
  MOCK_METHOD(std::optional<bool>,
              IsLiveInCWS,
              (const Extension&),
              (const, override));
  MOCK_METHOD(std::optional<CWSInfoServiceInterface::CWSInfo>,
              GetCWSInfo,
              (const Extension&),
              (const, override));
  MOCK_METHOD(void, CheckAndMaybeFetchInfo, (), (override));
  MOCK_METHOD(void,
              AddObserver,
              (CWSInfoServiceInterface::Observer*),
              (override));
  MOCK_METHOD(void,
              RemoveObserver,
              (CWSInfoServiceInterface::Observer*),
              (override));
};

class ExtensionAdminPolicyTest : public ExtensionManagementServiceTest {
 public:
  ExtensionAdminPolicyTest() = default;
  ~ExtensionAdminPolicyTest() override = default;

  void SetUpPolicyProvider() {
    provider_ = std::make_unique<StandardManagementPolicyProvider>(
        extension_management_.get(), profile_.get());
  }

  void CreateExtension(ManifestLocation location) {
    base::DictValue values;
    CreateExtensionFromValues(location, &values);
  }

  void CreateHostedApp(ManifestLocation location) {
    base::DictValue values;
    values.SetByDottedPath(manifest_keys::kWebURLs,
                           base::Value(base::Value::Type::LIST));
    values.SetByDottedPath(manifest_keys::kLaunchWebURL,
                           "http://www.example.com");
    CreateExtensionFromValues(location, &values);
  }

  void CreateExtensionFromValues(ManifestLocation location,
                                 base::DictValue* values) {
    values->Set(manifest_keys::kName, "test");
    values->Set(manifest_keys::kVersion, "0.1");
    values->Set(manifest_keys::kManifestVersion, 2);
    std::u16string error;
    extension_ = Extension::Create(base::FilePath(), location, *values,
                                   Extension::NO_FLAGS, &error);
    ASSERT_TRUE(extension_.get());
  }

  // Wrappers for legacy admin policy functions, for testing purpose only.
  bool BlocklistedByDefault(const base::ListValue* blocklist);
  bool UserMayLoad(const base::ListValue* blocklist,
                   const base::ListValue* allowlist,
                   const base::ListValue* allowed_types,
                   const Extension* extension,
                   std::u16string* error);
  bool UserMayModifySettings(const Extension* extension, std::u16string* error);
  bool ExtensionMayModifySettings(const Extension* source_extension,
                                  const Extension* extension,
                                  std::u16string* error);
  bool MustRemainEnabled(const Extension* extension, std::u16string* error);

 protected:
  std::unique_ptr<StandardManagementPolicyProvider> provider_;
  scoped_refptr<Extension> extension_;
};

bool ExtensionAdminPolicyTest::BlocklistedByDefault(
    const base::ListValue* blocklist) {
  SetUpPolicyProvider();
  if (blocklist) {
    SetPref(true, pref_names::kInstallDenyList,
            base::Value(blocklist->Clone()));
  }
  return extension_management_->BlocklistedByDefault();
}

bool ExtensionAdminPolicyTest::UserMayLoad(const base::ListValue* blocklist,
                                           const base::ListValue* allowlist,
                                           const base::ListValue* allowed_types,
                                           const Extension* extension,
                                           std::u16string* error) {
  SetUpPolicyProvider();
  if (blocklist) {
    SetPref(true, pref_names::kInstallDenyList,
            base::Value(blocklist->Clone()));
  }
  if (allowlist) {
    SetPref(true, pref_names::kInstallAllowList,
            base::Value(allowlist->Clone()));
  }
  if (allowed_types) {
    SetPref(true, pref_names::kAllowedTypes,
            base::Value(allowed_types->Clone()));
  }
  return provider_->UserMayLoad(extension, error);
}

bool ExtensionAdminPolicyTest::UserMayModifySettings(const Extension* extension,
                                                     std::u16string* error) {
  SetUpPolicyProvider();
  return provider_->UserMayModifySettings(extension, error);
}

bool ExtensionAdminPolicyTest::ExtensionMayModifySettings(
    const Extension* source_extension,
    const Extension* extension,
    std::u16string* error) {
  SetUpPolicyProvider();
  return provider_->ExtensionMayModifySettings(source_extension, extension,
                                               error);
}

bool ExtensionAdminPolicyTest::MustRemainEnabled(const Extension* extension,
                                                 std::u16string* error) {
  SetUpPolicyProvider();
  return provider_->MustRemainEnabled(extension, error);
}

// Verify that preference controlled by legacy ExtensionInstallSources policy is
// handled well.
TEST_F(ExtensionManagementServiceTest, LegacyInstallSources) {
  base::ListValue allowed_sites_pref;
  allowed_sites_pref.Append("https://www.example.com/foo");
  allowed_sites_pref.Append("https://corp.mycompany.com/*");
  SetPref(true, pref_names::kAllowedInstallSites,
          base::Value(std::move(allowed_sites_pref)));
  ASSERT_TRUE(ReadGlobalSettings()->install_sources);
  const URLPatternSet& allowed_sites = *ReadGlobalSettings()->install_sources;
  EXPECT_FALSE(allowed_sites.is_empty());
  EXPECT_TRUE(allowed_sites.MatchesURL(GURL("https://www.example.com/foo")));
  EXPECT_FALSE(allowed_sites.MatchesURL(GURL("https://www.example.com/bar")));
  EXPECT_TRUE(
      allowed_sites.MatchesURL(GURL("https://corp.mycompany.com/entry")));
  EXPECT_FALSE(
      allowed_sites.MatchesURL(GURL("https://www.mycompany.com/entry")));
}

// Verify that preference controlled by legacy ExtensionAllowedTypes policy is
// handled well.
TEST_F(ExtensionManagementServiceTest, LegacyAllowedTypes) {
  base::ListValue allowed_types_pref;
  allowed_types_pref.Append(std::to_underlying(Manifest::Type::kTheme));
  allowed_types_pref.Append(std::to_underlying(Manifest::Type::kUserScript));

  SetPref(true, pref_names::kAllowedTypes,
          base::Value(std::move(allowed_types_pref)));
  ASSERT_TRUE(ReadGlobalSettings()->allowed_types);
  const std::vector<Manifest::Type>& allowed_types =
      *ReadGlobalSettings()->allowed_types;
  EXPECT_EQ(allowed_types.size(), 2u);
  EXPECT_FALSE(
      std::ranges::contains(allowed_types, Manifest::Type::kExtension));
  EXPECT_TRUE(std::ranges::contains(allowed_types, Manifest::Type::kTheme));
  EXPECT_TRUE(
      std::ranges::contains(allowed_types, Manifest::Type::kUserScript));
}

// Verify that preference controlled by legacy ExtensionInstallBlocklist policy
// is handled well.
TEST_F(ExtensionManagementServiceTest, LegacyInstallBlocklist) {
  base::ListValue denied_list_pref;
  denied_list_pref.Append(kTargetExtension);

  SetPref(true, pref_names::kInstallDenyList,
          base::Value(std::move(denied_list_pref)));
  EXPECT_EQ(GetInstallationModeById(kTargetExtension),
            ManagedInstallationMode::kBlocked);
  EXPECT_EQ(GetInstallationModeById(kNonExistingExtension),
            ManagedInstallationMode::kAllowed);
}

// Verify that preference controlled by legacy ExtensionInstallAllowlist policy
// is handled well.
TEST_F(ExtensionManagementServiceTest, LegacyAllowlist) {
  base::ListValue denied_list_pref;
  denied_list_pref.Append("*");
  base::ListValue allowed_list_pref;
  allowed_list_pref.Append(kTargetExtension);

  SetPref(true, pref_names::kInstallDenyList,
          base::Value(std::move(denied_list_pref)));
  SetPref(true, pref_names::kInstallAllowList,
          base::Value(allowed_list_pref.Clone()));
  EXPECT_EQ(GetInstallationModeById(kTargetExtension),
            ManagedInstallationMode::kAllowed);
  EXPECT_EQ(GetInstallationModeById(kNonExistingExtension),
            ManagedInstallationMode::kBlocked);

  // Verify that install allowlist preference set by user is ignored.
  RemovePref(true, pref_names::kInstallAllowList);
  SetPref(false, pref_names::kInstallAllowList,
          base::Value(std::move(allowed_list_pref)));
  EXPECT_EQ(GetInstallationModeById(kTargetExtension),
            ManagedInstallationMode::kBlocked);
}

// Verify that preference controlled by legacy ExtensionInstallForcelist policy
// is handled well.
TEST_F(ExtensionManagementServiceTest, LegacyInstallForcelist) {
  base::DictValue forced_list_pref;
  ExternalPolicyLoader::AddExtension(forced_list_pref, kTargetExtension,
                                     kExampleUpdateUrl);

  SetPref(true, pref_names::kInstallForceList, forced_list_pref.Clone());
  EXPECT_EQ(GetInstallationModeById(kTargetExtension),
            ManagedInstallationMode::kForced);
  CheckAutomaticallyInstalledUpdateUrl(kTargetExtension, kExampleUpdateUrl);
  EXPECT_EQ(GetInstallationModeById(kNonExistingExtension),
            ManagedInstallationMode::kAllowed);

  // Verify that install forcelist preference set by user is ignored.
  RemovePref(true, pref_names::kInstallForceList);
  SetPref(false, pref_names::kInstallForceList, forced_list_pref.Clone());
  EXPECT_EQ(GetInstallationModeById(kTargetExtension),
            ManagedInstallationMode::kAllowed);
}

// Verify that update url is overridden for extensions specified in
// |kInstallForcelist| pref but |installation_mode| is missing in
// |kExtensionSettings| pref.
TEST_F(ExtensionManagementServiceTest,
       InstallUpdateUrlEnforcedForceInstalledPref) {
  base::DictValue forced_list_pref;
  ExternalPolicyLoader::AddExtension(forced_list_pref, kTargetExtension,
                                     kExampleUpdateUrl);
  ExternalPolicyLoader::AddExtension(forced_list_pref, kTargetExtension2,
                                     kExampleUpdateUrl);

  SetPref(true, pref_names::kInstallForceList, forced_list_pref.Clone());
  EXPECT_EQ(GetInstallationModeById(kTargetExtension),
            ManagedInstallationMode::kForced);
  EXPECT_EQ(GetInstallationModeById(kTargetExtension2),
            ManagedInstallationMode::kForced);

  SetExampleDictPref(kExampleDictPreferenceWithoutInstallationMode);

  // Verify that the update URL is overridden for kTargetExtension.
  EXPECT_EQ(GetInstallationModeById(kTargetExtension),
            ManagedInstallationMode::kForced);
  EXPECT_TRUE(IsUpdateUrlOverridden(kTargetExtension));

  // Verify that the update URL is not overridden for kTargetExtension2 because
  // |override_update_url| flag is not specified for it in |kExtensionSettings|
  // pref.
  EXPECT_EQ(GetInstallationModeById(kTargetExtension2),
            ManagedInstallationMode::kForced);
  EXPECT_FALSE(IsUpdateUrlOverridden(kTargetExtension2));
}

// Verify that update url is not overridden for extensions not specified in
// |kInstallForcelist| and |installation_mode| is missing in
// |kExtensionSettings|.
TEST_F(ExtensionManagementServiceTest,
       InstallUpdateUrlEnforcedForceInstalledPrefMissing) {
  base::DictValue forced_list_pref;
  ExternalPolicyLoader::AddExtension(forced_list_pref, kTargetExtension2,
                                     kExampleUpdateUrl);
  SetPref(true, pref_names::kInstallForceList, forced_list_pref.Clone());

  EXPECT_EQ(GetInstallationModeById(kTargetExtension2),
            ManagedInstallationMode::kForced);

  SetExampleDictPref(kExampleDictPreferenceWithoutInstallationMode);

  // Verify that the update URL is not overridden for kTargetExtension as it is
  // not listed in |kInstallForcelist| pref.
  EXPECT_NE(GetInstallationModeById(kTargetExtension),
            ManagedInstallationMode::kForced);
  EXPECT_FALSE(IsUpdateUrlOverridden(kTargetExtension));
}

// Verify that update url is overridden for extensions which are marked as
// 'force_installed' and |override_update_url| is true for them in
// |kExtensionSettings|.
TEST_F(ExtensionManagementServiceTest,
       InstallUpdateUrlEnforcedExtensionSettings) {
  SetExampleDictPref(kExampleForceInstalledDictPreference);

  // Verify that the update URL is overridden for kTargetExtension.
  EXPECT_EQ(GetInstallationModeById(kTargetExtension),
            ManagedInstallationMode::kForced);
  EXPECT_TRUE(IsUpdateUrlOverridden(kTargetExtension));

  // Verify that the update URL is not overridden for kTargetExtension2 because
  // |override_update_url| flag is not specified for it in |kExtensionSettings|
  // pref.
  EXPECT_EQ(GetInstallationModeById(kTargetExtension2),
            ManagedInstallationMode::kForced);
  EXPECT_FALSE(IsUpdateUrlOverridden(kTargetExtension2));
}

// Verify that the force-installed extension specified in the preference
// |kInstallUpdateUrlEnforced| is ignored if the update URL is a webstore update
// URL.
TEST_F(ExtensionManagementServiceTest,
       InstallUpdateUrlEnforcedWebstoreUpdateUrl) {
  base::DictValue forced_list_pref;
  ExternalPolicyLoader::AddExtension(forced_list_pref, kTargetExtension,
                                     extension_urls::kChromeWebstoreUpdateURL);
  ExternalPolicyLoader::AddExtension(forced_list_pref, kTargetExtension2,
                                     kExampleUpdateUrl);

  SetPref(true, pref_names::kInstallForceList, forced_list_pref.Clone());
  EXPECT_EQ(GetInstallationModeById(kTargetExtension),
            ManagedInstallationMode::kForced);
  EXPECT_EQ(GetInstallationModeById(kTargetExtension2),
            ManagedInstallationMode::kForced);

  SetExampleDictPref(kExampleDictPreferenceWithoutInstallationMode);

  // Verify that the update URL is not overridden for kTargetExtension because
  // |update_url| is a Chrome web store URL.
  EXPECT_EQ(GetInstallationModeById(kTargetExtension),
            ManagedInstallationMode::kForced);
  EXPECT_FALSE(IsUpdateUrlOverridden(kTargetExtension));
}

// Tests handling of exceeding number of urls
TEST_F(ExtensionManagementServiceTest, HostsMaximumExceeded) {
  const char policy_template[] =
      "{"
      "  \"abcdefghijklmnopabcdefghijklmnop\": {"
      "    \"installation_mode\": \"allowed\","
      "    \"runtime_blocked_hosts\": [%s],"
      "    \"runtime_allowed_hosts\": [%s]"
      "  }"
      "}";

  std::string urls;
  for (size_t i = 0; i < 200; ++i) {
    urls.append("\"*://example" + base::NumberToString(i) + ".com\",");
  }

  std::string policy =
      base::StringPrintf(policy_template, urls.c_str(), urls.c_str());
  SetExampleDictPref(policy);
  EXPECT_EQ(100u, GetPolicyBlockedHosts(kTargetExtension).size());
  EXPECT_EQ(100u, GetPolicyAllowedHosts(kTargetExtension).size());
}

// Tests that multiple entries for a dictionary are all applied.
TEST_F(ExtensionManagementServiceTest, MultipleEntries) {
  SetExampleDictPref(kExampleDictPreferenceWithMultipleEntries);

  EXPECT_EQ(GetInstallationModeById(kTargetExtension2),
            ManagedInstallationMode::kBlocked);

  EXPECT_FALSE(CheckMinimumVersion(kTargetExtension2, "1.0"));
}

// Tests parsing of new dictionary preference.
TEST_F(ExtensionManagementServiceTest, PreferenceParsing) {
  SetExampleDictPref(kExampleDictPreference);

  // Verifies the installation mode settings.
  EXPECT_TRUE(extension_management_->BlocklistedByDefault());
  EXPECT_EQ(GetInstallationModeById(kTargetExtension),
            ManagedInstallationMode::kAllowed);
  EXPECT_EQ(GetInstallationModeById(kTargetExtension2),
            ManagedInstallationMode::kForced);
  CheckAutomaticallyInstalledUpdateUrl(kTargetExtension2, kExampleUpdateUrl);
  EXPECT_EQ(GetInstallationModeById(kTargetExtension3),
            ManagedInstallationMode::kRecommended);
  CheckAutomaticallyInstalledUpdateUrl(kTargetExtension3, kExampleUpdateUrl);
  EXPECT_EQ(GetInstallationModeById(kNonExistingExtension),
            ManagedInstallationMode::kBlocked);
  EXPECT_EQ(GetInstallationModeByUpdateUrl(kExampleUpdateUrl),
            ManagedInstallationMode::kAllowed);
  EXPECT_TRUE(GetPolicyBlockedHosts(kTargetExtension).is_empty());
  EXPECT_TRUE(GetPolicyBlockedHosts(kTargetExtension4)
                  .MatchesURL(GURL("http://test.foo.com/test")));
  EXPECT_TRUE(GetPolicyBlockedHosts(kTargetExtension4)
                  .MatchesURL(GURL("https://bar.org/test")));
  EXPECT_TRUE(GetBlockedInstallMessage(kTargetExtension).empty());
  EXPECT_EQ("Custom Error Extension4",
            GetBlockedInstallMessage(kTargetExtension4));
  EXPECT_EQ("Custom Error Default",
            GetBlockedInstallMessage(kNonExistingExtension));

  // Verifies using multiple extensions as a key.
  EXPECT_EQ(GetInstallationModeById(kTargetExtension5),
            ManagedInstallationMode::kAllowed);
  EXPECT_EQ(GetInstallationModeById(kTargetExtension6),
            ManagedInstallationMode::kAllowed);
  EXPECT_EQ(GetInstallationModeById(kTargetExtension7),
            ManagedInstallationMode::kAllowed);
  EXPECT_EQ(GetInstallationModeById(kTargetExtension8),
            ManagedInstallationMode::kAllowed);

  // Verifies global settings.
  ASSERT_TRUE(ReadGlobalSettings()->install_sources);
  const URLPatternSet& allowed_sites = *ReadGlobalSettings()->install_sources;
  EXPECT_EQ(allowed_sites.size(), 1u);
  EXPECT_TRUE(allowed_sites.MatchesURL(GURL("http://foo.com/entry")));
  EXPECT_FALSE(allowed_sites.MatchesURL(GURL("http://bar.com/entry")));
  EXPECT_TRUE(GetPolicyBlockedHosts(kNonExistingExtension)
                  .MatchesURL(GURL("http://example.com/default")));

  ASSERT_TRUE(ReadGlobalSettings()->allowed_types);
  const std::vector<Manifest::Type>& allowed_types =
      *ReadGlobalSettings()->allowed_types;
  EXPECT_EQ(allowed_types.size(), 2u);
  EXPECT_TRUE(std::ranges::contains(allowed_types, Manifest::Type::kTheme));
  EXPECT_TRUE(
      std::ranges::contains(allowed_types, Manifest::Type::kUserScript));

  // Verifies blocked permission allowlist settings.
  APIPermissionSet api_permission_set;
  api_permission_set.clear();
  api_permission_set.insert(APIPermissionID::kFileSystem);
  api_permission_set.insert(APIPermissionID::kDownloads);
  EXPECT_EQ(api_permission_set,
            GetBlockedAPIPermissionsById(kNonExistingExtension));

  api_permission_set.clear();
  api_permission_set.insert(APIPermissionID::kFileSystem);
  api_permission_set.insert(APIPermissionID::kDownloads);
  api_permission_set.insert(APIPermissionID::kBookmark);
  EXPECT_EQ(api_permission_set, GetBlockedAPIPermissionsById(kTargetExtension));

  api_permission_set.clear();
  api_permission_set.insert(APIPermissionID::kDownloads);
  EXPECT_EQ(api_permission_set,
            GetBlockedAPIPermissionsById(kTargetExtension2));

  api_permission_set.clear();
  api_permission_set.insert(APIPermissionID::kFileSystem);
  api_permission_set.insert(APIPermissionID::kHistory);
  EXPECT_EQ(api_permission_set,
            GetBlockedAPIPermissionsById(kTargetExtension3));

  api_permission_set.clear();
  api_permission_set.insert(APIPermissionID::kFileSystem);
  api_permission_set.insert(APIPermissionID::kBookmark);
  EXPECT_EQ(api_permission_set,
            GetBlockedAPIPermissionsByUpdateUrl(kExampleUpdateUrl));

  // Verifies minimum version settings.
  EXPECT_FALSE(CheckMinimumVersion(kTargetExtension, "1.0.99"));
  EXPECT_TRUE(CheckMinimumVersion(kTargetExtension, "1.1"));
  EXPECT_TRUE(CheckMinimumVersion(kTargetExtension, "1.1.0.1"));

  // Verifies that an extension using the default scope where
  // no custom blocked install message is defined returns an empty string.
  SetExampleDictPref(kExampleDictNoCustomError);
  EXPECT_EQ("", GetBlockedInstallMessage(kNonExistingExtension));
}

// Tests the handling of installation mode in case it's specified in both
// per-extension and per-update-url settings.
TEST_F(ExtensionManagementServiceTest, InstallationModeConflictHandling) {
  SetExampleDictPref(kExampleDictPreference);

  // Per-extension installation mode settings should always override
  // per-update-url settings.
  EXPECT_EQ(GetInstallationMode(kTargetExtension, kExampleUpdateUrl),
            ManagedInstallationMode::kAllowed);
  EXPECT_EQ(GetInstallationMode(kTargetExtension2, kExampleUpdateUrl),
            ManagedInstallationMode::kForced);
  EXPECT_EQ(GetInstallationMode(kTargetExtension3, kExampleUpdateUrl),
            ManagedInstallationMode::kRecommended);
}

// Tests the handling of blocked permissions in case it's specified in both
// per-extension and per-update-url settings.
TEST_F(ExtensionManagementServiceTest, BlockedPermissionsConflictHandling) {
  SetExampleDictPref(kExampleDictPreference);

  // Both settings should be overridden.
  APIPermissionSet blocked_permissions_for_update_url;
  blocked_permissions_for_update_url.insert(APIPermissionID::kFileSystem);
  blocked_permissions_for_update_url.insert(APIPermissionID::kBookmark);

  APIPermissionSet api_permission_set;

  api_permission_set = blocked_permissions_for_update_url.Clone();
  api_permission_set.insert(APIPermissionID::kFileSystem);
  api_permission_set.insert(APIPermissionID::kDownloads);
  api_permission_set.insert(APIPermissionID::kBookmark);
  EXPECT_EQ(api_permission_set,
            GetBlockedAPIPermissions(kTargetExtension, kExampleUpdateUrl));

  api_permission_set = blocked_permissions_for_update_url.Clone();
  api_permission_set.insert(APIPermissionID::kDownloads);
  EXPECT_EQ(api_permission_set,
            GetBlockedAPIPermissions(kTargetExtension2, kExampleUpdateUrl));

  api_permission_set = blocked_permissions_for_update_url.Clone();
  api_permission_set.insert(APIPermissionID::kFileSystem);
  api_permission_set.insert(APIPermissionID::kHistory);
  EXPECT_EQ(api_permission_set,
            GetBlockedAPIPermissions(kTargetExtension3, kExampleUpdateUrl));

  // Default blocked permissions will not be inherited.
  EXPECT_EQ(blocked_permissions_for_update_url,
            GetBlockedAPIPermissions(kTargetExtension4, kExampleUpdateUrl));
  EXPECT_EQ(
      APIPermissionSet(),
      GetBlockedAPIPermissions(kTargetExtension4,
                               "https://www.example.com/another_update_url"));
}

TEST_F(ExtensionManagementServiceTest, DefaultHostExtensionsOverride) {
  SetExampleDictPref(base::StringPrintf(
      R"({
    "%s": {
      "runtime_allowed_hosts": ["https://allow.extension.com"],
      "runtime_blocked_hosts": ["https://block.extension.com"],
    },
    "%s": {},
    "*": {
      "runtime_allowed_hosts": ["https://allow.default.com"],
      "runtime_blocked_hosts": ["https://block.default.com"],
    },
  })",
      kTargetExtension, kTargetExtension2));

  // Override allow/block host for the first extension.
  URLPatternSet expected_extension_allowed_set_1;
  URLPatternSet expected_extension_blocked_set_1;
  expected_extension_allowed_set_1.AddPattern(
      {URLPattern::SCHEME_ALL, "https://allow.extension.com/*"});
  expected_extension_blocked_set_1.AddPattern(
      {URLPattern::SCHEME_ALL, "https://block.extension.com/*"});

  EXPECT_EQ(expected_extension_allowed_set_1,
            GetPolicyAllowedHosts(kTargetExtension));
  EXPECT_EQ(expected_extension_blocked_set_1,
            GetPolicyBlockedHosts(kTargetExtension));

  // Empty allow/block host for the second extension.
  EXPECT_EQ(URLPatternSet(), GetPolicyAllowedHosts(kTargetExtension2));
  EXPECT_EQ(URLPatternSet(), GetPolicyBlockedHosts(kTargetExtension2));

  // Default allow/block host for the third extension.
  URLPatternSet expected_extension_allowed_set_3;
  URLPatternSet expected_extension_blocked_set_3;
  expected_extension_allowed_set_3.AddPattern(
      {URLPattern::SCHEME_ALL, "https://allow.default.com/*"});
  expected_extension_blocked_set_3.AddPattern(
      {URLPattern::SCHEME_ALL, "https://block.default.com/*"});

  EXPECT_EQ(expected_extension_allowed_set_3,
            GetPolicyAllowedHosts(kTargetExtension3));
  EXPECT_EQ(expected_extension_blocked_set_3,
            GetPolicyBlockedHosts(kTargetExtension3));
}

// Tests the 'minimum_version_required' settings of extension management.
TEST_F(ExtensionManagementServiceTest, kMinimumVersionRequired) {
  EXPECT_TRUE(CheckMinimumVersion(kTargetExtension, "0.0"));
  EXPECT_TRUE(CheckMinimumVersion(kTargetExtension, "3.0.0"));
  EXPECT_TRUE(CheckMinimumVersion(kTargetExtension, "9999.0"));

  {
    PrefUpdater pref(pref_service_.get());
    pref.SetMinimumVersionRequired(kTargetExtension, "3.0");
  }

  EXPECT_FALSE(CheckMinimumVersion(kTargetExtension, "0.0"));
  EXPECT_FALSE(CheckMinimumVersion(kTargetExtension, "2.99"));
  EXPECT_TRUE(CheckMinimumVersion(kTargetExtension, "3.0.0"));
  EXPECT_TRUE(CheckMinimumVersion(kTargetExtension, "3.0.1"));
  EXPECT_TRUE(CheckMinimumVersion(kTargetExtension, "4.0"));
}

// Tests functionality of new preference as to deprecate legacy
// ExtensionInstallSources policy.
TEST_F(ExtensionManagementServiceTest, NewInstallSources) {
  // Set the legacy preference, and verifies that it works.
  base::ListValue allowed_sites_pref;
  allowed_sites_pref.Append("https://www.example.com/foo");
  SetPref(true, pref_names::kAllowedInstallSites,
          base::Value(std::move(allowed_sites_pref)));
  ASSERT_TRUE(ReadGlobalSettings()->install_sources);
  EXPECT_TRUE(ReadGlobalSettings()->install_sources->MatchesURL(
      GURL("https://www.example.com/foo")));

  // Set the new dictionary preference.
  {
    PrefUpdater updater(pref_service_.get());
    updater.ClearInstallSources();
  }
  // Verifies that the new one overrides the legacy ones.
  ASSERT_TRUE(ReadGlobalSettings()->install_sources);
  EXPECT_FALSE(ReadGlobalSettings()->install_sources->MatchesURL(
      GURL("https://www.example.com/foo")));

  // Updates the new dictionary preference.
  {
    PrefUpdater updater(pref_service_.get());
    updater.AddInstallSource("https://corp.mycompany.com/*");
  }
  ASSERT_TRUE(ReadGlobalSettings()->install_sources);
  EXPECT_TRUE(ReadGlobalSettings()->install_sources->MatchesURL(
      GURL("https://corp.mycompany.com/entry")));
}

// Tests functionality of new preference as to deprecate legacy
// ExtensionAllowedTypes policy.
TEST_F(ExtensionManagementServiceTest, NewAllowedTypes) {
  // Set the legacy preference, and verifies that it works.
  base::ListValue allowed_types_pref;
  allowed_types_pref.Append(std::to_underlying(Manifest::Type::kUserScript));
  SetPref(true, pref_names::kAllowedTypes,
          base::Value(allowed_types_pref.Clone()));
  ASSERT_TRUE(ReadGlobalSettings()->allowed_types);
  EXPECT_EQ(ReadGlobalSettings()->allowed_types->size(), 1u);
  EXPECT_EQ(ReadGlobalSettings()->allowed_types.value()[0],
            Manifest::Type::kUserScript);

  // Set the new dictionary preference.
  {
    PrefUpdater updater(pref_service_.get());
    updater.ClearAllowedTypes();
  }
  // Verifies that the new one overrides the legacy ones.
  ASSERT_TRUE(ReadGlobalSettings()->allowed_types);
  EXPECT_EQ(ReadGlobalSettings()->allowed_types->size(), 0u);

  // Updates the new dictionary preference.
  {
    PrefUpdater updater(pref_service_.get());
    updater.AddAllowedType("theme");
  }
  ASSERT_TRUE(ReadGlobalSettings()->allowed_types);
  EXPECT_EQ(ReadGlobalSettings()->allowed_types->size(), 1u);
  EXPECT_EQ(ReadGlobalSettings()->allowed_types.value()[0],
            Manifest::Type::kTheme);
}

// Tests functionality of new preference as to deprecate legacy
// ExtensionInstallBlocklist policy.
TEST_F(ExtensionManagementServiceTest, NewInstallBlocklist) {
  // Set the new dictionary preference.
  {
    PrefUpdater updater(pref_service_.get());
    updater.SetBlocklistedByDefault(false);  // Allowed by default.
    updater.SetIndividualExtensionInstallationAllowed(kTargetExtension, false);
    updater.ClearPerExtensionSettings(kTargetExtension2);
  }
  EXPECT_FALSE(extension_management_->BlocklistedByDefault());
  EXPECT_EQ(GetInstallationModeById(kTargetExtension),
            ManagedInstallationMode::kBlocked);
  EXPECT_EQ(GetInstallationModeById(kNonExistingExtension),
            ManagedInstallationMode::kAllowed);

  // Set legacy preference.
  base::ListValue denied_list_pref;
  denied_list_pref.Append("*");
  denied_list_pref.Append(kTargetExtension2);
  SetPref(true, pref_names::kInstallDenyList,
          base::Value(std::move(denied_list_pref)));

  base::ListValue allowed_list_pref;
  allowed_list_pref.Append(kTargetExtension);
  SetPref(true, pref_names::kInstallAllowList,
          base::Value(std::move(allowed_list_pref)));

  // Verifies that the new one have higher priority over the legacy ones.
  EXPECT_FALSE(extension_management_->BlocklistedByDefault());
  EXPECT_EQ(GetInstallationModeById(kTargetExtension),
            ManagedInstallationMode::kBlocked);
  EXPECT_EQ(GetInstallationModeById(kTargetExtension2),
            ManagedInstallationMode::kBlocked);
  EXPECT_EQ(GetInstallationModeById(kNonExistingExtension),
            ManagedInstallationMode::kAllowed);
}

// Tests functionality of new preference as to deprecate legacy
// ExtensionInstallAllowlist policy.
TEST_F(ExtensionManagementServiceTest, NewAllowlist) {
  // Set the new dictionary preference.
  {
    PrefUpdater updater(pref_service_.get());
    updater.SetBlocklistedByDefault(true);  // Disallowed by default.
    updater.SetIndividualExtensionInstallationAllowed(kTargetExtension, true);
    updater.ClearPerExtensionSettings(kTargetExtension2);
  }
  EXPECT_TRUE(extension_management_->BlocklistedByDefault());
  EXPECT_EQ(GetInstallationModeById(kTargetExtension),
            ManagedInstallationMode::kAllowed);
  EXPECT_EQ(GetInstallationModeById(kNonExistingExtension),
            ManagedInstallationMode::kBlocked);

  // Set legacy preference.
  base::ListValue denied_list_pref;
  denied_list_pref.Append(kTargetExtension);
  SetPref(true, pref_names::kInstallDenyList,
          base::Value(std::move(denied_list_pref)));

  base::ListValue allowed_list_pref;
  allowed_list_pref.Append(kTargetExtension2);
  SetPref(true, pref_names::kInstallAllowList,
          base::Value(std::move(allowed_list_pref)));

  // Verifies that the new one have higher priority over the legacy ones.
  EXPECT_TRUE(extension_management_->BlocklistedByDefault());
  EXPECT_EQ(GetInstallationModeById(kTargetExtension),
            ManagedInstallationMode::kAllowed);
  EXPECT_EQ(GetInstallationModeById(kTargetExtension2),
            ManagedInstallationMode::kAllowed);
  EXPECT_EQ(GetInstallationModeById(kNonExistingExtension),
            ManagedInstallationMode::kBlocked);
}

// Tests functionality of new preference as to deprecate legacy
// ExtensionInstallForcelist policy.
TEST_F(ExtensionManagementServiceTest, NewInstallForcelist) {
  // Set some legacy preferences, to verify that the new one overrides the
  // legacy ones.
  base::ListValue denied_list_pref;
  denied_list_pref.Append(kTargetExtension);
  SetPref(true, pref_names::kInstallDenyList,
          base::Value(std::move(denied_list_pref)));

  // Set the new dictionary preference.
  {
    PrefUpdater updater(pref_service_.get());
    updater.SetIndividualExtensionAutoInstalled(kTargetExtension,
                                                kExampleUpdateUrl, true);
  }
  EXPECT_EQ(GetInstallationModeById(kTargetExtension),
            ManagedInstallationMode::kForced);
  CheckAutomaticallyInstalledUpdateUrl(kTargetExtension, kExampleUpdateUrl);
  EXPECT_EQ(GetInstallationModeById(kNonExistingExtension),
            ManagedInstallationMode::kAllowed);
}

// Tests the behavior of IsInstallationExplicitlyAllowed().
TEST_F(ExtensionManagementServiceTest, IsInstallationExplicitlyAllowed) {
  SetExampleDictPref(kExampleDictPreference);

  // Constant name indicates the installation_mode of extensions in example
  // preference.
  const char* allowed = kTargetExtension;
  const char* forced = kTargetExtension2;
  const char* recommended = kTargetExtension3;
  const char* blocked = kTargetExtension4;
  const char* removed = kTargetExtension9;
  const char* not_specified = kNonExistingExtension;

  // BlocklistedByDefault() is true in example preference.
  EXPECT_TRUE(extension_management_->IsInstallationExplicitlyAllowed(allowed));
  EXPECT_TRUE(extension_management_->IsInstallationExplicitlyAllowed(forced));
  EXPECT_TRUE(
      extension_management_->IsInstallationExplicitlyAllowed(recommended));
  EXPECT_FALSE(extension_management_->IsInstallationExplicitlyAllowed(blocked));
  EXPECT_FALSE(extension_management_->IsInstallationExplicitlyAllowed(removed));
  EXPECT_FALSE(
      extension_management_->IsInstallationExplicitlyAllowed(not_specified));

  // Set BlocklistedByDefault() to false.
  PrefUpdater pref(pref_service_.get());
  pref.SetBlocklistedByDefault(false);

  // The result should remain the same.
  EXPECT_TRUE(extension_management_->IsInstallationExplicitlyAllowed(allowed));
  EXPECT_TRUE(extension_management_->IsInstallationExplicitlyAllowed(forced));
  EXPECT_TRUE(
      extension_management_->IsInstallationExplicitlyAllowed(recommended));
  EXPECT_FALSE(extension_management_->IsInstallationExplicitlyAllowed(blocked));
  EXPECT_FALSE(extension_management_->IsInstallationExplicitlyAllowed(removed));
  EXPECT_FALSE(
      extension_management_->IsInstallationExplicitlyAllowed(not_specified));
}

TEST_F(ExtensionManagementServiceTest, IsInstallationExplicitlyBlocked) {
  SetExampleDictPref(kExampleDictPreference);

  // Constant name indicates the installation_mode of extensions in example
  // preference.
  const char* allowed = kTargetExtension;
  const char* forced = kTargetExtension2;
  const char* recommended = kTargetExtension3;
  const char* blocked = kTargetExtension4;
  const char* removed = kTargetExtension9;
  const char* not_specified = kNonExistingExtension;

  // BlocklistedByDefault() is true in example preference.
  EXPECT_FALSE(extension_management_->IsInstallationExplicitlyBlocked(allowed));
  EXPECT_FALSE(extension_management_->IsInstallationExplicitlyBlocked(forced));
  EXPECT_FALSE(
      extension_management_->IsInstallationExplicitlyBlocked(recommended));
  EXPECT_TRUE(extension_management_->IsInstallationExplicitlyBlocked(blocked));
  EXPECT_TRUE(extension_management_->IsInstallationExplicitlyBlocked(removed));
  EXPECT_FALSE(
      extension_management_->IsInstallationExplicitlyBlocked(not_specified));

  PrefUpdater pref(pref_service_.get());
  pref.SetBlocklistedByDefault(false);

  EXPECT_FALSE(extension_management_->IsInstallationExplicitlyBlocked(allowed));
  EXPECT_FALSE(extension_management_->IsInstallationExplicitlyBlocked(forced));
  EXPECT_FALSE(
      extension_management_->IsInstallationExplicitlyBlocked(recommended));
  EXPECT_TRUE(extension_management_->IsInstallationExplicitlyBlocked(blocked));
  EXPECT_TRUE(extension_management_->IsInstallationExplicitlyBlocked(removed));
  EXPECT_FALSE(
      extension_management_->IsInstallationExplicitlyBlocked(not_specified));
}

TEST_F(ExtensionManagementServiceTest,
       ExtensionsAreBlockedByDefaultForExtensionRequest) {
  // When extension request policy is set to true, all extensions are blocked by
  // default.
  SetPref(true, enterprise_reporting::kCloudExtensionRequestEnabled,
          std::make_unique<base::Value>(true));
  EXPECT_TRUE(extension_management_->BlocklistedByDefault());
  EXPECT_EQ(ManagedInstallationMode::kBlocked,
            GetInstallationModeById(kTargetExtension));
  // However, it will be overridden by ExtensionSettings
  SetExampleDictPref(R"({
    "*": {
      "installation_mode": "removed",
    }
  })");
  EXPECT_EQ(ManagedInstallationMode::kRemoved,
            GetInstallationModeById(kTargetExtension));
}

// Verifies that extensions that do not update CWS are always allowed by
// the ExtensionUnpublishedAvailability policy check function since this policy
// ignores them.
TEST_F(ExtensionManagementServiceTest,
       UnpublishedCheckForNonCWSUpdateExtensions) {
  // Create test extensions that don't update from CWS.
  scoped_refptr<const Extension> offstore_extension =
      CreateOffstoreExtension(kNonExistingExtension);
  scoped_refptr<const Extension> forced_extension =
      CreateForcedExtension(kTargetExtension3);
  // Create mock CWS service. Verify it is not queried for these policy
  // checks.
  testing::NiceMock<MockCWSInfoService> mock_cws_info_service;
  SetCWSInfoService(&mock_cws_info_service);
  EXPECT_CALL(mock_cws_info_service, GetCWSInfo).Times(0);
  // Verify that the extensions are allowed regardless of policy setting.
  SetPref(true, pref_names::kExtensionUnpublishedAvailability,
          base::Value(static_cast<int>(
              internal::GlobalSettings::UnpublishedAvailability::
                  kAllowUnpublished)));
  EXPECT_TRUE(extension_management_->IsAllowedByUnpublishedAvailabilityPolicy(
      offstore_extension.get()));
  EXPECT_TRUE(extension_management_->IsAllowedByUnpublishedAvailabilityPolicy(
      forced_extension.get()));

  SetPref(true, pref_names::kExtensionUnpublishedAvailability,
          base::Value(static_cast<int>(
              internal::GlobalSettings::UnpublishedAvailability::
                  kDisableUnpublished)));
  EXPECT_TRUE(extension_management_->IsAllowedByUnpublishedAvailabilityPolicy(
      offstore_extension.get()));
  EXPECT_TRUE(extension_management_->IsAllowedByUnpublishedAvailabilityPolicy(
      forced_extension.get()));
  SetCWSInfoService(nullptr);
}

// Verifies that a CWS extensions is allowed if the
// ExtensionUnpublishedAvailability policy setting is kAllowUnpublished.
TEST_F(ExtensionManagementServiceTest,
       UnpublishedCheckWithPolicySettingAllowUnpublished) {
  // Configure the policy.
  SetPref(true, pref_names::kExtensionUnpublishedAvailability,
          base::Value(static_cast<int>(
              internal::GlobalSettings::UnpublishedAvailability::
                  kAllowUnpublished)));
  // Create a test extension.
  scoped_refptr<const Extension> normal_extension =
      CreateNormalExtension(kTargetExtension);
  // CWS publish state should not be queried when this extension is checked.
  testing::NiceMock<MockCWSInfoService> mock_cws_info_service;
  SetCWSInfoService(&mock_cws_info_service);
  EXPECT_CALL(mock_cws_info_service, GetCWSInfo).Times(0);
  // Verify that the extension is allowed.
  EXPECT_TRUE(extension_management_->IsAllowedByUnpublishedAvailabilityPolicy(
      normal_extension.get()));
  SetCWSInfoService(nullptr);
}

// If ExtensionUnpublishedAvailability policy setting is
// kDisableUnpublished, verify that:
// - an extension is allowed if it is currently published in CWS or if the
//   CWS publish information is missing
// - an extension is disallowed if it is not currently published in CWS
TEST_F(ExtensionManagementServiceTest,
       UnpublishedCheckWithPolicySettingDisableUnpublished) {
  // Configure the policy.
  SetPref(true, pref_names::kExtensionUnpublishedAvailability,
          base::Value(static_cast<int>(
              internal::GlobalSettings::UnpublishedAvailability::
                  kDisableUnpublished)));
  // Create a test extension.
  scoped_refptr<const Extension> normal_extension =
      CreateNormalExtension(kTargetExtension);
  // Create mock CWSInfoService to verify GetCWSInfo is called.
  testing::NiceMock<MockCWSInfoService> mock_cws_info_service;
  SetCWSInfoService(&mock_cws_info_service);
  // Set up responses to GetCWSInfo calls.
  CWSInfoServiceInterface::CWSInfo cws_info_live = {
      /*is_present=*/true,
      /*is_live=*/true,
      /*last_update_time=*/base::Time::Now(),
      CWSInfoServiceInterface::CWSViolationType::kNone,
      /*unpublished_long_ago=*/false,
      /*no_privacy_practice=*/false};
  CWSInfoServiceInterface::CWSInfo cws_info_not_live = {
      /*is_present=*/true,
      /*is_live=*/false,
      /*last_update_time=*/base::Time::Now(),
      CWSInfoServiceInterface::CWSViolationType::kNone,
      /*unpublished_long_ago=*/false,
      /*no_privacy_practice=*/false};
  CWSInfoServiceInterface::CWSInfo cws_info_malware = {
      /*is_present=*/true,
      /*is_live=*/false,
      /*last_update_time=*/base::Time::Now(),
      CWSInfoServiceInterface::CWSViolationType::kMalware,
      /*unpublished_long_ago=*/false,
      /*no_privacy_practice=*/false};
  EXPECT_CALL(mock_cws_info_service, GetCWSInfo)
      .WillOnce(testing::Return(cws_info_live))
      .WillOnce(testing::Return(cws_info_malware))
      .WillOnce(testing::Return(cws_info_not_live))
      .WillOnce(testing::Return(std::nullopt));
  // Verify that the extension is allowed when it is live in CWS.
  EXPECT_TRUE(extension_management_->IsAllowedByUnpublishedAvailabilityPolicy(
      normal_extension.get()));
  // Verify that the extension is ignored, i.e. allowed, when it is malware.
  EXPECT_TRUE(extension_management_->IsAllowedByUnpublishedAvailabilityPolicy(
      normal_extension.get()));
  // Verify that the extension is disallowed when it is not live in CWS.
  EXPECT_FALSE(extension_management_->IsAllowedByUnpublishedAvailabilityPolicy(
      normal_extension.get()));
  // Verify that the extensions is allowed if CWS publish status is missing.
  EXPECT_TRUE(extension_management_->IsAllowedByUnpublishedAvailabilityPolicy(
      normal_extension.get()));
  SetCWSInfoService(nullptr);
}

TEST_F(ExtensionManagementServiceTest, IsFileUrlNavigationAllowed) {
  EXPECT_EQ(IsFileUrlNavigationAllowed(kTargetExtension), false);
  EXPECT_EQ(IsFileUrlNavigationAllowed(kTargetExtension2), false);

  SetExampleDictPref(base::StringPrintf(
      R"({
    "%s": {
      "file_url_navigation_allowed": true
    }
  })",
      kTargetExtension));
  EXPECT_EQ(IsFileUrlNavigationAllowed(kTargetExtension), true);
  EXPECT_EQ(IsFileUrlNavigationAllowed(kTargetExtension2), false);
}

TEST_F(ExtensionManagementServiceTest, ToolbarPinModeParsing) {
  const char kToolbarPinPref[] = R"(
{
  "%s": {
    "toolbar_pin": "%s"
  },
  "%s": {
    "toolbar_pin": "%s"
  },
  "%s": {
    "toolbar_pin": "%s"
  }
})";

  // Test valid values.
  SetExampleDictPref(base::StringPrintf(
      kToolbarPinPref, kTargetExtension, "default_unpinned", kTargetExtension2,
      "default_pinned", kTargetExtension3, "force_pinned"));

  EXPECT_EQ(GetToolbarPinMode(kTargetExtension),
            extensions::ManagedToolbarPinMode::kDefaultUnpinned);
  EXPECT_EQ(GetToolbarPinMode(kTargetExtension2),
            extensions::ManagedToolbarPinMode::kDefaultPinned);
  EXPECT_EQ(GetToolbarPinMode(kTargetExtension3),
            extensions::ManagedToolbarPinMode::kForcePinned);

  // Test with no value set. It should return kNotSet.
  SetExampleDictPref(base::StringPrintf(R"({
    "%s": {}
  })",
                                        kTargetExtension5));
  EXPECT_EQ(GetToolbarPinMode(kTargetExtension5),
            extensions::ManagedToolbarPinMode::kNotSet);
}

TEST_F(ExtensionManagementServiceTest, ToolbarPinModeParsingFailsForInvalid) {
  // An invalid value for `toolbar_pin` should fail to parse.
  SetExampleDictPref(base::StringPrintf(R"({
    "%s": {
      "toolbar_pin": "invalid_value",
      "installation_mode": "blocked"
    }
  })",
                                        kTargetExtension));
  // Because parsing failed, the installation_mode was not applied, therefore
  // it should fall back to the default (kAllowed).
  EXPECT_EQ(GetInstallationModeById(kTargetExtension),
            ManagedInstallationMode::kAllowed);
}

TEST_F(ExtensionManagementServiceTest, IsAllowedByUnpackedDeveloperModePolicy) {
  base::test::ScopedFeatureList feature_list(
      extensions_features::kExtensionDisableUnsupportedDeveloper);
  scoped_refptr<const Extension> unpacked_extension =
      CreateOffstoreExtension(kNonExistingExtension);

  SetPref(false, prefs::kExtensionsUIDeveloperMode, base::Value(false));
  EXPECT_FALSE(extension_management_->IsAllowedByUnpackedDeveloperModePolicy(
      *unpacked_extension));

  SetPref(false, prefs::kExtensionsUIDeveloperMode, base::Value(true));
  EXPECT_TRUE(extension_management_->IsAllowedByUnpackedDeveloperModePolicy(
      *unpacked_extension));
}

TEST_F(ExtensionManagementServiceTest, IsForceInstalledInLowTrustEnvironment) {
  {
    // Low trust environment. Verify that the extension is considered
    // force-installed in a low-trust environment on Windows and Mac.
    policy::ScopedManagementServiceOverrideForTesting browser_management(
        policy::ManagementServiceFactory::GetForPlatform(),
        policy::EnterpriseManagementAuthority::NONE);
    scoped_refptr<const Extension> forced_extension =
        CreateForcedExtension(kTargetExtension3, Extension::NO_FLAGS);

    constexpr bool expect_low_trust =
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
        true;
#else
        false;
#endif
    EXPECT_EQ(extension_management_->IsForceInstalledInLowTrustEnvironment(
                  *forced_extension),
              expect_low_trust);
  }
  {
    // High trust environment. Verify that the extension is not considered
    // force-installed in a low-trust environment.
    policy::ScopedManagementServiceOverrideForTesting browser_management(
        policy::ManagementServiceFactory::GetForPlatform(),
        policy::EnterpriseManagementAuthority::CLOUD_DOMAIN);
    scoped_refptr<const Extension> forced_extension =
        CreateForcedExtension(kTargetExtension3, Extension::NO_FLAGS);

    EXPECT_FALSE(extension_management_->IsForceInstalledInLowTrustEnvironment(
        *forced_extension));
  }
}

TEST_F(ExtensionManagementServiceTest,
       IsGreylistedForceInstalledInLowTrustEnvironment) {
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
  base::test::ScopedFeatureList feature_list(
      kDisableForceInstalledExtensionsInLowTrustEnviromentWhenGreylisted);
#endif

  {
    // Greylisted, force-installed in a low-trust environment.

    // Force-install an extension in low-trust environment.
    policy::ScopedManagementServiceOverrideForTesting browser_management(
        policy::ManagementServiceFactory::GetForPlatform(),
        policy::EnterpriseManagementAuthority::NONE);
    scoped_refptr<const Extension> forced_extension =
        CreateForcedExtension(kTargetExtension3, Extension::NO_FLAGS);

    // Greylist the extension.
    blocklist_prefs::SetSafeBrowsingExtensionBlocklistState(
        forced_extension->id(),
        BitMapBlocklistState::BLOCKLISTED_POTENTIALLY_UNWANTED,
        ExtensionPrefs::Get(profile_.get()));

    constexpr bool expect_greylisted_in_low_trust =
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
        true;
#else
        false;
#endif
    EXPECT_EQ(
        extension_management_->IsGreylistedForceInstalledInLowTrustEnvironment(
            forced_extension->id()),
        expect_greylisted_in_low_trust);
  }
  {
    // Greylisted, force-installed in a high-trust environment.

    // Force-install an extension in high-trust environment.
    policy::ScopedManagementServiceOverrideForTesting browser_management(
        policy::ManagementServiceFactory::GetForPlatform(),
        policy::EnterpriseManagementAuthority::CLOUD_DOMAIN);
    scoped_refptr<const Extension> forced_extension =
        CreateForcedExtension(kTargetExtension3, Extension::NO_FLAGS);

    // Greylist the extension.
    blocklist_prefs::SetSafeBrowsingExtensionBlocklistState(
        forced_extension->id(),
        BitMapBlocklistState::BLOCKLISTED_POTENTIALLY_UNWANTED,
        ExtensionPrefs::Get(profile_.get()));

    EXPECT_FALSE(
        extension_management_->IsGreylistedForceInstalledInLowTrustEnvironment(
            forced_extension->id()));
  }
  {
    // Not greylisted.

    // Force-install an extension in low-trust environment.
    policy::ScopedManagementServiceOverrideForTesting browser_management(
        policy::ManagementServiceFactory::GetForPlatform(),
        policy::EnterpriseManagementAuthority::NONE);
    scoped_refptr<const Extension> forced_extension =
        CreateForcedExtension(kTargetExtension3, Extension::NO_FLAGS);

    // Don't greylist the extension.
    blocklist_prefs::SetSafeBrowsingExtensionBlocklistState(
        forced_extension->id(), BitMapBlocklistState::NOT_BLOCKLISTED,
        ExtensionPrefs::Get(profile_.get()));

    EXPECT_FALSE(
        extension_management_->IsGreylistedForceInstalledInLowTrustEnvironment(
            forced_extension->id()));
  }
  {
    // Not force-installed.

    // Set up a non-forced extension in a low-trust environment.
    policy::ScopedManagementServiceOverrideForTesting browser_management(
        policy::ManagementServiceFactory::GetForPlatform(),
        policy::EnterpriseManagementAuthority::NONE);
    scoped_refptr<const Extension> extension =
        CreateNormalExtension(kTargetExtension);

    // Greylist the extension.
    blocklist_prefs::SetSafeBrowsingExtensionBlocklistState(
        extension->id(), BitMapBlocklistState::BLOCKLISTED_POTENTIALLY_UNWANTED,
        ExtensionPrefs::Get(profile_.get()));

    EXPECT_FALSE(
        extension_management_->IsGreylistedForceInstalledInLowTrustEnvironment(
            extension->id()));
  }
}

TEST_F(ExtensionManagementServiceTest,
       ShouldBlockForceInstalledOffstoreExtension) {
  {
    // Low trust environment. Verify that extension is not allowed on
    // Windows and Mac.
    policy::ScopedManagementServiceOverrideForTesting browser_management(
        policy::ManagementServiceFactory::GetForPlatform(),
        policy::EnterpriseManagementAuthority::NONE);
    scoped_refptr<const Extension> forced_extension =
        CreateForcedExtension(kTargetExtension3, Extension::NO_FLAGS);

    bool expect_blocked =
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
        true;
#else
        false;
#endif
    EXPECT_EQ(extension_management_->ShouldBlockForceInstalledOffstoreExtension(
                  *forced_extension),
              expect_blocked);
  }
  {
    // High trust environment. Verify that extension is allowed.
    policy::ScopedManagementServiceOverrideForTesting browser_management(
        policy::ManagementServiceFactory::GetForPlatform(),
        policy::EnterpriseManagementAuthority::CLOUD_DOMAIN);
    scoped_refptr<const Extension> forced_extension =
        CreateForcedExtension(kTargetExtension3, Extension::NO_FLAGS);

    EXPECT_FALSE(
        extension_management_->ShouldBlockForceInstalledOffstoreExtension(
            *forced_extension));
  }
}

// Tests the flag value indicating that extensions are blocklisted by default.
TEST_F(ExtensionAdminPolicyTest, BlocklistedByDefault) {
  EXPECT_FALSE(BlocklistedByDefault(nullptr));

  base::ListValue blocklist;
  blocklist.Append(kNonExistingExtension);
  EXPECT_FALSE(BlocklistedByDefault(&blocklist));
  blocklist.Append("*");
  EXPECT_TRUE(BlocklistedByDefault(&blocklist));

  blocklist.clear();
  blocklist.Append("*");
  EXPECT_TRUE(BlocklistedByDefault(&blocklist));
}

// Tests UserMayLoad for required extensions.
TEST_F(ExtensionAdminPolicyTest, UserMayLoadRequired) {
  CreateExtension(ManifestLocation::kComponent);
  EXPECT_TRUE(
      UserMayLoad(nullptr, nullptr, nullptr, extension_.get(), nullptr));
  std::u16string error;
  EXPECT_TRUE(UserMayLoad(nullptr, nullptr, nullptr, extension_.get(), &error));
  EXPECT_TRUE(error.empty());

  // Required extensions may load even if they're on the blocklist.
  base::ListValue blocklist;
  blocklist.Append(extension_->id());
  EXPECT_TRUE(
      UserMayLoad(&blocklist, nullptr, nullptr, extension_.get(), nullptr));

  blocklist.Append("*");
  EXPECT_TRUE(
      UserMayLoad(&blocklist, nullptr, nullptr, extension_.get(), nullptr));
}

// Tests UserMayLoad when no blocklist exists, or it's empty.
TEST_F(ExtensionAdminPolicyTest, UserMayLoadNoBlocklist) {
  CreateExtension(ManifestLocation::kInternal);
  EXPECT_TRUE(
      UserMayLoad(nullptr, nullptr, nullptr, extension_.get(), nullptr));
  base::ListValue blocklist;
  EXPECT_TRUE(
      UserMayLoad(&blocklist, nullptr, nullptr, extension_.get(), nullptr));
  std::u16string error;
  EXPECT_TRUE(
      UserMayLoad(&blocklist, nullptr, nullptr, extension_.get(), &error));
  EXPECT_TRUE(error.empty());
}

// Tests UserMayLoad for an extension on the allowlist.
TEST_F(ExtensionAdminPolicyTest, UserMayLoadAllowlisted) {
  CreateExtension(ManifestLocation::kInternal);

  base::ListValue allowlist;
  allowlist.Append(extension_->id());
  EXPECT_TRUE(
      UserMayLoad(nullptr, &allowlist, nullptr, extension_.get(), nullptr));

  base::ListValue blocklist;
  blocklist.Append(extension_->id());
  EXPECT_TRUE(
      UserMayLoad(nullptr, &allowlist, nullptr, extension_.get(), nullptr));
  std::u16string error;
  EXPECT_TRUE(
      UserMayLoad(nullptr, &allowlist, nullptr, extension_.get(), &error));
  EXPECT_TRUE(error.empty());
}

// Tests UserMayLoad for an extension on the blocklist.
TEST_F(ExtensionAdminPolicyTest, UserMayLoadBlocklisted) {
  CreateExtension(ManifestLocation::kInternal);

  // Blocklisted by default.
  base::ListValue blocklist;
  blocklist.Append("*");
  EXPECT_FALSE(
      UserMayLoad(&blocklist, nullptr, nullptr, extension_.get(), nullptr));
  std::u16string error;
  EXPECT_FALSE(
      UserMayLoad(&blocklist, nullptr, nullptr, extension_.get(), &error));
  EXPECT_FALSE(error.empty());

  // Extension on the blocklist, with and without wildcard.
  blocklist.Append(extension_->id());
  EXPECT_FALSE(
      UserMayLoad(&blocklist, nullptr, nullptr, extension_.get(), nullptr));
  blocklist.clear();
  blocklist.Append(extension_->id());
  EXPECT_FALSE(
      UserMayLoad(&blocklist, nullptr, nullptr, extension_.get(), nullptr));

  // With an allowlist. There's no such thing as an allowlist wildcard.
  base::ListValue allowlist;
  allowlist.Append("behllobkkfkfnphdnhnkndlbkcpglgmj");
  EXPECT_FALSE(
      UserMayLoad(&blocklist, &allowlist, nullptr, extension_.get(), nullptr));
  allowlist.Append("*");
  EXPECT_FALSE(
      UserMayLoad(&blocklist, &allowlist, nullptr, extension_.get(), nullptr));
}

TEST_F(ExtensionAdminPolicyTest, UserMayLoadAllowedTypes) {
  CreateExtension(ManifestLocation::kInternal);
  EXPECT_TRUE(
      UserMayLoad(nullptr, nullptr, nullptr, extension_.get(), nullptr));

  base::ListValue allowed_types;
  EXPECT_FALSE(
      UserMayLoad(nullptr, nullptr, &allowed_types, extension_.get(), nullptr));

  allowed_types.Append(std::to_underlying(Manifest::Type::kExtension));
  EXPECT_TRUE(
      UserMayLoad(nullptr, nullptr, &allowed_types, extension_.get(), nullptr));

  CreateHostedApp(ManifestLocation::kInternal);
  EXPECT_FALSE(
      UserMayLoad(nullptr, nullptr, &allowed_types, extension_.get(), nullptr));

  CreateHostedApp(ManifestLocation::kExternalPolicyDownload);
  EXPECT_FALSE(
      UserMayLoad(nullptr, nullptr, &allowed_types, extension_.get(), nullptr));
}

TEST_F(ExtensionAdminPolicyTest, UserMayModifySettings) {
  CreateExtension(ManifestLocation::kInternal);
  EXPECT_TRUE(UserMayModifySettings(extension_.get(), nullptr));
  std::u16string error;
  EXPECT_TRUE(UserMayModifySettings(extension_.get(), &error));
  EXPECT_TRUE(error.empty());

  CreateExtension(ManifestLocation::kExternalPolicyDownload);
  error.clear();
  EXPECT_FALSE(UserMayModifySettings(extension_.get(), nullptr));
  EXPECT_FALSE(UserMayModifySettings(extension_.get(), &error));
  EXPECT_FALSE(error.empty());
}

TEST_F(ExtensionAdminPolicyTest, ExtensionMayModifySettings) {
  CreateExtension(ManifestLocation::kExternalPolicyDownload);
  auto external_policy_download = extension_;
  CreateExtension(ManifestLocation::kExternalPolicy);
  auto external_policy = extension_;
  CreateExtension(ManifestLocation::kExternalPref);
  auto external_pref = extension_;
  CreateExtension(ManifestLocation::kComponent);
  auto component = extension_;
  CreateExtension(ManifestLocation::kComponent);
  auto component2 = extension_;
  // Make sure that component/policy/external extensions cannot modify component
  // extensions (no extension may modify a component extension).
  EXPECT_FALSE(ExtensionMayModifySettings(external_policy_download.get(),
                                          component.get(), nullptr));
  EXPECT_FALSE(
      ExtensionMayModifySettings(component2.get(), component.get(), nullptr));
  EXPECT_FALSE(ExtensionMayModifySettings(external_pref.get(), component.get(),
                                          nullptr));

  // Only component/policy extensions *can* modify policy extensions, and eg.
  // external cannot.
  EXPECT_TRUE(ExtensionMayModifySettings(
      external_policy.get(), external_policy_download.get(), nullptr));
  EXPECT_TRUE(ExtensionMayModifySettings(
      component.get(), external_policy_download.get(), nullptr));
  EXPECT_FALSE(ExtensionMayModifySettings(
      external_pref.get(), external_policy_download.get(), nullptr));
}

TEST_F(ExtensionAdminPolicyTest, MustRemainEnabled) {
  CreateExtension(ManifestLocation::kExternalPolicyDownload);
  EXPECT_TRUE(MustRemainEnabled(extension_.get(), nullptr));
  std::u16string error;
  EXPECT_TRUE(MustRemainEnabled(extension_.get(), &error));
  EXPECT_FALSE(error.empty());

  CreateExtension(ManifestLocation::kInternal);
  error.clear();
  EXPECT_FALSE(MustRemainEnabled(extension_.get(), nullptr));
  EXPECT_FALSE(MustRemainEnabled(extension_.get(), &error));
  EXPECT_TRUE(error.empty());
}

}  // namespace extensions
