| // Copyright 2025 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/interest_group/interest_group_manager_impl.h" |
| |
| #include <cstddef> |
| #include <iterator> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/check_op.h" |
| #include "base/containers/flat_set.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_forward.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/metrics/metrics_hashes.h" |
| #include "base/run_loop.h" |
| #include "base/strings/escape.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/task/sequenced_task_runner.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/time/time.h" |
| #include "components/metrics/dwa/dwa_recorder.h" |
| #include "content/browser/interest_group/interest_group_features.h" |
| #include "content/browser/interest_group/interest_group_manager_impl.h" |
| #include "content/browser/interest_group/test_interest_group_observer.h" |
| #include "content/public/browser/k_anonymity_service_delegate.h" |
| #include "services/network/public/cpp/shared_url_loader_factory.h" |
| #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" |
| #include "services/network/test/test_shared_url_loader_factory.h" |
| #include "services/network/test/test_url_loader_factory.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/common/features_generated.h" |
| #include "third_party/blink/public/common/interest_group/interest_group.h" |
| #include "url/origin.h" |
| |
| namespace content { |
| |
| class InterestGroupManagerImplTestPeer { |
| public: |
| explicit InterestGroupManagerImplTestPeer( |
| InterestGroupManagerImpl* interest_group_manager) |
| : interest_group_manager_(interest_group_manager) {} |
| |
| void UpdateInterestGroup(blink::InterestGroupKey group_key, |
| InterestGroupUpdate update) { |
| base::test::TestFuture<bool> update_complete_signal; |
| interest_group_manager_->UpdateInterestGroup( |
| group_key, std::move(update), update_complete_signal.GetCallback()); |
| EXPECT_TRUE(update_complete_signal.Wait()); |
| } |
| |
| raw_ptr<InterestGroupManagerImpl> interest_group_manager_; |
| }; |
| |
| namespace { |
| |
| class TestKAnonymityServiceDelegate : public KAnonymityServiceDelegate { |
| public: |
| TestKAnonymityServiceDelegate() = default; |
| |
| void JoinSet(std::string id, |
| base::OnceCallback<void(bool)> callback) override { |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), true)); |
| } |
| |
| void QuerySets( |
| std::vector<std::string> ids, |
| base::OnceCallback<void(std::vector<bool>)> callback) override { |
| size_t ids_size = ids.size(); |
| std::move(ids.begin(), ids.end(), std::back_inserter(queried_ids_)); |
| |
| // Return that nothing is k-anonymous. |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), |
| std::vector<bool>(ids_size, false))); |
| } |
| |
| base::TimeDelta GetJoinInterval() override { return base::Seconds(1); } |
| base::TimeDelta GetQueryInterval() override { return base::Seconds(1); } |
| |
| const std::vector<std::string>& queried_ids() const { return queried_ids_; } |
| |
| private: |
| std::vector<std::string> queried_ids_; |
| }; |
| |
| class InterestGroupManagerImplTest : public testing::Test { |
| protected: |
| void SetUp() override { |
| ASSERT_TRUE(temp_directory_.CreateUniqueTempDir()); |
| interest_group_manager_ = std::make_unique<InterestGroupManagerImpl>( |
| temp_directory_.GetPath(), /*in_memory=*/true, |
| InterestGroupManagerImpl::ProcessMode::kInRenderer, |
| test_url_loader_factory_.GetSafeWeakWrapper(), |
| base::BindRepeating(&InterestGroupManagerImplTest::GetKAnonDelegate, |
| base::Unretained(this))); |
| } |
| |
| std::vector<url::Origin> GetAllInterestGroupOwners() { |
| base::test::TestFuture<std::vector<url::Origin>> result; |
| interest_group_manager_->GetAllInterestGroupOwners(result.GetCallback()); |
| return result.Get(); |
| } |
| |
| scoped_refptr<StorageInterestGroups> GetInterestGroupsForOwner( |
| const url::Origin& owner) { |
| base::test::TestFuture<scoped_refptr<StorageInterestGroups>> result; |
| interest_group_manager_->GetInterestGroupsForOwner( |
| /*devtools_auction_id=*/std::nullopt, owner, result.GetCallback()); |
| return result.Get(); |
| } |
| |
| SingleStorageInterestGroup GetSingleInterestGroup(url::Origin test_origin) { |
| std::vector<url::Origin> origins = GetAllInterestGroupOwners(); |
| EXPECT_EQ(1u, origins.size()); |
| EXPECT_EQ(test_origin, origins[0]); |
| scoped_refptr<StorageInterestGroups> interest_groups = |
| GetInterestGroupsForOwner(test_origin); |
| CHECK_EQ(1u, interest_groups->size()); |
| return std::move(interest_groups->GetInterestGroups()[0]); |
| } |
| |
| KAnonymityServiceDelegate* GetKAnonDelegate() { return &k_anon_delegate_; } |
| |
| base::ScopedTempDir temp_directory_; |
| |
| // This uses MOCK_TIME so that we can declare expectations on join_time and |
| // last_updated below. |
| base::test::TaskEnvironment task_environment_{ |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME}; |
| |
| network::TestURLLoaderFactory test_url_loader_factory_; |
| TestKAnonymityServiceDelegate k_anon_delegate_; |
| |
| std::unique_ptr<InterestGroupManagerImpl> interest_group_manager_; |
| }; |
| |
| blink::InterestGroup NewInterestGroup(url::Origin owner, std::string name) { |
| blink::InterestGroup result; |
| result.owner = owner; |
| result.name = name; |
| result.bidding_url = owner.GetURL().Resolve("/bidding_script.js"); |
| result.update_url = owner.GetURL().Resolve("/update_script.js"); |
| result.expiry = base::Time::Now() + base::Days(30); |
| result.execution_mode = |
| blink::InterestGroup::ExecutionMode::kCompatibilityMode; |
| return result; |
| } |
| |
| TEST_F(InterestGroupManagerImplTest, JoinInterestGroupWithNoAds) { |
| base::HistogramTester histograms; |
| const url::Origin test_origin = |
| url::Origin::Create(GURL("https://owner.example.com")); |
| blink::InterestGroup test_group = NewInterestGroup(test_origin, "example"); |
| |
| TestInterestGroupObserver interest_group_observer; |
| interest_group_manager_->AddInterestGroupObserver(&interest_group_observer); |
| |
| interest_group_manager_->JoinInterestGroup(test_group, test_origin.GetURL()); |
| interest_group_observer.WaitForAccesses( |
| {{/*devtools_auction_id=*/"global", |
| InterestGroupManagerImpl::InterestGroupObserver::AccessType::kJoin, |
| /*owner_origin=*/test_origin, /*ig_name=*/"example", |
| /*bid=*/std::nullopt, /*bid_currency=*/std::nullopt, |
| /*component_seller_origin=*/std::nullopt}}); |
| |
| EXPECT_THAT(k_anon_delegate_.queried_ids(), testing::ElementsAre()); |
| |
| SingleStorageInterestGroup loaded_group = GetSingleInterestGroup(test_origin); |
| |
| EXPECT_EQ(test_origin, loaded_group->interest_group.owner); |
| EXPECT_EQ("example", loaded_group->interest_group.name); |
| EXPECT_EQ(1, loaded_group->bidding_browser_signals->join_count); |
| EXPECT_EQ(0, loaded_group->bidding_browser_signals->bid_count); |
| EXPECT_EQ(test_origin, loaded_group->joining_origin); |
| EXPECT_EQ(base::Time::Now(), loaded_group->join_time); |
| EXPECT_EQ(base::Time::Now(), loaded_group->last_updated); |
| EXPECT_EQ(base::Time::Min(), loaded_group->next_update_after); |
| |
| histograms.ExpectTotalCount( |
| "Ads.InterestGroup.NumSelectableBuyerAndSellerReportingIds", 0); |
| } |
| |
| TEST_F(InterestGroupManagerImplTest, JoinInterestGroupWithOneAd) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndDisableFeature(features::kFledgeCacheKAnonHashedKeys); |
| |
| base::HistogramTester histograms; |
| const url::Origin test_origin = |
| url::Origin::Create(GURL("https://owner.example.com")); |
| blink::InterestGroup test_group = NewInterestGroup(test_origin, "example"); |
| test_group.ads.emplace(); |
| test_group.ads->emplace_back( |
| /*render_gurl=*/GURL("https://full.example.com/ad1"), |
| /*metadata=*/"metadata1"); |
| |
| TestInterestGroupObserver interest_group_observer; |
| interest_group_manager_->AddInterestGroupObserver(&interest_group_observer); |
| |
| interest_group_manager_->JoinInterestGroup(test_group, test_origin.GetURL()); |
| interest_group_observer.WaitForAccesses( |
| {{/*devtools_auction_id=*/"global", |
| InterestGroupManagerImpl::InterestGroupObserver::AccessType::kJoin, |
| /*owner_origin=*/test_origin, /*ig_name=*/"example", |
| /*bid=*/std::nullopt, /*bid_currency=*/std::nullopt, |
| /*component_seller_origin=*/std::nullopt}}); |
| |
| EXPECT_THAT(k_anon_delegate_.queried_ids(), testing::SizeIs(2)); |
| |
| SingleStorageInterestGroup loaded_group = GetSingleInterestGroup(test_origin); |
| ASSERT_EQ(1u, loaded_group->interest_group.ads->size()); |
| const blink::InterestGroup::Ad& ad = (*loaded_group->interest_group.ads)[0]; |
| EXPECT_EQ(GURL("https://full.example.com/ad1"), ad.render_url()); |
| EXPECT_EQ("metadata1", ad.metadata); |
| |
| histograms.ExpectTotalCount( |
| "Ads.InterestGroup.NumSelectableBuyerAndSellerReportingIds", 0); |
| } |
| |
| TEST_F(InterestGroupManagerImplTest, |
| JoinInterestGroupWithOneAdAndSelectableBuyerAndSellerReportingIds) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitWithFeatures({blink::features::kFledgeAuctionDealSupport}, |
| {features::kFledgeCacheKAnonHashedKeys}); |
| |
| base::HistogramTester histograms; |
| const url::Origin test_origin = |
| url::Origin::Create(GURL("https://owner.example.com")); |
| blink::InterestGroup test_group = NewInterestGroup(test_origin, "example"); |
| test_group.ads.emplace(); |
| test_group.ads->emplace_back( |
| /*render_gurl=*/GURL("https://full.example.com/ad1"), |
| /*metadata=*/"metadata1", |
| /*size_group=*/std::nullopt, |
| /*buyer_reporting_id=*/std::nullopt, |
| /*buyer_and_seller_reporting_id=*/std::nullopt, |
| /*selectable_buyer_and_seller_reporting_ids=*/ |
| std::vector<std::string>({"selectable1", "selectable2", "selectable3"})); |
| |
| { |
| TestInterestGroupObserver interest_group_observer; |
| interest_group_manager_->AddInterestGroupObserver(&interest_group_observer); |
| |
| interest_group_manager_->JoinInterestGroup(test_group, |
| test_origin.GetURL()); |
| interest_group_observer.WaitForAccesses( |
| {{/*devtools_auction_id=*/"global", |
| InterestGroupManagerImpl::InterestGroupObserver::AccessType::kJoin, |
| /*owner_origin=*/test_origin, /*ig_name=*/"example", |
| /*bid=*/std::nullopt, /*bid_currency=*/std::nullopt, |
| /*component_seller_origin=*/std::nullopt}}); |
| |
| EXPECT_THAT(k_anon_delegate_.queried_ids(), testing::SizeIs(5)); |
| |
| SingleStorageInterestGroup loaded_group = |
| GetSingleInterestGroup(test_origin); |
| ASSERT_EQ(1u, loaded_group->interest_group.ads->size()); |
| const blink::InterestGroup::Ad& ad = (*loaded_group->interest_group.ads)[0]; |
| EXPECT_EQ(GURL("https://full.example.com/ad1"), ad.render_url()); |
| EXPECT_EQ("metadata1", ad.metadata); |
| EXPECT_THAT( |
| *ad.selectable_buyer_and_seller_reporting_ids, |
| testing::ElementsAre("selectable1", "selectable2", "selectable3")); |
| |
| interest_group_manager_->RemoveInterestGroupObserver( |
| &interest_group_observer); |
| } |
| |
| histograms.ExpectUniqueSample( |
| "Ads.InterestGroup.NumSelectableBuyerAndSellerReportingIds", 3, 1); |
| |
| { |
| base::test::ScopedFeatureList disabled_feature_list; |
| disabled_feature_list.InitAndDisableFeature( |
| blink::features::kFledgeAuctionDealSupport); |
| |
| TestInterestGroupObserver interest_group_observer; |
| interest_group_manager_->AddInterestGroupObserver(&interest_group_observer); |
| |
| interest_group_manager_->JoinInterestGroup(test_group, |
| test_origin.GetURL()); |
| interest_group_observer.WaitForAccesses( |
| {{/*devtools_auction_id=*/"global", |
| InterestGroupManagerImpl::InterestGroupObserver::AccessType::kJoin, |
| /*owner_origin=*/test_origin, /*ig_name=*/"example", |
| /*bid=*/std::nullopt, /*bid_currency=*/std::nullopt, |
| /*component_seller_origin=*/std::nullopt}}); |
| |
| EXPECT_THAT(k_anon_delegate_.queried_ids(), testing::SizeIs(5)); |
| |
| SingleStorageInterestGroup loaded_group = |
| GetSingleInterestGroup(test_origin); |
| ASSERT_EQ(1u, loaded_group->interest_group.ads->size()); |
| const blink::InterestGroup::Ad& ad = (*loaded_group->interest_group.ads)[0]; |
| EXPECT_EQ(GURL("https://full.example.com/ad1"), ad.render_url()); |
| EXPECT_EQ("metadata1", ad.metadata); |
| EXPECT_EQ(ad.selectable_buyer_and_seller_reporting_ids, std::nullopt); |
| } |
| } |
| |
| TEST_F(InterestGroupManagerImplTest, DwaMetricRecordsJoinPermissionResult) { |
| base::test::ScopedFeatureList scoped_feature_list; |
| scoped_feature_list.InitAndEnableFeature(metrics::dwa::kDwaFeature); |
| |
| metrics::dwa::DwaRecorder* recorder = metrics::dwa::DwaRecorder::Get(); |
| recorder->EnableRecording(); |
| |
| const url::Origin frame_origin = |
| url::Origin::Create(GURL("https://page.test")); |
| |
| const struct ExpectedRequest { |
| const char* owner_host; |
| bool permission_granted; |
| int expected_result_metric; |
| } kExpectedRequests[] = { |
| {"owner1.test", /*permission_granted=*/true, |
| /*expected_result_metric=*/0}, |
| {"owner2.test", /*permission_granted=*/false, |
| /*expected_result_metric=*/1}, |
| }; |
| |
| for (const auto& test_case : kExpectedRequests) { |
| recorder->Purge(); |
| ASSERT_THAT(recorder->GetEntriesForTesting(), testing::IsEmpty()); |
| |
| const url::Origin owner = url::Origin::Create( |
| GURL(base::StrCat({"https://", test_case.owner_host}))); |
| const GURL permissions_url( |
| base::StrCat({"https://", test_case.owner_host, |
| "/.well-known/interest-group/permissions/?origin=", |
| base::EscapeQueryParamValue(frame_origin.Serialize(), |
| /*use_plus=*/false)})); |
| |
| auto head = network::mojom::URLResponseHead::New(); |
| head->headers = net::HttpResponseHeaders::TryToCreate( |
| "HTTP/1.1 200 OK\nContent-Type: application/json\n"); |
| head->headers->SetHeader("Access-Control-Allow-Origin", "*"); |
| head->mime_type = "application/json"; |
| |
| const std::string response_body = |
| base::StringPrintf(R"({"joinAdInterestGroup": %s})", |
| test_case.permission_granted ? "true" : "false"); |
| test_url_loader_factory_.AddResponse( |
| permissions_url, std::move(head), response_body, |
| network::URLLoaderCompletionStatus(net::OK)); |
| |
| base::RunLoop run_loop; |
| interest_group_manager_->CheckPermissionsAndJoinInterestGroup( |
| NewInterestGroup(owner, "bar"), frame_origin.GetURL(), frame_origin, |
| net::NetworkIsolationKey(), /*report_result_only=*/false, |
| test_url_loader_factory_, |
| base::BindRepeating( |
| [](const std::vector<url::Origin>&) { return true; }), |
| base::BindOnce( |
| [](base::RunLoop* run_loop, bool /* failed_well_known_check */) { |
| run_loop->Quit(); |
| }, |
| &run_loop)); |
| run_loop.Run(); |
| |
| const auto& granted_entries = recorder->GetEntriesForTesting(); |
| ASSERT_EQ(granted_entries.size(), 1u); |
| EXPECT_EQ(granted_entries[0]->event_hash, |
| base::HashMetricName("InterestGroupJoin")); |
| EXPECT_EQ(granted_entries[0]->content_hash, |
| base::HashMetricName(test_case.owner_host)); |
| EXPECT_THAT( |
| granted_entries[0]->metrics, |
| testing::UnorderedElementsAre(testing::Pair( |
| base::HashMetricName("Result"), test_case.expected_result_metric))); |
| } |
| } |
| |
| TEST_F(InterestGroupManagerImplTest, UpdateInterestGroupWithNoAds) { |
| base::HistogramTester histograms; |
| const url::Origin test_origin = |
| url::Origin::Create(GURL("https://owner.example.com")); |
| blink::InterestGroup test_group = NewInterestGroup(test_origin, "example"); |
| |
| TestInterestGroupObserver interest_group_observer; |
| interest_group_manager_->AddInterestGroupObserver(&interest_group_observer); |
| |
| interest_group_manager_->JoinInterestGroup(test_group, test_origin.GetURL()); |
| interest_group_observer.WaitForAccesses( |
| {{/*devtools_auction_id=*/"global", |
| InterestGroupManagerImpl::InterestGroupObserver::AccessType::kJoin, |
| /*owner_origin=*/test_origin, /*ig_name=*/"example", |
| /*bid=*/std::nullopt, /*bid_currency=*/std::nullopt, |
| /*component_seller_origin=*/std::nullopt}}); |
| |
| InterestGroupUpdate update; |
| update.ads.emplace(); |
| |
| InterestGroupManagerImplTestPeer(interest_group_manager_.get()) |
| .UpdateInterestGroup(blink::InterestGroupKey(test_origin, "example"), |
| std::move(update)); |
| |
| EXPECT_THAT(k_anon_delegate_.queried_ids(), testing::ElementsAre()); |
| |
| SingleStorageInterestGroup loaded_group = GetSingleInterestGroup(test_origin); |
| |
| EXPECT_EQ(test_origin, loaded_group->interest_group.owner); |
| EXPECT_EQ("example", loaded_group->interest_group.name); |
| EXPECT_EQ(1, loaded_group->bidding_browser_signals->join_count); |
| EXPECT_EQ(0, loaded_group->bidding_browser_signals->bid_count); |
| EXPECT_EQ(test_origin, loaded_group->joining_origin); |
| EXPECT_EQ(base::Time::Now(), loaded_group->join_time); |
| EXPECT_EQ(base::Time::Now(), loaded_group->last_updated); |
| EXPECT_NE(base::Time::Min(), loaded_group->next_update_after); |
| |
| histograms.ExpectTotalCount( |
| "Ads.InterestGroup.NumSelectableBuyerAndSellerReportingIds", 0); |
| } |
| |
| TEST_F(InterestGroupManagerImplTest, UpdateInterestGroupWithOneAd) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndDisableFeature(features::kFledgeCacheKAnonHashedKeys); |
| |
| base::HistogramTester histograms; |
| const url::Origin test_origin = |
| url::Origin::Create(GURL("https://owner.example.com")); |
| blink::InterestGroup test_group = NewInterestGroup(test_origin, "example"); |
| |
| TestInterestGroupObserver interest_group_observer; |
| interest_group_manager_->AddInterestGroupObserver(&interest_group_observer); |
| |
| interest_group_manager_->JoinInterestGroup(test_group, test_origin.GetURL()); |
| interest_group_observer.WaitForAccesses( |
| {{/*devtools_auction_id=*/"global", |
| InterestGroupManagerImpl::InterestGroupObserver::AccessType::kJoin, |
| /*owner_origin=*/test_origin, /*ig_name=*/"example", |
| /*bid=*/std::nullopt, /*bid_currency=*/std::nullopt, |
| /*component_seller_origin=*/std::nullopt}}); |
| |
| InterestGroupUpdate update; |
| update.ads.emplace(); |
| update.ads->emplace_back( |
| /*render_gurl=*/GURL("https://full.example.com/ad1"), |
| /*metadata=*/"metadata1"); |
| |
| InterestGroupManagerImplTestPeer(interest_group_manager_.get()) |
| .UpdateInterestGroup(blink::InterestGroupKey(test_origin, "example"), |
| std::move(update)); |
| |
| EXPECT_THAT(k_anon_delegate_.queried_ids(), testing::SizeIs(2)); |
| |
| SingleStorageInterestGroup loaded_group = GetSingleInterestGroup(test_origin); |
| ASSERT_EQ(1u, loaded_group->interest_group.ads->size()); |
| const blink::InterestGroup::Ad& ad = (*loaded_group->interest_group.ads)[0]; |
| EXPECT_EQ(GURL("https://full.example.com/ad1"), ad.render_url()); |
| EXPECT_EQ("metadata1", ad.metadata); |
| |
| histograms.ExpectTotalCount( |
| "Ads.InterestGroup.NumSelectableBuyerAndSellerReportingIds", 0); |
| } |
| |
| TEST_F(InterestGroupManagerImplTest, |
| UpdateInterestGroupWithOneAdAndSelectableBuyerAndSellerReportingIds) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitWithFeatures({blink::features::kFledgeAuctionDealSupport}, |
| {features::kFledgeCacheKAnonHashedKeys}); |
| |
| base::HistogramTester histograms; |
| const url::Origin test_origin = |
| url::Origin::Create(GURL("https://owner.example.com")); |
| blink::InterestGroup test_group = NewInterestGroup(test_origin, "example"); |
| |
| TestInterestGroupObserver interest_group_observer; |
| interest_group_manager_->AddInterestGroupObserver(&interest_group_observer); |
| |
| interest_group_manager_->JoinInterestGroup(test_group, test_origin.GetURL()); |
| interest_group_observer.WaitForAccesses( |
| {{/*devtools_auction_id=*/"global", |
| InterestGroupManagerImpl::InterestGroupObserver::AccessType::kJoin, |
| /*owner_origin=*/test_origin, /*ig_name=*/"example", |
| /*bid=*/std::nullopt, /*bid_currency=*/std::nullopt, |
| /*component_seller_origin=*/std::nullopt}}); |
| |
| { |
| InterestGroupUpdate update; |
| update.ads.emplace(); |
| update.ads->emplace_back( |
| /*render_gurl=*/GURL("https://full.example.com/ad1"), |
| /*metadata=*/"metadata1", |
| /*size_group=*/std::nullopt, |
| /*buyer_reporting_id=*/std::nullopt, |
| /*buyer_and_seller_reporting_id=*/std::nullopt, |
| /*selectable_buyer_and_seller_reporting_ids=*/ |
| std::vector<std::string>( |
| {"selectable1", "selectable2", "selectable3"})); |
| |
| InterestGroupManagerImplTestPeer(interest_group_manager_.get()) |
| .UpdateInterestGroup(blink::InterestGroupKey(test_origin, "example"), |
| std::move(update)); |
| |
| EXPECT_THAT(k_anon_delegate_.queried_ids(), testing::SizeIs(5)); |
| |
| SingleStorageInterestGroup loaded_group = |
| GetSingleInterestGroup(test_origin); |
| ASSERT_EQ(1u, loaded_group->interest_group.ads->size()); |
| const blink::InterestGroup::Ad& ad = (*loaded_group->interest_group.ads)[0]; |
| EXPECT_EQ(GURL("https://full.example.com/ad1"), ad.render_url()); |
| EXPECT_EQ("metadata1", ad.metadata); |
| EXPECT_THAT( |
| *ad.selectable_buyer_and_seller_reporting_ids, |
| testing::ElementsAre("selectable1", "selectable2", "selectable3")); |
| } |
| |
| histograms.ExpectUniqueSample( |
| "Ads.InterestGroup.NumSelectableBuyerAndSellerReportingIds", 3, 1); |
| |
| { |
| base::test::ScopedFeatureList disabled_feature_list; |
| disabled_feature_list.InitAndDisableFeature( |
| blink::features::kFledgeAuctionDealSupport); |
| |
| InterestGroupUpdate update; |
| update.ads.emplace(); |
| update.ads->emplace_back( |
| /*render_gurl=*/GURL("https://full.example.com/ad1"), |
| /*metadata=*/"metadata1", |
| /*size_group=*/std::nullopt, |
| /*buyer_reporting_id=*/std::nullopt, |
| /*buyer_and_seller_reporting_id=*/std::nullopt, |
| /*selectable_buyer_and_seller_reporting_ids=*/ |
| std::vector<std::string>( |
| {"selectable1", "selectable2", "selectable3"})); |
| |
| InterestGroupManagerImplTestPeer(interest_group_manager_.get()) |
| .UpdateInterestGroup(blink::InterestGroupKey(test_origin, "example"), |
| std::move(update)); |
| |
| SingleStorageInterestGroup loaded_group = |
| GetSingleInterestGroup(test_origin); |
| ASSERT_EQ(1u, loaded_group->interest_group.ads->size()); |
| const blink::InterestGroup::Ad& ad = (*loaded_group->interest_group.ads)[0]; |
| EXPECT_EQ(GURL("https://full.example.com/ad1"), ad.render_url()); |
| EXPECT_EQ("metadata1", ad.metadata); |
| EXPECT_EQ(ad.selectable_buyer_and_seller_reporting_ids, std::nullopt); |
| } |
| } |
| |
| TEST_F(InterestGroupManagerImplTest, RecordRandomDebugReportLockout) { |
| base::test::TestFuture<std::optional<DebugReportLockoutAndCooldowns>> result; |
| base::flat_set<url::Origin> origins; |
| interest_group_manager_->GetDebugReportLockoutAndCooldowns( |
| origins, result.GetCallback()); |
| ASSERT_TRUE(result.Get().has_value()); |
| EXPECT_FALSE(result.Get()->lockout.has_value()); |
| |
| base::Time now = base::Time::Now(); |
| interest_group_manager_->RecordRandomDebugReportLockout(now); |
| base::test::TestFuture<std::optional<DebugReportLockoutAndCooldowns>> result2; |
| interest_group_manager_->GetDebugReportLockoutAndCooldowns( |
| origins, result2.GetCallback()); |
| |
| ASSERT_TRUE(result2.Get().has_value()); |
| EXPECT_TRUE(result2.Get()->lockout.has_value()); |
| base::Time expected_time = base::Time::FromDeltaSinceWindowsEpoch( |
| now.ToDeltaSinceWindowsEpoch().CeilToMultiple(base::Hours(1))); |
| EXPECT_EQ(expected_time, result2.Get()->lockout->starting_time); |
| EXPECT_GE(result2.Get()->lockout->duration, base::Days(1)); |
| EXPECT_LE(result2.Get()->lockout->duration, base::Days(90)); |
| } |
| } // namespace |
| } // namespace content |