[go: nahoru, domu]

blob: bdc395f7135638a8cc993378f42c78c817c58694 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/first_party_sets/first_party_sets_handler_impl.h"
#include <string>
#include "base/callback_helpers.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/callback_helpers.h"
#include "base/json/json_reader.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/version.h"
#include "content/browser/first_party_sets/first_party_set_parser.h"
#include "content/browser/first_party_sets/local_set_declaration.h"
#include "content/public/browser/first_party_sets_handler.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_browser_context.h"
#include "net/base/schemeful_site.h"
#include "net/first_party_sets/first_party_set_entry.h"
#include "net/first_party_sets/first_party_set_metadata.h"
#include "net/first_party_sets/first_party_sets_cache_filter.h"
#include "net/first_party_sets/first_party_sets_context_config.h"
#include "net/first_party_sets/global_first_party_sets.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "url/gurl.h"
using ::testing::_;
using ::testing::Eq;
using ::testing::IsEmpty;
using ::testing::Not;
using ::testing::Optional;
using ::testing::Pair;
using ::testing::SizeIs;
using ::testing::UnorderedElementsAre;
// Some of these tests overlap with FirstPartySetParser unittests, but
// overlapping test coverage isn't the worst thing.
namespace content {
namespace {
using ParseErrorType = FirstPartySetsHandler::ParseErrorType;
using ParseWarningType = FirstPartySetsHandler::ParseWarningType;
const char* kAdditionsField = "additions";
const char* kPrimaryField = "primary";
const char* kCctldsField = "ccTLDs";
const char* kFirstPartySetsClearSiteDataOutcomeHistogram =
"FirstPartySets.Initialization.ClearSiteDataOutcomeType";
} // namespace
TEST(FirstPartySetsHandlerImpl, ValidateEnterprisePolicy_ValidPolicy) {
base::Value input = base::JSONReader::Read(R"(
{
"replacements": [
{
"primary": "https://primary1.test",
"associatedSites": ["https://associatedsite1.test"]
}
],
"additions": [
{
"primary": "https://primary2.test",
"associatedSites": ["https://associatedsite2.test"]
}
]
}
)")
.value();
// Validation doesn't fail with an error and there are no warnings to output.
std::pair<absl::optional<FirstPartySetsHandler::ParseError>,
std::vector<FirstPartySetsHandler::ParseWarning>>
opt_error_and_warnings =
FirstPartySetsHandler::ValidateEnterprisePolicy(input.GetDict());
EXPECT_FALSE(opt_error_and_warnings.first.has_value());
EXPECT_THAT(opt_error_and_warnings.second, IsEmpty());
}
TEST(FirstPartySetsHandlerImpl,
ValidateEnterprisePolicy_ValidPolicyWithWarnings) {
// Some input that matches our policies schema but returns non-fatal warnings.
base::Value input = base::JSONReader::Read(R"(
{
"replacements": [],
"additions": [
{
"primary": "https://primary1.test",
"associatedSites": ["https://associatedsite1.test"],
"ccTLDs": {
"https://non-canonical.test": ["https://primary1.test"]
}
}
]
}
)")
.value();
// Validation succeeds without errors.
std::pair<absl::optional<FirstPartySetsHandler::ParseError>,
std::vector<FirstPartySetsHandler::ParseWarning>>
opt_error_and_warnings =
FirstPartySetsHandler::ValidateEnterprisePolicy(input.GetDict());
EXPECT_FALSE(opt_error_and_warnings.first.has_value());
// Outputs metadata that can be used to surface a descriptive warning.
EXPECT_EQ(opt_error_and_warnings.second,
std::vector<FirstPartySetsHandler::ParseWarning>{
FirstPartySetsHandler::ParseWarning(
ParseWarningType::kCctldKeyNotCanonical,
{kAdditionsField, 0, kCctldsField,
"https://non-canonical.test"})});
}
TEST(FirstPartySetsHandlerImpl, ValidateEnterprisePolicy_InvalidPolicy) {
// Some input that matches our policies schema but breaks FPS invariants.
// For more test coverage, see the ParseSetsFromEnterprisePolicy unit tests.
base::Value input = base::JSONReader::Read(R"(
{
"replacements": [
{
"primary": "https://primary1.test",
"associatedSites": ["https://associatedsite1.test"]
}
],
"additions": [
{
"primary": "https://primary1.test",
"associatedSites": ["https://associatedsite2.test"]
}
]
}
)")
.value();
// Validation fails with an error.
std::pair<absl::optional<FirstPartySetsHandler::ParseError>,
std::vector<FirstPartySetsHandler::ParseWarning>>
opt_error_and_warnings =
FirstPartySetsHandler::ValidateEnterprisePolicy(input.GetDict());
EXPECT_TRUE(opt_error_and_warnings.first.has_value());
// An appropriate ParseError is returned.
EXPECT_EQ(
opt_error_and_warnings.first.value(),
FirstPartySetsHandler::ParseError(ParseErrorType::kNonDisjointSets,
{kAdditionsField, 0, kPrimaryField}));
}
class FirstPartySetsHandlerImplEnabledTest : public ::testing::Test {
public:
explicit FirstPartySetsHandlerImplEnabledTest()
: handler_(FirstPartySetsHandlerImpl::CreateForTesting(
/*enabled=*/true,
/*embedder_will_provide_public_sets=*/true)) {
CHECK(scoped_dir_.CreateUniqueTempDir());
CHECK(PathExists(scoped_dir_.GetPath()));
}
base::File WritePublicSetsFile(base::StringPiece content) {
base::FilePath path =
scoped_dir_.GetPath().Append(FILE_PATH_LITERAL("sets_file.json"));
CHECK(base::WriteFile(path, content));
return base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
}
net::GlobalFirstPartySets GetSetsAndWait(FirstPartySetsHandlerImpl& handler) {
base::test::TestFuture<net::GlobalFirstPartySets> future;
absl::optional<net::GlobalFirstPartySets> result =
handler.GetSets(future.GetCallback());
return result.has_value() ? std::move(result).value() : future.Take();
}
void ClearSiteDataOnChangedSetsForContextAndWait(
FirstPartySetsHandlerImpl& handler,
BrowserContext* context,
const std::string& browser_context_id,
net::FirstPartySetsContextConfig context_config) {
base::RunLoop run_loop;
handler.ClearSiteDataOnChangedSetsForContext(
base::BindLambdaForTesting([&]() { return context; }),
browser_context_id, std::move(context_config),
base::BindLambdaForTesting(
[&](net::FirstPartySetsContextConfig,
net::FirstPartySetsCacheFilter) { run_loop.Quit(); }));
run_loop.Run();
}
absl::optional<net::GlobalFirstPartySets> GetPersistedGlobalSetsAndWait(
FirstPartySetsHandlerImpl& handler,
const std::string& browser_context_id) {
base::test::TestFuture<absl::optional<net::GlobalFirstPartySets>> future;
handler.GetPersistedGlobalSetsForTesting(browser_context_id,
future.GetCallback());
return future.Take();
}
absl::optional<bool> HasEntryInBrowserContextsClearedAndWait(
FirstPartySetsHandlerImpl& handler,
const std::string& browser_context_id) {
base::test::TestFuture<absl::optional<bool>> future;
handler.HasBrowserContextClearedForTesting(browser_context_id,
future.GetCallback());
return future.Take();
}
net::GlobalFirstPartySets GetSetsAndWait() {
return GetSetsAndWait(handler());
}
void ClearSiteDataOnChangedSetsForContextAndWait(
BrowserContext* context,
const std::string& browser_context_id,
net::FirstPartySetsContextConfig context_config) {
ClearSiteDataOnChangedSetsForContextAndWait(
handler(), context, browser_context_id, std::move(context_config));
}
absl::optional<net::GlobalFirstPartySets> GetPersistedGlobalSetsAndWait(
const std::string& browser_context_id) {
return GetPersistedGlobalSetsAndWait(handler(), browser_context_id);
}
absl::optional<bool> HasEntryInBrowserContextsClearedAndWait(
const std::string& browser_context_id) {
return HasEntryInBrowserContextsClearedAndWait(handler(),
browser_context_id);
}
FirstPartySetsHandlerImpl& handler() { return handler_; }
BrowserContext* context() { return &context_; }
protected:
base::ScopedTempDir scoped_dir_;
private:
BrowserTaskEnvironment env_;
TestBrowserContext context_;
FirstPartySetsHandlerImpl handler_;
};
TEST_F(FirstPartySetsHandlerImplEnabledTest, EmptyDBPath) {
net::SchemefulSite example(GURL("https://example.test"));
net::SchemefulSite associated(GURL("https://associatedsite1.test"));
handler().SetPublicFirstPartySets(base::Version("0.0.1"),
WritePublicSetsFile(""));
// Empty `user_data_dir` will fail to load persisted sets, but that will not
// prevent `on_sets_ready` from being invoked.
handler().Init(
/*user_data_dir=*/{},
LocalSetDeclaration(
R"({"primary": "https://example.test",)"
R"("associatedSites": ["https://associatedsite1.test"]})"));
EXPECT_THAT(
GetSetsAndWait().FindEntries({example, associated},
net::FirstPartySetsContextConfig()),
UnorderedElementsAre(
Pair(example, net::FirstPartySetEntry(
example, net::SiteType::kPrimary, absl::nullopt)),
Pair(associated, net::FirstPartySetEntry(
example, net::SiteType::kAssociated, 0))));
}
TEST_F(FirstPartySetsHandlerImplEnabledTest,
ClearSiteDataOnChangedSetsForContext_FeatureNotEnabled) {
base::test::ScopedFeatureList features;
features.InitAndEnableFeatureWithParameters(
features::kFirstPartySets,
{{features::kFirstPartySetsClearSiteDataOnChangedSets.name, "false"}});
base::HistogramTester histogram;
net::SchemefulSite foo(GURL("https://foo.test"));
net::SchemefulSite associated(GURL("https://associatedsite.test"));
const std::string browser_context_id = "profile";
const std::string input =
R"({"primary": "https://foo.test", )"
R"("associatedSites": ["https://associatedsite.test"]})";
ASSERT_TRUE(base::JSONReader::Read(input));
handler().SetPublicFirstPartySets(base::Version("0.0.1"),
WritePublicSetsFile(input));
handler().Init(scoped_dir_.GetPath(), LocalSetDeclaration());
ASSERT_THAT(GetSetsAndWait().FindEntries({foo, associated},
net::FirstPartySetsContextConfig()),
UnorderedElementsAre(
Pair(foo, net::FirstPartySetEntry(
foo, net::SiteType::kPrimary, absl::nullopt)),
Pair(associated, net::FirstPartySetEntry(
foo, net::SiteType::kAssociated, 0))));
ClearSiteDataOnChangedSetsForContextAndWait(
context(), browser_context_id, net::FirstPartySetsContextConfig());
EXPECT_THAT(
GetPersistedGlobalSetsAndWait(browser_context_id)
->FindEntries({foo, associated}, net::FirstPartySetsContextConfig()),
IsEmpty());
// Should not be recorded.
histogram.ExpectTotalCount(kFirstPartySetsClearSiteDataOutcomeHistogram, 0);
}
TEST_F(FirstPartySetsHandlerImplEnabledTest,
ClearSiteDataOnChangedSetsForContext_ManualSet_Successful) {
base::test::ScopedFeatureList features;
features.InitAndEnableFeatureWithParameters(
features::kFirstPartySets,
{{features::kFirstPartySetsClearSiteDataOnChangedSets.name, "true"}});
net::SchemefulSite foo(GURL("https://foo.test"));
net::SchemefulSite associated(GURL("https://associatedsite.test"));
net::SchemefulSite associated2(GURL("https://associatedsite2.test"));
const std::string browser_context_id = "profile";
base::HistogramTester histogram;
FirstPartySetsHandlerImpl handler =
FirstPartySetsHandlerImpl::CreateForTesting(true, false);
const std::string input =
R"({"primary": "https://foo.test", )"
R"("associatedSites": ["https://associatedsite.test"]})";
ASSERT_TRUE(base::JSONReader::Read(input));
handler.Init(scoped_dir_.GetPath(), LocalSetDeclaration(input));
// Should not yet be recorded.
histogram.ExpectTotalCount(kFirstPartySetsClearSiteDataOutcomeHistogram, 0);
ClearSiteDataOnChangedSetsForContextAndWait(
handler, context(), browser_context_id,
net::FirstPartySetsContextConfig());
EXPECT_THAT(
GetPersistedGlobalSetsAndWait(handler, browser_context_id)
->FindEntries({foo, associated}, net::FirstPartySetsContextConfig()),
UnorderedElementsAre(
Pair(foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary,
absl::nullopt)),
Pair(associated,
net::FirstPartySetEntry(foo, net::SiteType::kAssociated,
absl::nullopt))));
histogram.ExpectUniqueSample(
kFirstPartySetsClearSiteDataOutcomeHistogram,
FirstPartySetsHandlerImpl::ClearSiteDataOutcomeType::kSuccess, 1);
}
TEST_F(FirstPartySetsHandlerImplEnabledTest,
ClearSiteDataOnChangedSetsForContext_PublicSetsWithDiff_Successful) {
base::test::ScopedFeatureList features;
features.InitAndEnableFeatureWithParameters(
features::kFirstPartySets,
{{features::kFirstPartySetsClearSiteDataOnChangedSets.name, "true"}});
net::SchemefulSite foo(GURL("https://foo.test"));
net::SchemefulSite associated(GURL("https://associatedsite.test"));
net::SchemefulSite associated2(GURL("https://associatedsite2.test"));
const std::string browser_context_id = "profile";
{
base::HistogramTester histogram;
FirstPartySetsHandlerImpl handler =
FirstPartySetsHandlerImpl::CreateForTesting(true, true);
const std::string input =
R"({"primary": "https://foo.test", )"
R"("associatedSites": ["https://associatedsite.test"]})";
ASSERT_TRUE(base::JSONReader::Read(input));
handler.SetPublicFirstPartySets(base::Version("0.0.1"),
WritePublicSetsFile(input));
handler.Init(scoped_dir_.GetPath(), LocalSetDeclaration());
EXPECT_THAT(
HasEntryInBrowserContextsClearedAndWait(handler, browser_context_id),
Optional(false));
// Should not yet be recorded.
histogram.ExpectTotalCount(kFirstPartySetsClearSiteDataOutcomeHistogram, 0);
ClearSiteDataOnChangedSetsForContextAndWait(
handler, context(), browser_context_id,
net::FirstPartySetsContextConfig());
EXPECT_THAT(GetPersistedGlobalSetsAndWait(handler, browser_context_id)
->FindEntries({foo, associated},
net::FirstPartySetsContextConfig()),
UnorderedElementsAre(
Pair(foo, net::FirstPartySetEntry(
foo, net::SiteType::kPrimary, absl::nullopt)),
Pair(associated,
net::FirstPartySetEntry(
foo, net::SiteType::kAssociated, absl::nullopt))));
EXPECT_THAT(
HasEntryInBrowserContextsClearedAndWait(handler, browser_context_id),
Optional(true));
histogram.ExpectUniqueSample(
kFirstPartySetsClearSiteDataOutcomeHistogram,
FirstPartySetsHandlerImpl::ClearSiteDataOutcomeType::kSuccess, 1);
// Make sure the database is closed properly before being opened again.
handler.SynchronouslyResetDBHelperForTesting();
}
// Verify FPS transition clearing is working for non-empty sites-to-clear
// list.
{
base::HistogramTester histogram;
FirstPartySetsHandlerImpl handler =
FirstPartySetsHandlerImpl::CreateForTesting(true, true);
const std::string input =
R"({"primary": "https://foo.test", )"
R"("associatedSites": ["https://associatedsite2.test"]})";
ASSERT_TRUE(base::JSONReader::Read(input));
// The new public sets need to be associated with a different version.
handler.SetPublicFirstPartySets(base::Version("0.0.2"),
WritePublicSetsFile(input));
handler.Init(scoped_dir_.GetPath(), LocalSetDeclaration());
// Should not yet be recorded.
histogram.ExpectTotalCount(kFirstPartySetsClearSiteDataOutcomeHistogram, 0);
ClearSiteDataOnChangedSetsForContextAndWait(
handler, context(), browser_context_id,
net::FirstPartySetsContextConfig());
EXPECT_THAT(GetPersistedGlobalSetsAndWait(handler, browser_context_id)
->FindEntries({foo, associated2},
net::FirstPartySetsContextConfig()),
UnorderedElementsAre(
Pair(foo, net::FirstPartySetEntry(
foo, net::SiteType::kPrimary, absl::nullopt)),
Pair(associated2,
net::FirstPartySetEntry(
foo, net::SiteType::kAssociated, absl::nullopt))));
EXPECT_THAT(
HasEntryInBrowserContextsClearedAndWait(handler, browser_context_id),
Optional(true));
histogram.ExpectUniqueSample(
kFirstPartySetsClearSiteDataOutcomeHistogram,
FirstPartySetsHandlerImpl::ClearSiteDataOutcomeType::kSuccess, 1);
}
}
TEST_F(FirstPartySetsHandlerImplEnabledTest,
ClearSiteDataOnChangedSetsForContext_EmptyDBPath) {
base::test::ScopedFeatureList features;
features.InitAndEnableFeatureWithParameters(
features::kFirstPartySets,
{{features::kFirstPartySetsClearSiteDataOnChangedSets.name, "true"}});
base::HistogramTester histogram;
net::SchemefulSite foo(GURL("https://foo.test"));
net::SchemefulSite associated(GURL("https://associatedsite.test"));
const std::string browser_context_id = "profile";
const std::string input =
R"({"primary": "https://foo.test", )"
R"("associatedSites": ["https://associatedsite.test"]})";
ASSERT_TRUE(base::JSONReader::Read(input));
handler().SetPublicFirstPartySets(base::Version("0.0.1"),
WritePublicSetsFile(input));
handler().Init(
/*user_data_dir=*/{}, LocalSetDeclaration());
ASSERT_THAT(GetSetsAndWait().FindEntries({foo, associated},
net::FirstPartySetsContextConfig()),
UnorderedElementsAre(
Pair(foo, net::FirstPartySetEntry(
foo, net::SiteType::kPrimary, absl::nullopt)),
Pair(associated, net::FirstPartySetEntry(
foo, net::SiteType::kAssociated, 0))));
base::RunLoop run_loop;
ClearSiteDataOnChangedSetsForContextAndWait(
context(), browser_context_id, net::FirstPartySetsContextConfig());
EXPECT_EQ(GetPersistedGlobalSetsAndWait(browser_context_id), absl::nullopt);
// Should not be recorded.
histogram.ExpectTotalCount(kFirstPartySetsClearSiteDataOutcomeHistogram, 0);
}
TEST_F(FirstPartySetsHandlerImplEnabledTest,
ClearSiteDataOnChangedSetsForContext_BeforeSetsReady) {
base::test::ScopedFeatureList features;
features.InitAndEnableFeatureWithParameters(
features::kFirstPartySets,
{{features::kFirstPartySetsClearSiteDataOnChangedSets.name, "true"}});
base::HistogramTester histogram;
handler().Init(scoped_dir_.GetPath(), LocalSetDeclaration());
const std::string browser_context_id = "profile";
base::test::TestFuture<net::FirstPartySetsContextConfig,
net::FirstPartySetsCacheFilter>
future;
handler().ClearSiteDataOnChangedSetsForContext(
base::BindLambdaForTesting([&]() { return context(); }),
browser_context_id, net::FirstPartySetsContextConfig(),
future.GetCallback());
handler().SetPublicFirstPartySets(
base::Version("0.0.1"),
WritePublicSetsFile(
R"({"primary": "https://foo.test", )"
R"("associatedSites": ["https://associatedsite.test"]})"));
EXPECT_TRUE(future.Wait());
net::SchemefulSite foo(GURL("https://foo.test"));
net::SchemefulSite associated(GURL("https://associatedsite.test"));
EXPECT_THAT(
GetPersistedGlobalSetsAndWait(browser_context_id)
->FindEntries({foo, associated}, net::FirstPartySetsContextConfig()),
UnorderedElementsAre(
Pair(foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary,
absl::nullopt)),
Pair(associated,
net::FirstPartySetEntry(foo, net::SiteType::kAssociated,
absl::nullopt))));
histogram.ExpectUniqueSample(
kFirstPartySetsClearSiteDataOutcomeHistogram,
FirstPartySetsHandlerImpl::ClearSiteDataOutcomeType::kSuccess, 1);
}
TEST_F(FirstPartySetsHandlerImplEnabledTest,
GetSetsIfEnabledAndReady_AfterSetsReady) {
net::SchemefulSite example(GURL("https://example.test"));
net::SchemefulSite associated(GURL("https://associatedsite.test"));
const std::string input =
R"({"primary": "https://example.test", )"
R"("associatedSites": ["https://associatedsite.test"]})";
ASSERT_TRUE(base::JSONReader::Read(input));
handler().SetPublicFirstPartySets(base::Version("1.2.3"),
WritePublicSetsFile(input));
handler().Init(scoped_dir_.GetPath(), LocalSetDeclaration());
// Wait until initialization is complete.
GetSetsAndWait();
EXPECT_THAT(
handler()
.GetSets(base::NullCallback())
.value()
.FindEntries({example, associated},
net::FirstPartySetsContextConfig()),
UnorderedElementsAre(
Pair(example, net::FirstPartySetEntry(
example, net::SiteType::kPrimary, absl::nullopt)),
Pair(associated, net::FirstPartySetEntry(
example, net::SiteType::kAssociated, 0))));
}
TEST_F(FirstPartySetsHandlerImplEnabledTest,
GetSetsIfEnabledAndReady_BeforeSetsReady) {
net::SchemefulSite example(GURL("https://example.test"));
net::SchemefulSite associated(GURL("https://associatedsite.test"));
// Call GetSets before the sets are ready, and before Init has been called.
base::test::TestFuture<net::GlobalFirstPartySets> future;
EXPECT_EQ(handler().GetSets(future.GetCallback()), absl::nullopt);
handler().Init(scoped_dir_.GetPath(), LocalSetDeclaration());
const std::string input =
R"({"primary": "https://example.test", )"
R"("associatedSites": ["https://associatedsite.test"]})";
ASSERT_TRUE(base::JSONReader::Read(input));
handler().SetPublicFirstPartySets(base::Version("1.2.3"),
WritePublicSetsFile(input));
EXPECT_THAT(
future.Get().FindEntries({example, associated},
net::FirstPartySetsContextConfig()),
UnorderedElementsAre(
Pair(example, net::FirstPartySetEntry(
example, net::SiteType::kPrimary, absl::nullopt)),
Pair(associated, net::FirstPartySetEntry(
example, net::SiteType::kAssociated, 0))));
EXPECT_THAT(
handler()
.GetSets(base::NullCallback())
.value()
.FindEntries({example, associated},
net::FirstPartySetsContextConfig()),
UnorderedElementsAre(
Pair(example, net::FirstPartySetEntry(
example, net::SiteType::kPrimary, absl::nullopt)),
Pair(associated, net::FirstPartySetEntry(
example, net::SiteType::kAssociated, 0))));
}
TEST_F(FirstPartySetsHandlerImplEnabledTest,
ComputeFirstPartySetMetadata_SynchronousResult) {
handler().Init(scoped_dir_.GetPath(), LocalSetDeclaration());
handler().SetPublicFirstPartySets(
base::Version("1.2.3"),
WritePublicSetsFile(
R"({"primary": "https://example.test", )"
R"("associatedSites": ["https://associatedsite.test"]})"));
// Exploit another helper to wait until the public sets file has been read.
GetSetsAndWait();
net::SchemefulSite example(GURL("https://example.test"));
net::SchemefulSite associated(GURL("https://associatedsite.test"));
base::test::TestFuture<net::FirstPartySetMetadata> future;
handler().ComputeFirstPartySetMetadata(example, &associated,
/*party_context=*/{},
net::FirstPartySetsContextConfig(),
future.GetCallback());
EXPECT_TRUE(future.IsReady());
EXPECT_NE(future.Take(), net::FirstPartySetMetadata());
}
TEST_F(FirstPartySetsHandlerImplEnabledTest,
ComputeFirstPartySetMetadata_AsynchronousResult) {
// Send query before the sets are ready.
base::test::TestFuture<net::FirstPartySetMetadata> future;
net::SchemefulSite example(GURL("https://example.test"));
net::SchemefulSite associated(GURL("https://associatedsite.test"));
handler().ComputeFirstPartySetMetadata(
example, &associated, /*party_context=*/{},
net::FirstPartySetsContextConfig(), future.GetCallback());
EXPECT_FALSE(future.IsReady());
handler().Init(scoped_dir_.GetPath(), LocalSetDeclaration());
handler().SetPublicFirstPartySets(
base::Version("1.2.3"),
WritePublicSetsFile(
R"({"primary": "https://example.test", )"
R"("associatedSites": ["https://associatedsite.test"]})"));
EXPECT_NE(future.Get(), net::FirstPartySetMetadata());
}
class FirstPartySetsHandlerGetContextConfigForPolicyTest
: public FirstPartySetsHandlerImplEnabledTest {
public:
FirstPartySetsHandlerGetContextConfigForPolicyTest() {
handler().Init(scoped_dir_.GetPath(), LocalSetDeclaration());
}
// Writes the public list of First-Party Sets which GetContextConfigForPolicy
// awaits.
//
// Initializes the First-Party Sets with the following relationship:
//
// [
// {
// "primary": "https://primary1.test",
// "associatedSites": ["https://associatedsite1.test",
// "https://associatedsite2.test"]
// }
// ]
void InitPublicFirstPartySets() {
net::SchemefulSite primary1(GURL("https://primary1.test"));
net::SchemefulSite associated1(GURL("https://associatedsite1.test"));
net::SchemefulSite associated2(GURL("https://associatedsite2.test"));
const std::string input =
R"({"primary": "https://primary1.test", )"
R"("associatedSites": ["https://associatedsite1.test", "https://associatedsite2.test"]})";
ASSERT_TRUE(base::JSONReader::Read(input));
handler().SetPublicFirstPartySets(base::Version("1.2.3"),
WritePublicSetsFile(input));
ASSERT_THAT(
GetSetsAndWait().FindEntries({primary1, associated1, associated2},
net::FirstPartySetsContextConfig()),
SizeIs(3));
}
protected:
base::OnceCallback<void(net::FirstPartySetsContextConfig)>
GetConfigCallback() {
return future_.GetCallback();
}
net::FirstPartySetsContextConfig GetConfig() { return future_.Take(); }
private:
base::test::TestFuture<net::FirstPartySetsContextConfig> future_;
};
TEST_F(FirstPartySetsHandlerGetContextConfigForPolicyTest,
DefaultOverridesPolicy_DefaultContextConfigs) {
base::Value policy = base::JSONReader::Read(R"({})").value();
handler().GetContextConfigForPolicy(&policy.GetDict(), GetConfigCallback());
InitPublicFirstPartySets();
EXPECT_EQ(GetConfig(), net::FirstPartySetsContextConfig());
}
TEST_F(FirstPartySetsHandlerGetContextConfigForPolicyTest,
MalformedOverridesPolicy_DefaultContextConfigs) {
base::Value policy = base::JSONReader::Read(R"({
"replacements": 123,
"additions": true
})")
.value();
handler().GetContextConfigForPolicy(&policy.GetDict(), GetConfigCallback());
InitPublicFirstPartySets();
EXPECT_EQ(GetConfig(), net::FirstPartySetsContextConfig());
}
TEST_F(FirstPartySetsHandlerGetContextConfigForPolicyTest,
NonDefaultOverridesPolicy_NonDefaultContextConfigs) {
base::Value policy = base::JSONReader::Read(R"(
{
"replacements": [
{
"primary": "https://associatedsite1.test",
"associatedSites": ["https://primary3.test"]
}
],
"additions": [
{
"primary": "https://primary2.test",
"associatedSites": ["https://associatedsite2.test"]
}
]
}
)")
.value();
handler().GetContextConfigForPolicy(&policy.GetDict(), GetConfigCallback());
InitPublicFirstPartySets();
// We don't care what the customizations are, here; we only care that they're
// not a no-op.
EXPECT_FALSE(GetConfig().empty());
}
} // namespace content