xref: /aosp_15_r20/external/cronet/net/reporting/reporting_header_parser_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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_header_parser.h"
6 
7 #include <optional>
8 #include <sstream>
9 #include <string>
10 #include <vector>
11 
12 #include "base/functional/bind.h"
13 #include "base/json/json_reader.h"
14 #include "base/strings/strcat.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/test/metrics/histogram_tester.h"
17 #include "base/test/scoped_feature_list.h"
18 #include "base/test/simple_test_tick_clock.h"
19 #include "base/time/time.h"
20 #include "base/values.h"
21 #include "net/base/features.h"
22 #include "net/base/isolation_info.h"
23 #include "net/base/network_anonymization_key.h"
24 #include "net/base/schemeful_site.h"
25 #include "net/reporting/mock_persistent_reporting_store.h"
26 #include "net/reporting/reporting_cache.h"
27 #include "net/reporting/reporting_endpoint.h"
28 #include "net/reporting/reporting_test_util.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30 #include "url/gurl.h"
31 #include "url/origin.h"
32 
33 namespace net {
34 namespace {
35 
36 using CommandType = MockPersistentReportingStore::Command::Type;
37 using Dictionary = structured_headers::Dictionary;
38 
39 constexpr char kReportingHeaderTypeHistogram[] = "Net.Reporting.HeaderType";
40 
41 class ReportingHeaderParserTestBase
42     : public ReportingTestBase,
43       public ::testing::WithParamInterface<bool> {
44  protected:
ReportingHeaderParserTestBase()45   ReportingHeaderParserTestBase() {
46     ReportingPolicy policy;
47     policy.max_endpoints_per_origin = 10;
48     policy.max_endpoint_count = 20;
49     UsePolicy(policy);
50 
51     if (GetParam())
52       store_ = std::make_unique<MockPersistentReportingStore>();
53     else
54       store_ = nullptr;
55     UseStore(store_.get());
56   }
57   ~ReportingHeaderParserTestBase() override = default;
58 
SetUp()59   void SetUp() override {
60     // All ReportingCache methods assume that the store has been initialized.
61     if (mock_store()) {
62       mock_store()->LoadReportingClients(
63           base::BindOnce(&ReportingCache::AddClientsLoadedFromStore,
64                          base::Unretained(cache())));
65       mock_store()->FinishLoading(true);
66     }
67   }
68 
mock_store()69   MockPersistentReportingStore* mock_store() { return store_.get(); }
70 
71   base::test::ScopedFeatureList feature_list_;
72   const GURL kUrl1_ = GURL("https://origin1.test/path");
73   const url::Origin kOrigin1_ = url::Origin::Create(kUrl1_);
74   const GURL kUrl2_ = GURL("https://origin2.test/path");
75   const url::Origin kOrigin2_ = url::Origin::Create(kUrl2_);
76   const NetworkAnonymizationKey kNak_ =
77       NetworkAnonymizationKey::CreateSameSite(SchemefulSite(kOrigin1_));
78   const NetworkAnonymizationKey kOtherNak_ =
79       NetworkAnonymizationKey::CreateSameSite(SchemefulSite(kOrigin2_));
80   const IsolationInfo kIsolationInfo_ =
81       IsolationInfo::Create(IsolationInfo::RequestType::kOther,
82                             kOrigin1_,
83                             kOrigin1_,
84                             SiteForCookies::FromOrigin(kOrigin1_));
85   const GURL kUrlEtld_ = GURL("https://co.uk/foo.html/");
86   const url::Origin kOriginEtld_ = url::Origin::Create(kUrlEtld_);
87   const GURL kEndpoint1_ = GURL("https://endpoint1.test/");
88   const GURL kEndpoint2_ = GURL("https://endpoint2.test/");
89   const GURL kEndpoint3_ = GURL("https://endpoint3.test/");
90   const GURL kEndpointPathAbsolute_ =
91       GURL("https://origin1.test/path-absolute-url");
92   const std::string kGroup1_ = "group1";
93   const std::string kGroup2_ = "group2";
94   // There are 2^3 = 8 of these to test the different combinations of matching
95   // vs mismatching NAK, origin, and group.
96   const ReportingEndpointGroupKey kGroupKey11_ =
97       ReportingEndpointGroupKey(kNak_, kOrigin1_, kGroup1_);
98   const ReportingEndpointGroupKey kGroupKey21_ =
99       ReportingEndpointGroupKey(kNak_, kOrigin2_, kGroup1_);
100   const ReportingEndpointGroupKey kGroupKey12_ =
101       ReportingEndpointGroupKey(kNak_, kOrigin1_, kGroup2_);
102   const ReportingEndpointGroupKey kGroupKey22_ =
103       ReportingEndpointGroupKey(kNak_, kOrigin2_, kGroup2_);
104 
105  private:
106   std::unique_ptr<MockPersistentReportingStore> store_;
107 };
108 
109 // This test is parametrized on a boolean that represents whether to use a
110 // MockPersistentReportingStore.
111 class ReportingHeaderParserTest : public ReportingHeaderParserTestBase {
112  protected:
ReportingHeaderParserTest()113   ReportingHeaderParserTest() {
114     // This is a private API of the reporting service, so no need to test the
115     // case kPartitionNelAndReportingByNetworkIsolationKey is disabled - the
116     // feature is only applied at the entry points of the service.
117     feature_list_.InitAndEnableFeature(
118         features::kPartitionNelAndReportingByNetworkIsolationKey);
119   }
120 
MakeEndpointGroup(const std::string & name,const std::vector<ReportingEndpoint::EndpointInfo> & endpoints,OriginSubdomains include_subdomains=OriginSubdomains::DEFAULT,base::TimeDelta ttl=base::Days (1),url::Origin origin=url::Origin ())121   ReportingEndpointGroup MakeEndpointGroup(
122       const std::string& name,
123       const std::vector<ReportingEndpoint::EndpointInfo>& endpoints,
124       OriginSubdomains include_subdomains = OriginSubdomains::DEFAULT,
125       base::TimeDelta ttl = base::Days(1),
126       url::Origin origin = url::Origin()) {
127     ReportingEndpointGroupKey group_key(kNak_ /* unused */,
128                                         url::Origin() /* unused */, name);
129     ReportingEndpointGroup group;
130     group.group_key = group_key;
131     group.include_subdomains = include_subdomains;
132     group.ttl = ttl;
133     group.endpoints = std::move(endpoints);
134     return group;
135   }
136 
137   // Constructs a string which would represent a single group in a Report-To
138   // header. If |group_name| is an empty string, the group name will be omitted
139   // (and thus default to "default" when parsed). Setting |omit_defaults| omits
140   // the priority, weight, and include_subdomains fields if they are default,
141   // otherwise they are spelled out fully.
ConstructHeaderGroupString(const ReportingEndpointGroup & group,bool omit_defaults=true)142   std::string ConstructHeaderGroupString(const ReportingEndpointGroup& group,
143                                          bool omit_defaults = true) {
144     std::ostringstream s;
145     s << "{ ";
146 
147     if (!group.group_key.group_name.empty()) {
148       s << "\"group\": \"" << group.group_key.group_name << "\", ";
149     }
150 
151     s << "\"max_age\": " << group.ttl.InSeconds() << ", ";
152 
153     if (group.include_subdomains != OriginSubdomains::DEFAULT) {
154       s << "\"include_subdomains\": true, ";
155     } else if (!omit_defaults) {
156       s << "\"include_subdomains\": false, ";
157     }
158 
159     s << "\"endpoints\": [";
160     for (const ReportingEndpoint::EndpointInfo& endpoint_info :
161          group.endpoints) {
162       s << "{ ";
163       s << "\"url\": \"" << endpoint_info.url.spec() << "\"";
164 
165       if (!omit_defaults ||
166           endpoint_info.priority !=
167               ReportingEndpoint::EndpointInfo::kDefaultPriority) {
168         s << ", \"priority\": " << endpoint_info.priority;
169       }
170 
171       if (!omit_defaults ||
172           endpoint_info.weight !=
173               ReportingEndpoint::EndpointInfo::kDefaultWeight) {
174         s << ", \"weight\": " << endpoint_info.weight;
175       }
176 
177       s << " }, ";
178     }
179     if (!group.endpoints.empty())
180       s.seekp(-2, s.cur);  // Overwrite trailing comma and space.
181     s << "]";
182 
183     s << " }";
184 
185     return s.str();
186   }
187 
ParseHeader(const NetworkAnonymizationKey & network_anonymization_key,const url::Origin & origin,const std::string & json)188   void ParseHeader(const NetworkAnonymizationKey& network_anonymization_key,
189                    const url::Origin& origin,
190                    const std::string& json) {
191     std::optional<base::Value> value = base::JSONReader::Read("[" + json + "]");
192     if (value) {
193       ReportingHeaderParser::ParseReportToHeader(
194           context(), network_anonymization_key, origin, value->GetList());
195     }
196   }
197 };
198 
199 // TODO(juliatuttle): Ideally these tests should be expecting that JSON parsing
200 // (and therefore header parsing) may happen asynchronously, but the entire
201 // pipeline is also tested by NetworkErrorLoggingEndToEndTest.
202 
TEST_P(ReportingHeaderParserTest,Invalid)203 TEST_P(ReportingHeaderParserTest, Invalid) {
204   static const struct {
205     const char* header_value;
206     const char* description;
207   } kInvalidHeaderTestCases[] = {
208       {"{\"max_age\":1, \"endpoints\": [{}]}", "missing url"},
209       {"{\"max_age\":1, \"endpoints\": [{\"url\":0}]}", "non-string url"},
210       {"{\"max_age\":1, \"endpoints\": [{\"url\":\"//scheme/relative\"}]}",
211        "scheme-relative url"},
212       {"{\"max_age\":1, \"endpoints\": [{\"url\":\"relative/path\"}]}",
213        "path relative url"},
214       {"{\"max_age\":1, \"endpoints\": [{\"url\":\"http://insecure/\"}]}",
215        "insecure url"},
216       {"{\"endpoints\": [{\"url\":\"https://endpoint/\"}]}", "missing max_age"},
217       {"{\"max_age\":\"\", \"endpoints\": [{\"url\":\"https://endpoint/\"}]}",
218        "non-integer max_age"},
219       {"{\"max_age\":-1, \"endpoints\": [{\"url\":\"https://endpoint/\"}]}",
220        "negative max_age"},
221       {"{\"max_age\":1, \"group\":0, "
222        "\"endpoints\": [{\"url\":\"https://endpoint/\"}]}",
223        "non-string group"},
224 
225       // Note that a non-boolean include_subdomains field is *not* invalid, per
226       // the spec.
227 
228       // Priority should be a nonnegative integer.
229       {"{\"max_age\":1, "
230        "\"endpoints\": [{\"url\":\"https://endpoint/\",\"priority\":\"\"}]}",
231        "non-integer priority"},
232       {"{\"max_age\":1, "
233        "\"endpoints\": [{\"url\":\"https://endpoint/\",\"priority\":-1}]}",
234        "negative priority"},
235 
236       // Weight should be a non-negative integer.
237       {"{\"max_age\":1, "
238        "\"endpoints\": [{\"url\":\"https://endpoint/\",\"weight\":\"\"}]}",
239        "non-integer weight"},
240       {"{\"max_age\":1, "
241        "\"endpoints\": [{\"url\":\"https://endpoint/\",\"weight\":-1}]}",
242        "negative weight"},
243 
244       {"[{\"max_age\":1, \"endpoints\": [{\"url\":\"https://a/\"}]},"
245        "{\"max_age\":1, \"endpoints\": [{\"url\":\"https://b/\"}]}]",
246        "wrapped in list"}};
247 
248   base::HistogramTester histograms;
249   int invalid_case_count = 0;
250 
251   for (const auto& test_case : kInvalidHeaderTestCases) {
252     ParseHeader(kNak_, kOrigin1_, test_case.header_value);
253     invalid_case_count++;
254 
255     EXPECT_EQ(0u, cache()->GetEndpointCount())
256         << "Invalid Report-To header (" << test_case.description << ": \""
257         << test_case.header_value << "\") parsed as valid.";
258     histograms.ExpectBucketCount(
259         kReportingHeaderTypeHistogram,
260         ReportingHeaderParser::ReportingHeaderType::kReportToInvalid,
261         invalid_case_count);
262     if (mock_store()) {
263       mock_store()->Flush();
264       EXPECT_EQ(0, mock_store()->StoredEndpointsCount());
265       EXPECT_EQ(0, mock_store()->StoredEndpointGroupsCount());
266     }
267   }
268   histograms.ExpectBucketCount(
269       kReportingHeaderTypeHistogram,
270       ReportingHeaderParser::ReportingHeaderType::kReportTo, 0);
271 }
272 
TEST_P(ReportingHeaderParserTest,Basic)273 TEST_P(ReportingHeaderParserTest, Basic) {
274   std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}};
275   base::HistogramTester histograms;
276 
277   std::string header =
278       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints));
279 
280   ParseHeader(kNak_, kOrigin1_, header);
281   EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
282   histograms.ExpectBucketCount(
283       kReportingHeaderTypeHistogram,
284       ReportingHeaderParser::ReportingHeaderType::kReportTo, 1);
285   EXPECT_TRUE(
286       EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
287   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
288   EXPECT_EQ(1u, cache()->GetEndpointCount());
289   ReportingEndpoint endpoint = FindEndpointInCache(kGroupKey11_, kEndpoint1_);
290   ASSERT_TRUE(endpoint);
291   EXPECT_EQ(kOrigin1_, endpoint.group_key.origin);
292   EXPECT_EQ(kGroup1_, endpoint.group_key.group_name);
293   EXPECT_EQ(kEndpoint1_, endpoint.info.url);
294   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
295             endpoint.info.priority);
296   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
297             endpoint.info.weight);
298 
299   if (mock_store()) {
300     mock_store()->Flush();
301     EXPECT_EQ(1, mock_store()->StoredEndpointsCount());
302     EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
303     MockPersistentReportingStore::CommandList expected_commands;
304     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
305                                    kGroupKey11_, kEndpoint1_);
306     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
307                                    kGroupKey11_);
308     EXPECT_THAT(mock_store()->GetAllCommands(),
309                 testing::IsSupersetOf(expected_commands));
310   }
311 }
312 
TEST_P(ReportingHeaderParserTest,PathAbsoluteURLEndpoint)313 TEST_P(ReportingHeaderParserTest, PathAbsoluteURLEndpoint) {
314   std::string header =
315       "{\"group\": \"group1\", \"max_age\":1, \"endpoints\": "
316       "[{\"url\":\"/path-absolute-url\"}]}";
317   base::HistogramTester histograms;
318 
319   ParseHeader(kNak_, kOrigin1_, header);
320   EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
321   histograms.ExpectBucketCount(
322       kReportingHeaderTypeHistogram,
323       ReportingHeaderParser::ReportingHeaderType::kReportTo, 1);
324   EXPECT_TRUE(
325       EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
326   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
327   EXPECT_EQ(1u, cache()->GetEndpointCount());
328   ReportingEndpoint endpoint =
329       FindEndpointInCache(kGroupKey11_, kEndpointPathAbsolute_);
330   ASSERT_TRUE(endpoint);
331   EXPECT_EQ(kOrigin1_, endpoint.group_key.origin);
332   EXPECT_EQ(kGroup1_, endpoint.group_key.group_name);
333   EXPECT_EQ(kEndpointPathAbsolute_, endpoint.info.url);
334   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
335             endpoint.info.priority);
336   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
337             endpoint.info.weight);
338 
339   if (mock_store()) {
340     mock_store()->Flush();
341     EXPECT_EQ(1, mock_store()->StoredEndpointsCount());
342     EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
343     MockPersistentReportingStore::CommandList expected_commands;
344     expected_commands.emplace_back(
345         CommandType::ADD_REPORTING_ENDPOINT,
346         ReportingEndpoint(kGroupKey11_, ReportingEndpoint::EndpointInfo{
347                                             kEndpointPathAbsolute_}));
348     expected_commands.emplace_back(
349         CommandType::ADD_REPORTING_ENDPOINT_GROUP,
350         CachedReportingEndpointGroup(
351             kGroupKey11_, OriginSubdomains::DEFAULT /* irrelevant */,
352             base::Time() /* irrelevant */, base::Time() /* irrelevant */));
353     EXPECT_THAT(mock_store()->GetAllCommands(),
354                 testing::IsSupersetOf(expected_commands));
355   }
356 }
357 
TEST_P(ReportingHeaderParserTest,OmittedGroupName)358 TEST_P(ReportingHeaderParserTest, OmittedGroupName) {
359   ReportingEndpointGroupKey kGroupKey(kNak_, kOrigin1_, "default");
360   std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}};
361   std::string header =
362       ConstructHeaderGroupString(MakeEndpointGroup(std::string(), endpoints));
363 
364   ParseHeader(kNak_, kOrigin1_, header);
365   EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
366   EXPECT_TRUE(EndpointGroupExistsInCache(kGroupKey, OriginSubdomains::DEFAULT));
367   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
368   EXPECT_EQ(1u, cache()->GetEndpointCount());
369   ReportingEndpoint endpoint = FindEndpointInCache(kGroupKey, kEndpoint1_);
370   ASSERT_TRUE(endpoint);
371   EXPECT_EQ(kOrigin1_, endpoint.group_key.origin);
372   EXPECT_EQ("default", endpoint.group_key.group_name);
373   EXPECT_EQ(kEndpoint1_, endpoint.info.url);
374   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
375             endpoint.info.priority);
376   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
377             endpoint.info.weight);
378 
379   if (mock_store()) {
380     mock_store()->Flush();
381     EXPECT_EQ(1, mock_store()->StoredEndpointsCount());
382     EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
383     MockPersistentReportingStore::CommandList expected_commands;
384     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
385                                    kGroupKey, kEndpoint1_);
386     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
387                                    kGroupKey);
388     EXPECT_THAT(mock_store()->GetAllCommands(),
389                 testing::IsSupersetOf(expected_commands));
390   }
391 }
392 
TEST_P(ReportingHeaderParserTest,IncludeSubdomainsTrue)393 TEST_P(ReportingHeaderParserTest, IncludeSubdomainsTrue) {
394   std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}};
395 
396   std::string header = ConstructHeaderGroupString(
397       MakeEndpointGroup(kGroup1_, endpoints, OriginSubdomains::INCLUDE));
398   ParseHeader(kNak_, kOrigin1_, header);
399 
400   EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
401   EXPECT_TRUE(
402       EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::INCLUDE));
403   EXPECT_EQ(1u, cache()->GetEndpointCount());
404   EXPECT_TRUE(EndpointExistsInCache(kGroupKey11_, kEndpoint1_));
405 
406   if (mock_store()) {
407     mock_store()->Flush();
408     EXPECT_EQ(1, mock_store()->StoredEndpointsCount());
409     EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
410     MockPersistentReportingStore::CommandList expected_commands;
411     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
412                                    kGroupKey11_, kEndpoint1_);
413     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
414                                    kGroupKey11_);
415     EXPECT_THAT(mock_store()->GetAllCommands(),
416                 testing::IsSupersetOf(expected_commands));
417   }
418 }
419 
TEST_P(ReportingHeaderParserTest,IncludeSubdomainsFalse)420 TEST_P(ReportingHeaderParserTest, IncludeSubdomainsFalse) {
421   std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}};
422 
423   std::string header = ConstructHeaderGroupString(
424       MakeEndpointGroup(kGroup1_, endpoints, OriginSubdomains::EXCLUDE),
425       false /* omit_defaults */);
426   ParseHeader(kNak_, kOrigin1_, header);
427 
428   EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
429   EXPECT_TRUE(
430       EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::EXCLUDE));
431   EXPECT_EQ(1u, cache()->GetEndpointCount());
432   EXPECT_TRUE(EndpointExistsInCache(kGroupKey11_, kEndpoint1_));
433 
434   if (mock_store()) {
435     mock_store()->Flush();
436     EXPECT_EQ(1, mock_store()->StoredEndpointsCount());
437     EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
438     MockPersistentReportingStore::CommandList expected_commands;
439     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
440                                    kGroupKey11_, kEndpoint1_);
441     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
442                                    kGroupKey11_);
443     EXPECT_THAT(mock_store()->GetAllCommands(),
444                 testing::IsSupersetOf(expected_commands));
445   }
446 }
447 
TEST_P(ReportingHeaderParserTest,IncludeSubdomainsEtldRejected)448 TEST_P(ReportingHeaderParserTest, IncludeSubdomainsEtldRejected) {
449   ReportingEndpointGroupKey kGroupKey(kNak_, kOriginEtld_, kGroup1_);
450   std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}};
451 
452   std::string header = ConstructHeaderGroupString(
453       MakeEndpointGroup(kGroup1_, endpoints, OriginSubdomains::INCLUDE));
454   ParseHeader(kNak_, kOriginEtld_, header);
455 
456   EXPECT_EQ(0u, cache()->GetEndpointGroupCountForTesting());
457   EXPECT_FALSE(
458       EndpointGroupExistsInCache(kGroupKey, OriginSubdomains::INCLUDE));
459   EXPECT_EQ(0u, cache()->GetEndpointCount());
460   EXPECT_FALSE(EndpointExistsInCache(kGroupKey, kEndpoint1_));
461 }
462 
TEST_P(ReportingHeaderParserTest,NonIncludeSubdomainsEtldAccepted)463 TEST_P(ReportingHeaderParserTest, NonIncludeSubdomainsEtldAccepted) {
464   ReportingEndpointGroupKey kGroupKey(kNak_, kOriginEtld_, kGroup1_);
465   std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}};
466 
467   std::string header = ConstructHeaderGroupString(
468       MakeEndpointGroup(kGroup1_, endpoints, OriginSubdomains::EXCLUDE));
469   ParseHeader(kNak_, kOriginEtld_, header);
470 
471   EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
472   EXPECT_TRUE(EndpointGroupExistsInCache(kGroupKey, OriginSubdomains::EXCLUDE));
473   EXPECT_EQ(1u, cache()->GetEndpointCount());
474   EXPECT_TRUE(EndpointExistsInCache(kGroupKey, kEndpoint1_));
475 }
476 
TEST_P(ReportingHeaderParserTest,IncludeSubdomainsNotBoolean)477 TEST_P(ReportingHeaderParserTest, IncludeSubdomainsNotBoolean) {
478   std::string header =
479       "{\"group\": \"" + kGroup1_ +
480       "\", "
481       "\"max_age\":86400, \"include_subdomains\": \"NotABoolean\", "
482       "\"endpoints\": [{\"url\":\"" +
483       kEndpoint1_.spec() + "\"}]}";
484   ParseHeader(kNak_, kOrigin1_, header);
485 
486   EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
487   EXPECT_TRUE(
488       EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
489   EXPECT_EQ(1u, cache()->GetEndpointCount());
490   EXPECT_TRUE(EndpointExistsInCache(kGroupKey11_, kEndpoint1_));
491 
492   if (mock_store()) {
493     mock_store()->Flush();
494     EXPECT_EQ(1, mock_store()->StoredEndpointsCount());
495     EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
496     MockPersistentReportingStore::CommandList expected_commands;
497     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
498                                    kGroupKey11_, kEndpoint1_);
499     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
500                                    kGroupKey11_);
501     EXPECT_THAT(mock_store()->GetAllCommands(),
502                 testing::IsSupersetOf(expected_commands));
503   }
504 }
505 
TEST_P(ReportingHeaderParserTest,NonDefaultPriority)506 TEST_P(ReportingHeaderParserTest, NonDefaultPriority) {
507   const int kNonDefaultPriority = 10;
508   std::vector<ReportingEndpoint::EndpointInfo> endpoints = {
509       {kEndpoint1_, kNonDefaultPriority}};
510 
511   std::string header =
512       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints));
513   ParseHeader(kNak_, kOrigin1_, header);
514 
515   EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
516   EXPECT_TRUE(
517       EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
518   EXPECT_EQ(1u, cache()->GetEndpointCount());
519   ReportingEndpoint endpoint = FindEndpointInCache(kGroupKey11_, kEndpoint1_);
520   ASSERT_TRUE(endpoint);
521   EXPECT_EQ(kNonDefaultPriority, endpoint.info.priority);
522   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
523             endpoint.info.weight);
524 
525   if (mock_store()) {
526     mock_store()->Flush();
527     EXPECT_EQ(1, mock_store()->StoredEndpointsCount());
528     EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
529     MockPersistentReportingStore::CommandList expected_commands;
530     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
531                                    kGroupKey11_, kEndpoint1_);
532     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
533                                    kGroupKey11_);
534     EXPECT_THAT(mock_store()->GetAllCommands(),
535                 testing::IsSupersetOf(expected_commands));
536   }
537 }
538 
TEST_P(ReportingHeaderParserTest,NonDefaultWeight)539 TEST_P(ReportingHeaderParserTest, NonDefaultWeight) {
540   const int kNonDefaultWeight = 10;
541   std::vector<ReportingEndpoint::EndpointInfo> endpoints = {
542       {kEndpoint1_, ReportingEndpoint::EndpointInfo::kDefaultPriority,
543        kNonDefaultWeight}};
544 
545   std::string header =
546       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints));
547   ParseHeader(kNak_, kOrigin1_, header);
548 
549   EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
550   EXPECT_TRUE(
551       EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
552   EXPECT_EQ(1u, cache()->GetEndpointCount());
553   ReportingEndpoint endpoint = FindEndpointInCache(kGroupKey11_, kEndpoint1_);
554   ASSERT_TRUE(endpoint);
555   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
556             endpoint.info.priority);
557   EXPECT_EQ(kNonDefaultWeight, endpoint.info.weight);
558 
559   if (mock_store()) {
560     mock_store()->Flush();
561     EXPECT_EQ(1, mock_store()->StoredEndpointsCount());
562     EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
563     MockPersistentReportingStore::CommandList expected_commands;
564     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
565                                    kGroupKey11_, kEndpoint1_);
566     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
567                                    kGroupKey11_);
568     EXPECT_THAT(mock_store()->GetAllCommands(),
569                 testing::IsSupersetOf(expected_commands));
570   }
571 }
572 
TEST_P(ReportingHeaderParserTest,MaxAge)573 TEST_P(ReportingHeaderParserTest, MaxAge) {
574   const int kMaxAgeSecs = 100;
575   base::TimeDelta ttl = base::Seconds(kMaxAgeSecs);
576   base::Time expires = clock()->Now() + ttl;
577 
578   std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}};
579 
580   std::string header = ConstructHeaderGroupString(
581       MakeEndpointGroup(kGroup1_, endpoints, OriginSubdomains::DEFAULT, ttl));
582 
583   ParseHeader(kNak_, kOrigin1_, header);
584   EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
585   EXPECT_TRUE(EndpointGroupExistsInCache(kGroupKey11_,
586                                          OriginSubdomains::DEFAULT, expires));
587 
588   if (mock_store()) {
589     mock_store()->Flush();
590     EXPECT_EQ(1, mock_store()->StoredEndpointsCount());
591     EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
592     MockPersistentReportingStore::CommandList expected_commands;
593     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
594                                    kGroupKey11_, kEndpoint1_);
595     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
596                                    kGroupKey11_);
597     EXPECT_THAT(mock_store()->GetAllCommands(),
598                 testing::IsSupersetOf(expected_commands));
599   }
600 }
601 
TEST_P(ReportingHeaderParserTest,MultipleEndpointsSameGroup)602 TEST_P(ReportingHeaderParserTest, MultipleEndpointsSameGroup) {
603   std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_},
604                                                             {kEndpoint2_}};
605   std::string header =
606       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints));
607 
608   ParseHeader(kNak_, kOrigin1_, header);
609   EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
610   EXPECT_TRUE(
611       EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
612   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
613   EXPECT_EQ(2u, cache()->GetEndpointCount());
614   ReportingEndpoint endpoint = FindEndpointInCache(kGroupKey11_, kEndpoint1_);
615   ASSERT_TRUE(endpoint);
616   EXPECT_EQ(kOrigin1_, endpoint.group_key.origin);
617   EXPECT_EQ(kGroup1_, endpoint.group_key.group_name);
618   EXPECT_EQ(kEndpoint1_, endpoint.info.url);
619   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
620             endpoint.info.priority);
621   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
622             endpoint.info.weight);
623 
624   ReportingEndpoint endpoint2 = FindEndpointInCache(kGroupKey11_, kEndpoint2_);
625   ASSERT_TRUE(endpoint2);
626   EXPECT_EQ(kOrigin1_, endpoint2.group_key.origin);
627   EXPECT_EQ(kGroup1_, endpoint2.group_key.group_name);
628   EXPECT_EQ(kEndpoint2_, endpoint2.info.url);
629   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
630             endpoint2.info.priority);
631   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
632             endpoint2.info.weight);
633 
634   if (mock_store()) {
635     mock_store()->Flush();
636     EXPECT_EQ(2, mock_store()->StoredEndpointsCount());
637     EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
638     MockPersistentReportingStore::CommandList expected_commands;
639     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
640                                    kGroupKey11_, kEndpoint1_);
641     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
642                                    kGroupKey11_, kEndpoint2_);
643     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
644                                    kGroupKey11_);
645     EXPECT_THAT(mock_store()->GetAllCommands(),
646                 testing::IsSupersetOf(expected_commands));
647   }
648 }
649 
TEST_P(ReportingHeaderParserTest,MultipleEndpointsDifferentGroups)650 TEST_P(ReportingHeaderParserTest, MultipleEndpointsDifferentGroups) {
651   std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {{kEndpoint1_}};
652   std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {{kEndpoint1_}};
653   std::string header =
654       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1)) +
655       ", " +
656       ConstructHeaderGroupString(MakeEndpointGroup(kGroup2_, endpoints2));
657 
658   ParseHeader(kNak_, kOrigin1_, header);
659   EXPECT_EQ(2u, cache()->GetEndpointGroupCountForTesting());
660   EXPECT_TRUE(
661       EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
662   EXPECT_TRUE(
663       EndpointGroupExistsInCache(kGroupKey12_, OriginSubdomains::DEFAULT));
664   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
665 
666   EXPECT_EQ(2u, cache()->GetEndpointCount());
667   ReportingEndpoint endpoint = FindEndpointInCache(kGroupKey11_, kEndpoint1_);
668   ASSERT_TRUE(endpoint);
669   EXPECT_EQ(kOrigin1_, endpoint.group_key.origin);
670   EXPECT_EQ(kGroup1_, endpoint.group_key.group_name);
671   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
672             endpoint.info.priority);
673   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
674             endpoint.info.weight);
675 
676   ReportingEndpoint endpoint2 = FindEndpointInCache(kGroupKey12_, kEndpoint1_);
677   ASSERT_TRUE(endpoint2);
678   EXPECT_EQ(kOrigin1_, endpoint2.group_key.origin);
679   EXPECT_EQ(kGroup2_, endpoint2.group_key.group_name);
680   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
681             endpoint2.info.priority);
682   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
683             endpoint2.info.weight);
684 
685   if (mock_store()) {
686     mock_store()->Flush();
687     EXPECT_EQ(2, mock_store()->StoredEndpointsCount());
688     EXPECT_EQ(2, mock_store()->StoredEndpointGroupsCount());
689     MockPersistentReportingStore::CommandList expected_commands;
690     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
691                                    kGroupKey11_, kEndpoint1_);
692     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
693                                    kGroupKey12_, kEndpoint1_);
694     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
695                                    kGroupKey11_);
696     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
697                                    kGroupKey12_);
698     EXPECT_THAT(mock_store()->GetAllCommands(),
699                 testing::IsSupersetOf(expected_commands));
700   }
701 }
702 
TEST_P(ReportingHeaderParserTest,MultipleHeadersFromDifferentOrigins)703 TEST_P(ReportingHeaderParserTest, MultipleHeadersFromDifferentOrigins) {
704   // First origin sets a header with two endpoints in the same group.
705   std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {{kEndpoint1_},
706                                                              {kEndpoint2_}};
707   std::string header1 =
708       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1));
709   ParseHeader(kNak_, kOrigin1_, header1);
710 
711   // Second origin has two endpoint groups.
712   std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {{kEndpoint1_}};
713   std::vector<ReportingEndpoint::EndpointInfo> endpoints3 = {{kEndpoint2_}};
714   std::string header2 =
715       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints2)) +
716       ", " +
717       ConstructHeaderGroupString(MakeEndpointGroup(kGroup2_, endpoints3));
718   ParseHeader(kNak_, kOrigin2_, header2);
719 
720   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
721   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin2_));
722 
723   EXPECT_EQ(3u, cache()->GetEndpointGroupCountForTesting());
724   EXPECT_TRUE(
725       EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
726   EXPECT_TRUE(
727       EndpointGroupExistsInCache(kGroupKey21_, OriginSubdomains::DEFAULT));
728   EXPECT_TRUE(
729       EndpointGroupExistsInCache(kGroupKey22_, OriginSubdomains::DEFAULT));
730 
731   EXPECT_EQ(4u, cache()->GetEndpointCount());
732   EXPECT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint1_));
733   EXPECT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint2_));
734   EXPECT_TRUE(FindEndpointInCache(kGroupKey21_, kEndpoint1_));
735   EXPECT_TRUE(FindEndpointInCache(kGroupKey22_, kEndpoint2_));
736 
737   if (mock_store()) {
738     mock_store()->Flush();
739     EXPECT_EQ(4, mock_store()->StoredEndpointsCount());
740     EXPECT_EQ(3, mock_store()->StoredEndpointGroupsCount());
741     MockPersistentReportingStore::CommandList expected_commands;
742     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
743                                    kGroupKey11_, kEndpoint1_);
744     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
745                                    kGroupKey11_, kEndpoint2_);
746     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
747                                    kGroupKey21_, kEndpoint1_);
748     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
749                                    kGroupKey22_, kEndpoint2_);
750     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
751                                    kGroupKey11_);
752     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
753                                    kGroupKey21_);
754     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
755                                    kGroupKey22_);
756     EXPECT_THAT(mock_store()->GetAllCommands(),
757                 testing::IsSupersetOf(expected_commands));
758   }
759 }
760 
761 // Test that each combination of NAK, origin, and group name is considered
762 // distinct.
763 // See also: ReportingCacheTest.ClientsKeyedByEndpointGroupKey
TEST_P(ReportingHeaderParserTest,EndpointGroupKey)764 TEST_P(ReportingHeaderParserTest, EndpointGroupKey) {
765   // Raise the endpoint limits for this test.
766   ReportingPolicy policy;
767   policy.max_endpoints_per_origin = 5;  // This test should use 4.
768   policy.max_endpoint_count = 20;       // This test should use 16.
769   UsePolicy(policy);
770 
771   std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {{kEndpoint1_},
772                                                              {kEndpoint2_}};
773   std::string header1 =
774       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1)) +
775       ", " +
776       ConstructHeaderGroupString(MakeEndpointGroup(kGroup2_, endpoints1));
777 
778   const ReportingEndpointGroupKey kOtherGroupKey11 =
779       ReportingEndpointGroupKey(kOtherNak_, kOrigin1_, kGroup1_);
780   const ReportingEndpointGroupKey kOtherGroupKey21 =
781       ReportingEndpointGroupKey(kOtherNak_, kOrigin2_, kGroup1_);
782   const ReportingEndpointGroupKey kOtherGroupKey12 =
783       ReportingEndpointGroupKey(kOtherNak_, kOrigin1_, kGroup2_);
784   const ReportingEndpointGroupKey kOtherGroupKey22 =
785       ReportingEndpointGroupKey(kOtherNak_, kOrigin2_, kGroup2_);
786 
787   const struct {
788     NetworkAnonymizationKey network_anonymization_key;
789     GURL url;
790     ReportingEndpointGroupKey group1_key;
791     ReportingEndpointGroupKey group2_key;
792   } kHeaderSources[] = {
793       {kNak_, kUrl1_, kGroupKey11_, kGroupKey12_},
794       {kNak_, kUrl2_, kGroupKey21_, kGroupKey22_},
795       {kOtherNak_, kUrl1_, kOtherGroupKey11, kOtherGroupKey12},
796       {kOtherNak_, kUrl2_, kOtherGroupKey21, kOtherGroupKey22},
797   };
798 
799   size_t endpoint_group_count = 0u;
800   size_t endpoint_count = 0u;
801   MockPersistentReportingStore::CommandList expected_commands;
802 
803   // Set 2 endpoints in each of 2 groups for each of 2x2 combinations of
804   // (NAK, origin).
805   for (const auto& source : kHeaderSources) {
806     // Verify pre-parsing state
807     EXPECT_FALSE(FindEndpointInCache(source.group1_key, kEndpoint1_));
808     EXPECT_FALSE(FindEndpointInCache(source.group1_key, kEndpoint2_));
809     EXPECT_FALSE(FindEndpointInCache(source.group2_key, kEndpoint1_));
810     EXPECT_FALSE(FindEndpointInCache(source.group2_key, kEndpoint2_));
811     EXPECT_FALSE(EndpointGroupExistsInCache(source.group1_key,
812                                             OriginSubdomains::DEFAULT));
813     EXPECT_FALSE(EndpointGroupExistsInCache(source.group2_key,
814                                             OriginSubdomains::DEFAULT));
815 
816     ParseHeader(source.network_anonymization_key,
817                 url::Origin::Create(source.url), header1);
818     endpoint_group_count += 2u;
819     endpoint_count += 4u;
820     EXPECT_EQ(endpoint_group_count, cache()->GetEndpointGroupCountForTesting());
821     EXPECT_EQ(endpoint_count, cache()->GetEndpointCount());
822 
823     // Verify post-parsing state
824     EXPECT_TRUE(FindEndpointInCache(source.group1_key, kEndpoint1_));
825     EXPECT_TRUE(FindEndpointInCache(source.group1_key, kEndpoint2_));
826     EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint1_));
827     EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint2_));
828     EXPECT_TRUE(EndpointGroupExistsInCache(source.group1_key,
829                                            OriginSubdomains::DEFAULT));
830     EXPECT_TRUE(EndpointGroupExistsInCache(source.group2_key,
831                                            OriginSubdomains::DEFAULT));
832 
833     if (mock_store()) {
834       mock_store()->Flush();
835       EXPECT_EQ(static_cast<int>(endpoint_count),
836                 mock_store()->StoredEndpointsCount());
837       EXPECT_EQ(static_cast<int>(endpoint_group_count),
838                 mock_store()->StoredEndpointGroupsCount());
839       expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
840                                      source.group1_key, kEndpoint1_);
841       expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
842                                      source.group1_key, kEndpoint2_);
843       expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
844                                      source.group1_key);
845       expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
846                                      source.group2_key, kEndpoint1_);
847       expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
848                                      source.group2_key, kEndpoint2_);
849       expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
850                                      source.group2_key);
851       EXPECT_THAT(mock_store()->GetAllCommands(),
852                   testing::IsSupersetOf(expected_commands));
853     }
854   }
855 
856   // Check that expected data is present in the ReportingCache at the end.
857   for (const auto& source : kHeaderSources) {
858     EXPECT_TRUE(FindEndpointInCache(source.group1_key, kEndpoint1_));
859     EXPECT_TRUE(FindEndpointInCache(source.group1_key, kEndpoint2_));
860     EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint1_));
861     EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint2_));
862     EXPECT_TRUE(EndpointGroupExistsInCache(source.group1_key,
863                                            OriginSubdomains::DEFAULT));
864     EXPECT_TRUE(EndpointGroupExistsInCache(source.group2_key,
865                                            OriginSubdomains::DEFAULT));
866     EXPECT_TRUE(cache()->ClientExistsForTesting(
867         source.network_anonymization_key, url::Origin::Create(source.url)));
868   }
869 
870   // Test updating existing configurations
871 
872   // This removes endpoint 1, updates the priority of endpoint 2, and adds
873   // endpoint 3.
874   std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {{kEndpoint2_, 2},
875                                                              {kEndpoint3_}};
876   // Removes group 1, updates include_subdomains for group 2.
877   std::string header2 = ConstructHeaderGroupString(
878       MakeEndpointGroup(kGroup2_, endpoints2, OriginSubdomains::INCLUDE));
879 
880   for (const auto& source : kHeaderSources) {
881     // Verify pre-update state
882     EXPECT_TRUE(EndpointGroupExistsInCache(source.group1_key,
883                                            OriginSubdomains::DEFAULT));
884     EXPECT_TRUE(EndpointGroupExistsInCache(source.group2_key,
885                                            OriginSubdomains::DEFAULT));
886     EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint1_));
887     ReportingEndpoint endpoint =
888         FindEndpointInCache(source.group2_key, kEndpoint2_);
889     EXPECT_TRUE(endpoint);
890     EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
891               endpoint.info.priority);
892     EXPECT_FALSE(FindEndpointInCache(source.group2_key, kEndpoint3_));
893 
894     ParseHeader(source.network_anonymization_key,
895                 url::Origin::Create(source.url), header2);
896     endpoint_group_count--;
897     endpoint_count -= 2;
898     EXPECT_EQ(endpoint_group_count, cache()->GetEndpointGroupCountForTesting());
899     EXPECT_EQ(endpoint_count, cache()->GetEndpointCount());
900 
901     // Verify post-update state
902     EXPECT_FALSE(EndpointGroupExistsInCache(source.group1_key,
903                                             OriginSubdomains::DEFAULT));
904     EXPECT_TRUE(EndpointGroupExistsInCache(source.group2_key,
905                                            OriginSubdomains::INCLUDE));
906     EXPECT_FALSE(FindEndpointInCache(source.group2_key, kEndpoint1_));
907     endpoint = FindEndpointInCache(source.group2_key, kEndpoint2_);
908     EXPECT_TRUE(endpoint);
909     EXPECT_EQ(2, endpoint.info.priority);
910     EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint3_));
911 
912     if (mock_store()) {
913       mock_store()->Flush();
914       EXPECT_EQ(static_cast<int>(endpoint_count),
915                 mock_store()->StoredEndpointsCount());
916       EXPECT_EQ(static_cast<int>(endpoint_group_count),
917                 mock_store()->StoredEndpointGroupsCount());
918       expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
919                                      source.group1_key, kEndpoint1_);
920       expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
921                                      source.group1_key, kEndpoint2_);
922       expected_commands.emplace_back(
923           CommandType::DELETE_REPORTING_ENDPOINT_GROUP, source.group1_key);
924       expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
925                                      source.group2_key, kEndpoint1_);
926       expected_commands.emplace_back(
927           CommandType::UPDATE_REPORTING_ENDPOINT_DETAILS, source.group2_key,
928           kEndpoint2_);
929       expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
930                                      source.group2_key, kEndpoint3_);
931       expected_commands.emplace_back(
932           CommandType::UPDATE_REPORTING_ENDPOINT_GROUP_DETAILS,
933           source.group2_key);
934       EXPECT_THAT(mock_store()->GetAllCommands(),
935                   testing::IsSupersetOf(expected_commands));
936     }
937   }
938 
939   // Check that expected data is present in the ReportingCache at the end.
940   for (const auto& source : kHeaderSources) {
941     EXPECT_FALSE(FindEndpointInCache(source.group1_key, kEndpoint1_));
942     EXPECT_FALSE(FindEndpointInCache(source.group1_key, kEndpoint2_));
943     EXPECT_FALSE(FindEndpointInCache(source.group2_key, kEndpoint1_));
944     EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint2_));
945     EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint3_));
946     EXPECT_FALSE(EndpointGroupExistsInCache(source.group1_key,
947                                             OriginSubdomains::DEFAULT));
948     EXPECT_TRUE(EndpointGroupExistsInCache(source.group2_key,
949                                            OriginSubdomains::INCLUDE));
950     EXPECT_TRUE(cache()->ClientExistsForTesting(
951         source.network_anonymization_key, url::Origin::Create(source.url)));
952   }
953 }
954 
TEST_P(ReportingHeaderParserTest,HeaderErroneouslyContainsMultipleGroupsOfSameName)955 TEST_P(ReportingHeaderParserTest,
956        HeaderErroneouslyContainsMultipleGroupsOfSameName) {
957   // Add a preexisting header to test that a header with multiple groups of the
958   // same name is treated as if it specified a single group with the combined
959   // set of specified endpoints. In particular, it must overwrite/update any
960   // preexisting group all at once. See https://crbug.com/1116529.
961   std::vector<ReportingEndpoint::EndpointInfo> preexisting = {{kEndpoint1_}};
962   std::string preexisting_header =
963       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, preexisting));
964 
965   ParseHeader(kNak_, kOrigin1_, preexisting_header);
966   EXPECT_TRUE(
967       EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
968   EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
969   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
970   EXPECT_EQ(1u, cache()->GetEndpointCount());
971   ReportingEndpoint endpoint = FindEndpointInCache(kGroupKey11_, kEndpoint1_);
972   ASSERT_TRUE(endpoint);
973 
974   if (mock_store()) {
975     mock_store()->Flush();
976     EXPECT_EQ(1, mock_store()->StoredEndpointsCount());
977     EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
978     MockPersistentReportingStore::CommandList expected_commands;
979     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
980                                    kGroupKey11_, kEndpoint1_);
981     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
982                                    kGroupKey11_);
983     EXPECT_THAT(mock_store()->GetAllCommands(),
984                 testing::IsSupersetOf(expected_commands));
985     // Reset commands so we can check that the next part, adding the header with
986     // duplicate groups, does not cause clearing of preexisting endpoints twice.
987     mock_store()->ClearCommands();
988   }
989 
990   std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {{kEndpoint1_}};
991   std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {{kEndpoint2_}};
992   std::string duplicate_groups_header =
993       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1)) +
994       ", " +
995       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints2));
996 
997   ParseHeader(kNak_, kOrigin1_, duplicate_groups_header);
998   // Result is as if they set the two groups with the same name as one group.
999   EXPECT_TRUE(
1000       EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
1001   EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
1002 
1003   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1004 
1005   EXPECT_EQ(2u, cache()->GetEndpointCount());
1006   ReportingEndpoint endpoint1 = FindEndpointInCache(kGroupKey11_, kEndpoint1_);
1007   ASSERT_TRUE(endpoint);
1008   EXPECT_EQ(kOrigin1_, endpoint.group_key.origin);
1009   EXPECT_EQ(kGroup1_, endpoint.group_key.group_name);
1010   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
1011             endpoint.info.priority);
1012   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
1013             endpoint.info.weight);
1014 
1015   ReportingEndpoint endpoint2 = FindEndpointInCache(kGroupKey11_, kEndpoint2_);
1016   ASSERT_TRUE(endpoint2);
1017   EXPECT_EQ(kOrigin1_, endpoint2.group_key.origin);
1018   EXPECT_EQ(kGroup1_, endpoint2.group_key.group_name);
1019   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
1020             endpoint2.info.priority);
1021   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
1022             endpoint2.info.weight);
1023 
1024   if (mock_store()) {
1025     mock_store()->Flush();
1026     EXPECT_EQ(2, mock_store()->StoredEndpointsCount());
1027     EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
1028     MockPersistentReportingStore::CommandList expected_commands;
1029     expected_commands.emplace_back(
1030         CommandType::UPDATE_REPORTING_ENDPOINT_DETAILS, kGroupKey11_,
1031         kEndpoint1_);
1032     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1033                                    kGroupKey11_, kEndpoint2_);
1034     expected_commands.emplace_back(
1035         CommandType::UPDATE_REPORTING_ENDPOINT_GROUP_DETAILS, kGroupKey11_);
1036     MockPersistentReportingStore::CommandList actual_commands =
1037         mock_store()->GetAllCommands();
1038     EXPECT_THAT(actual_commands, testing::IsSupersetOf(expected_commands));
1039     for (const auto& command : actual_commands) {
1040       EXPECT_NE(CommandType::DELETE_REPORTING_ENDPOINT, command.type);
1041       EXPECT_NE(CommandType::DELETE_REPORTING_ENDPOINT_GROUP, command.type);
1042 
1043       // The endpoint with URL kEndpoint1_ is only ever updated, not added anew.
1044       EXPECT_NE(
1045           MockPersistentReportingStore::Command(
1046               CommandType::ADD_REPORTING_ENDPOINT, kGroupKey11_, kEndpoint1_),
1047           command);
1048       // The group is only ever updated, not added anew.
1049       EXPECT_NE(MockPersistentReportingStore::Command(
1050                     CommandType::ADD_REPORTING_ENDPOINT_GROUP, kGroupKey11_),
1051                 command);
1052     }
1053   }
1054 }
1055 
TEST_P(ReportingHeaderParserTest,HeaderErroneouslyContainsGroupsWithRedundantEndpoints)1056 TEST_P(ReportingHeaderParserTest,
1057        HeaderErroneouslyContainsGroupsWithRedundantEndpoints) {
1058   std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_},
1059                                                             {kEndpoint1_}};
1060   std::string header =
1061       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints));
1062   ParseHeader(kNak_, kOrigin1_, header);
1063 
1064   // We should dedupe the identical endpoint URLs.
1065   EXPECT_EQ(1u, cache()->GetEndpointCount());
1066   ASSERT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint1_));
1067 
1068   EXPECT_TRUE(
1069       EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
1070   EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
1071 
1072   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1073 }
1074 
TEST_P(ReportingHeaderParserTest,HeaderErroneouslyContainsMultipleGroupsOfSameNameAndEndpoints)1075 TEST_P(ReportingHeaderParserTest,
1076        HeaderErroneouslyContainsMultipleGroupsOfSameNameAndEndpoints) {
1077   std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}};
1078   std::string header =
1079       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints)) +
1080       ", " + ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints));
1081   ParseHeader(kNak_, kOrigin1_, header);
1082 
1083   // We should dedupe the identical endpoint URLs, even when they're in
1084   // different group.
1085   EXPECT_EQ(1u, cache()->GetEndpointCount());
1086   ASSERT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint1_));
1087 
1088   EXPECT_TRUE(
1089       EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
1090   EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
1091 
1092   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1093 }
1094 
TEST_P(ReportingHeaderParserTest,HeaderErroneouslyContainsGroupsOfSameNameAndOverlappingEndpoints)1095 TEST_P(ReportingHeaderParserTest,
1096        HeaderErroneouslyContainsGroupsOfSameNameAndOverlappingEndpoints) {
1097   std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {{kEndpoint1_},
1098                                                              {kEndpoint2_}};
1099   std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {{kEndpoint1_},
1100                                                              {kEndpoint3_}};
1101   std::string header =
1102       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1)) +
1103       ", " +
1104       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints2));
1105   ParseHeader(kNak_, kOrigin1_, header);
1106 
1107   // We should dedupe the identical endpoint URLs, even when they're in
1108   // different group.
1109   EXPECT_EQ(3u, cache()->GetEndpointCount());
1110   ASSERT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint1_));
1111   ASSERT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint2_));
1112   ASSERT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint3_));
1113 
1114   EXPECT_TRUE(
1115       EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
1116   EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
1117 
1118   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1119 }
1120 
TEST_P(ReportingHeaderParserTest,OverwriteOldHeader)1121 TEST_P(ReportingHeaderParserTest, OverwriteOldHeader) {
1122   // First, the origin sets a header with two endpoints in the same group.
1123   std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {
1124       {kEndpoint1_, 10 /* priority */}, {kEndpoint2_}};
1125   std::string header1 =
1126       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1));
1127   ParseHeader(kNak_, kOrigin1_, header1);
1128 
1129   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1130   EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
1131   EXPECT_TRUE(
1132       EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
1133   EXPECT_EQ(2u, cache()->GetEndpointCount());
1134   EXPECT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint1_));
1135   EXPECT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint2_));
1136   if (mock_store()) {
1137     mock_store()->Flush();
1138     EXPECT_EQ(2,
1139               mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1140     EXPECT_EQ(1, mock_store()->CountCommands(
1141                      CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1142     MockPersistentReportingStore::CommandList expected_commands;
1143     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1144                                    kGroupKey11_, kEndpoint1_);
1145     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1146                                    kGroupKey11_, kEndpoint2_);
1147     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1148                                    kGroupKey11_);
1149     EXPECT_THAT(mock_store()->GetAllCommands(),
1150                 testing::IsSupersetOf(expected_commands));
1151   }
1152 
1153   // Second header from the same origin should overwrite the previous one.
1154   std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {
1155       // This endpoint should update the priority of the existing one.
1156       {kEndpoint1_, 20 /* priority */}};
1157   // The second endpoint in this group will be deleted.
1158   // This group is new.
1159   std::vector<ReportingEndpoint::EndpointInfo> endpoints3 = {{kEndpoint2_}};
1160   std::string header2 =
1161       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints2)) +
1162       ", " +
1163       ConstructHeaderGroupString(MakeEndpointGroup(kGroup2_, endpoints3));
1164   ParseHeader(kNak_, kOrigin1_, header2);
1165 
1166   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1167 
1168   EXPECT_TRUE(
1169       EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
1170   EXPECT_TRUE(
1171       EndpointGroupExistsInCache(kGroupKey12_, OriginSubdomains::DEFAULT));
1172 
1173   EXPECT_EQ(2u, cache()->GetEndpointCount());
1174   EXPECT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint1_));
1175   EXPECT_EQ(20, FindEndpointInCache(kGroupKey11_, kEndpoint1_).info.priority);
1176   EXPECT_FALSE(FindEndpointInCache(kGroupKey11_, kEndpoint2_));
1177   EXPECT_TRUE(FindEndpointInCache(kGroupKey12_, kEndpoint2_));
1178   if (mock_store()) {
1179     mock_store()->Flush();
1180     EXPECT_EQ(2 + 1,
1181               mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1182     EXPECT_EQ(1 + 1, mock_store()->CountCommands(
1183                          CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1184     EXPECT_EQ(
1185         1, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT));
1186     MockPersistentReportingStore::CommandList expected_commands;
1187     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1188                                    kGroupKey12_, kEndpoint2_);
1189     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1190                                    kGroupKey12_);
1191     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1192                                    kGroupKey11_, kEndpoint2_);
1193     EXPECT_THAT(mock_store()->GetAllCommands(),
1194                 testing::IsSupersetOf(expected_commands));
1195   }
1196 }
1197 
TEST_P(ReportingHeaderParserTest,OverwriteOldHeaderWithCompletelyNew)1198 TEST_P(ReportingHeaderParserTest, OverwriteOldHeaderWithCompletelyNew) {
1199   ReportingEndpointGroupKey kGroupKey1(kNak_, kOrigin1_, "1");
1200   ReportingEndpointGroupKey kGroupKey2(kNak_, kOrigin1_, "2");
1201   ReportingEndpointGroupKey kGroupKey3(kNak_, kOrigin1_, "3");
1202   ReportingEndpointGroupKey kGroupKey4(kNak_, kOrigin1_, "4");
1203   ReportingEndpointGroupKey kGroupKey5(kNak_, kOrigin1_, "5");
1204   std::vector<ReportingEndpoint::EndpointInfo> endpoints1_1 = {{MakeURL(10)},
1205                                                                {MakeURL(11)}};
1206   std::vector<ReportingEndpoint::EndpointInfo> endpoints2_1 = {{MakeURL(20)},
1207                                                                {MakeURL(21)}};
1208   std::vector<ReportingEndpoint::EndpointInfo> endpoints3_1 = {{MakeURL(30)},
1209                                                                {MakeURL(31)}};
1210   std::string header1 =
1211       ConstructHeaderGroupString(MakeEndpointGroup("1", endpoints1_1)) + ", " +
1212       ConstructHeaderGroupString(MakeEndpointGroup("2", endpoints2_1)) + ", " +
1213       ConstructHeaderGroupString(MakeEndpointGroup("3", endpoints3_1));
1214   ParseHeader(kNak_, kOrigin1_, header1);
1215   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1216   EXPECT_EQ(3u, cache()->GetEndpointGroupCountForTesting());
1217   EXPECT_TRUE(
1218       EndpointGroupExistsInCache(kGroupKey1, OriginSubdomains::DEFAULT));
1219   EXPECT_TRUE(
1220       EndpointGroupExistsInCache(kGroupKey2, OriginSubdomains::DEFAULT));
1221   EXPECT_TRUE(
1222       EndpointGroupExistsInCache(kGroupKey3, OriginSubdomains::DEFAULT));
1223   EXPECT_EQ(6u, cache()->GetEndpointCount());
1224   if (mock_store()) {
1225     mock_store()->Flush();
1226     EXPECT_EQ(6,
1227               mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1228     EXPECT_EQ(3, mock_store()->CountCommands(
1229                      CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1230     MockPersistentReportingStore::CommandList expected_commands;
1231     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1232                                    kGroupKey1, endpoints1_1[0].url);
1233     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1234                                    kGroupKey1, endpoints1_1[1].url);
1235     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1236                                    kGroupKey2, endpoints2_1[0].url);
1237     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1238                                    kGroupKey2, endpoints2_1[1].url);
1239     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1240                                    kGroupKey3, endpoints3_1[0].url);
1241     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1242                                    kGroupKey3, endpoints3_1[1].url);
1243     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1244                                    kGroupKey1);
1245     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1246                                    kGroupKey2);
1247     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1248                                    kGroupKey3);
1249     EXPECT_THAT(mock_store()->GetAllCommands(),
1250                 testing::IsSupersetOf(expected_commands));
1251   }
1252 
1253   // Replace endpoints in each group with completely new endpoints.
1254   std::vector<ReportingEndpoint::EndpointInfo> endpoints1_2 = {{MakeURL(12)}};
1255   std::vector<ReportingEndpoint::EndpointInfo> endpoints2_2 = {{MakeURL(22)}};
1256   std::vector<ReportingEndpoint::EndpointInfo> endpoints3_2 = {{MakeURL(32)}};
1257   std::string header2 =
1258       ConstructHeaderGroupString(MakeEndpointGroup("1", endpoints1_2)) + ", " +
1259       ConstructHeaderGroupString(MakeEndpointGroup("2", endpoints2_2)) + ", " +
1260       ConstructHeaderGroupString(MakeEndpointGroup("3", endpoints3_2));
1261   ParseHeader(kNak_, kOrigin1_, header2);
1262   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1263   EXPECT_EQ(3u, cache()->GetEndpointGroupCountForTesting());
1264   EXPECT_TRUE(
1265       EndpointGroupExistsInCache(kGroupKey1, OriginSubdomains::DEFAULT));
1266   EXPECT_TRUE(
1267       EndpointGroupExistsInCache(kGroupKey2, OriginSubdomains::DEFAULT));
1268   EXPECT_TRUE(
1269       EndpointGroupExistsInCache(kGroupKey3, OriginSubdomains::DEFAULT));
1270   EXPECT_EQ(3u, cache()->GetEndpointCount());
1271   EXPECT_TRUE(FindEndpointInCache(kGroupKey1, MakeURL(12)));
1272   EXPECT_FALSE(FindEndpointInCache(kGroupKey1, MakeURL(10)));
1273   EXPECT_FALSE(FindEndpointInCache(kGroupKey1, MakeURL(11)));
1274   EXPECT_TRUE(FindEndpointInCache(kGroupKey2, MakeURL(22)));
1275   EXPECT_FALSE(FindEndpointInCache(kGroupKey2, MakeURL(20)));
1276   EXPECT_FALSE(FindEndpointInCache(kGroupKey2, MakeURL(21)));
1277   EXPECT_TRUE(FindEndpointInCache(kGroupKey3, MakeURL(32)));
1278   EXPECT_FALSE(FindEndpointInCache(kGroupKey3, MakeURL(30)));
1279   EXPECT_FALSE(FindEndpointInCache(kGroupKey3, MakeURL(31)));
1280   if (mock_store()) {
1281     mock_store()->Flush();
1282     EXPECT_EQ(6 + 3,
1283               mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1284     EXPECT_EQ(3, mock_store()->CountCommands(
1285                      CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1286     EXPECT_EQ(
1287         6, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT));
1288     EXPECT_EQ(0, mock_store()->CountCommands(
1289                      CommandType::DELETE_REPORTING_ENDPOINT_GROUP));
1290     MockPersistentReportingStore::CommandList expected_commands;
1291     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1292                                    kGroupKey1, endpoints1_2[0].url);
1293     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1294                                    kGroupKey2, endpoints2_2[0].url);
1295     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1296                                    kGroupKey3, endpoints3_2[0].url);
1297     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1298                                    kGroupKey1, endpoints1_1[0].url);
1299     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1300                                    kGroupKey1, endpoints1_1[1].url);
1301     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1302                                    kGroupKey2, endpoints2_1[0].url);
1303     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1304                                    kGroupKey2, endpoints2_1[1].url);
1305     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1306                                    kGroupKey3, endpoints3_1[0].url);
1307     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1308                                    kGroupKey3, endpoints3_1[1].url);
1309     EXPECT_THAT(mock_store()->GetAllCommands(),
1310                 testing::IsSupersetOf(expected_commands));
1311   }
1312 
1313   // Replace all the groups with completely new groups.
1314   std::vector<ReportingEndpoint::EndpointInfo> endpoints4_3 = {{MakeURL(40)}};
1315   std::vector<ReportingEndpoint::EndpointInfo> endpoints5_3 = {{MakeURL(50)}};
1316   std::string header3 =
1317       ConstructHeaderGroupString(MakeEndpointGroup("4", endpoints4_3)) + ", " +
1318       ConstructHeaderGroupString(MakeEndpointGroup("5", endpoints5_3));
1319   ParseHeader(kNak_, kOrigin1_, header3);
1320   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1321   EXPECT_EQ(2u, cache()->GetEndpointGroupCountForTesting());
1322   EXPECT_TRUE(
1323       EndpointGroupExistsInCache(kGroupKey4, OriginSubdomains::DEFAULT));
1324   EXPECT_TRUE(
1325       EndpointGroupExistsInCache(kGroupKey4, OriginSubdomains::DEFAULT));
1326   EXPECT_FALSE(
1327       EndpointGroupExistsInCache(kGroupKey1, OriginSubdomains::DEFAULT));
1328   EXPECT_FALSE(
1329       EndpointGroupExistsInCache(kGroupKey2, OriginSubdomains::DEFAULT));
1330   EXPECT_FALSE(
1331       EndpointGroupExistsInCache(kGroupKey3, OriginSubdomains::DEFAULT));
1332   EXPECT_EQ(2u, cache()->GetEndpointCount());
1333   if (mock_store()) {
1334     mock_store()->Flush();
1335     EXPECT_EQ(6 + 3 + 2,
1336               mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1337     EXPECT_EQ(3 + 2, mock_store()->CountCommands(
1338                          CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1339     EXPECT_EQ(6 + 3, mock_store()->CountCommands(
1340                          CommandType::DELETE_REPORTING_ENDPOINT));
1341     EXPECT_EQ(3, mock_store()->CountCommands(
1342                      CommandType::DELETE_REPORTING_ENDPOINT_GROUP));
1343     MockPersistentReportingStore::CommandList expected_commands;
1344     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1345                                    kGroupKey4, endpoints4_3[0].url);
1346     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1347                                    kGroupKey5, endpoints5_3[0].url);
1348     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1349                                    kGroupKey4);
1350     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1351                                    kGroupKey5);
1352     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1353                                    kGroupKey1, endpoints1_2[0].url);
1354     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1355                                    kGroupKey2, endpoints2_2[0].url);
1356     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1357                                    kGroupKey3, endpoints3_2[0].url);
1358     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
1359                                    kGroupKey1);
1360     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
1361                                    kGroupKey2);
1362     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
1363                                    kGroupKey3);
1364     EXPECT_THAT(mock_store()->GetAllCommands(),
1365                 testing::IsSupersetOf(expected_commands));
1366   }
1367 }
1368 
TEST_P(ReportingHeaderParserTest,ZeroMaxAgeRemovesEndpointGroup)1369 TEST_P(ReportingHeaderParserTest, ZeroMaxAgeRemovesEndpointGroup) {
1370   // Without a pre-existing client, max_age: 0 should do nothing.
1371   ASSERT_EQ(0u, cache()->GetEndpointCount());
1372   ParseHeader(kNak_, kOrigin1_,
1373               "{\"endpoints\":[{\"url\":\"" + kEndpoint1_.spec() +
1374                   "\"}],\"max_age\":0}");
1375   EXPECT_EQ(0u, cache()->GetEndpointCount());
1376   if (mock_store()) {
1377     mock_store()->Flush();
1378     EXPECT_EQ(0,
1379               mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1380     EXPECT_EQ(0, mock_store()->CountCommands(
1381                      CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1382   }
1383 
1384   // Set a header with two endpoint groups.
1385   std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {{kEndpoint1_}};
1386   std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {{kEndpoint2_}};
1387   std::string header1 =
1388       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1)) +
1389       ", " +
1390       ConstructHeaderGroupString(MakeEndpointGroup(kGroup2_, endpoints2));
1391   ParseHeader(kNak_, kOrigin1_, header1);
1392 
1393   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1394   EXPECT_EQ(2u, cache()->GetEndpointGroupCountForTesting());
1395   EXPECT_TRUE(
1396       EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
1397   EXPECT_TRUE(
1398       EndpointGroupExistsInCache(kGroupKey12_, OriginSubdomains::DEFAULT));
1399   EXPECT_EQ(2u, cache()->GetEndpointCount());
1400   if (mock_store()) {
1401     mock_store()->Flush();
1402     EXPECT_EQ(2,
1403               mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1404     EXPECT_EQ(2, mock_store()->CountCommands(
1405                      CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1406     MockPersistentReportingStore::CommandList expected_commands;
1407     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1408                                    kGroupKey11_, kEndpoint1_);
1409     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1410                                    kGroupKey12_, kEndpoint2_);
1411     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1412                                    kGroupKey11_);
1413     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1414                                    kGroupKey12_);
1415     EXPECT_THAT(mock_store()->GetAllCommands(),
1416                 testing::IsSupersetOf(expected_commands));
1417   }
1418 
1419   // Set another header with max_age: 0 to delete one of the groups.
1420   std::string header2 =
1421       ConstructHeaderGroupString(MakeEndpointGroup(
1422           kGroup1_, endpoints1, OriginSubdomains::DEFAULT, base::Seconds(0))) +
1423       ", " +
1424       ConstructHeaderGroupString(
1425           MakeEndpointGroup(kGroup2_, endpoints2));  // Other group stays.
1426   ParseHeader(kNak_, kOrigin1_, header2);
1427 
1428   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1429   EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
1430 
1431   // Group was deleted.
1432   EXPECT_FALSE(
1433       EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
1434   // Other group remains in the cache.
1435   EXPECT_TRUE(
1436       EndpointGroupExistsInCache(kGroupKey12_, OriginSubdomains::DEFAULT));
1437   EXPECT_EQ(1u, cache()->GetEndpointCount());
1438   if (mock_store()) {
1439     mock_store()->Flush();
1440     EXPECT_EQ(2,
1441               mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1442     EXPECT_EQ(2, mock_store()->CountCommands(
1443                      CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1444     EXPECT_EQ(
1445         1, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT));
1446     EXPECT_EQ(1, mock_store()->CountCommands(
1447                      CommandType::DELETE_REPORTING_ENDPOINT_GROUP));
1448     MockPersistentReportingStore::CommandList expected_commands;
1449     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1450                                    kGroupKey11_, kEndpoint1_);
1451     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
1452                                    kGroupKey11_);
1453     EXPECT_THAT(mock_store()->GetAllCommands(),
1454                 testing::IsSupersetOf(expected_commands));
1455   }
1456 
1457   // Set another header with max_age: 0 to delete the other group. (Should work
1458   // even if the endpoints field is an empty list.)
1459   std::string header3 = ConstructHeaderGroupString(MakeEndpointGroup(
1460       kGroup2_, std::vector<ReportingEndpoint::EndpointInfo>(),
1461       OriginSubdomains::DEFAULT, base::Seconds(0)));
1462   ParseHeader(kNak_, kOrigin1_, header3);
1463 
1464   // Deletion of the last remaining group also deletes the client for this
1465   // origin.
1466   EXPECT_FALSE(ClientExistsInCacheForOrigin(kOrigin1_));
1467   EXPECT_EQ(0u, cache()->GetEndpointGroupCountForTesting());
1468   EXPECT_EQ(0u, cache()->GetEndpointCount());
1469   if (mock_store()) {
1470     mock_store()->Flush();
1471     EXPECT_EQ(2,
1472               mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1473     EXPECT_EQ(2, mock_store()->CountCommands(
1474                      CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1475     EXPECT_EQ(1 + 1, mock_store()->CountCommands(
1476                          CommandType::DELETE_REPORTING_ENDPOINT));
1477     EXPECT_EQ(1 + 1, mock_store()->CountCommands(
1478                          CommandType::DELETE_REPORTING_ENDPOINT_GROUP));
1479     MockPersistentReportingStore::CommandList expected_commands;
1480     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1481                                    kGroupKey12_, kEndpoint2_);
1482     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
1483                                    kGroupKey12_);
1484     EXPECT_THAT(mock_store()->GetAllCommands(),
1485                 testing::IsSupersetOf(expected_commands));
1486   }
1487 }
1488 
1489 // Invalid advertisements that parse as JSON should remove an endpoint group,
1490 // while those that don't are ignored.
TEST_P(ReportingHeaderParserTest,InvalidAdvertisementRemovesEndpointGroup)1491 TEST_P(ReportingHeaderParserTest, InvalidAdvertisementRemovesEndpointGroup) {
1492   std::string invalid_non_json_header = "Goats should wear hats.";
1493   std::string invalid_json_header = "\"Goats should wear hats.\"";
1494 
1495   // Without a pre-existing client, neither invalid header does anything.
1496 
1497   ASSERT_EQ(0u, cache()->GetEndpointCount());
1498   ParseHeader(kNak_, kOrigin1_, invalid_non_json_header);
1499   EXPECT_EQ(0u, cache()->GetEndpointCount());
1500   if (mock_store()) {
1501     mock_store()->Flush();
1502     EXPECT_EQ(0,
1503               mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1504     EXPECT_EQ(0, mock_store()->CountCommands(
1505                      CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1506   }
1507 
1508   ASSERT_EQ(0u, cache()->GetEndpointCount());
1509   ParseHeader(kNak_, kOrigin1_, invalid_json_header);
1510   EXPECT_EQ(0u, cache()->GetEndpointCount());
1511   if (mock_store()) {
1512     mock_store()->Flush();
1513     EXPECT_EQ(0,
1514               mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1515     EXPECT_EQ(0, mock_store()->CountCommands(
1516                      CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1517   }
1518 
1519   // Set a header with two endpoint groups.
1520   std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {{kEndpoint1_}};
1521   std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {{kEndpoint2_}};
1522   std::string header1 =
1523       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1)) +
1524       ", " +
1525       ConstructHeaderGroupString(MakeEndpointGroup(kGroup2_, endpoints2));
1526   ParseHeader(kNak_, kOrigin1_, header1);
1527 
1528   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1529   EXPECT_EQ(2u, cache()->GetEndpointGroupCountForTesting());
1530   EXPECT_TRUE(
1531       EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
1532   EXPECT_TRUE(
1533       EndpointGroupExistsInCache(kGroupKey12_, OriginSubdomains::DEFAULT));
1534   EXPECT_EQ(2u, cache()->GetEndpointCount());
1535   if (mock_store()) {
1536     mock_store()->Flush();
1537     EXPECT_EQ(2,
1538               mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1539     EXPECT_EQ(2, mock_store()->CountCommands(
1540                      CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1541     MockPersistentReportingStore::CommandList expected_commands;
1542     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1543                                    kGroupKey11_, kEndpoint1_);
1544     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1545                                    kGroupKey12_, kEndpoint2_);
1546     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1547                                    kGroupKey11_);
1548     expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1549                                    kGroupKey12_);
1550     EXPECT_THAT(mock_store()->GetAllCommands(),
1551                 testing::IsSupersetOf(expected_commands));
1552   }
1553 
1554   // Set another header with max_age: 0 to delete one of the groups.
1555   std::string header2 =
1556       ConstructHeaderGroupString(MakeEndpointGroup(
1557           kGroup1_, endpoints1, OriginSubdomains::DEFAULT, base::Seconds(0))) +
1558       ", " +
1559       ConstructHeaderGroupString(
1560           MakeEndpointGroup(kGroup2_, endpoints2));  // Other group stays.
1561   ParseHeader(kNak_, kOrigin1_, header2);
1562 
1563   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1564   EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
1565 
1566   // Group was deleted.
1567   EXPECT_FALSE(
1568       EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
1569   // Other group remains in the cache.
1570   EXPECT_TRUE(
1571       EndpointGroupExistsInCache(kGroupKey12_, OriginSubdomains::DEFAULT));
1572   EXPECT_EQ(1u, cache()->GetEndpointCount());
1573   if (mock_store()) {
1574     mock_store()->Flush();
1575     EXPECT_EQ(2,
1576               mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1577     EXPECT_EQ(2, mock_store()->CountCommands(
1578                      CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1579     EXPECT_EQ(
1580         1, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT));
1581     EXPECT_EQ(1, mock_store()->CountCommands(
1582                      CommandType::DELETE_REPORTING_ENDPOINT_GROUP));
1583     MockPersistentReportingStore::CommandList expected_commands;
1584     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1585                                    kGroupKey11_, kEndpoint1_);
1586     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
1587                                    kGroupKey11_);
1588     EXPECT_THAT(mock_store()->GetAllCommands(),
1589                 testing::IsSupersetOf(expected_commands));
1590   }
1591 
1592   // Invalid header values that are not JSON lists (without the outer brackets)
1593   // are ignored.
1594   ParseHeader(kNak_, kOrigin1_, invalid_non_json_header);
1595   EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1596   EXPECT_TRUE(
1597       EndpointGroupExistsInCache(kGroupKey12_, OriginSubdomains::DEFAULT));
1598   EXPECT_EQ(1u, cache()->GetEndpointCount());
1599   if (mock_store()) {
1600     mock_store()->Flush();
1601     EXPECT_EQ(2,
1602               mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1603     EXPECT_EQ(2, mock_store()->CountCommands(
1604                      CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1605     EXPECT_EQ(
1606         1, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT));
1607     EXPECT_EQ(1, mock_store()->CountCommands(
1608                      CommandType::DELETE_REPORTING_ENDPOINT_GROUP));
1609     MockPersistentReportingStore::CommandList expected_commands;
1610     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1611                                    kGroupKey11_, kEndpoint1_);
1612     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
1613                                    kGroupKey11_);
1614     EXPECT_THAT(mock_store()->GetAllCommands(),
1615                 testing::IsSupersetOf(expected_commands));
1616   }
1617 
1618   // Invalid headers that do parse as JSON should delete the corresponding
1619   // client.
1620   ParseHeader(kNak_, kOrigin1_, invalid_json_header);
1621 
1622   // Deletion of the last remaining group also deletes the client for this
1623   // origin.
1624   EXPECT_FALSE(ClientExistsInCacheForOrigin(kOrigin1_));
1625   EXPECT_EQ(0u, cache()->GetEndpointGroupCountForTesting());
1626   EXPECT_EQ(0u, cache()->GetEndpointCount());
1627   if (mock_store()) {
1628     mock_store()->Flush();
1629     EXPECT_EQ(2,
1630               mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1631     EXPECT_EQ(2, mock_store()->CountCommands(
1632                      CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1633     EXPECT_EQ(1 + 1, mock_store()->CountCommands(
1634                          CommandType::DELETE_REPORTING_ENDPOINT));
1635     EXPECT_EQ(1 + 1, mock_store()->CountCommands(
1636                          CommandType::DELETE_REPORTING_ENDPOINT_GROUP));
1637     MockPersistentReportingStore::CommandList expected_commands;
1638     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1639                                    kGroupKey12_, kEndpoint2_);
1640     expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
1641                                    kGroupKey12_);
1642     EXPECT_THAT(mock_store()->GetAllCommands(),
1643                 testing::IsSupersetOf(expected_commands));
1644   }
1645 }
1646 
TEST_P(ReportingHeaderParserTest,EvictEndpointsOverPerOriginLimit1)1647 TEST_P(ReportingHeaderParserTest, EvictEndpointsOverPerOriginLimit1) {
1648   // Set a header with too many endpoints, all in the same group.
1649   std::vector<ReportingEndpoint::EndpointInfo> endpoints;
1650   for (size_t i = 0; i < policy().max_endpoints_per_origin + 1; ++i) {
1651     endpoints.push_back({MakeURL(i)});
1652   }
1653   std::string header =
1654       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints));
1655   ParseHeader(kNak_, kOrigin1_, header);
1656 
1657   // Endpoint count should be at most the limit.
1658   EXPECT_GE(policy().max_endpoints_per_origin, cache()->GetEndpointCount());
1659 
1660   if (mock_store()) {
1661     mock_store()->Flush();
1662     EXPECT_EQ(policy().max_endpoints_per_origin + 1,
1663               static_cast<unsigned long>(mock_store()->CountCommands(
1664                   CommandType::ADD_REPORTING_ENDPOINT)));
1665     EXPECT_EQ(1, mock_store()->CountCommands(
1666                      CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1667     EXPECT_EQ(
1668         1, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT));
1669   }
1670 }
1671 
TEST_P(ReportingHeaderParserTest,EvictEndpointsOverPerOriginLimit2)1672 TEST_P(ReportingHeaderParserTest, EvictEndpointsOverPerOriginLimit2) {
1673   // Set a header with too many endpoints, in different groups.
1674   std::string header;
1675   for (size_t i = 0; i < policy().max_endpoints_per_origin + 1; ++i) {
1676     std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{MakeURL(i)}};
1677     header = header + ConstructHeaderGroupString(MakeEndpointGroup(
1678                           base::NumberToString(i), endpoints));
1679     if (i != policy().max_endpoints_per_origin)
1680       header = header + ", ";
1681   }
1682   ParseHeader(kNak_, kOrigin1_, header);
1683 
1684   // Endpoint count should be at most the limit.
1685   EXPECT_GE(policy().max_endpoints_per_origin, cache()->GetEndpointCount());
1686 
1687   if (mock_store()) {
1688     mock_store()->Flush();
1689     EXPECT_EQ(policy().max_endpoints_per_origin + 1,
1690               static_cast<unsigned long>(mock_store()->CountCommands(
1691                   CommandType::ADD_REPORTING_ENDPOINT)));
1692     EXPECT_EQ(policy().max_endpoints_per_origin + 1,
1693               static_cast<unsigned long>(mock_store()->CountCommands(
1694                   CommandType::ADD_REPORTING_ENDPOINT_GROUP)));
1695     EXPECT_EQ(
1696         1, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT));
1697     EXPECT_EQ(1, mock_store()->CountCommands(
1698                      CommandType::DELETE_REPORTING_ENDPOINT_GROUP));
1699   }
1700 }
1701 
TEST_P(ReportingHeaderParserTest,EvictEndpointsOverGlobalLimit)1702 TEST_P(ReportingHeaderParserTest, EvictEndpointsOverGlobalLimit) {
1703   // Set headers from different origins up to the global limit.
1704   for (size_t i = 0; i < policy().max_endpoint_count; ++i) {
1705     std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{MakeURL(i)}};
1706     std::string header =
1707         ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints));
1708     ParseHeader(kNak_, url::Origin::Create(MakeURL(i)), header);
1709   }
1710   EXPECT_EQ(policy().max_endpoint_count, cache()->GetEndpointCount());
1711 
1712   // Parse one more header to trigger eviction.
1713   ParseHeader(kNak_, kOrigin1_,
1714               "{\"endpoints\":[{\"url\":\"" + kEndpoint1_.spec() +
1715                   "\"}],\"max_age\":1}");
1716 
1717   // Endpoint count should be at most the limit.
1718   EXPECT_GE(policy().max_endpoint_count, cache()->GetEndpointCount());
1719 
1720   if (mock_store()) {
1721     mock_store()->Flush();
1722     EXPECT_EQ(policy().max_endpoint_count + 1,
1723               static_cast<unsigned long>(mock_store()->CountCommands(
1724                   CommandType::ADD_REPORTING_ENDPOINT)));
1725     EXPECT_EQ(policy().max_endpoint_count + 1,
1726               static_cast<unsigned long>(mock_store()->CountCommands(
1727                   CommandType::ADD_REPORTING_ENDPOINT_GROUP)));
1728     EXPECT_EQ(
1729         1, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT));
1730     EXPECT_EQ(1, mock_store()->CountCommands(
1731                      CommandType::DELETE_REPORTING_ENDPOINT_GROUP));
1732   }
1733 }
1734 
1735 INSTANTIATE_TEST_SUITE_P(ReportingHeaderParserStoreTest,
1736                          ReportingHeaderParserTest,
1737                          testing::Bool());
1738 
1739 // This test is parametrized on a boolean that represents whether to use a
1740 // MockPersistentReportingStore.
1741 class ReportingHeaderParserStructuredHeaderTest
1742     : public ReportingHeaderParserTestBase {
1743  protected:
ReportingHeaderParserStructuredHeaderTest()1744   ReportingHeaderParserStructuredHeaderTest() {
1745     // Enable kDocumentReporting to support new StructuredHeader-based
1746     // Reporting-Endpoints header.
1747     feature_list_.InitWithFeatures(
1748         {features::kPartitionNelAndReportingByNetworkIsolationKey,
1749          features::kDocumentReporting},
1750         {});
1751   }
1752 
1753   ~ReportingHeaderParserStructuredHeaderTest() override = default;
1754 
MakeEndpointGroup(const std::string & name,const std::vector<ReportingEndpoint::EndpointInfo> & endpoints,url::Origin origin=url::Origin ())1755   ReportingEndpointGroup MakeEndpointGroup(
1756       const std::string& name,
1757       const std::vector<ReportingEndpoint::EndpointInfo>& endpoints,
1758       url::Origin origin = url::Origin()) {
1759     ReportingEndpointGroupKey group_key(kNak_ /* unused */,
1760                                         url::Origin() /* unused */, name);
1761     ReportingEndpointGroup group;
1762     group.group_key = group_key;
1763     group.include_subdomains = OriginSubdomains::EXCLUDE;
1764     group.ttl = base::Days(30);
1765     group.endpoints = std::move(endpoints);
1766     return group;
1767   }
1768 
1769   // Constructs a string which would represent a single endpoint in a
1770   // Reporting-Endpoints header.
ConstructHeaderGroupString(const ReportingEndpointGroup & group)1771   std::string ConstructHeaderGroupString(const ReportingEndpointGroup& group) {
1772     std::string header = group.group_key.group_name;
1773     if (header.empty())
1774       return header;
1775     base::StrAppend(&header, {"="});
1776     if (group.endpoints.empty())
1777       return header;
1778     base::StrAppend(&header, {"\"", group.endpoints.front().url.spec(), "\""});
1779     return header;
1780   }
1781 
ParseHeader(const base::UnguessableToken & reporting_source,const IsolationInfo & isolation_info,const url::Origin & origin,const std::string & header_string)1782   void ParseHeader(const base::UnguessableToken& reporting_source,
1783                    const IsolationInfo& isolation_info,
1784                    const url::Origin& origin,
1785                    const std::string& header_string) {
1786     std::optional<base::flat_map<std::string, std::string>> header_map =
1787         ParseReportingEndpoints(header_string);
1788 
1789     if (header_map) {
1790       ReportingHeaderParser::ProcessParsedReportingEndpointsHeader(
1791           context(), reporting_source, isolation_info,
1792           isolation_info.network_anonymization_key(), origin, *header_map);
1793     }
1794   }
ProcessParsedHeader(const base::UnguessableToken & reporting_source,const IsolationInfo & isolation_info,const url::Origin & origin,const std::optional<base::flat_map<std::string,std::string>> & header_map)1795   void ProcessParsedHeader(
1796       const base::UnguessableToken& reporting_source,
1797       const IsolationInfo& isolation_info,
1798       const url::Origin& origin,
1799       const std::optional<base::flat_map<std::string, std::string>>&
1800           header_map) {
1801     ReportingHeaderParser::ProcessParsedReportingEndpointsHeader(
1802         context(), reporting_source, isolation_info,
1803         isolation_info.network_anonymization_key(), origin, *header_map);
1804   }
1805 
1806   const base::UnguessableToken kReportingSource_ =
1807       base::UnguessableToken::Create();
1808 };
1809 
TEST_P(ReportingHeaderParserStructuredHeaderTest,ParseInvalid)1810 TEST_P(ReportingHeaderParserStructuredHeaderTest, ParseInvalid) {
1811   static const struct {
1812     const char* header_value;
1813     const char* description;
1814   } kInvalidHeaderTestCases[] = {
1815       {"default=", "missing url"},
1816       {"default=1", "non-string url"},
1817   };
1818 
1819   for (auto& test_case : kInvalidHeaderTestCases) {
1820     auto parsed_result = ParseReportingEndpoints(test_case.header_value);
1821 
1822     EXPECT_FALSE(parsed_result.has_value())
1823         << "Invalid Reporting-Endpoints header (" << test_case.description
1824         << ": \"" << test_case.header_value << "\") parsed as valid.";
1825   }
1826 }
1827 
TEST_P(ReportingHeaderParserStructuredHeaderTest,ProcessInvalid)1828 TEST_P(ReportingHeaderParserStructuredHeaderTest, ProcessInvalid) {
1829   static const struct {
1830     const char* header_value;
1831     const char* description;
1832   } kInvalidHeaderTestCases[] = {
1833       {"default=\"//scheme/relative\"", "scheme-relative url"},
1834       {"default=\"relative/path\"", "path relative url"},
1835       {"default=\"http://insecure/\"", "insecure url"}};
1836 
1837   base::HistogramTester histograms;
1838   int invalid_case_count = 0;
1839 
1840   for (auto& test_case : kInvalidHeaderTestCases) {
1841     auto parsed_result = ParseReportingEndpoints(test_case.header_value);
1842 
1843     EXPECT_TRUE(parsed_result.has_value())
1844         << "Syntactically valid Reporting-Endpoints header (\""
1845         << test_case.description << ": \"" << test_case.header_value
1846         << "\") parsed as invalid.";
1847     ProcessParsedHeader(kReportingSource_, kIsolationInfo_, kOrigin1_,
1848                         parsed_result);
1849 
1850     invalid_case_count++;
1851     histograms.ExpectBucketCount(
1852         kReportingHeaderTypeHistogram,
1853         ReportingHeaderParser::ReportingHeaderType::kReportingEndpointsInvalid,
1854         invalid_case_count);
1855 
1856     // The endpoint should not have been set up in the cache.
1857     ReportingEndpoint endpoint =
1858         cache()->GetV1EndpointForTesting(kReportingSource_, "default");
1859     EXPECT_FALSE(endpoint);
1860   }
1861   histograms.ExpectBucketCount(
1862       kReportingHeaderTypeHistogram,
1863       ReportingHeaderParser::ReportingHeaderType::kReportingEndpoints, 0);
1864 }
1865 
TEST_P(ReportingHeaderParserStructuredHeaderTest,ParseBasic)1866 TEST_P(ReportingHeaderParserStructuredHeaderTest, ParseBasic) {
1867   std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}};
1868 
1869   std::string header =
1870       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints));
1871   auto parsed_result = ParseReportingEndpoints(header);
1872 
1873   EXPECT_TRUE(parsed_result.has_value())
1874       << "Valid Reporting-Endpoints header (\"" << header
1875       << "\") parsed as invalid.";
1876   EXPECT_EQ(1u, parsed_result->size());
1877   EXPECT_EQ(parsed_result->at(kGroup1_), kEndpoint1_.spec());
1878 }
1879 
TEST_P(ReportingHeaderParserStructuredHeaderTest,Basic)1880 TEST_P(ReportingHeaderParserStructuredHeaderTest, Basic) {
1881   base::HistogramTester histograms;
1882   std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}};
1883 
1884   std::string header =
1885       ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints));
1886   auto parsed_result = ParseReportingEndpoints(header);
1887   ProcessParsedHeader(kReportingSource_, kIsolationInfo_, kOrigin1_,
1888                       parsed_result);
1889 
1890   // Ensure that the endpoint was not inserted into the persistent endpoint
1891   // groups used for v0 reporting.
1892   EXPECT_EQ(0u, cache()->GetEndpointGroupCountForTesting());
1893 
1894   ReportingEndpoint endpoint =
1895       cache()->GetV1EndpointForTesting(kReportingSource_, kGroup1_);
1896   EXPECT_TRUE(endpoint);
1897 
1898   IsolationInfo isolation_info = cache()->GetIsolationInfoForEndpoint(endpoint);
1899   EXPECT_TRUE(isolation_info.IsEqualForTesting(kIsolationInfo_));
1900   EXPECT_EQ(kOrigin1_, endpoint.group_key.origin);
1901   EXPECT_EQ(kGroup1_, endpoint.group_key.group_name);
1902   EXPECT_EQ(kEndpoint1_, endpoint.info.url);
1903   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
1904             endpoint.info.priority);
1905   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
1906             endpoint.info.weight);
1907   histograms.ExpectBucketCount(
1908       kReportingHeaderTypeHistogram,
1909       ReportingHeaderParser::ReportingHeaderType::kReportingEndpoints, 1);
1910 
1911   // Ephemeral endpoints should not be persisted in the store
1912   if (mock_store()) {
1913     mock_store()->Flush();
1914     EXPECT_EQ(0, mock_store()->StoredEndpointsCount());
1915     EXPECT_EQ(0, mock_store()->StoredEndpointGroupsCount());
1916   }
1917 }
1918 
TEST_P(ReportingHeaderParserStructuredHeaderTest,PathAbsoluteURLEndpoint)1919 TEST_P(ReportingHeaderParserStructuredHeaderTest, PathAbsoluteURLEndpoint) {
1920   base::HistogramTester histograms;
1921   std::string header = "group1=\"/path-absolute-url\"";
1922   auto parsed_result = ParseReportingEndpoints(header);
1923   ProcessParsedHeader(kReportingSource_, kIsolationInfo_, kOrigin1_,
1924                       parsed_result);
1925 
1926   // Ensure that the endpoint was not inserted into the persistent endpoint
1927   // groups used for v0 reporting.
1928   EXPECT_EQ(0u, cache()->GetEndpointGroupCountForTesting());
1929 
1930   ReportingEndpoint endpoint =
1931       cache()->GetV1EndpointForTesting(kReportingSource_, kGroup1_);
1932   EXPECT_TRUE(endpoint);
1933   EXPECT_EQ(kOrigin1_, endpoint.group_key.origin);
1934   EXPECT_EQ(kGroup1_, endpoint.group_key.group_name);
1935   EXPECT_EQ(kEndpointPathAbsolute_, endpoint.info.url);
1936   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
1937             endpoint.info.priority);
1938   EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
1939             endpoint.info.weight);
1940   histograms.ExpectBucketCount(
1941       kReportingHeaderTypeHistogram,
1942       ReportingHeaderParser::ReportingHeaderType::kReportingEndpoints, 1);
1943 
1944   // Ephemeral endpoints should not be persisted in the store
1945   if (mock_store()) {
1946     mock_store()->Flush();
1947     EXPECT_EQ(0, mock_store()->StoredEndpointsCount());
1948     EXPECT_EQ(0, mock_store()->StoredEndpointGroupsCount());
1949   }
1950 }
1951 
1952 INSTANTIATE_TEST_SUITE_P(ReportingHeaderParserStoreTest,
1953                          ReportingHeaderParserStructuredHeaderTest,
1954                          testing::Bool());
1955 
1956 }  // namespace
1957 }  // namespace net
1958