910e62b5创建于 1月15日历史提交
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "extensions/browser/updater/update_data_provider.h"

#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <vector>

#include "base/containers/contains.h"
#include "base/containers/flat_set.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "components/update_client/update_client.h"
#include "extensions/browser/disable_reason.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/extensions_test.h"
#include "extensions/browser/test_extensions_browser_client.h"
#include "extensions/browser/updater/extension_installer.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/extension_id.h"

using extensions::mojom::ManifestLocation;

namespace extensions {

class UpdateDataProviderExtensionsBrowserClient
    : public TestExtensionsBrowserClient {
 public:
  explicit UpdateDataProviderExtensionsBrowserClient(
      content::BrowserContext* context)
      : TestExtensionsBrowserClient(context) {}

  UpdateDataProviderExtensionsBrowserClient(
      const UpdateDataProviderExtensionsBrowserClient&) = delete;
  UpdateDataProviderExtensionsBrowserClient& operator=(
      const UpdateDataProviderExtensionsBrowserClient&) = delete;

  ~UpdateDataProviderExtensionsBrowserClient() override = default;

  bool IsExtensionEnabled(const std::string& id,
                          content::BrowserContext* context) const override {
    return base::Contains(enabled_ids_, id);
  }

  void AddEnabledExtension(const std::string& id) { enabled_ids_.insert(id); }

 private:
  std::set<std::string> enabled_ids_;
};

class UpdateDataProviderTest : public ExtensionsTest {
 public:
  using UpdateClientCallback = UpdateDataProvider::UpdateClientCallback;

  UpdateDataProviderTest() = default;
  ~UpdateDataProviderTest() override = default;

  void SetUp() override {
    SetExtensionsBrowserClient(
        std::make_unique<UpdateDataProviderExtensionsBrowserClient>(
            browser_context()));
    ExtensionsTest::SetUp();
  }

 protected:
  ExtensionSystem* extension_system() {
    return ExtensionSystem::Get(browser_context());
  }

  ExtensionRegistry* extension_registry() {
    return ExtensionRegistry::Get(browser_context());
  }

  // Helper function that creates a file at |relative_path| within |directory|
  // and fills it with |content|.
  bool AddFileToDirectory(const base::FilePath& directory,
                          const base::FilePath& relative_path,
                          const std::string& content) const {
    const base::FilePath full_path = directory.Append(relative_path);
    return base::CreateDirectory(full_path.DirName()) &&
           base::WriteFile(full_path, content);
  }

  void AddExtension(const ExtensionId& extension_id,
                    const std::string& version,
                    bool enabled,
                    const base::flat_set<int>& disable_reasons,
                    ManifestLocation location) {
    AddExtension(extension_id, version, "", enabled, disable_reasons, location);
  }

  void AddExtension(const ExtensionId& extension_id,
                    const std::string& version,
                    const std::string& fingerprint,
                    bool enabled,
                    const base::flat_set<int>& disable_reasons,
                    ManifestLocation location) {
    base::ScopedTempDir temp_dir;
    ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
    ASSERT_TRUE(base::PathExists(temp_dir.GetPath()));

    base::FilePath foo_js(FILE_PATH_LITERAL("foo.js"));
    base::FilePath bar_html(FILE_PATH_LITERAL("bar/bar.html"));
    ASSERT_TRUE(AddFileToDirectory(temp_dir.GetPath(), foo_js, "hello"))
        << "Failed to write " << temp_dir.GetPath().value() << "/"
        << foo_js.value();
    ASSERT_TRUE(AddFileToDirectory(temp_dir.GetPath(), bar_html, "world"));

    ExtensionBuilder builder;
    auto manifest_builder = base::Value::Dict()
                                .Set("name", "My First Extension")
                                .Set("version", version)
                                .Set("manifest_version", 2);
    if (!fingerprint.empty()) {
      manifest_builder.Set("differential_fingerprint", fingerprint);
    }
    builder.SetManifest(std::move(manifest_builder));
    builder.SetID(extension_id);
    builder.SetPath(temp_dir.GetPath());
    builder.SetLocation(location);

    auto* test_browser_client =
        static_cast<UpdateDataProviderExtensionsBrowserClient*>(
            extensions_browser_client());
    if (enabled) {
      extension_registry()->AddEnabled(builder.Build());
      test_browser_client->AddEnabledExtension(extension_id);
    } else {
      extension_registry()->AddDisabled(builder.Build());

      if (!disable_reasons.empty()) {
        // Some tests (e.g. GetData_DisabledExtension_WithNoReason) disable an
        // extension with no disable reasons. In this case we shouldn't call
        // `AddDisableReasons` because it expects at least one disable reason.
        // TODO(crbug.com/40554334): We can clean this up after
        // `Extension::State` is removed.
        //
        // Use raw manipulation passkey to add disable reasons because some
        // tests pass unknown reasons in the `disable_reasons` set.
        ExtensionPrefs::DisableReasonRawManipulationPasskey passkey;
        ExtensionPrefs::Get(browser_context())
            ->AddRawDisableReasons(passkey, extension_id, disable_reasons);
      }
    }

    const Extension* extension =
        extension_registry()->GetInstalledExtension(extension_id);
    ASSERT_NE(nullptr, extension);
    ASSERT_EQ(version, extension->VersionString());
  }

