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_test_util.h"
30 #include "net/test/test_with_task_environment.h"
31 #include "testing/gmock/include/gmock/gmock.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33 #include "url/gurl.h"
34 #include "url/origin.h"
35
36 namespace net {
37 namespace {
38
39 using CommandType = MockPersistentReportingStore::Command::Type;
40
41 // The tests are parametrized on a boolean value which represents whether to use
42 // a MockPersistentReportingStore (if false, no store is used).
43 class ReportingServiceTest : public ::testing::TestWithParam<bool>,
44 public WithTaskEnvironment {
45 protected:
46 const GURL kUrl_ = GURL("https://origin/path");
47 const GURL kUrl2_ = GURL("https://origin2/path");
48 const url::Origin kOrigin_ = url::Origin::Create(kUrl_);
49 const url::Origin kOrigin2_ = url::Origin::Create(kUrl2_);
50 const GURL kEndpoint_ = GURL("https://endpoint/");
51 const GURL kEndpoint2_ = GURL("https://endpoint2/");
52 const std::string kUserAgent_ = "Mozilla/1.0";
53 const std::string kGroup_ = "group";
54 const std::string kGroup2_ = "group2";
55 const std::string kType_ = "type";
56 const std::optional<base::UnguessableToken> kReportingSource_ =
57 base::UnguessableToken::Create();
58 const NetworkAnonymizationKey kNak_ =
59 NetworkAnonymizationKey::CreateSameSite(SchemefulSite(kOrigin_));
60 const NetworkAnonymizationKey kNak2_ =
61 NetworkAnonymizationKey::CreateSameSite(SchemefulSite(kOrigin2_));
62 const ReportingEndpointGroupKey kGroupKey_ =
63 ReportingEndpointGroupKey(kNak_, kOrigin_, kGroup_);
64 const ReportingEndpointGroupKey kGroupKey2_ =
65 ReportingEndpointGroupKey(kNak2_, kOrigin2_, kGroup_);
66 const IsolationInfo kIsolationInfo_ =
67 IsolationInfo::Create(IsolationInfo::RequestType::kOther,
68 kOrigin_,
69 kOrigin_,
70 SiteForCookies::FromOrigin(kOrigin_));
71
ReportingServiceTest()72 ReportingServiceTest() {
73 feature_list_.InitAndEnableFeature(
74 features::kPartitionNelAndReportingByNetworkIsolationKey);
75 Init();
76 }
77
78 // Initializes, or re-initializes, |service_| and its dependencies.
Init()79 void Init() {
80 if (GetParam()) {
81 store_ = std::make_unique<MockPersistentReportingStore>();
82 } else {
83 store_ = nullptr;
84 }
85
86 auto test_context = std::make_unique<TestReportingContext>(
87 &clock_, &tick_clock_, ReportingPolicy(), store_.get());
88 context_ = test_context.get();
89
90 service_ = ReportingService::CreateForTesting(std::move(test_context));
91 }
92
93 // If the store exists, simulate finishing loading the store, which should
94 // make the rest of the test run synchronously.
FinishLoading(bool load_success)95 void FinishLoading(bool load_success) {
96 if (store_) {
97 store_->FinishLoading(load_success);
98 }
99 }
100
store()101 MockPersistentReportingStore* store() { return store_.get(); }
context()102 TestReportingContext* context() { return context_; }
service()103 ReportingService* service() { return service_.get(); }
104
105 private:
106 base::test::ScopedFeatureList feature_list_;
107
108 base::SimpleTestClock clock_;
109 base::SimpleTestTickClock tick_clock_;
110
111 std::unique_ptr<MockPersistentReportingStore> store_;
112 std::unique_ptr<ReportingService> service_;
113 raw_ptr<TestReportingContext> context_ = nullptr;
114 };
115
TEST_P(ReportingServiceTest,QueueReport)116 TEST_P(ReportingServiceTest, QueueReport) {
117 service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
118 kType_, base::Value::Dict(), 0);
119 FinishLoading(true /* load_success */);
120
121 std::vector<raw_ptr<const ReportingReport, VectorExperimental>> reports;
122 context()->cache()->GetReports(&reports);
123 ASSERT_EQ(1u, reports.size());
124 EXPECT_EQ(kUrl_, reports[0]->url);
125 EXPECT_EQ(kNak_, reports[0]->network_anonymization_key);
126 EXPECT_EQ(kUserAgent_, reports[0]->user_agent);
127 EXPECT_EQ(kGroup_, reports[0]->group);
128 EXPECT_EQ(kType_, reports[0]->type);
129 }
130
TEST_P(ReportingServiceTest,QueueReportSanitizeUrl)131 TEST_P(ReportingServiceTest, QueueReportSanitizeUrl) {
132 // Same as kUrl_ but with username, password, and fragment.
133 GURL url = GURL("https://username:password@origin/path#fragment");
134 service()->QueueReport(url, kReportingSource_, kNak_, kUserAgent_, kGroup_,
135 kType_, base::Value::Dict(), 0);
136 FinishLoading(true /* load_success */);
137
138 std::vector<raw_ptr<const ReportingReport, VectorExperimental>> reports;
139 context()->cache()->GetReports(&reports);
140 ASSERT_EQ(1u, reports.size());
141 EXPECT_EQ(kUrl_, reports[0]->url);
142 EXPECT_EQ(kNak_, reports[0]->network_anonymization_key);
143 EXPECT_EQ(kUserAgent_, reports[0]->user_agent);
144 EXPECT_EQ(kGroup_, reports[0]->group);
145 EXPECT_EQ(kType_, reports[0]->type);
146 }
147
TEST_P(ReportingServiceTest,DontQueueReportInvalidUrl)148 TEST_P(ReportingServiceTest, DontQueueReportInvalidUrl) {
149 GURL url = GURL("https://");
150 // This does not trigger an attempt to load from the store because the url
151 // is immediately rejected as invalid.
152 service()->QueueReport(url, kReportingSource_, kNak_, kUserAgent_, kGroup_,
153 kType_, base::Value::Dict(), 0);
154
155 std::vector<raw_ptr<const ReportingReport, VectorExperimental>> reports;
156 context()->cache()->GetReports(&reports);
157 ASSERT_EQ(0u, reports.size());
158 }
159
TEST_P(ReportingServiceTest,QueueReportNetworkIsolationKeyDisabled)160 TEST_P(ReportingServiceTest, QueueReportNetworkIsolationKeyDisabled) {
161 base::test::ScopedFeatureList feature_list;
162 feature_list.InitAndDisableFeature(
163 features::kPartitionNelAndReportingByNetworkIsolationKey);
164
165 // Re-create the store, so it reads the new feature value.
166 Init();
167
168 service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
169 kType_, base::Value::Dict(), 0);
170 FinishLoading(true /* load_success */);
171
172 std::vector<raw_ptr<const ReportingReport, VectorExperimental>> reports;
173 context()->cache()->GetReports(&reports);
174 ASSERT_EQ(1u, reports.size());
175
176 // NetworkAnonymizationKey should be empty, instead of kNak_;
177 EXPECT_EQ(NetworkAnonymizationKey(), reports[0]->network_anonymization_key);
178 EXPECT_NE(kNak_, reports[0]->network_anonymization_key);
179
180 EXPECT_EQ(kUrl_, reports[0]->url);
181 EXPECT_EQ(kUserAgent_, reports[0]->user_agent);
182 EXPECT_EQ(kGroup_, reports[0]->group);
183 EXPECT_EQ(kType_, reports[0]->type);
184 }
185
TEST_P(ReportingServiceTest,ProcessReportToHeader)186 TEST_P(ReportingServiceTest, ProcessReportToHeader) {
187 service()->ProcessReportToHeader(kOrigin_, kNak_,
188 "{\"endpoints\":[{\"url\":\"" +
189 kEndpoint_.spec() +
190 "\"}],"
191 "\"group\":\"" +
192 kGroup_ +
193 "\","
194 "\"max_age\":86400}");
195 FinishLoading(true /* load_success */);
196
197 EXPECT_EQ(1u, context()->cache()->GetEndpointCount());
198 EXPECT_TRUE(context()->cache()->GetEndpointForTesting(
199 ReportingEndpointGroupKey(kNak_, kOrigin_, kGroup_), kEndpoint_));
200 }
201
TEST_P(ReportingServiceTest,ProcessReportingEndpointsHeader)202 TEST_P(ReportingServiceTest, ProcessReportingEndpointsHeader) {
203 base::test::ScopedFeatureList feature_list;
204 feature_list.InitAndEnableFeature(net::features::kDocumentReporting);
205 auto parsed_header =
206 ParseReportingEndpoints(kGroup_ + "=\"" + kEndpoint_.spec() + "\"");
207 ASSERT_TRUE(parsed_header.has_value());
208 service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_,
209 kIsolationInfo_, *parsed_header);
210 FinishLoading(true /* load_success */);
211
212 // Endpoint should not be part of the persistent store.
213 EXPECT_EQ(0u, context()->cache()->GetEndpointCount());
214 // Endpoint should be associated with the reporting source.
215 ReportingEndpoint cached_endpoint =
216 context()->cache()->GetV1EndpointForTesting(*kReportingSource_, kGroup_);
217 EXPECT_TRUE(cached_endpoint);
218
219 // Ensure that the NAK is stored properly with the endpoint group.
220 EXPECT_FALSE(cached_endpoint.group_key.network_anonymization_key.IsEmpty());
221 }
222
TEST_P(ReportingServiceTest,ProcessReportingEndpointsHeaderNetworkIsolationKeyDisabled)223 TEST_P(ReportingServiceTest,
224 ProcessReportingEndpointsHeaderNetworkIsolationKeyDisabled) {
225 base::test::ScopedFeatureList feature_list;
226 feature_list.InitWithFeatures(
227 {net::features::kDocumentReporting},
228 {features::kPartitionNelAndReportingByNetworkIsolationKey});
229
230 // Re-create the store, so it reads the new feature value.
231 Init();
232
233 auto parsed_header =
234 ParseReportingEndpoints(kGroup_ + "=\"" + kEndpoint_.spec() + "\"");
235 ASSERT_TRUE(parsed_header.has_value());
236 service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_,
237 kIsolationInfo_, *parsed_header);
238 FinishLoading(true /* load_success */);
239
240 // Endpoint should not be part of the persistent store.
241 EXPECT_EQ(0u, context()->cache()->GetEndpointCount());
242 // Endpoint should be associated with the reporting source.
243 ReportingEndpoint cached_endpoint =
244 context()->cache()->GetV1EndpointForTesting(*kReportingSource_, kGroup_);
245 EXPECT_TRUE(cached_endpoint);
246
247 // When isolation is disabled, cached endpoints should have a null NAK.
248 EXPECT_TRUE(cached_endpoint.group_key.network_anonymization_key.IsEmpty());
249 }
250
TEST_P(ReportingServiceTest,SendReportsAndRemoveSource)251 TEST_P(ReportingServiceTest, SendReportsAndRemoveSource) {
252 base::test::ScopedFeatureList feature_list;
253 feature_list.InitAndEnableFeature(net::features::kDocumentReporting);
254 auto parsed_header =
255 ParseReportingEndpoints(kGroup_ + "=\"" + kEndpoint_.spec() + "\", " +
256 kGroup2_ + "=\"" + kEndpoint2_.spec() + "\"");
257 ASSERT_TRUE(parsed_header.has_value());
258 service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_,
259 kIsolationInfo_, *parsed_header);
260 // This report should be sent immediately, starting the delivery agent timer.
261 service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
262 kType_, base::Value::Dict(), 0);
263
264 FinishLoading(true /* load_success */);
265
266 std::vector<raw_ptr<const ReportingReport, VectorExperimental>> reports;
267 context()->cache()->GetReports(&reports);
268 ASSERT_EQ(1u, reports.size());
269 EXPECT_EQ(0u, context()->cache()->GetReportCountWithStatusForTesting(
270 ReportingReport::Status::QUEUED));
271
272 // Now simulate the source being destroyed.
273 service()->SendReportsAndRemoveSource(*kReportingSource_);
274
275 // There should be no queued reports, but the previously sent report should
276 // still be pending.
277 EXPECT_EQ(0u, context()->cache()->GetReportCountWithStatusForTesting(
278 ReportingReport::Status::QUEUED));
279 EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting(
280 ReportingReport::Status::PENDING));
281 // Source should be marked as expired.
282 ASSERT_TRUE(
283 context()->cache()->GetExpiredSources().contains(*kReportingSource_));
284 }
285
286 // Flaky in ChromeOS: crbug.com/1356127
287 #if BUILDFLAG(IS_CHROMEOS)
288 #define MAYBE_SendReportsAndRemoveSourceWithPendingReports \
289 DISABLED_SendReportsAndRemoveSourceWithPendingReports
290 #else
291 #define MAYBE_SendReportsAndRemoveSourceWithPendingReports \
292 SendReportsAndRemoveSourceWithPendingReports
293 #endif
TEST_P(ReportingServiceTest,MAYBE_SendReportsAndRemoveSourceWithPendingReports)294 TEST_P(ReportingServiceTest,
295 MAYBE_SendReportsAndRemoveSourceWithPendingReports) {
296 base::test::ScopedFeatureList feature_list;
297 feature_list.InitAndEnableFeature(net::features::kDocumentReporting);
298 auto parsed_header =
299 ParseReportingEndpoints(kGroup_ + "=\"" + kEndpoint_.spec() + "\", " +
300 kGroup2_ + "=\"" + kEndpoint2_.spec() + "\"");
301 ASSERT_TRUE(parsed_header.has_value());
302 service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_,
303 kIsolationInfo_, *parsed_header);
304 // This report should be sent immediately, starting the delivery agent timer.
305 service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
306 kType_, base::Value::Dict(), 0);
307
308 FinishLoading(true /* load_success */);
309
310 std::vector<raw_ptr<const ReportingReport, VectorExperimental>> reports;
311 context()->cache()->GetReports(&reports);
312 ASSERT_EQ(1u, reports.size());
313 EXPECT_EQ(0u, context()->cache()->GetReportCountWithStatusForTesting(
314 ReportingReport::Status::QUEUED));
315 EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting(
316 ReportingReport::Status::PENDING));
317
318 // Queue another report, which should remain queued.
319 service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
320 kType_, base::Value::Dict(), 0);
321 EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting(
322 ReportingReport::Status::QUEUED));
323 EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting(
324 ReportingReport::Status::PENDING));
325
326 // Now simulate the source being destroyed.
327 service()->SendReportsAndRemoveSource(*kReportingSource_);
328
329 // The report should still be queued, while the source should be marked as
330 // expired. (The original report is still pending.)
331 EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting(
332 ReportingReport::Status::QUEUED));
333 EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting(
334 ReportingReport::Status::PENDING));
335 ASSERT_TRUE(
336 context()->cache()->GetExpiredSources().contains(kReportingSource_));
337 }
338
TEST_P(ReportingServiceTest,ProcessReportingEndpointsHeaderPathAbsolute)339 TEST_P(ReportingServiceTest, ProcessReportingEndpointsHeaderPathAbsolute) {
340 base::test::ScopedFeatureList feature_list;
341 feature_list.InitAndEnableFeature(net::features::kDocumentReporting);
342 auto parsed_header = ParseReportingEndpoints(kGroup_ + "=\"/path-absolute\"");
343 ASSERT_TRUE(parsed_header.has_value());
344 service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_,
345 kIsolationInfo_, *parsed_header);
346 FinishLoading(true /* load_success */);
347
348 // Endpoint should not be part of the persistent store.
349 EXPECT_EQ(0u, context()->cache()->GetEndpointCount());
350 // Endpoint should be associated with the reporting source.
351 ReportingEndpoint endpoint =
352 context()->cache()->GetV1EndpointForTesting(*kReportingSource_, kGroup_);
353 EXPECT_TRUE(endpoint);
354 // Endpoint should have the correct path.
355 EXPECT_EQ(kUrl_.Resolve("/path-absolute"), endpoint.info.url);
356 }
357
TEST_P(ReportingServiceTest,ProcessReportToHeaderPathAbsolute)358 TEST_P(ReportingServiceTest, ProcessReportToHeaderPathAbsolute) {
359 service()->ProcessReportToHeader(
360 kOrigin_, kNak_,
361 "{\"endpoints\":[{\"url\":\"/path-absolute\"}],"
362 "\"group\":\"" +
363 kGroup_ +
364 "\","
365 "\"max_age\":86400}");
366 FinishLoading(true /* load_success */);
367
368 EXPECT_EQ(1u, context()->cache()->GetEndpointCount());
369 }
370
TEST_P(ReportingServiceTest,ProcessReportToHeader_TooLong)371 TEST_P(ReportingServiceTest, ProcessReportToHeader_TooLong) {
372 const std::string header_too_long =
373 "{\"endpoints\":[{\"url\":\"" + kEndpoint_.spec() +
374 "\"}],"
375 "\"group\":\"" +
376 kGroup_ +
377 "\","
378 "\"max_age\":86400," +
379 "\"junk\":\"" + std::string(32 * 1024, 'a') + "\"}";
380 // This does not trigger an attempt to load from the store because the header
381 // is immediately rejected as invalid.
382 service()->ProcessReportToHeader(kOrigin_, kNak_, header_too_long);
383
384 EXPECT_EQ(0u, context()->cache()->GetEndpointCount());
385 }
386
TEST_P(ReportingServiceTest,ProcessReportToHeader_TooDeep)387 TEST_P(ReportingServiceTest, ProcessReportToHeader_TooDeep) {
388 const std::string header_too_deep = "{\"endpoints\":[{\"url\":\"" +
389 kEndpoint_.spec() +
390 "\"}],"
391 "\"group\":\"" +
392 kGroup_ +
393 "\","
394 "\"max_age\":86400," +
395 "\"junk\":[[[[[[[[[[]]]]]]]]]]}";
396 // This does not trigger an attempt to load from the store because the header
397 // is immediately rejected as invalid.
398 service()->ProcessReportToHeader(kOrigin_, kNak_, header_too_deep);
399
400 EXPECT_EQ(0u, context()->cache()->GetEndpointCount());
401 }
402
TEST_P(ReportingServiceTest,ProcessReportToHeaderNetworkIsolationKeyDisabled)403 TEST_P(ReportingServiceTest, ProcessReportToHeaderNetworkIsolationKeyDisabled) {
404 base::test::ScopedFeatureList feature_list;
405 feature_list.InitAndDisableFeature(
406 features::kPartitionNelAndReportingByNetworkIsolationKey);
407
408 // Re-create the store, so it reads the new feature value.
409 Init();
410
411 service()->ProcessReportToHeader(kOrigin_, kNak_,
412 "{\"endpoints\":[{\"url\":\"" +
413 kEndpoint_.spec() +
414 "\"}],"
415 "\"group\":\"" +
416 kGroup_ +
417 "\","
418 "\"max_age\":86400}");
419 FinishLoading(true /* load_success */);
420
421 EXPECT_EQ(1u, context()->cache()->GetEndpointCount());
422 EXPECT_FALSE(context()->cache()->GetEndpointForTesting(
423 ReportingEndpointGroupKey(kNak_, kOrigin_, kGroup_), kEndpoint_));
424 EXPECT_TRUE(context()->cache()->GetEndpointForTesting(
425 ReportingEndpointGroupKey(NetworkAnonymizationKey(), kOrigin_, kGroup_),
426 kEndpoint_));
427 }
428
TEST_P(ReportingServiceTest,WriteToStore)429 TEST_P(ReportingServiceTest, WriteToStore) {
430 if (!store()) {
431 return;
432 }
433
434 MockPersistentReportingStore::CommandList expected_commands;
435
436 // This first call to any public method triggers a load. The load will block
437 // until we call FinishLoading.
438 service()->ProcessReportToHeader(kOrigin_, kNak_,
439 "{\"endpoints\":[{\"url\":\"" +
440 kEndpoint_.spec() +
441 "\"}],"
442 "\"group\":\"" +
443 kGroup_ +
444 "\","
445 "\"max_age\":86400}");
446 expected_commands.emplace_back(CommandType::LOAD_REPORTING_CLIENTS);
447 EXPECT_THAT(store()->GetAllCommands(),
448 testing::UnorderedElementsAreArray(expected_commands));
449
450 // Unblock the load. The will let the remaining calls to the service complete
451 // without blocking.
452 FinishLoading(true /* load_success */);
453 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
454 kGroupKey_, kEndpoint_);
455 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
456 kGroupKey_);
457 EXPECT_THAT(store()->GetAllCommands(),
458 testing::UnorderedElementsAreArray(expected_commands));
459
460 service()->ProcessReportToHeader(kOrigin2_, kNak2_,
461 "{\"endpoints\":[{\"url\":\"" +
462 kEndpoint_.spec() +
463 "\"}],"
464 "\"group\":\"" +
465 kGroup_ +
466 "\","
467 "\"max_age\":86400}");
468 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
469 kGroupKey2_, kEndpoint_);
470 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
471 kGroupKey2_);
472 EXPECT_THAT(store()->GetAllCommands(),
473 testing::UnorderedElementsAreArray(expected_commands));
474
475 service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
476 kType_, base::Value::Dict(), 0);
477 expected_commands.emplace_back(
478 CommandType::UPDATE_REPORTING_ENDPOINT_GROUP_ACCESS_TIME, kGroupKey_);
479 EXPECT_THAT(store()->GetAllCommands(),
480 testing::UnorderedElementsAreArray(expected_commands));
481
482 service()->RemoveBrowsingData(
483 ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS,
484 base::BindRepeating(
485 [](const url::Origin& origin) { return origin.host() == "origin"; }));
486 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
487 kGroupKey_, kEndpoint_);
488 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
489 kGroupKey_);
490 expected_commands.emplace_back(CommandType::FLUSH);
491 EXPECT_THAT(store()->GetAllCommands(),
492 testing::UnorderedElementsAreArray(expected_commands));
493
494 service()->RemoveAllBrowsingData(
495 ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS);
496 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
497 kGroupKey2_, kEndpoint_);
498 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
499 kGroupKey2_);
500 expected_commands.emplace_back(CommandType::FLUSH);
501 EXPECT_THAT(store()->GetAllCommands(),
502 testing::UnorderedElementsAreArray(expected_commands));
503 }
504
TEST_P(ReportingServiceTest,WaitUntilLoadFinishesBeforeWritingToStore)505 TEST_P(ReportingServiceTest, WaitUntilLoadFinishesBeforeWritingToStore) {
506 if (!store()) {
507 return;
508 }
509
510 MockPersistentReportingStore::CommandList expected_commands;
511
512 // This first call to any public method triggers a load. The load will block
513 // until we call FinishLoading.
514 service()->ProcessReportToHeader(kOrigin_, kNak_,
515 "{\"endpoints\":[{\"url\":\"" +
516 kEndpoint_.spec() +
517 "\"}],"
518 "\"group\":\"" +
519 kGroup_ +
520 "\","
521 "\"max_age\":86400}");
522 expected_commands.emplace_back(CommandType::LOAD_REPORTING_CLIENTS);
523 EXPECT_THAT(store()->GetAllCommands(),
524 testing::UnorderedElementsAreArray(expected_commands));
525
526 service()->ProcessReportToHeader(kOrigin2_, kNak2_,
527 "{\"endpoints\":[{\"url\":\"" +
528 kEndpoint_.spec() +
529 "\"}],"
530 "\"group\":\"" +
531 kGroup_ +
532 "\","
533 "\"max_age\":86400}");
534 EXPECT_THAT(store()->GetAllCommands(),
535 testing::UnorderedElementsAreArray(expected_commands));
536
537 service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
538 kType_, base::Value::Dict(), 0);
539 EXPECT_THAT(store()->GetAllCommands(),
540 testing::UnorderedElementsAreArray(expected_commands));
541
542 service()->RemoveBrowsingData(
543 ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS,
544 base::BindRepeating(
545 [](const url::Origin& origin) { return origin.host() == "origin"; }));
546 EXPECT_THAT(store()->GetAllCommands(),
547 testing::UnorderedElementsAreArray(expected_commands));
548
549 service()->RemoveAllBrowsingData(
550 ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS);
551 EXPECT_THAT(store()->GetAllCommands(),
552 testing::UnorderedElementsAreArray(expected_commands));
553
554 // Unblock the load. The will let the remaining calls to the service complete
555 // without blocking.
556 FinishLoading(true /* load_success */);
557 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
558 kGroupKey_, kEndpoint_);
559 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
560 kGroupKey2_, kEndpoint_);
561 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
562 kGroupKey_);
563 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
564 kGroupKey2_);
565 expected_commands.emplace_back(
566 CommandType::UPDATE_REPORTING_ENDPOINT_GROUP_ACCESS_TIME, kGroupKey_);
567 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
568 kGroupKey_, kEndpoint_);
569 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
570 kGroupKey_);
571 expected_commands.emplace_back(CommandType::FLUSH);
572 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
573 kGroupKey2_, kEndpoint_);
574 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
575 kGroupKey2_);
576 expected_commands.emplace_back(CommandType::FLUSH);
577 EXPECT_THAT(store()->GetAllCommands(),
578 testing::UnorderedElementsAreArray(expected_commands));
579 }
580
581 INSTANTIATE_TEST_SUITE_P(ReportingServiceStoreTest,
582 ReportingServiceTest,
583 ::testing::Bool());
584 } // namespace
585 } // namespace net
586