| // 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 "net/reporting/reporting_service.h" |
| |
| #include <memory> |
| #include <string> |
| |
| #include "base/functional/bind.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/time/tick_clock.h" |
| #include "base/values.h" |
| #include "net/base/features.h" |
| #include "net/base/isolation_info.h" |
| #include "net/base/network_anonymization_key.h" |
| #include "net/base/schemeful_site.h" |
| #include "net/reporting/mock_persistent_reporting_store.h" |
| #include "net/reporting/reporting_browsing_data_remover.h" |
| #include "net/reporting/reporting_cache.h" |
| #include "net/reporting/reporting_context.h" |
| #include "net/reporting/reporting_endpoint.h" |
| #include "net/reporting/reporting_policy.h" |
| #include "net/reporting/reporting_report.h" |
| #include "net/reporting/reporting_service.h" |
| #include "net/reporting/reporting_test_util.h" |
| #include "net/test/test_with_task_environment.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" |
| #include "url/origin.h" |
| |
| namespace net { |
| namespace { |
| |
| using CommandType = MockPersistentReportingStore::Command::Type; |
| |
| // The tests are parametrized on a boolean value which represents whether to use |
| // a MockPersistentReportingStore (if false, no store is used). |
| class ReportingServiceTest : public ::testing::TestWithParam<bool>, |
| public WithTaskEnvironment { |
| protected: |
| const GURL kUrl_ = GURL("https://origin/path"); |
| const GURL kUrl2_ = GURL("https://origin2/path"); |
| const url::Origin kOrigin_ = url::Origin::Create(kUrl_); |
| const url::Origin kOrigin2_ = url::Origin::Create(kUrl2_); |
| const GURL kEndpoint_ = GURL("https://endpoint/"); |
| const GURL kEndpoint2_ = GURL("https://endpoint2/"); |
| const std::string kUserAgent_ = "Mozilla/1.0"; |
| const std::string kGroup_ = "group"; |
| const std::string kGroup2_ = "group2"; |
| const std::string kType_ = "type"; |
| const absl::optional<base::UnguessableToken> kReportingSource_ = |
| base::UnguessableToken::Create(); |
| const NetworkAnonymizationKey kNak_ = |
| NetworkAnonymizationKey::CreateSameSite(SchemefulSite(kOrigin_)); |
| const NetworkAnonymizationKey kNak2_ = |
| NetworkAnonymizationKey::CreateSameSite(SchemefulSite(kOrigin2_)); |
| const ReportingEndpointGroupKey kGroupKey_ = |
| ReportingEndpointGroupKey(kNak_, kOrigin_, kGroup_); |
| const ReportingEndpointGroupKey kGroupKey2_ = |
| ReportingEndpointGroupKey(kNak2_, kOrigin2_, kGroup_); |
| const IsolationInfo kIsolationInfo_ = |
| IsolationInfo::Create(IsolationInfo::RequestType::kOther, |
| kOrigin_, |
| kOrigin_, |
| SiteForCookies::FromOrigin(kOrigin_)); |
| |
| ReportingServiceTest() { |
| feature_list_.InitAndEnableFeature( |
| features::kPartitionNelAndReportingByNetworkIsolationKey); |
| Init(); |
| } |
| |
| // Initializes, or re-initializes, |service_| and its dependencies. |
| void Init() { |
| if (GetParam()) |
| store_ = std::make_unique<MockPersistentReportingStore>(); |
| else |
| store_ = nullptr; |
| |
| auto test_context = std::make_unique<TestReportingContext>( |
| &clock_, &tick_clock_, ReportingPolicy(), store_.get()); |
| context_ = test_context.get(); |
| |
| service_ = ReportingService::CreateForTesting(std::move(test_context)); |
| } |
| |
| // If the store exists, simulate finishing loading the store, which should |
| // make the rest of the test run synchronously. |
| void FinishLoading(bool load_success) { |
| if (store_) |
| store_->FinishLoading(load_success); |
| } |
| |
| MockPersistentReportingStore* store() { return store_.get(); } |
| TestReportingContext* context() { return context_; } |
| ReportingService* service() { return service_.get(); } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| |
| base::SimpleTestClock clock_; |
| base::SimpleTestTickClock tick_clock_; |
| |
| std::unique_ptr<MockPersistentReportingStore> store_; |
| raw_ptr<TestReportingContext> context_; |
| std::unique_ptr<ReportingService> service_; |
| }; |
| |
| TEST_P(ReportingServiceTest, QueueReport) { |
| service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_, |
| kType_, base::Value::Dict(), 0); |
| FinishLoading(true /* load_success */); |
| |
| std::vector<const ReportingReport*> reports; |
| context()->cache()->GetReports(&reports); |
| ASSERT_EQ(1u, reports.size()); |
| EXPECT_EQ(kUrl_, reports[0]->url); |
| EXPECT_EQ(kNak_, reports[0]->network_anonymization_key); |
| EXPECT_EQ(kUserAgent_, reports[0]->user_agent); |
| EXPECT_EQ(kGroup_, reports[0]->group); |
| EXPECT_EQ(kType_, reports[0]->type); |
| } |
| |
| TEST_P(ReportingServiceTest, QueueReportSanitizeUrl) { |
| // Same as kUrl_ but with username, password, and fragment. |
| GURL url = GURL("https://username:password@origin/path#fragment"); |
| service()->QueueReport(url, kReportingSource_, kNak_, kUserAgent_, kGroup_, |
| kType_, base::Value::Dict(), 0); |
| FinishLoading(true /* load_success */); |
| |
| std::vector<const ReportingReport*> reports; |
| context()->cache()->GetReports(&reports); |
| ASSERT_EQ(1u, reports.size()); |
| EXPECT_EQ(kUrl_, reports[0]->url); |
| EXPECT_EQ(kNak_, reports[0]->network_anonymization_key); |
| EXPECT_EQ(kUserAgent_, reports[0]->user_agent); |
| EXPECT_EQ(kGroup_, reports[0]->group); |
| EXPECT_EQ(kType_, reports[0]->type); |
| } |
| |
| TEST_P(ReportingServiceTest, DontQueueReportInvalidUrl) { |
| GURL url = GURL("https://"); |
| // This does not trigger an attempt to load from the store because the url |
| // is immediately rejected as invalid. |
| service()->QueueReport(url, kReportingSource_, kNak_, kUserAgent_, kGroup_, |
| kType_, base::Value::Dict(), 0); |
| |
| std::vector<const ReportingReport*> reports; |
| context()->cache()->GetReports(&reports); |
| ASSERT_EQ(0u, reports.size()); |
| } |
| |
| TEST_P(ReportingServiceTest, QueueReportNetworkIsolationKeyDisabled) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndDisableFeature( |
| features::kPartitionNelAndReportingByNetworkIsolationKey); |
| |
| // Re-create the store, so it reads the new feature value. |
| Init(); |
| |
| service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_, |
| kType_, base::Value::Dict(), 0); |
| FinishLoading(true /* load_success */); |
| |
| std::vector<const ReportingReport*> reports; |
| context()->cache()->GetReports(&reports); |
| ASSERT_EQ(1u, reports.size()); |
| |
| // NetworkAnonymizationKey should be empty, instead of kNak_; |
| EXPECT_EQ(NetworkAnonymizationKey(), reports[0]->network_anonymization_key); |
| EXPECT_NE(kNak_, reports[0]->network_anonymization_key); |
| |
| EXPECT_EQ(kUrl_, reports[0]->url); |
| EXPECT_EQ(kUserAgent_, reports[0]->user_agent); |
| EXPECT_EQ(kGroup_, reports[0]->group); |
| EXPECT_EQ(kType_, reports[0]->type); |
| } |
| |
| TEST_P(ReportingServiceTest, ProcessReportToHeader) { |
| service()->ProcessReportToHeader(kOrigin_, kNak_, |
| "{\"endpoints\":[{\"url\":\"" + |
| kEndpoint_.spec() + |
| "\"}]," |
| "\"group\":\"" + |
| kGroup_ + |
| "\"," |
| "\"max_age\":86400}"); |
| FinishLoading(true /* load_success */); |
| |
| EXPECT_EQ(1u, context()->cache()->GetEndpointCount()); |
| EXPECT_TRUE(context()->cache()->GetEndpointForTesting( |
| ReportingEndpointGroupKey(kNak_, kOrigin_, kGroup_), kEndpoint_)); |
| } |
| |
| TEST_P(ReportingServiceTest, ProcessReportingEndpointsHeader) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(net::features::kDocumentReporting); |
| auto parsed_header = |
| ParseReportingEndpoints(kGroup_ + "=\"" + kEndpoint_.spec() + "\""); |
| ASSERT_TRUE(parsed_header.has_value()); |
| service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_, |
| kIsolationInfo_, *parsed_header); |
| FinishLoading(true /* load_success */); |
| |
| // Endpoint should not be part of the persistent store. |
| EXPECT_EQ(0u, context()->cache()->GetEndpointCount()); |
| // Endpoint should be associated with the reporting source. |
| ReportingEndpoint cached_endpoint = |
| context()->cache()->GetV1EndpointForTesting(*kReportingSource_, kGroup_); |
| EXPECT_TRUE(cached_endpoint); |
| |
| // Ensure that the NIK is stored properly with the endpoint group. |
| EXPECT_FALSE(cached_endpoint.group_key.network_anonymization_key.IsEmpty()); |
| } |
| |
| TEST_P(ReportingServiceTest, |
| ProcessReportingEndpointsHeaderNetworkIsolationKeyDisabled) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitWithFeatures( |
| {net::features::kDocumentReporting}, |
| {features::kPartitionNelAndReportingByNetworkIsolationKey}); |
| |
| // Re-create the store, so it reads the new feature value. |
| Init(); |
| |
| auto parsed_header = |
| ParseReportingEndpoints(kGroup_ + "=\"" + kEndpoint_.spec() + "\""); |
| ASSERT_TRUE(parsed_header.has_value()); |
| service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_, |
| kIsolationInfo_, *parsed_header); |
| FinishLoading(true /* load_success */); |
| |
| // Endpoint should not be part of the persistent store. |
| EXPECT_EQ(0u, context()->cache()->GetEndpointCount()); |
| // Endpoint should be associated with the reporting source. |
| ReportingEndpoint cached_endpoint = |
| context()->cache()->GetV1EndpointForTesting(*kReportingSource_, kGroup_); |
| EXPECT_TRUE(cached_endpoint); |
| |
| // When isolation is disabled, cached endpoints should have a null NIK. |
| EXPECT_TRUE(cached_endpoint.group_key.network_anonymization_key.IsEmpty()); |
| } |
| |
| TEST_P(ReportingServiceTest, SendReportsAndRemoveSource) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(net::features::kDocumentReporting); |
| auto parsed_header = |
| ParseReportingEndpoints(kGroup_ + "=\"" + kEndpoint_.spec() + "\", " + |
| kGroup2_ + "=\"" + kEndpoint2_.spec() + "\""); |
| ASSERT_TRUE(parsed_header.has_value()); |
| service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_, |
| kIsolationInfo_, *parsed_header); |
| // This report should be sent immediately, starting the delivery agent timer. |
| service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_, |
| kType_, base::Value::Dict(), 0); |
| |
| FinishLoading(true /* load_success */); |
| |
| std::vector<const ReportingReport*> reports; |
| context()->cache()->GetReports(&reports); |
| ASSERT_EQ(1u, reports.size()); |
| EXPECT_EQ(0u, context()->cache()->GetReportCountWithStatusForTesting( |
| ReportingReport::Status::QUEUED)); |
| |
| // Now simulate the source being destroyed. |
| service()->SendReportsAndRemoveSource(*kReportingSource_); |
| |
| // There should be no queued reports, but the previously sent report should |
| // still be pending. |
| EXPECT_EQ(0u, context()->cache()->GetReportCountWithStatusForTesting( |
| ReportingReport::Status::QUEUED)); |
| EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting( |
| ReportingReport::Status::PENDING)); |
| // Source should be marked as expired. |
| ASSERT_TRUE( |
| context()->cache()->GetExpiredSources().contains(*kReportingSource_)); |
| } |
| |
| // Flaky in ChromeOS: crbug.com/1356127 |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_SendReportsAndRemoveSourceWithPendingReports \ |
| DISABLED_SendReportsAndRemoveSourceWithPendingReports |
| #else |
| #define MAYBE_SendReportsAndRemoveSourceWithPendingReports \ |
| SendReportsAndRemoveSourceWithPendingReports |
| #endif |
| TEST_P(ReportingServiceTest, |
| MAYBE_SendReportsAndRemoveSourceWithPendingReports) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(net::features::kDocumentReporting); |
| auto parsed_header = |
| ParseReportingEndpoints(kGroup_ + "=\"" + kEndpoint_.spec() + "\", " + |
| kGroup2_ + "=\"" + kEndpoint2_.spec() + "\""); |
| ASSERT_TRUE(parsed_header.has_value()); |
| service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_, |
| kIsolationInfo_, *parsed_header); |
| // This report should be sent immediately, starting the delivery agent timer. |
| service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_, |
| kType_, base::Value::Dict(), 0); |
| |
| FinishLoading(true /* load_success */); |
| |
| std::vector<const ReportingReport*> reports; |
| context()->cache()->GetReports(&reports); |
| ASSERT_EQ(1u, reports.size()); |
| EXPECT_EQ(0u, context()->cache()->GetReportCountWithStatusForTesting( |
| ReportingReport::Status::QUEUED)); |
| EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting( |
| ReportingReport::Status::PENDING)); |
| |
| // Queue another report, which should remain queued. |
| service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_, |
| kType_, base::Value::Dict(), 0); |
| EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting( |
| ReportingReport::Status::QUEUED)); |
| EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting( |
| ReportingReport::Status::PENDING)); |
| |
| // Now simulate the source being destroyed. |
| service()->SendReportsAndRemoveSource(*kReportingSource_); |
| |
| // The report should still be queued, while the source should be marked as |
| // expired. (The original report is still pending.) |
| EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting( |
| ReportingReport::Status::QUEUED)); |
| EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting( |
| ReportingReport::Status::PENDING)); |
| ASSERT_TRUE( |
| context()->cache()->GetExpiredSources().contains(kReportingSource_)); |
| } |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_ProcessReportingEndpointsHeaderPathAbsolute DISABLED_ProcessReportingEndpointsHeaderPathAbsolute |
| #else |
| #define MAYBE_ProcessReportingEndpointsHeaderPathAbsolute ProcessReportingEndpointsHeaderPathAbsolute |
| #endif |
| TEST_P(ReportingServiceTest, MAYBE_ProcessReportingEndpointsHeaderPathAbsolute) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndEnableFeature(net::features::kDocumentReporting); |
| auto parsed_header = ParseReportingEndpoints(kGroup_ + "=\"/path-absolute\""); |
| ASSERT_TRUE(parsed_header.has_value()); |
| service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_, |
| kIsolationInfo_, *parsed_header); |
| FinishLoading(true /* load_success */); |
| |
| // Endpoint should not be part of the persistent store. |
| EXPECT_EQ(0u, context()->cache()->GetEndpointCount()); |
| // Endpoint should be associated with the reporting source. |
| ReportingEndpoint endpoint = |
| context()->cache()->GetV1EndpointForTesting(*kReportingSource_, kGroup_); |
| EXPECT_TRUE(endpoint); |
| // Endpoint should have the correct path. |
| EXPECT_EQ(kUrl_.Resolve("/path-absolute"), endpoint.info.url); |
| } |
| |
| TEST_P(ReportingServiceTest, ProcessReportToHeaderPathAbsolute) { |
| service()->ProcessReportToHeader( |
| kOrigin_, kNak_, |
| "{\"endpoints\":[{\"url\":\"/path-absolute\"}]," |
| "\"group\":\"" + |
| kGroup_ + |
| "\"," |
| "\"max_age\":86400}"); |
| FinishLoading(true /* load_success */); |
| |
| EXPECT_EQ(1u, context()->cache()->GetEndpointCount()); |
| } |
| |
| TEST_P(ReportingServiceTest, ProcessReportToHeader_TooLong) { |
| const std::string header_too_long = |
| "{\"endpoints\":[{\"url\":\"" + kEndpoint_.spec() + |
| "\"}]," |
| "\"group\":\"" + |
| kGroup_ + |
| "\"," |
| "\"max_age\":86400," + |
| "\"junk\":\"" + std::string(32 * 1024, 'a') + "\"}"; |
| // This does not trigger an attempt to load from the store because the header |
| // is immediately rejected as invalid. |
| service()->ProcessReportToHeader(kOrigin_, kNak_, header_too_long); |
| |
| EXPECT_EQ(0u, context()->cache()->GetEndpointCount()); |
| } |
| |
| TEST_P(ReportingServiceTest, ProcessReportToHeader_TooDeep) { |
| const std::string header_too_deep = "{\"endpoints\":[{\"url\":\"" + |
| kEndpoint_.spec() + |
| "\"}]," |
| "\"group\":\"" + |
| kGroup_ + |
| "\"," |
| "\"max_age\":86400," + |
| "\"junk\":[[[[[[[[[[]]]]]]]]]]}"; |
| // This does not trigger an attempt to load from the store because the header |
| // is immediately rejected as invalid. |
| service()->ProcessReportToHeader(kOrigin_, kNak_, header_too_deep); |
| |
| EXPECT_EQ(0u, context()->cache()->GetEndpointCount()); |
| } |
| |
| TEST_P(ReportingServiceTest, ProcessReportToHeaderNetworkIsolationKeyDisabled) { |
| base::test::ScopedFeatureList feature_list; |
| feature_list.InitAndDisableFeature( |
| features::kPartitionNelAndReportingByNetworkIsolationKey); |
| |
| // Re-create the store, so it reads the new feature value. |
| Init(); |
| |
| service()->ProcessReportToHeader(kOrigin_, kNak_, |
| "{\"endpoints\":[{\"url\":\"" + |
| kEndpoint_.spec() + |
| "\"}]," |
| "\"group\":\"" + |
| kGroup_ + |
| "\"," |
| "\"max_age\":86400}"); |
| FinishLoading(true /* load_success */); |
| |
| EXPECT_EQ(1u, context()->cache()->GetEndpointCount()); |
| EXPECT_FALSE(context()->cache()->GetEndpointForTesting( |
| ReportingEndpointGroupKey(kNak_, kOrigin_, kGroup_), kEndpoint_)); |
| EXPECT_TRUE(context()->cache()->GetEndpointForTesting( |
| ReportingEndpointGroupKey(NetworkAnonymizationKey(), kOrigin_, kGroup_), |
| kEndpoint_)); |
| } |
| |
| TEST_P(ReportingServiceTest, WriteToStore) { |
| if (!store()) |
| return; |
| |
| MockPersistentReportingStore::CommandList expected_commands; |
| |
| // This first call to any public method triggers a load. The load will block |
| // until we call FinishLoading. |
| service()->ProcessReportToHeader(kOrigin_, kNak_, |
| "{\"endpoints\":[{\"url\":\"" + |
| kEndpoint_.spec() + |
| "\"}]," |
| "\"group\":\"" + |
| kGroup_ + |
| "\"," |
| "\"max_age\":86400}"); |
| expected_commands.emplace_back(CommandType::LOAD_REPORTING_CLIENTS); |
| EXPECT_THAT(store()->GetAllCommands(), |
| testing::UnorderedElementsAreArray(expected_commands)); |
| |
| // Unblock the load. The will let the remaining calls to the service complete |
| // without blocking. |
| FinishLoading(true /* load_success */); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey_, kEndpoint_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey_); |
| EXPECT_THAT(store()->GetAllCommands(), |
| testing::UnorderedElementsAreArray(expected_commands)); |
| |
| service()->ProcessReportToHeader(kOrigin2_, kNak2_, |
| "{\"endpoints\":[{\"url\":\"" + |
| kEndpoint_.spec() + |
| "\"}]," |
| "\"group\":\"" + |
| kGroup_ + |
| "\"," |
| "\"max_age\":86400}"); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey2_, kEndpoint_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey2_); |
| EXPECT_THAT(store()->GetAllCommands(), |
| testing::UnorderedElementsAreArray(expected_commands)); |
| |
| service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_, |
| kType_, base::Value::Dict(), 0); |
| expected_commands.emplace_back( |
| CommandType::UPDATE_REPORTING_ENDPOINT_GROUP_ACCESS_TIME, kGroupKey_); |
| EXPECT_THAT(store()->GetAllCommands(), |
| testing::UnorderedElementsAreArray(expected_commands)); |
| |
| service()->RemoveBrowsingData( |
| ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS, |
| base::BindRepeating( |
| [](const url::Origin& origin) { return origin.host() == "origin"; })); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT, |
| kGroupKey_, kEndpoint_); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP, |
| kGroupKey_); |
| expected_commands.emplace_back(CommandType::FLUSH); |
| EXPECT_THAT(store()->GetAllCommands(), |
| testing::UnorderedElementsAreArray(expected_commands)); |
| |
| service()->RemoveAllBrowsingData( |
| ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT, |
| kGroupKey2_, kEndpoint_); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP, |
| kGroupKey2_); |
| expected_commands.emplace_back(CommandType::FLUSH); |
| EXPECT_THAT(store()->GetAllCommands(), |
| testing::UnorderedElementsAreArray(expected_commands)); |
| } |
| |
| TEST_P(ReportingServiceTest, WaitUntilLoadFinishesBeforeWritingToStore) { |
| if (!store()) |
| return; |
| |
| MockPersistentReportingStore::CommandList expected_commands; |
| |
| // This first call to any public method triggers a load. The load will block |
| // until we call FinishLoading. |
| service()->ProcessReportToHeader(kOrigin_, kNak_, |
| "{\"endpoints\":[{\"url\":\"" + |
| kEndpoint_.spec() + |
| "\"}]," |
| "\"group\":\"" + |
| kGroup_ + |
| "\"," |
| "\"max_age\":86400}"); |
| expected_commands.emplace_back(CommandType::LOAD_REPORTING_CLIENTS); |
| EXPECT_THAT(store()->GetAllCommands(), |
| testing::UnorderedElementsAreArray(expected_commands)); |
| |
| service()->ProcessReportToHeader(kOrigin2_, kNak2_, |
| "{\"endpoints\":[{\"url\":\"" + |
| kEndpoint_.spec() + |
| "\"}]," |
| "\"group\":\"" + |
| kGroup_ + |
| "\"," |
| "\"max_age\":86400}"); |
| EXPECT_THAT(store()->GetAllCommands(), |
| testing::UnorderedElementsAreArray(expected_commands)); |
| |
| service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_, |
| kType_, base::Value::Dict(), 0); |
| EXPECT_THAT(store()->GetAllCommands(), |
| testing::UnorderedElementsAreArray(expected_commands)); |
| |
| service()->RemoveBrowsingData( |
| ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS, |
| base::BindRepeating( |
| [](const url::Origin& origin) { return origin.host() == "origin"; })); |
| EXPECT_THAT(store()->GetAllCommands(), |
| testing::UnorderedElementsAreArray(expected_commands)); |
| |
| service()->RemoveAllBrowsingData( |
| ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS); |
| EXPECT_THAT(store()->GetAllCommands(), |
| testing::UnorderedElementsAreArray(expected_commands)); |
| |
| // Unblock the load. The will let the remaining calls to the service complete |
| // without blocking. |
| FinishLoading(true /* load_success */); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey_, kEndpoint_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT, |
| kGroupKey2_, kEndpoint_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey_); |
| expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP, |
| kGroupKey2_); |
| expected_commands.emplace_back( |
| CommandType::UPDATE_REPORTING_ENDPOINT_GROUP_ACCESS_TIME, kGroupKey_); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT, |
| kGroupKey_, kEndpoint_); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP, |
| kGroupKey_); |
| expected_commands.emplace_back(CommandType::FLUSH); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT, |
| kGroupKey2_, kEndpoint_); |
| expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP, |
| kGroupKey2_); |
| expected_commands.emplace_back(CommandType::FLUSH); |
| EXPECT_THAT(store()->GetAllCommands(), |
| testing::UnorderedElementsAreArray(expected_commands)); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(ReportingServiceStoreTest, |
| ReportingServiceTest, |
| ::testing::Bool()); |
| } // namespace |
| } // namespace net |