blob: 891d339f550d67438a0455757db62f7301a16a7f [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/skills/internal/skills_downloader.h"
#include <string>
#include <utility>
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "components/skills/proto/skill.pb.h"
#include "net/http/http_status_code.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace skills {
namespace {
// This must match kSkillsDownloaderGstaticUrl in skills_downloader.cc
inline constexpr char kSkillsDownloaderGstaticUrl[] =
"https://www.gstatic.com/chrome/skills/first_party_skills_binary";
std::string CreateValidSkillsListString() {
skills::proto::SkillsList skills_list;
auto* skill = skills_list.add_skills();
skill->set_name("/test-skill");
skill->set_category("Test Category");
skill->set_prompt("Test prompt.");
skill->set_id("test-id");
return skills_list.SerializeAsString();
}
void AddValidSkillsResponse(
network::TestURLLoaderFactory& test_url_loader_factory) {
test_url_loader_factory.AddResponse(kSkillsDownloaderGstaticUrl,
CreateValidSkillsListString());
}
void AddInvalidSkillsResponse(
network::TestURLLoaderFactory& test_url_loader_factory) {
test_url_loader_factory.AddResponse(kSkillsDownloaderGstaticUrl,
"This is not a protobuf");
}
} // namespace
class SkillsDownloaderTest : public testing::Test {
protected:
base::test::TaskEnvironment task_environment_;
};
TEST_F(SkillsDownloaderTest, ReturnsValidSkills) {
network::TestURLLoaderFactory test_url_loader_factory;
AddValidSkillsResponse(test_url_loader_factory);
SkillsDownloader downloader(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory));
std::unique_ptr<SkillsDownloader::SkillsMap> result_map;
base::RunLoop run_loop;
downloader.FetchDiscoverySkills(base::BindLambdaForTesting(
[&](std::unique_ptr<SkillsDownloader::SkillsMap> skills_map) {
result_map = std::move(skills_map);
run_loop.Quit();
}));
run_loop.Run();
// Verify the result.
ASSERT_TRUE(result_map);
ASSERT_EQ(result_map->size(), 1u);
auto it = result_map->find("test-id");
ASSERT_NE(it, result_map->end());
EXPECT_EQ(it->second.name(), "/test-skill");
}
TEST_F(SkillsDownloaderTest, ReturnsValidSkillsWithRetry) {
network::TestURLLoaderFactory test_url_loader_factory;
SkillsDownloader downloader(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory));
std::unique_ptr<SkillsDownloader::SkillsMap> result_map;
base::RunLoop run_loop;
// 1. First call triggers a 404 (Not Found).
// Note: We use 404 instead of 500 here to ensure the SimpleURLLoader fails
// immediately without triggering its internal auto-retry logic
// (RETRY_ON_5XX), allowing us to test the class's ability to handle a fresh
// fetch attempt.
downloader.FetchDiscoverySkills(base::BindLambdaForTesting(
[&](std::unique_ptr<SkillsDownloader::SkillsMap> skills_map) {
// Expect failure (nullptr) for the first attempt.
EXPECT_FALSE(skills_map);
run_loop.Quit();
}));
test_url_loader_factory.SimulateResponseForPendingRequest(
kSkillsDownloaderGstaticUrl, "", net::HTTP_NOT_FOUND);
run_loop.Run();
// 2. Second call returns valid data.
AddValidSkillsResponse(test_url_loader_factory);
base::RunLoop second_run_loop;
downloader.FetchDiscoverySkills(base::BindLambdaForTesting(
[&](std::unique_ptr<SkillsDownloader::SkillsMap> skills_map) {
result_map = std::move(skills_map);
second_run_loop.Quit();
}));
second_run_loop.Run();
ASSERT_TRUE(result_map);
EXPECT_EQ(result_map->begin()->first, "test-id");
}
TEST_F(SkillsDownloaderTest, ReturnsInvalidProtocolBuffer) {
network::TestURLLoaderFactory test_url_loader_factory;
AddInvalidSkillsResponse(test_url_loader_factory);
SkillsDownloader downloader(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory));
base::RunLoop run_loop;
downloader.FetchDiscoverySkills(base::BindLambdaForTesting(
[&](std::unique_ptr<SkillsDownloader::SkillsMap> skills_map) {
EXPECT_FALSE(skills_map);
run_loop.Quit();
}));
run_loop.Run();
}
TEST_F(SkillsDownloaderTest, ReturnsEmptyResponse) {
network::TestURLLoaderFactory test_url_loader_factory;
test_url_loader_factory.AddResponse(kSkillsDownloaderGstaticUrl, "");
SkillsDownloader downloader(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory));
base::RunLoop run_loop;
downloader.FetchDiscoverySkills(base::BindLambdaForTesting(
[&](std::unique_ptr<SkillsDownloader::SkillsMap> skills_map) {
EXPECT_FALSE(skills_map);
run_loop.Quit();
}));
run_loop.Run();
}
} // namespace skills