  const std::string kExtensionId1 = "adbncddmehfkgipkidpdiheffobcpfma";
  const std::string kExtensionId2 = "ldnnhddmnhbkjipkidpdiheffobcpfmf";
};

TEST_F(UpdateDataProviderTest, GetData_NoDataAdded) {
  scoped_refptr<UpdateDataProvider> data_provider =
      base::MakeRefCounted<UpdateDataProvider>(nullptr);

  std::vector<std::optional<update_client::CrxComponent>> data;
  data_provider->GetData(
      false /*install_immediately*/, ExtensionUpdateDataMap(), {kExtensionId1},
      base::BindLambdaForTesting(
          [&](const std::vector<std::optional<update_client::CrxComponent>>&
                  output) { data = output; }));
  ASSERT_EQ(1UL, data.size());
  EXPECT_EQ(data[0], std::nullopt);
}

TEST_F(UpdateDataProviderTest, GetData_Fingerprint) {
  scoped_refptr<UpdateDataProvider> data_provider =
      base::MakeRefCounted<UpdateDataProvider>(browser_context());

  const std::string version = "0.1.2.3";
  const std::string fingerprint = "1.0123456789abcdef";
  AddExtension(kExtensionId1, version, true,
               /*disable_reasons=*/{}, ManifestLocation::kInternal);
  AddExtension(kExtensionId2, version, fingerprint, true,
               /*disable_reasons=*/{}, ManifestLocation::kInternal);

  ExtensionUpdateDataMap update_data;
  update_data[kExtensionId1] = {};
  update_data[kExtensionId2] = {};

  std::vector<std::optional<update_client::CrxComponent>> data;
  data_provider->GetData(
      false /*install_immediately*/, update_data,
      {kExtensionId1, kExtensionId2},
      base::BindLambdaForTesting(
          [&](const std::vector<std::optional<update_client::CrxComponent>>&
                  output) { data = output; }));

  ASSERT_EQ(2UL, data.size());
  EXPECT_EQ(version, data[0]->version.GetString());
  EXPECT_EQ(version, data[1]->version.GetString());
  EXPECT_EQ("2." + version, data[0]->fingerprint);
  EXPECT_EQ(fingerprint, data[1]->fingerprint);
}

TEST_F(UpdateDataProviderTest, GetData_EnabledExtension) {
  scoped_refptr<UpdateDataProvider> data_provider =
      base::MakeRefCounted<UpdateDataProvider>(browser_context());

  const std::string version = "0.1.2.3";
  AddExtension(kExtensionId1, version, true,
               /*disable_reasons=*/{}, ManifestLocation::kInternal);

  ExtensionUpdateDataMap update_data;
  update_data[kExtensionId1] = {};

  std::vector<std::optional<update_client::CrxComponent>> data;
  data_provider->GetData(
      false /*install_immediately*/, update_data, {kExtensionId1},
      base::BindLambdaForTesting(
          [&](const std::vector<std::optional<update_client::CrxComponent>>&
                  output) { data = output; }));

  ASSERT_EQ(1UL, data.size());
  EXPECT_EQ(version, data[0]->version.GetString());
  EXPECT_NE(nullptr, data[0]->installer.get());
  EXPECT_EQ(0UL, data[0]->disabled_reasons.size());
  EXPECT_EQ("internal", data[0]->install_location);
}

TEST_F(UpdateDataProviderTest, GetData_EnabledExtensionWithData) {
  scoped_refptr<UpdateDataProvider> data_provider =
      base::MakeRefCounted<UpdateDataProvider>(browser_context());

  const std::string version = "0.1.2.3";
  AddExtension(kExtensionId1, version, true,
               /*disable_reasons=*/{}, ManifestLocation::kExternalPref);

  ExtensionUpdateDataMap update_data;
  auto& info = update_data[kExtensionId1];
  info.is_corrupt_reinstall = true;
  info.install_source = "webstore";

  std::vector<std::optional<update_client::CrxComponent>> data;
  data_provider->GetData(
      true /*install_immediately*/, update_data, {kExtensionId1},
      base::BindLambdaForTesting(
          [&](const std::vector<std::optional<update_client::CrxComponent>>&
                  output) { data = output; }));

  ASSERT_EQ(1UL, data.size());
  EXPECT_EQ("0.0.0.0", data[0]->version.GetString());
  EXPECT_EQ("reinstall", data[0]->install_source);
  EXPECT_EQ("external", data[0]->install_location);
  EXPECT_NE(nullptr, data[0]->installer.get());
  EXPECT_EQ(0UL, data[0]->disabled_reasons.size());
}

TEST_F(UpdateDataProviderTest, GetData_DisabledExtension_WithNoReason) {
  scoped_refptr<UpdateDataProvider> data_provider =
      base::MakeRefCounted<UpdateDataProvider>(browser_context());

  const std::string version = "0.1.2.3";
  AddExtension(kExtensionId1, version, false,
               /*disable_reasons=*/{}, ManifestLocation::kExternalRegistry);

  ExtensionUpdateDataMap update_data;
  update_data[kExtensionId1] = {};

  std::vector<std::optional<update_client::CrxComponent>> data;
  data_provider->GetData(
      false /*install_immediately*/, update_data, {kExtensionId1},
      base::BindLambdaForTesting(
          [&](const std::vector<std::optional<update_client::CrxComponent>>&
                  output) { data = output; }));

  ASSERT_EQ(1UL, data.size());
  EXPECT_EQ(version, data[0]->version.GetString());
  EXPECT_NE(nullptr, data[0]->installer.get());
  ASSERT_EQ(1UL, data[0]->disabled_reasons.size());
  EXPECT_EQ(disable_reason::DisableReason::DISABLE_NONE,
            data[0]->disabled_reasons[0]);
  EXPECT_EQ("external", data[0]->install_location);
}

TEST_F(UpdateDataProviderTest, GetData_DisabledExtension_UnknownReason) {
  scoped_refptr<UpdateDataProvider> data_provider =
      base::MakeRefCounted<UpdateDataProvider>(browser_context());

  const std::string version = "0.1.2.3";
  AddExtension(kExtensionId1, version, false,
               {disable_reason::DisableReason::DISABLE_REASON_LAST},
               ManifestLocation::kCommandLine);

  ExtensionUpdateDataMap update_data;
  update_data[kExtensionId1] = {};

  std::vector<std::optional<update_client::CrxComponent>> data;
  data_provider->GetData(
      true /*install_immediately*/, update_data, {kExtensionId1},
      base::BindLambdaForTesting(
          [&](const std::vector<std::optional<update_client::CrxComponent>>&
                  output) { data = output; }));

  ASSERT_EQ(1UL, data.size());
  EXPECT_EQ(version, data[0]->version.GetString());
  EXPECT_NE(nullptr, data[0]->installer.get());
  ASSERT_EQ(1UL, data[0]->disabled_reasons.size());
  EXPECT_EQ(disable_reason::DisableReason::DISABLE_NONE,
            data[0]->disabled_reasons[0]);
  EXPECT_EQ("other", data[0]->install_location);
}

TEST_F(UpdateDataProviderTest, GetData_DisabledExtension_WithReasons) {
  scoped_refptr<UpdateDataProvider> data_provider =
      base::MakeRefCounted<UpdateDataProvider>(browser_context());

  const std::string version = "0.1.2.3";
  AddExtension(kExtensionId1, version, false,
               {disable_reason::DisableReason::DISABLE_USER_ACTION,
                disable_reason::DisableReason::DISABLE_CORRUPTED},
               ManifestLocation::kExternalPolicyDownload);

  ExtensionUpdateDataMap update_data;
  update_data[kExtensionId1] = {};

  std::vector<std::optional<update_client::CrxComponent>> data;
  data_provider->GetData(
      false /*install_immediately*/, update_data, {kExtensionId1},
      base::BindLambdaForTesting(
          [&](const std::vector<std::optional<update_client::CrxComponent>>&
                  output) { data = output; }));

  ASSERT_EQ(1UL, data.size());
  EXPECT_EQ(version, data[0]->version.GetString());
  EXPECT_NE(nullptr, data[0]->installer.get());
  ASSERT_EQ(2UL, data[0]->disabled_reasons.size());
  EXPECT_EQ(disable_reason::DisableReason::DISABLE_USER_ACTION,
            data[0]->disabled_reasons[0]);
  EXPECT_EQ(disable_reason::DisableReason::DISABLE_CORRUPTED,
            data[0]->disabled_reasons[1]);
  EXPECT_EQ("policy", data[0]->install_location);
}

TEST_F(UpdateDataProviderTest,
       GetData_DisabledExtension_WithReasonsAndUnknownReason) {
  scoped_refptr<UpdateDataProvider> data_provider =
      base::MakeRefCounted<UpdateDataProvider>(browser_context());

  const std::string version = "0.1.2.3";
  AddExtension(kExtensionId1, version, false,
               {disable_reason::DisableReason::DISABLE_USER_ACTION,
                disable_reason::DisableReason::DISABLE_CORRUPTED,
                disable_reason::DisableReason::DISABLE_REASON_LAST},
               ManifestLocation::kExternalPrefDownload);

  ExtensionUpdateDataMap update_data;
  update_data[kExtensionId1] = {};

  std::vector<std::optional<update_client::CrxComponent>> data;
  data_provider->GetData(
      true /*install_immediately*/, update_data, {kExtensionId1},
      base::BindLambdaForTesting(
          [&](const std::vector<std::optional<update_client::CrxComponent>>&
                  output) { data = output; }));

  ASSERT_EQ(1UL, data.size());
  EXPECT_EQ(version, data[0]->version.GetString());
  EXPECT_NE(nullptr, data[0]->installer.get());
  ASSERT_EQ(3UL, data[0]->disabled_reasons.size());
  EXPECT_EQ(disable_reason::DisableReason::DISABLE_NONE,
            data[0]->disabled_reasons[0]);
  EXPECT_EQ(disable_reason::DisableReason::DISABLE_USER_ACTION,
            data[0]->disabled_reasons[1]);
  EXPECT_EQ(disable_reason::DisableReason::DISABLE_CORRUPTED,
            data[0]->disabled_reasons[2]);
  EXPECT_EQ("external", data[0]->install_location);
}

TEST_F(UpdateDataProviderTest, GetData_MultipleExtensions) {
  // GetData with more than 1 extension.
  scoped_refptr<UpdateDataProvider> data_provider =
      base::MakeRefCounted<UpdateDataProvider>(browser_context());

  const std::string version1 = "0.1.2.3";
  const std::string version2 = "9.8.7.6";
  AddExtension(kExtensionId1, version1, true,
               /*disable_reasons=*/{}, ManifestLocation::kExternalRegistry);
  AddExtension(kExtensionId2, version2, true,
               /*disable_reasons=*/{}, ManifestLocation::kUnpacked);

  ExtensionUpdateDataMap update_data;
  update_data[kExtensionId1] = {};
  update_data[kExtensionId2] = {};

  std::vector<std::optional<update_client::CrxComponent>> data;
  data_provider->GetData(
      false /*install_immediately*/, update_data,
      {kExtensionId1, kExtensionId2},
      base::BindLambdaForTesting(
          [&](const std::vector<std::optional<update_client::CrxComponent>>&
                  output) { data = output; }));

  ASSERT_EQ(2UL, data.size());
  EXPECT_EQ(version1, data[0]->version.GetString());
  EXPECT_NE(nullptr, data[0]->installer.get());
  EXPECT_EQ(0UL, data[0]->disabled_reasons.size());
  EXPECT_EQ("external", data[0]->install_location);
  EXPECT_EQ(version2, data[1]->version.GetString());
  EXPECT_NE(nullptr, data[1]->installer.get());
  EXPECT_EQ(0UL, data[1]->disabled_reasons.size());
  EXPECT_EQ("other", data[1]->install_location);
}

TEST_F(UpdateDataProviderTest, GetData_MultipleExtensions_DisabledExtension) {
  // One extension is disabled.
  scoped_refptr<UpdateDataProvider> data_provider =
      base::MakeRefCounted<UpdateDataProvider>(browser_context());

  const std::string version1 = "0.1.2.3";
  const std::string version2 = "9.8.7.6";
  AddExtension(kExtensionId1, version1, false,
               {disable_reason::DisableReason::DISABLE_CORRUPTED},
               ManifestLocation::kInternal);
  AddExtension(kExtensionId2, version2, true,
               /*disable_reasons=*/{}, ManifestLocation::kExternalPrefDownload);

  ExtensionUpdateDataMap update_data;
  update_data[kExtensionId1] = {};
  update_data[kExtensionId2] = {};

  std::vector<std::optional<update_client::CrxComponent>> data;
  data_provider->GetData(
      true /*install_immediately*/, update_data, {kExtensionId1, kExtensionId2},
      base::BindLambdaForTesting(
          [&](const std::vector<std::optional<update_client::CrxComponent>>&
                  output) { data = output; }));

  ASSERT_EQ(2UL, data.size());
  EXPECT_EQ(version1, data[0]->version.GetString());
  EXPECT_NE(nullptr, data[0]->installer.get());
  ASSERT_EQ(1UL, data[0]->disabled_reasons.size());
  EXPECT_EQ(disable_reason::DisableReason::DISABLE_CORRUPTED,
            data[0]->disabled_reasons[0]);
  EXPECT_EQ("internal", data[0]->install_location);

  EXPECT_EQ(version2, data[1]->version.GetString());
  EXPECT_NE(nullptr, data[1]->installer.get());
  EXPECT_EQ(0UL, data[1]->disabled_reasons.size());
  EXPECT_EQ("external", data[1]->install_location);
}

TEST_F(UpdateDataProviderTest,
       GetData_MultipleExtensions_NotInstalledExtension) {
  // One extension is not installed.
  scoped_refptr<UpdateDataProvider> data_provider =
      base::MakeRefCounted<UpdateDataProvider>(browser_context());

  const std::string version = "0.1.2.3";
  AddExtension(kExtensionId1, version, true,
               /*disable_reasons=*/{}, ManifestLocation::kComponent);

  ExtensionUpdateDataMap update_data;
  update_data[kExtensionId1] = {};
  update_data[kExtensionId2] = {};

  std::vector<std::optional<update_client::CrxComponent>> data;
  data_provider->GetData(
      false /*install_immediately*/, update_data,
      {kExtensionId1, kExtensionId2},
      base::BindLambdaForTesting(
          [&](const std::vector<std::optional<update_client::CrxComponent>>&
                  output) { data = output; }));

  ASSERT_EQ(2UL, data.size());
  ASSERT_NE(std::nullopt, data[0]);
  EXPECT_EQ(version, data[0]->version.GetString());
  EXPECT_NE(nullptr, data[0]->installer.get());
  EXPECT_EQ(0UL, data[0]->disabled_reasons.size());
  EXPECT_EQ("other", data[0]->install_location);

  EXPECT_EQ(std::nullopt, data[1]);
}

TEST_F(UpdateDataProviderTest, GetData_MultipleExtensions_CorruptExtension) {
  // With non-default data, one extension is corrupted:
  // is_corrupt_reinstall=true.
  scoped_refptr<UpdateDataProvider> data_provider =
      base::MakeRefCounted<UpdateDataProvider>(browser_context());

  const std::string version1 = "0.1.2.3";
  const std::string version2 = "9.8.7.6";
  const std::string initial_version = "0.0.0.0";
  AddExtension(kExtensionId1, version1, true,
               /*disable_reasons=*/{}, ManifestLocation::kExternalComponent);
  AddExtension(kExtensionId2, version2, true,
               /*disable_reasons=*/{}, ManifestLocation::kExternalPolicy);

  ExtensionUpdateDataMap update_data;
  auto& info1 = update_data[kExtensionId1];
  auto& info2 = update_data[kExtensionId2];

  info1.install_source = "webstore";
  info2.is_corrupt_reinstall = true;
  info2.install_source = "sideload";

  std::vector<std::optional<update_client::CrxComponent>> data;
  data_provider->GetData(
      true /*install_immediately*/, update_data, {kExtensionId1, kExtensionId2},
      base::BindLambdaForTesting(
          [&](const std::vector<std::optional<update_client::CrxComponent>>&
                  output) { data = output; }));

  ASSERT_EQ(2UL, data.size());
  EXPECT_EQ(version1, data[0]->version.GetString());
  EXPECT_EQ("webstore", data[0]->install_source);
  EXPECT_EQ("other", data[0]->install_location);
  EXPECT_NE(nullptr, data[0]->installer.get());
  EXPECT_EQ(0UL, data[0]->disabled_reasons.size());
  EXPECT_EQ(initial_version, data[1]->version.GetString());
  EXPECT_EQ("reinstall", data[1]->install_source);
  EXPECT_EQ("policy", data[1]->install_location);
  EXPECT_NE(nullptr, data[1]->installer.get());
  EXPECT_EQ(0UL, data[1]->disabled_reasons.size());
}

TEST_F(UpdateDataProviderTest, GetData_InstallImmediately) {
  // Verify that GetData propagtes install_immediately correctly to the crx
  // installer.
  AddExtension(kExtensionId1, "0.1.1.3", true,
               /*disable_reasons=*/{}, ManifestLocation::kInternal);

  scoped_refptr<UpdateDataProvider> data_provider =
      base::MakeRefCounted<UpdateDataProvider>(browser_context());

  ExtensionUpdateDataMap update_data;
  update_data[kExtensionId1] = {};

  std::vector<std::optional<update_client::CrxComponent>> data1;
  data_provider->GetData(
      false /*install_immediately*/, update_data, {kExtensionId1},
      base::BindLambdaForTesting(
          [&](const std::vector<std::optional<update_client::CrxComponent>>&
                  output) { data1 = output; }));
  ASSERT_EQ(1UL, data1.size());
  ASSERT_NE(nullptr, data1[0]->installer.get());
  const ExtensionInstaller* installer1 =
      static_cast<ExtensionInstaller*>(data1[0]->installer.get());
  EXPECT_FALSE(installer1->install_immediately());

  std::vector<std::optional<update_client::CrxComponent>> data2;
  data_provider->GetData(
      true /*install_immediately*/, update_data, {kExtensionId1},
      base::BindLambdaForTesting(
          [&](const std::vector<std::optional<update_client::CrxComponent>>&
                  output) { data2 = output; }));
  ASSERT_EQ(1UL, data2.size());
  ASSERT_NE(nullptr, data2[0]->installer.get());
  const ExtensionInstaller* installer2 =
      static_cast<ExtensionInstaller*>(data2[0]->installer.get());
  EXPECT_TRUE(installer2->install_immediately());
}

TEST_F(UpdateDataProviderTest, GetData_Pending_Version) {
  scoped_refptr<UpdateDataProvider> data_provider =
      base::MakeRefCounted<UpdateDataProvider>(browser_context());

  const std::string version = "0.1.2.3";
  const std::string pending_version = "0.1.2.4";
  const std::string pending_fingerprint = "fingerprint";

  AddExtension(kExtensionId1, version, true,
               /*disable_reasons=*/{}, ManifestLocation::kInternal);

  ExtensionUpdateDataMap update_data;
  update_data[kExtensionId1] = {};
  update_data[kExtensionId1].pending_version = pending_version;
  update_data[kExtensionId1].pending_fingerprint = pending_fingerprint;

  std::vector<std::optional<update_client::CrxComponent>> data;
  data_provider->GetData(
      false /*install_immediately*/, update_data, {kExtensionId1},
      base::BindLambdaForTesting(
          [&](const std::vector<std::optional<update_client::CrxComponent>>&
                  output) { data = output; }));

  ASSERT_EQ(1UL, data.size());
  EXPECT_EQ(pending_version, data[0]->version.GetString());
  EXPECT_EQ(pending_fingerprint, data[0]->fingerprint);
}

}  // namespace extensions