• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/reporting/reporting_service.h"
6 
7 #include <memory>
8 #include <optional>
9 #include <string>
10 
11 #include "base/functional/bind.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/memory/raw_ptr.h"
14 #include "base/test/scoped_feature_list.h"
15 #include "base/time/tick_clock.h"
16 #include "base/values.h"
17 #include "net/base/features.h"
18 #include "net/base/isolation_info.h"
19 #include "net/base/network_anonymization_key.h"
20 #include "net/base/schemeful_site.h"
21 #include "net/reporting/mock_persistent_reporting_store.h"
22 #include "net/reporting/reporting_browsing_data_remover.h"
23 #include "net/reporting/reporting_cache.h"
24 #include "net/reporting/reporting_context.h"
25 #include "net/reporting/reporting_endpoint.h"
26 #include "net/reporting/reporting_policy.h"
27 #include "net/reporting/reporting_report.h"
28 #include "net/reporting/reporting_service.h"
29 #include "net/reporting/reporting_target_type.h"
30 #include "net/reporting/reporting_test_util.h"
31 #include "net/test/test_with_task_environment.h"
32 #include "net/url_request/url_request_context_builder.h"
33 #include "net/url_request/url_request_test_util.h"
34 #include "testing/gmock/include/gmock/gmock.h"
35 #include "testing/gtest/include/gtest/gtest.h"
36 #include "url/gurl.h"
37 #include "url/origin.h"
38 
39 namespace net {
40 namespace {
41 
42 using CommandType = MockPersistentReportingStore::Command::Type;
43 
44 // The tests are parametrized on a boolean value which represents whether to use
45 // a MockPersistentReportingStore (if false, no store is used).
46 class ReportingServiceTest : public ::testing::TestWithParam<bool>,
47                              public WithTaskEnvironment {
48  protected:
49   const GURL kUrl_ = GURL("https://origin/path");
50   const GURL kUrl2_ = GURL("https://origin2/path");
51   const url::Origin kOrigin_ = url::Origin::Create(kUrl_);
52   const url::Origin kOrigin2_ = url::Origin::Create(kUrl2_);
53   const GURL kEndpoint_ = GURL("https://endpoint/");
54   const GURL kEndpoint2_ = GURL("https://endpoint2/");
55   const std::string kUserAgent_ = "Mozilla/1.0";
56   const std::string kGroup_ = "group";
57   const std::string kGroup2_ = "group2";
58   const std::string kType_ = "type";
59   const std::optional<base::UnguessableToken> kReportingSource_ =
60       base::UnguessableToken::Create();
61   const NetworkAnonymizationKey kNak_ =
62       NetworkAnonymizationKey::CreateSameSite(SchemefulSite(kOrigin_));
63   const NetworkAnonymizationKey kNak2_ =
64       NetworkAnonymizationKey::CreateSameSite(SchemefulSite(kOrigin2_));
65   const ReportingEndpointGroupKey kGroupKey_ =
66       ReportingEndpointGroupKey(kNak_,
67                                 kOrigin_,
68                                 kGroup_,
69                                 ReportingTargetType::kDeveloper);
70   const ReportingEndpointGroupKey kGroupKey2_ =
71       ReportingEndpointGroupKey(kNak2_,
72                                 kOrigin2_,
73                                 kGroup_,
74                                 ReportingTargetType::kDeveloper);
75   const IsolationInfo kIsolationInfo_ =
76       IsolationInfo::Create(IsolationInfo::RequestType::kOther,
77                             kOrigin_,
78                             kOrigin_,
79                             SiteForCookies::FromOrigin(kOrigin_));
80 
ReportingServiceTest()81   ReportingServiceTest() {
82     feature_list_.InitAndEnableFeature(
83         features::kPartitionConnectionsByNetworkIsolationKey);
84     Init();
85   }
86 
87   // Initializes, or re-initializes, |service_| and its dependencies.
Init()88   void Init() {
89     // Must destroy old service, if there is one, before destroying old store.
90     // Need to clear `context_` first, since it points to an object owned by the
91     // service.
92     context_ = nullptr;
93     service_.reset();
94 
95     if (GetParam()) {
96       store_ = std::make_unique<MockPersistentReportingStore>();
97     } else {
98       store_ = nullptr;
99     }
100 
101     auto test_context = std::make_unique<TestReportingContext>(
102         &clock_, &tick_clock_, ReportingPolicy(), store_.get());
103     context_ = test_context.get();
104 
105     service_ = ReportingService::CreateForTesting(std::move(test_context));
106   }
107 
108   // If the store exists, simulate finishing loading the store, which should
109   // make the rest of the test run synchronously.
FinishLoading(bool load_success)110   void FinishLoading(bool load_success) {
111     if (store_) {
112       store_->FinishLoading(load_success);
113     }
114   }
115 
store()116   MockPersistentReportingStore* store() { return store_.get(); }
context()117   TestReportingContext* context() { return context_; }
service()118   ReportingService* service() { return service_.get(); }
119 
120  private:
121   base::test::ScopedFeatureList feature_list_;
122 
123   base::SimpleTestClock clock_;
124   base::SimpleTestTickClock tick_clock_;
125 
126   std::unique_ptr<MockPersistentReportingStore> store_;
127   std::unique_ptr<ReportingService> service_;
128   raw_ptr<TestReportingContext> context_ = nullptr;
129 };
130 
TEST_P(ReportingServiceTest,QueueReport)131 TEST_P(ReportingServiceTest, QueueReport) {
132   service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
133                          kType_, base::Value::Dict(), 0,
134                          ReportingTargetType::kDeveloper);
135   FinishLoading(true /* load_success */);
136 
137   std::vector<raw_ptr<const ReportingReport, VectorExperimental>> reports;
138   context()->cache()->GetReports(&reports);
139   ASSERT_EQ(1u, reports.size());
140   EXPECT_EQ(kUrl_, reports[0]->url);
141   EXPECT_EQ(kNak_, reports[0]->network_anonymization_key);
142   EXPECT_EQ(kUserAgent_, reports[0]->user_agent);
143   EXPECT_EQ(kGroup_, reports[0]->group);
144   EXPECT_EQ(kType_, reports[0]->type);
145   EXPECT_EQ(ReportingTargetType::kDeveloper, reports[0]->target_type);
146 }
147 
TEST_P(ReportingServiceTest,QueueEnterpriseReport)148 TEST_P(ReportingServiceTest, QueueEnterpriseReport) {
149   service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
150                          kType_, base::Value::Dict(), 0,
151                          ReportingTargetType::kEnterprise);
152   FinishLoading(true /* load_success */);
153 
154   std::vector<raw_ptr<const ReportingReport, VectorExperimental>> reports;
155   context()->cache()->GetReports(&reports);
156   ASSERT_EQ(1u, reports.size());
157   EXPECT_EQ(kUrl_, reports[0]->url);
158   EXPECT_EQ(kNak_, reports[0]->network_anonymization_key);
159   EXPECT_EQ(kUserAgent_, reports[0]->user_agent);
160   EXPECT_EQ(kGroup_, reports[0]->group);
161   EXPECT_EQ(kType_, reports[0]->type);
162   EXPECT_EQ(ReportingTargetType::kEnterprise, reports[0]->target_type);
163 }
164 
TEST_P(ReportingServiceTest,QueueReportSanitizeUrl)165 TEST_P(ReportingServiceTest, QueueReportSanitizeUrl) {
166   // Same as kUrl_ but with username, password, and fragment.
167   GURL url = GURL("https://username:password@origin/path#fragment");
168   service()->QueueReport(url, kReportingSource_, kNak_, kUserAgent_, kGroup_,
169                          kType_, base::Value::Dict(), 0,
170                          ReportingTargetType::kDeveloper);
171   FinishLoading(true /* load_success */);
172 
173   std::vector<raw_ptr<const ReportingReport, VectorExperimental>> reports;
174   context()->cache()->GetReports(&reports);
175   ASSERT_EQ(1u, reports.size());
176   EXPECT_EQ(kUrl_, reports[0]->url);
177   EXPECT_EQ(kNak_, reports[0]->network_anonymization_key);
178   EXPECT_EQ(kUserAgent_, reports[0]->user_agent);
179   EXPECT_EQ(kGroup_, reports[0]->group);
180   EXPECT_EQ(kType_, reports[0]->type);
181 }
182 
TEST_P(ReportingServiceTest,DontQueueReportInvalidUrl)183 TEST_P(ReportingServiceTest, DontQueueReportInvalidUrl) {
184   GURL url = GURL("https://");
185   // This does not trigger an attempt to load from the store because the url
186   // is immediately rejected as invalid.
187   service()->QueueReport(url, kReportingSource_, kNak_, kUserAgent_, kGroup_,
188                          kType_, base::Value::Dict(), 0,
189                          ReportingTargetType::kDeveloper);
190 
191   std::vector<raw_ptr<const ReportingReport, VectorExperimental>> reports;
192   context()->cache()->GetReports(&reports);
193   ASSERT_EQ(0u, reports.size());
194 }
195 
TEST_P(ReportingServiceTest,QueueReportNetworkIsolationKeyDisabled)196 TEST_P(ReportingServiceTest, QueueReportNetworkIsolationKeyDisabled) {
197   base::test::ScopedFeatureList feature_list;
198   feature_list.InitAndDisableFeature(
199       features::kPartitionConnectionsByNetworkIsolationKey);
200 
201   // Re-create the store, so it reads the new feature value.
202   Init();
203 
204   service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
205                          kType_, base::Value::Dict(), 0,
206                          ReportingTargetType::kDeveloper);
207   FinishLoading(true /* load_success */);
208 
209   std::vector<raw_ptr<const ReportingReport, VectorExperimental>> reports;
210   context()->cache()->GetReports(&reports);
211   ASSERT_EQ(1u, reports.size());
212 
213   // NetworkAnonymizationKey should be empty, instead of kNak_;
214   EXPECT_EQ(NetworkAnonymizationKey(), reports[0]->network_anonymization_key);
215   EXPECT_NE(kNak_, reports[0]->network_anonymization_key);
216 
217   EXPECT_EQ(kUrl_, reports[0]->url);
218   EXPECT_EQ(kUserAgent_, reports[0]->user_agent);
219   EXPECT_EQ(kGroup_, reports[0]->group);
220   EXPECT_EQ(kType_, reports[0]->type);
221 }
222 
TEST_P(ReportingServiceTest,ProcessReportToHeader)223 TEST_P(ReportingServiceTest, ProcessReportToHeader) {
224   service()->ProcessReportToHeader(kOrigin_, kNak_,
225                                    "{\"endpoints\":[{\"url\":\"" +
226                                        kEndpoint_.spec() +
227                                        "\"}],"
228                                        "\"group\":\"" +
229                                        kGroup_ +
230                                        "\","
231                                        "\"max_age\":86400}");
232   FinishLoading(true /* load_success */);
233 
234   EXPECT_EQ(1u, context()->cache()->GetEndpointCount());
235   EXPECT_TRUE(context()->cache()->GetEndpointForTesting(
236       ReportingEndpointGroupKey(kNak_, kOrigin_, kGroup_,
237                                 ReportingTargetType::kDeveloper),
238       kEndpoint_));
239 }
240 
TEST_P(ReportingServiceTest,ProcessReportingEndpointsHeader)241 TEST_P(ReportingServiceTest, ProcessReportingEndpointsHeader) {
242   base::test::ScopedFeatureList feature_list;
243   feature_list.InitAndEnableFeature(net::features::kDocumentReporting);
244   auto parsed_header =
245       ParseReportingEndpoints(kGroup_ + "=\"" + kEndpoint_.spec() + "\"");
246   ASSERT_TRUE(parsed_header.has_value());
247   service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_,
248                                            kIsolationInfo_, *parsed_header);
249   FinishLoading(true /* load_success */);
250 
251   // Endpoint should not be part of the persistent store.
252   EXPECT_EQ(0u, context()->cache()->GetEndpointCount());
253   // Endpoint should be associated with the reporting source.
254   ReportingEndpoint cached_endpoint =
255       context()->cache()->GetV1EndpointForTesting(*kReportingSource_, kGroup_);
256   EXPECT_TRUE(cached_endpoint);
257 
258   // Ensure that the NAK is stored properly with the endpoint group.
259   EXPECT_FALSE(cached_endpoint.group_key.network_anonymization_key.IsEmpty());
260 }
261 
TEST_P(ReportingServiceTest,ProcessReportingEndpointsHeaderNetworkIsolationKeyDisabled)262 TEST_P(ReportingServiceTest,
263        ProcessReportingEndpointsHeaderNetworkIsolationKeyDisabled) {
264   base::test::ScopedFeatureList feature_list;
265   feature_list.InitWithFeatures(
266       {net::features::kDocumentReporting},
267       {features::kPartitionConnectionsByNetworkIsolationKey});
268 
269   // Re-create the store, so it reads the new feature value.
270   Init();
271 
272   auto parsed_header =
273       ParseReportingEndpoints(kGroup_ + "=\"" + kEndpoint_.spec() + "\"");
274   ASSERT_TRUE(parsed_header.has_value());
275   service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_,
276                                            kIsolationInfo_, *parsed_header);
277   FinishLoading(true /* load_success */);
278 
279   // Endpoint should not be part of the persistent store.
280   EXPECT_EQ(0u, context()->cache()->GetEndpointCount());
281   // Endpoint should be associated with the reporting source.
282   ReportingEndpoint cached_endpoint =
283       context()->cache()->GetV1EndpointForTesting(*kReportingSource_, kGroup_);
284   EXPECT_TRUE(cached_endpoint);
285 
286   // When isolation is disabled, cached endpoints should have a null NAK.
287   EXPECT_TRUE(cached_endpoint.group_key.network_anonymization_key.IsEmpty());
288 }
289 
TEST_P(ReportingServiceTest,SendReportsAndRemoveSource)290 TEST_P(ReportingServiceTest, SendReportsAndRemoveSource) {
291   base::test::ScopedFeatureList feature_list;
292   feature_list.InitAndEnableFeature(net::features::kDocumentReporting);
293   auto parsed_header =
294       ParseReportingEndpoints(kGroup_ + "=\"" + kEndpoint_.spec() + "\", " +
295                               kGroup2_ + "=\"" + kEndpoint2_.spec() + "\"");
296   ASSERT_TRUE(parsed_header.has_value());
297   service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_,
298                                            kIsolationInfo_, *parsed_header);
299   // This report should be sent immediately, starting the delivery agent timer.
300   service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
301                          kType_, base::Value::Dict(), 0,
302                          ReportingTargetType::kDeveloper);
303 
304   FinishLoading(true /* load_success */);
305 
306   std::vector<raw_ptr<const ReportingReport, VectorExperimental>> reports;
307   context()->cache()->GetReports(&reports);
308   ASSERT_EQ(1u, reports.size());
309   EXPECT_EQ(0u, context()->cache()->GetReportCountWithStatusForTesting(
310                     ReportingReport::Status::QUEUED));
311 
312   // Now simulate the source being destroyed.
313   service()->SendReportsAndRemoveSource(*kReportingSource_);
314 
315   // There should be no queued reports, but the previously sent report should
316   // still be pending.
317   EXPECT_EQ(0u, context()->cache()->GetReportCountWithStatusForTesting(
318                     ReportingReport::Status::QUEUED));
319   EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting(
320                     ReportingReport::Status::PENDING));
321   // Source should be marked as expired.
322   ASSERT_TRUE(
323       context()->cache()->GetExpiredSources().contains(*kReportingSource_));
324 }
325 
326 // Flaky in ChromeOS: crbug.com/1356127
327 #if BUILDFLAG(IS_CHROMEOS)
328 #define MAYBE_SendReportsAndRemoveSourceWithPendingReports \
329   DISABLED_SendReportsAndRemoveSourceWithPendingReports
330 #else
331 #define MAYBE_SendReportsAndRemoveSourceWithPendingReports \
332   SendReportsAndRemoveSourceWithPendingReports
333 #endif
TEST_P(ReportingServiceTest,MAYBE_SendReportsAndRemoveSourceWithPendingReports)334 TEST_P(ReportingServiceTest,
335        MAYBE_SendReportsAndRemoveSourceWithPendingReports) {
336   base::test::ScopedFeatureList feature_list;
337   feature_list.InitAndEnableFeature(net::features::kDocumentReporting);
338   auto parsed_header =
339       ParseReportingEndpoints(kGroup_ + "=\"" + kEndpoint_.spec() + "\", " +
340                               kGroup2_ + "=\"" + kEndpoint2_.spec() + "\"");
341   ASSERT_TRUE(parsed_header.has_value());
342   service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_,
343                                            kIsolationInfo_, *parsed_header);
344   // This report should be sent immediately, starting the delivery agent timer.
345   service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
346                          kType_, base::Value::Dict(), 0,
347                          ReportingTargetType::kDeveloper);
348 
349   FinishLoading(true /* load_success */);
350 
351   std::vector<raw_ptr<const ReportingReport, VectorExperimental>> reports;
352   context()->cache()->GetReports(&reports);
353   ASSERT_EQ(1u, reports.size());
354   EXPECT_EQ(0u, context()->cache()->GetReportCountWithStatusForTesting(
355                     ReportingReport::Status::QUEUED));
356   EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting(
357                     ReportingReport::Status::PENDING));
358 
359   // Queue another report, which should remain queued.
360   service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
361                          kType_, base::Value::Dict(), 0,
362                          ReportingTargetType::kDeveloper);
363   EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting(
364                     ReportingReport::Status::QUEUED));
365   EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting(
366                     ReportingReport::Status::PENDING));
367 
368   // Now simulate the source being destroyed.
369   service()->SendReportsAndRemoveSource(*kReportingSource_);
370 
371   // The report should still be queued, while the source should be marked as
372   // expired. (The original report is still pending.)
373   EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting(
374                     ReportingReport::Status::QUEUED));
375   EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting(
376                     ReportingReport::Status::PENDING));
377   ASSERT_TRUE(
378       context()->cache()->GetExpiredSources().contains(kReportingSource_));
379 }
380 
TEST_P(ReportingServiceTest,ProcessReportingEndpointsHeaderPathAbsolute)381 TEST_P(ReportingServiceTest, ProcessReportingEndpointsHeaderPathAbsolute) {
382   base::test::ScopedFeatureList feature_list;
383   feature_list.InitAndEnableFeature(net::features::kDocumentReporting);
384   auto parsed_header = ParseReportingEndpoints(kGroup_ + "=\"/path-absolute\"");
385   ASSERT_TRUE(parsed_header.has_value());
386   service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_,
387                                            kIsolationInfo_, *parsed_header);
388   FinishLoading(true /* load_success */);
389 
390   // Endpoint should not be part of the persistent store.
391   EXPECT_EQ(0u, context()->cache()->GetEndpointCount());
392   // Endpoint should be associated with the reporting source.
393   ReportingEndpoint endpoint =
394       context()->cache()->GetV1EndpointForTesting(*kReportingSource_, kGroup_);
395   EXPECT_TRUE(endpoint);
396   // Endpoint should have the correct path.
397   EXPECT_EQ(kUrl_.Resolve("/path-absolute"), endpoint.info.url);
398 }
399 
TEST_P(ReportingServiceTest,ProcessReportToHeaderPathAbsolute)400 TEST_P(ReportingServiceTest, ProcessReportToHeaderPathAbsolute) {
401   service()->ProcessReportToHeader(
402       kOrigin_, kNak_,
403       "{\"endpoints\":[{\"url\":\"/path-absolute\"}],"
404       "\"group\":\"" +
405           kGroup_ +
406           "\","
407           "\"max_age\":86400}");
408   FinishLoading(true /* load_success */);
409 
410   EXPECT_EQ(1u, context()->cache()->GetEndpointCount());
411 }
412 
TEST_P(ReportingServiceTest,ProcessReportToHeader_TooLong)413 TEST_P(ReportingServiceTest, ProcessReportToHeader_TooLong) {
414   const std::string header_too_long =
415       "{\"endpoints\":[{\"url\":\"" + kEndpoint_.spec() +
416       "\"}],"
417       "\"group\":\"" +
418       kGroup_ +
419       "\","
420       "\"max_age\":86400," +
421       "\"junk\":\"" + std::string(32 * 1024, 'a') + "\"}";
422   // This does not trigger an attempt to load from the store because the header
423   // is immediately rejected as invalid.
424   service()->ProcessReportToHeader(kOrigin_, kNak_, header_too_long);
425 
426   EXPECT_EQ(0u, context()->cache()->GetEndpointCount());
427 }
428 
TEST_P(ReportingServiceTest,ProcessReportToHeader_TooDeep)429 TEST_P(ReportingServiceTest, ProcessReportToHeader_TooDeep) {
430   const std::string header_too_deep = "{\"endpoints\":[{\"url\":\"" +
431                                       kEndpoint_.spec() +
432                                       "\"}],"
433                                       "\"group\":\"" +
434                                       kGroup_ +
435                                       "\","
436                                       "\"max_age\":86400," +
437                                       "\"junk\":[[[[[[[[[[]]]]]]]]]]}";
438   // This does not trigger an attempt to load from the store because the header
439   // is immediately rejected as invalid.
440   service()->ProcessReportToHeader(kOrigin_, kNak_, header_too_deep);
441 
442   EXPECT_EQ(0u, context()->cache()->GetEndpointCount());
443 }
444 
TEST_P(ReportingServiceTest,ProcessReportToHeaderNetworkIsolationKeyDisabled)445 TEST_P(ReportingServiceTest, ProcessReportToHeaderNetworkIsolationKeyDisabled) {
446   base::test::ScopedFeatureList feature_list;
447   feature_list.InitAndDisableFeature(
448       features::kPartitionConnectionsByNetworkIsolationKey);
449 
450   // Re-create the store, so it reads the new feature value.
451   Init();
452 
453   service()->ProcessReportToHeader(kOrigin_, kNak_,
454                                    "{\"endpoints\":[{\"url\":\"" +
455                                        kEndpoint_.spec() +
456                                        "\"}],"
457                                        "\"group\":\"" +
458                                        kGroup_ +
459                                        "\","
460                                        "\"max_age\":86400}");
461   FinishLoading(true /* load_success */);
462 
463   EXPECT_EQ(1u, context()->cache()->GetEndpointCount());
464   EXPECT_FALSE(context()->cache()->GetEndpointForTesting(
465       ReportingEndpointGroupKey(kNak_, kOrigin_, kGroup_,
466                                 ReportingTargetType::kDeveloper),
467       kEndpoint_));
468   EXPECT_TRUE(context()->cache()->GetEndpointForTesting(
469       ReportingEndpointGroupKey(NetworkAnonymizationKey(), kOrigin_, kGroup_,
470                                 ReportingTargetType::kDeveloper),
471       kEndpoint_));
472 }
473 
TEST_P(ReportingServiceTest,WriteToStore)474 TEST_P(ReportingServiceTest, WriteToStore) {
475   if (!store()) {
476     return;
477   }
478 
479   MockPersistentReportingStore::CommandList expected_commands;
480 
481   // This first call to any public method triggers a load. The load will block
482   // until we call FinishLoading.
483   service()->ProcessReportToHeader(kOrigin_, kNak_,
484                                    "{\"endpoints\":[{\"url\":\"" +
485                                        kEndpoint_.spec() +
486                                        "\"}],"
487                                        "\"group\":\"" +
488                                        kGroup_ +
489                                        "\","
490                                        "\"max_age\":86400}");
491   expected_commands.emplace_back(CommandType::LOAD_REPORTING_CLIENTS);
492   EXPECT_THAT(store()->GetAllCommands(),
493               testing::UnorderedElementsAreArray(expected_commands));
494 
495   // Unblock the load. The will let the remaining calls to the service complete
496   // without blocking.
497   FinishLoading(true /* load_success */);
498   expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
499                                  kGroupKey_, kEndpoint_);
500   expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
501                                  kGroupKey_);
502   EXPECT_THAT(store()->GetAllCommands(),
503               testing::UnorderedElementsAreArray(expected_commands));
504 
505   service()->ProcessReportToHeader(kOrigin2_, kNak2_,
506                                    "{\"endpoints\":[{\"url\":\"" +
507                                        kEndpoint_.spec() +
508                                        "\"}],"
509                                        "\"group\":\"" +
510                                        kGroup_ +
511                                        "\","
512                                        "\"max_age\":86400}");
513   expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
514                                  kGroupKey2_, kEndpoint_);
515   expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
516                                  kGroupKey2_);
517   EXPECT_THAT(store()->GetAllCommands(),
518               testing::UnorderedElementsAreArray(expected_commands));
519 
520   service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
521                          kType_, base::Value::Dict(), 0,
522                          ReportingTargetType::kDeveloper);
523   expected_commands.emplace_back(
524       CommandType::UPDATE_REPORTING_ENDPOINT_GROUP_ACCESS_TIME, kGroupKey_);
525   EXPECT_THAT(store()->GetAllCommands(),
526               testing::UnorderedElementsAreArray(expected_commands));
527 
528   service()->RemoveBrowsingData(
529       ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS,
530       base::BindRepeating(
531           [](const url::Origin& origin) { return origin.host() == "origin"; }));
532   expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
533                                  kGroupKey_, kEndpoint_);
534   expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
535                                  kGroupKey_);
536   expected_commands.emplace_back(CommandType::FLUSH);
537   EXPECT_THAT(store()->GetAllCommands(),
538               testing::UnorderedElementsAreArray(expected_commands));
539 
540   service()->RemoveAllBrowsingData(
541       ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS);
542   expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
543                                  kGroupKey2_, kEndpoint_);
544   expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
545                                  kGroupKey2_);
546   expected_commands.emplace_back(CommandType::FLUSH);
547   EXPECT_THAT(store()->GetAllCommands(),
548               testing::UnorderedElementsAreArray(expected_commands));
549 }
550 
TEST_P(ReportingServiceTest,WaitUntilLoadFinishesBeforeWritingToStore)551 TEST_P(ReportingServiceTest, WaitUntilLoadFinishesBeforeWritingToStore) {
552   if (!store()) {
553     return;
554   }
555 
556   MockPersistentReportingStore::CommandList expected_commands;
557 
558   // This first call to any public method triggers a load. The load will block
559   // until we call FinishLoading.
560   service()->ProcessReportToHeader(kOrigin_, kNak_,
561                                    "{\"endpoints\":[{\"url\":\"" +
562                                        kEndpoint_.spec() +
563                                        "\"}],"
564                                        "\"group\":\"" +
565                                        kGroup_ +
566                                        "\","
567                                        "\"max_age\":86400}");
568   expected_commands.emplace_back(CommandType::LOAD_REPORTING_CLIENTS);
569   EXPECT_THAT(store()->GetAllCommands(),
570               testing::UnorderedElementsAreArray(expected_commands));
571 
572   service()->ProcessReportToHeader(kOrigin2_, kNak2_,
573                                    "{\"endpoints\":[{\"url\":\"" +
574                                        kEndpoint_.spec() +
575                                        "\"}],"
576                                        "\"group\":\"" +
577                                        kGroup_ +
578                                        "\","
579                                        "\"max_age\":86400}");
580   EXPECT_THAT(store()->GetAllCommands(),
581               testing::UnorderedElementsAreArray(expected_commands));
582 
583   service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
584                          kType_, base::Value::Dict(), 0,
585                          ReportingTargetType::kDeveloper);
586   EXPECT_THAT(store()->GetAllCommands(),
587               testing::UnorderedElementsAreArray(expected_commands));
588 
589   service()->RemoveBrowsingData(
590       ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS,
591       base::BindRepeating(
592           [](const url::Origin& origin) { return origin.host() == "origin"; }));
593   EXPECT_THAT(store()->GetAllCommands(),
594               testing::UnorderedElementsAreArray(expected_commands));
595 
596   service()->RemoveAllBrowsingData(
597       ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS);
598   EXPECT_THAT(store()->GetAllCommands(),
599               testing::UnorderedElementsAreArray(expected_commands));
600 
601   // Unblock the load. The will let the remaining calls to the service complete
602   // without blocking.
603   FinishLoading(true /* load_success */);
604   expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
605                                  kGroupKey_, kEndpoint_);
606   expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
607                                  kGroupKey2_, kEndpoint_);
608   expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
609                                  kGroupKey_);
610   expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
611                                  kGroupKey2_);
612   expected_commands.emplace_back(
613       CommandType::UPDATE_REPORTING_ENDPOINT_GROUP_ACCESS_TIME, kGroupKey_);
614   expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
615                                  kGroupKey_, kEndpoint_);
616   expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
617                                  kGroupKey_);
618   expected_commands.emplace_back(CommandType::FLUSH);
619   expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
620                                  kGroupKey2_, kEndpoint_);
621   expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
622                                  kGroupKey2_);
623   expected_commands.emplace_back(CommandType::FLUSH);
624   EXPECT_THAT(store()->GetAllCommands(),
625               testing::UnorderedElementsAreArray(expected_commands));
626 }
627 
TEST_P(ReportingServiceTest,SetEnterpriseReportingEndpointsWithFeatureEnabled)628 TEST_P(ReportingServiceTest,
629        SetEnterpriseReportingEndpointsWithFeatureEnabled) {
630   base::test::ScopedFeatureList feature_list;
631   feature_list.InitAndEnableFeature(
632       net::features::kReportingApiEnableEnterpriseCookieIssues);
633   EXPECT_EQ(0u, context()->cache()->GetEnterpriseEndpointsForTesting().size());
634   base::flat_map<std::string, GURL> test_enterprise_endpoints{
635       {"endpoint-1", GURL("https://example.com/reports")},
636       {"endpoint-2", GURL("https://reporting.example/cookie-issues")},
637       {"endpoint-3", GURL("https://report-collector.example")},
638   };
639 
640   std::vector<ReportingEndpoint> expected_enterprise_endpoints = {
641       {ReportingEndpointGroupKey(NetworkAnonymizationKey(),
642                                  /*reporting_source=*/std::nullopt,
643                                  /*origin=*/std::nullopt, "endpoint-1",
644                                  ReportingTargetType::kEnterprise),
645        {.url = GURL("https://example.com/reports")}},
646       {ReportingEndpointGroupKey(NetworkAnonymizationKey(),
647                                  /*reporting_source=*/std::nullopt,
648                                  /*origin=*/std::nullopt, "endpoint-2",
649                                  ReportingTargetType::kEnterprise),
650        {.url = GURL("https://reporting.example/cookie-issues")}},
651       {ReportingEndpointGroupKey(NetworkAnonymizationKey(),
652                                  /*reporting_source=*/std::nullopt,
653                                  /*origin=*/std::nullopt, "endpoint-3",
654                                  ReportingTargetType::kEnterprise),
655        {.url = GURL("https://report-collector.example")}}};
656 
657   service()->SetEnterpriseReportingEndpoints(test_enterprise_endpoints);
658   EXPECT_EQ(expected_enterprise_endpoints,
659             context()->cache()->GetEnterpriseEndpointsForTesting());
660 }
661 
TEST_P(ReportingServiceTest,SetEnterpriseReportingEndpointsWithFeatureDisabled)662 TEST_P(ReportingServiceTest,
663        SetEnterpriseReportingEndpointsWithFeatureDisabled) {
664   EXPECT_EQ(0u, context()->cache()->GetEnterpriseEndpointsForTesting().size());
665   base::flat_map<std::string, GURL> test_enterprise_endpoints{
666       {"endpoint-1", GURL("https://example.com/reports")},
667       {"endpoint-2", GURL("https://reporting.example/cookie-issues")},
668       {"endpoint-3", GURL("https://report-collector.example")},
669   };
670 
671   service()->SetEnterpriseReportingEndpoints(test_enterprise_endpoints);
672   EXPECT_EQ(0u, context()->cache()->GetEnterpriseEndpointsForTesting().size());
673 }
674 
TEST_P(ReportingServiceTest,ReportingServiceConstructionWithFeatureEnabled)675 TEST_P(ReportingServiceTest, ReportingServiceConstructionWithFeatureEnabled) {
676   base::test::ScopedFeatureList feature_list;
677   feature_list.InitAndEnableFeature(
678       net::features::kReportingApiEnableEnterpriseCookieIssues);
679   base::flat_map<std::string, GURL> test_enterprise_endpoints{
680       {"endpoint-1", GURL("https://example.com/reports")},
681       {"endpoint-2", GURL("https://reporting.example/cookie-issues")},
682       {"endpoint-3", GURL("https://report-collector.example")},
683   };
684 
685   EXPECT_EQ(0u, service()
686                     ->GetContextForTesting()
687                     ->cache()
688                     ->GetEnterpriseEndpointsForTesting()
689                     .size());
690   std::unique_ptr<URLRequestContext> url_request_context =
691       CreateTestURLRequestContextBuilder()->Build();
692   std::unique_ptr<ReportingService> reporting_service_ptr =
693       ReportingService::Create(ReportingPolicy(), url_request_context.get(),
694                                store(), test_enterprise_endpoints);
695 
696   std::vector<ReportingEndpoint> expected_enterprise_endpoints = {
697       {ReportingEndpointGroupKey(NetworkAnonymizationKey(),
698                                  /*reporting_source=*/std::nullopt,
699                                  /*origin=*/std::nullopt, "endpoint-1",
700                                  ReportingTargetType::kEnterprise),
701        {.url = GURL("https://example.com/reports")}},
702       {ReportingEndpointGroupKey(NetworkAnonymizationKey(),
703                                  /*reporting_source=*/std::nullopt,
704                                  /*origin=*/std::nullopt, "endpoint-2",
705                                  ReportingTargetType::kEnterprise),
706        {.url = GURL("https://reporting.example/cookie-issues")}},
707       {ReportingEndpointGroupKey(NetworkAnonymizationKey(),
708                                  /*reporting_source=*/std::nullopt,
709                                  /*origin=*/std::nullopt, "endpoint-3",
710                                  ReportingTargetType::kEnterprise),
711        {.url = GURL("https://report-collector.example")}}};
712 
713   EXPECT_EQ(expected_enterprise_endpoints,
714             reporting_service_ptr->GetContextForTesting()
715                 ->cache()
716                 ->GetEnterpriseEndpointsForTesting());
717 }
718 
TEST_P(ReportingServiceTest,ReportingServiceConstructionWithFeatureDisabled)719 TEST_P(ReportingServiceTest, ReportingServiceConstructionWithFeatureDisabled) {
720   base::test::ScopedFeatureList feature_list;
721   feature_list.InitAndDisableFeature(
722       net::features::kReportingApiEnableEnterpriseCookieIssues);
723   base::flat_map<std::string, GURL> test_enterprise_endpoints{
724       {"endpoint-1", GURL("https://example.com/reports")},
725       {"endpoint-2", GURL("https://reporting.example/cookie-issues")},
726       {"endpoint-3", GURL("https://report-collector.example")},
727   };
728 
729   EXPECT_EQ(0u, service()
730                     ->GetContextForTesting()
731                     ->cache()
732                     ->GetEnterpriseEndpointsForTesting()
733                     .size());
734   std::unique_ptr<URLRequestContext> url_request_context =
735       CreateTestURLRequestContextBuilder()->Build();
736   std::unique_ptr<ReportingService> reporting_service_ptr =
737       ReportingService::Create(ReportingPolicy(), url_request_context.get(),
738                                store(), test_enterprise_endpoints);
739 
740   EXPECT_EQ(0u, reporting_service_ptr->GetContextForTesting()
741                     ->cache()
742                     ->GetEnterpriseEndpointsForTesting()
743                     .size());
744 }
745 
746 INSTANTIATE_TEST_SUITE_P(ReportingServiceStoreTest,
747                          ReportingServiceTest,
748                          ::testing::Bool());
749 }  // namespace
750 }  // namespace net
751