1 // Copyright 2022 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/first_party_sets/global_first_party_sets.h"
6
7 #include <optional>
8
9 #include "base/containers/flat_map.h"
10 #include "base/version.h"
11 #include "net/base/schemeful_site.h"
12 #include "net/first_party_sets/first_party_set_entry.h"
13 #include "net/first_party_sets/first_party_set_entry_override.h"
14 #include "net/first_party_sets/first_party_set_metadata.h"
15 #include "net/first_party_sets/first_party_sets_context_config.h"
16 #include "net/first_party_sets/local_set_declaration.h"
17 #include "net/first_party_sets/sets_mutation.h"
18 #include "testing/gmock/include/gmock/gmock-matchers.h"
19 #include "testing/gmock/include/gmock/gmock.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 #include "url/gurl.h"
22
23 using ::testing::IsEmpty;
24 using ::testing::Optional;
25 using ::testing::Pair;
26 using ::testing::UnorderedElementsAre;
27
28 namespace net {
29
30 namespace {
31
32 const base::Version kVersion("1.2.3");
33 const SchemefulSite kPrimary(GURL("https://primary.test"));
34 const SchemefulSite kPrimary2(GURL("https://primary2.test"));
35 const SchemefulSite kPrimary3(GURL("https://primary3.test"));
36 const SchemefulSite kAssociated1(GURL("https://associated1.test"));
37 const SchemefulSite kAssociated1Cctld(GURL("https://associated1.cctld"));
38 const SchemefulSite kAssociated1Cctld2(GURL("https://associated1.cctld2"));
39 const SchemefulSite kAssociated2(GURL("https://associated2.test"));
40 const SchemefulSite kAssociated3(GURL("https://associated3.test"));
41 const SchemefulSite kAssociated4(GURL("https://associated4.test"));
42 const SchemefulSite kAssociated5(GURL("https://associated5.test"));
43 const SchemefulSite kService(GURL("https://service.test"));
44
CollectEffectiveSetEntries(const GlobalFirstPartySets & sets,const FirstPartySetsContextConfig & config)45 base::flat_map<SchemefulSite, FirstPartySetEntry> CollectEffectiveSetEntries(
46 const GlobalFirstPartySets& sets,
47 const FirstPartySetsContextConfig& config) {
48 base::flat_map<SchemefulSite, FirstPartySetEntry> got;
49 sets.ForEachEffectiveSetEntry(
50 config, [&](const SchemefulSite& site, const FirstPartySetEntry& entry) {
51 EXPECT_FALSE(got.contains(site));
52 got[site] = entry;
53 return true;
54 });
55
56 // Consistency check: verify that all of the returned entries are what we'd
57 // get if we called FindEntry directly.
58 for (const auto& [site, entry] : got) {
59 EXPECT_EQ(sets.FindEntry(site, config).value(), entry);
60 }
61 return got;
62 }
63
64 } // namespace
65
66 class GlobalFirstPartySetsTest : public ::testing::Test {
67 public:
68 GlobalFirstPartySetsTest() = default;
69 };
70
TEST_F(GlobalFirstPartySetsTest,CtorSkipsInvalidVersion)71 TEST_F(GlobalFirstPartySetsTest, CtorSkipsInvalidVersion) {
72 GlobalFirstPartySets sets(
73 base::Version(), /*entries=*/
74 {
75 {kPrimary,
76 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
77 {kAssociated1,
78 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
79 },
80 /*aliases=*/{});
81
82 EXPECT_THAT(
83 sets.FindEntries({kPrimary, kAssociated1}, FirstPartySetsContextConfig()),
84 IsEmpty());
85 }
86
TEST_F(GlobalFirstPartySetsTest,Clone)87 TEST_F(GlobalFirstPartySetsTest, Clone) {
88 base::Version version("1.2.3.4.5");
89 const SchemefulSite example(GURL("https://example.test"));
90 const SchemefulSite example_cctld(GURL("https://example.cctld"));
91 const SchemefulSite member1(GURL("https://member1.test"));
92 const FirstPartySetEntry entry(example, SiteType::kPrimary, std::nullopt);
93 const FirstPartySetEntry member1_entry(example, SiteType::kAssociated, 1);
94
95 const SchemefulSite foo(GURL("https://foo.test"));
96 const SchemefulSite member2(GURL("https://member2.test"));
97 const FirstPartySetEntry foo_entry(foo, SiteType::kPrimary, std::nullopt);
98 const FirstPartySetEntry member2_entry(foo, SiteType::kAssociated, 1);
99
100 GlobalFirstPartySets sets(version,
101 /*entries=*/
102 {{example, entry}, {member1, member1_entry}},
103 /*aliases=*/{{example_cctld, example}});
104 sets.ApplyManuallySpecifiedSet(LocalSetDeclaration(
105 /*set_entries=*/{{foo, foo_entry}, {member2, member2_entry}},
106 /*aliases=*/{}));
107
108 EXPECT_EQ(sets, sets.Clone());
109 }
110
TEST_F(GlobalFirstPartySetsTest,FindEntry_Nonexistent)111 TEST_F(GlobalFirstPartySetsTest, FindEntry_Nonexistent) {
112 SchemefulSite example(GURL("https://example.test"));
113
114 EXPECT_THAT(
115 GlobalFirstPartySets().FindEntry(example, FirstPartySetsContextConfig()),
116 std::nullopt);
117 }
118
TEST_F(GlobalFirstPartySetsTest,FindEntry_Exists)119 TEST_F(GlobalFirstPartySetsTest, FindEntry_Exists) {
120 SchemefulSite example(GURL("https://example.test"));
121 SchemefulSite decoy_site(GURL("https://decoy.test"));
122 FirstPartySetEntry entry(example, SiteType::kPrimary, std::nullopt);
123 FirstPartySetEntry decoy_entry(example, SiteType::kAssociated, 1);
124
125 EXPECT_THAT(GlobalFirstPartySets(kVersion,
126 {
127 {example, entry},
128 {decoy_site, decoy_entry},
129 },
130 {})
131 .FindEntry(example, FirstPartySetsContextConfig()),
132 Optional(entry));
133 }
134
TEST_F(GlobalFirstPartySetsTest,FindEntry_NoNormalization)135 TEST_F(GlobalFirstPartySetsTest, FindEntry_NoNormalization) {
136 SchemefulSite https_example(GURL("https://example.test"));
137 SchemefulSite associated(GURL("https://associated.test"));
138 SchemefulSite wss_example(GURL("wss://example.test"));
139 FirstPartySetEntry entry(https_example, SiteType::kPrimary, std::nullopt);
140 FirstPartySetEntry assoc_entry(https_example, SiteType::kAssociated, 0);
141
142 EXPECT_THAT(GlobalFirstPartySets(kVersion,
143 {
144 {https_example, entry},
145 {associated, assoc_entry},
146 },
147 {})
148 .FindEntry(wss_example, FirstPartySetsContextConfig()),
149 std::nullopt);
150 }
151
TEST_F(GlobalFirstPartySetsTest,FindEntry_ExistsViaOverride)152 TEST_F(GlobalFirstPartySetsTest, FindEntry_ExistsViaOverride) {
153 SchemefulSite example(GURL("https://example.test"));
154 SchemefulSite associated(GURL("https://associated.test"));
155 FirstPartySetEntry public_entry(example, SiteType::kPrimary, std::nullopt);
156 FirstPartySetEntry assoc_entry(example, SiteType::kAssociated, 0);
157 FirstPartySetEntry override_entry(example, SiteType::kAssociated, 1);
158
159 FirstPartySetsContextConfig config(
160 {{example, net::FirstPartySetEntryOverride(override_entry)}});
161
162 EXPECT_THAT(GlobalFirstPartySets(kVersion,
163 {
164 {example, public_entry},
165 {associated, assoc_entry},
166 },
167 {})
168 .FindEntry(example, config),
169 Optional(override_entry));
170 }
171
TEST_F(GlobalFirstPartySetsTest,FindEntry_RemovedViaOverride)172 TEST_F(GlobalFirstPartySetsTest, FindEntry_RemovedViaOverride) {
173 SchemefulSite example(GURL("https://example.test"));
174 SchemefulSite associated(GURL("https://associated.test"));
175 FirstPartySetEntry public_entry(example, SiteType::kPrimary, std::nullopt);
176 FirstPartySetEntry assoc_entry(example, SiteType::kAssociated, 0);
177
178 FirstPartySetsContextConfig config(
179 {{example, net::FirstPartySetEntryOverride()}});
180
181 EXPECT_THAT(GlobalFirstPartySets(kVersion,
182 {
183 {example, public_entry},
184 {associated, assoc_entry},
185 },
186 {})
187 .FindEntry(example, config),
188 std::nullopt);
189 }
190
TEST_F(GlobalFirstPartySetsTest,FindEntry_ExistsViaAlias)191 TEST_F(GlobalFirstPartySetsTest, FindEntry_ExistsViaAlias) {
192 SchemefulSite example(GURL("https://example.test"));
193 SchemefulSite example_cctld(GURL("https://example.cctld"));
194 FirstPartySetEntry entry(example, SiteType::kPrimary, std::nullopt);
195
196 EXPECT_THAT(GlobalFirstPartySets(kVersion,
197 {
198 {example, entry},
199 },
200 {{example_cctld, example}})
201 .FindEntry(example_cctld, FirstPartySetsContextConfig()),
202 Optional(entry));
203 }
204
TEST_F(GlobalFirstPartySetsTest,FindEntry_ExistsViaOverrideWithDecoyAlias)205 TEST_F(GlobalFirstPartySetsTest, FindEntry_ExistsViaOverrideWithDecoyAlias) {
206 SchemefulSite example(GURL("https://example.test"));
207 SchemefulSite example_cctld(GURL("https://example.cctld"));
208 FirstPartySetEntry public_entry(example, SiteType::kPrimary, std::nullopt);
209 FirstPartySetEntry override_entry(example, SiteType::kAssociated, 1);
210
211 FirstPartySetsContextConfig config(
212 {{example_cctld, net::FirstPartySetEntryOverride(override_entry)}});
213
214 EXPECT_THAT(GlobalFirstPartySets(kVersion,
215 {
216 {example, public_entry},
217 },
218 {{example_cctld, example}})
219 .FindEntry(example_cctld, config),
220 Optional(override_entry));
221 }
222
TEST_F(GlobalFirstPartySetsTest,FindEntry_RemovedViaOverrideWithDecoyAlias)223 TEST_F(GlobalFirstPartySetsTest, FindEntry_RemovedViaOverrideWithDecoyAlias) {
224 SchemefulSite example(GURL("https://example.test"));
225 SchemefulSite example_cctld(GURL("https://example.cctld"));
226 FirstPartySetEntry public_entry(example, SiteType::kPrimary, std::nullopt);
227
228 FirstPartySetsContextConfig config(
229 {{example_cctld, net::FirstPartySetEntryOverride()}});
230
231 EXPECT_THAT(GlobalFirstPartySets(kVersion,
232 {
233 {example, public_entry},
234 },
235 {{example_cctld, example}})
236 .FindEntry(example_cctld, config),
237 std::nullopt);
238 }
239
TEST_F(GlobalFirstPartySetsTest,FindEntry_AliasesIgnoredForConfig)240 TEST_F(GlobalFirstPartySetsTest, FindEntry_AliasesIgnoredForConfig) {
241 SchemefulSite example(GURL("https://example.test"));
242 SchemefulSite example_cctld(GURL("https://example.cctld"));
243 FirstPartySetEntry public_entry(example, SiteType::kPrimary, std::nullopt);
244 FirstPartySetEntry override_entry(example, SiteType::kAssociated, 1);
245
246 FirstPartySetsContextConfig config(
247 {{example, net::FirstPartySetEntryOverride(override_entry)}});
248
249 // FindEntry should ignore aliases when using the customizations. Public
250 // aliases only apply to sites in the public sets.
251 EXPECT_THAT(GlobalFirstPartySets(kVersion,
252 {
253 {example, public_entry},
254 },
255 {{example_cctld, example}})
256 .FindEntry(example_cctld, config),
257 public_entry);
258 }
259
TEST_F(GlobalFirstPartySetsTest,Empty_Empty)260 TEST_F(GlobalFirstPartySetsTest, Empty_Empty) {
261 EXPECT_TRUE(GlobalFirstPartySets().empty());
262 }
263
TEST_F(GlobalFirstPartySetsTest,Empty_NonemptyEntries)264 TEST_F(GlobalFirstPartySetsTest, Empty_NonemptyEntries) {
265 EXPECT_FALSE(
266 GlobalFirstPartySets(
267 kVersion,
268 {
269 {kPrimary,
270 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
271 {kAssociated4,
272 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
273 },
274 {})
275 .empty());
276 }
277
TEST_F(GlobalFirstPartySetsTest,Empty_NonemptyManualSet)278 TEST_F(GlobalFirstPartySetsTest, Empty_NonemptyManualSet) {
279 GlobalFirstPartySets sets;
280 sets.ApplyManuallySpecifiedSet(LocalSetDeclaration(
281 /*set_entries=*/
282 {
283 {kPrimary,
284 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
285 {kAssociated4,
286 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
287 },
288 /*aliases=*/{}));
289 EXPECT_FALSE(sets.empty());
290 }
291
TEST_F(GlobalFirstPartySetsTest,InvalidPublicSetsVersion_NonemptyManualSet)292 TEST_F(GlobalFirstPartySetsTest, InvalidPublicSetsVersion_NonemptyManualSet) {
293 GlobalFirstPartySets sets(
294 base::Version(), /*entries=*/
295 {
296 {kPrimary,
297 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
298 {kAssociated1,
299 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
300 },
301 /*aliases=*/{});
302 ASSERT_TRUE(sets.empty());
303 sets.ApplyManuallySpecifiedSet(LocalSetDeclaration(
304 /*set_entries=*/
305 {
306 {kPrimary,
307 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
308 {kAssociated4,
309 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
310 },
311 /*aliases=*/{}));
312
313 // The manual set should still be available, even though the component was
314 // invalid.
315 EXPECT_FALSE(sets.empty());
316 EXPECT_THAT(
317 sets.FindEntries({kPrimary, kAssociated1, kAssociated4},
318 FirstPartySetsContextConfig()),
319 UnorderedElementsAre(
320 Pair(kPrimary,
321 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
322 Pair(kAssociated4,
323 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0))));
324 }
325
TEST_F(GlobalFirstPartySetsTest,ForEachEffectiveSetEntry_ManualSetAndConfig_FullIteration)326 TEST_F(GlobalFirstPartySetsTest,
327 ForEachEffectiveSetEntry_ManualSetAndConfig_FullIteration) {
328 GlobalFirstPartySets global_sets;
329 global_sets.ApplyManuallySpecifiedSet(LocalSetDeclaration(
330 /*set_entries=*/
331 {
332 {kPrimary,
333 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
334 {kAssociated4,
335 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
336 {kAssociated5,
337 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 1)},
338 },
339 /*aliases=*/{}));
340
341 // Modify kPrimary's set by removing kAssociated5 and modifying kAssociated4,
342 // via policy.
343 FirstPartySetsContextConfig config = global_sets.ComputeConfig(SetsMutation(
344 /*replacement_sets=*/
345 {
346 {
347 {kPrimary,
348 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
349 {kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
350 std::nullopt)},
351 {kAssociated1Cctld,
352 FirstPartySetEntry(kPrimary, SiteType::kAssociated,
353 std::nullopt)},
354 {kAssociated4, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
355 std::nullopt)},
356 {kService,
357 FirstPartySetEntry(kPrimary, SiteType::kService, std::nullopt)},
358 },
359 },
360 /*addition_sets=*/{}));
361
362 // Note that since the policy sets take precedence over the manual set,
363 // kAssociated5 is no longer in an FPS.
364 EXPECT_THAT(
365 CollectEffectiveSetEntries(global_sets, config),
366 UnorderedElementsAre(
367 Pair(kAssociated1Cctld,
368 FirstPartySetEntry(kPrimary, SiteType::kAssociated,
369 std::nullopt)),
370 Pair(kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
371 std::nullopt)),
372 Pair(kAssociated4, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
373 std::nullopt)),
374 Pair(kPrimary,
375 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
376 Pair(kService, FirstPartySetEntry(kPrimary, SiteType::kService,
377 std::nullopt))));
378 }
379
380 class PopulatedGlobalFirstPartySetsTest : public GlobalFirstPartySetsTest {
381 public:
PopulatedGlobalFirstPartySetsTest()382 PopulatedGlobalFirstPartySetsTest()
383 : global_sets_(
384 kVersion,
385 {
386 {kPrimary, FirstPartySetEntry(kPrimary,
387 SiteType::kPrimary,
388 std::nullopt)},
389 {kAssociated1,
390 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
391 {kAssociated2,
392 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 1)},
393 {kService, FirstPartySetEntry(kPrimary,
394 SiteType::kService,
395 std::nullopt)},
396 {kPrimary2, FirstPartySetEntry(kPrimary2,
397 SiteType::kPrimary,
398 std::nullopt)},
399 {kAssociated3,
400 FirstPartySetEntry(kPrimary2, SiteType::kAssociated, 0)},
401 },
402 {
403 {kAssociated1Cctld, kAssociated1},
404 }) {}
405
global_sets()406 GlobalFirstPartySets& global_sets() { return global_sets_; }
407
408 private:
409 GlobalFirstPartySets global_sets_;
410 };
411
TEST_F(PopulatedGlobalFirstPartySetsTest,ApplyManuallySpecifiedSet_DeduplicatesPrimaryPrimary)412 TEST_F(PopulatedGlobalFirstPartySetsTest,
413 ApplyManuallySpecifiedSet_DeduplicatesPrimaryPrimary) {
414 // kPrimary overlaps as primary of both sets, so the existing set should be
415 // wiped out.
416 global_sets().ApplyManuallySpecifiedSet(LocalSetDeclaration(
417 /*set_entries=*/
418 {
419 {kPrimary,
420 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
421 {kAssociated4,
422 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
423 },
424 /*aliases=*/{}));
425
426 EXPECT_THAT(
427 global_sets().FindEntries(
428 {
429 kPrimary,
430 kAssociated1,
431 kAssociated2,
432 kAssociated4,
433 kService,
434 kAssociated1Cctld,
435 },
436 FirstPartySetsContextConfig()),
437 UnorderedElementsAre(
438 Pair(kPrimary,
439 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
440 Pair(kAssociated4,
441 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0))));
442 }
443
TEST_F(PopulatedGlobalFirstPartySetsTest,ApplyManuallySpecifiedSet_DeduplicatesPrimaryNonprimary)444 TEST_F(PopulatedGlobalFirstPartySetsTest,
445 ApplyManuallySpecifiedSet_DeduplicatesPrimaryNonprimary) {
446 // kPrimary overlaps as a primary of the public set and non-primary of the CLI
447 // set, so the existing set should be wiped out.
448 global_sets().ApplyManuallySpecifiedSet(LocalSetDeclaration(
449 /*set_entries=*/
450 {
451 {kPrimary3,
452 FirstPartySetEntry(kPrimary3, SiteType::kPrimary, std::nullopt)},
453 {kPrimary, FirstPartySetEntry(kPrimary3, SiteType::kAssociated, 0)},
454 },
455 /*aliases=*/{}));
456
457 EXPECT_THAT(
458 global_sets().FindEntries(
459 {
460 kPrimary,
461 kAssociated1,
462 kAssociated2,
463 kAssociated4,
464 kService,
465 kPrimary3,
466 kAssociated1Cctld,
467 },
468 FirstPartySetsContextConfig()),
469 UnorderedElementsAre(
470 Pair(kPrimary3,
471 FirstPartySetEntry(kPrimary3, SiteType::kPrimary, std::nullopt)),
472 Pair(kPrimary,
473 FirstPartySetEntry(kPrimary3, SiteType::kAssociated, 0))));
474 }
475
TEST_F(PopulatedGlobalFirstPartySetsTest,ApplyManuallySpecifiedSet_DeduplicatesNonprimaryPrimary)476 TEST_F(PopulatedGlobalFirstPartySetsTest,
477 ApplyManuallySpecifiedSet_DeduplicatesNonprimaryPrimary) {
478 // kAssociated1 overlaps as a non-primary of the public set and primary of the
479 // CLI set, so the CLI set should steal it and wipe out its alias, but
480 // otherwise leave the set intact.
481 global_sets().ApplyManuallySpecifiedSet(LocalSetDeclaration(
482 /*set_entries=*/
483 {
484 {kAssociated1,
485 FirstPartySetEntry(kAssociated1, SiteType::kPrimary, std::nullopt)},
486 {kAssociated4,
487 FirstPartySetEntry(kAssociated1, SiteType::kAssociated, 0)},
488 },
489 /*aliases=*/{}));
490
491 EXPECT_THAT(
492 global_sets().FindEntries(
493 {
494 kPrimary,
495 kAssociated1,
496 kAssociated2,
497 kAssociated4,
498 kService,
499 kPrimary3,
500 kAssociated1Cctld,
501 },
502 FirstPartySetsContextConfig()),
503 UnorderedElementsAre(
504 Pair(kPrimary,
505 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
506 Pair(kAssociated2,
507 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 1)),
508 Pair(kService,
509 FirstPartySetEntry(kPrimary, SiteType::kService, std::nullopt)),
510 Pair(kAssociated1,
511 FirstPartySetEntry(kAssociated1, SiteType::kPrimary,
512 std::nullopt)),
513 Pair(kAssociated4,
514 FirstPartySetEntry(kAssociated1, SiteType::kAssociated, 0))));
515 }
516
TEST_F(PopulatedGlobalFirstPartySetsTest,ApplyManuallySpecifiedSet_DeduplicatesNonprimaryNonprimary)517 TEST_F(PopulatedGlobalFirstPartySetsTest,
518 ApplyManuallySpecifiedSet_DeduplicatesNonprimaryNonprimary) {
519 // kAssociated1 overlaps as a non-primary of the public set and non-primary of
520 // the CLI set, so the CLI set should steal it and wipe out its alias.
521 global_sets().ApplyManuallySpecifiedSet(LocalSetDeclaration(
522 /*set_entries=*/
523 {
524 {kPrimary3,
525 FirstPartySetEntry(kPrimary3, SiteType::kPrimary, std::nullopt)},
526 {kAssociated1,
527 FirstPartySetEntry(kPrimary3, SiteType::kAssociated, 0)},
528 },
529 /*aliases=*/{}));
530
531 EXPECT_THAT(
532 global_sets().FindEntries(
533 {
534 kPrimary,
535 kAssociated1,
536 kAssociated2,
537 kAssociated4,
538 kService,
539 kPrimary3,
540 kAssociated1Cctld,
541 },
542 FirstPartySetsContextConfig()),
543 UnorderedElementsAre(
544 Pair(kPrimary,
545 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
546 Pair(kAssociated2,
547 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 1)),
548 Pair(kService,
549 FirstPartySetEntry(kPrimary, SiteType::kService, std::nullopt)),
550 Pair(kPrimary3,
551 FirstPartySetEntry(kPrimary3, SiteType::kPrimary, std::nullopt)),
552 Pair(kAssociated1,
553 FirstPartySetEntry(kPrimary3, SiteType::kAssociated, 0))));
554 }
555
TEST_F(PopulatedGlobalFirstPartySetsTest,ApplyManuallySpecifiedSet_PrunesInducedSingletons)556 TEST_F(PopulatedGlobalFirstPartySetsTest,
557 ApplyManuallySpecifiedSet_PrunesInducedSingletons) {
558 // Steal kAssociated3, so that kPrimary2 becomes a singleton, and verify that
559 // kPrimary2 is no longer considered in a set.
560 global_sets().ApplyManuallySpecifiedSet(LocalSetDeclaration(
561 /*set_entries=*/
562 {
563 {kPrimary3,
564 FirstPartySetEntry(kPrimary3, SiteType::kPrimary, std::nullopt)},
565 {kAssociated3,
566 FirstPartySetEntry(kPrimary3, SiteType::kAssociated, 0)},
567 },
568 /*aliases=*/{}));
569
570 EXPECT_THAT(
571 global_sets().FindEntries({kPrimary2}, FirstPartySetsContextConfig()),
572 IsEmpty());
573 }
574
TEST_F(PopulatedGlobalFirstPartySetsTest,ApplyManuallySpecifiedSet_RespectsManualAlias)575 TEST_F(PopulatedGlobalFirstPartySetsTest,
576 ApplyManuallySpecifiedSet_RespectsManualAlias) {
577 // Both the public sets and the locally-defined set define an alias for
578 // kAssociated1, but both define a different set for that site too. Only the
579 // locally-defined alias should be observable.
580 global_sets().ApplyManuallySpecifiedSet(LocalSetDeclaration(
581 /*set_entries=*/
582 {
583 {kPrimary3,
584 FirstPartySetEntry(kPrimary3, SiteType::kPrimary, std::nullopt)},
585 {kAssociated1,
586 FirstPartySetEntry(kPrimary3, SiteType::kAssociated, 0)},
587 },
588 /*aliases=*/{
589 {kAssociated1Cctld2, kAssociated1},
590 }));
591
592 EXPECT_THAT(
593 global_sets().FindEntries(
594 {
595 kAssociated1,
596 kAssociated1Cctld,
597 kAssociated1Cctld2,
598 },
599 FirstPartySetsContextConfig()),
600 UnorderedElementsAre(
601 Pair(kAssociated1,
602 FirstPartySetEntry(kPrimary3, SiteType::kAssociated, 0)),
603 Pair(kAssociated1Cctld2,
604 FirstPartySetEntry(kPrimary3, SiteType::kAssociated, 0))));
605 }
606
TEST_F(PopulatedGlobalFirstPartySetsTest,ForEachPublicSetEntry_FullIteration)607 TEST_F(PopulatedGlobalFirstPartySetsTest, ForEachPublicSetEntry_FullIteration) {
608 int count = 0;
609 EXPECT_TRUE(global_sets().ForEachPublicSetEntry(
610 [&](const SchemefulSite& site, const FirstPartySetEntry& entry) {
611 ++count;
612 return true;
613 }));
614 EXPECT_EQ(count, 7);
615 }
616
TEST_F(PopulatedGlobalFirstPartySetsTest,ForEachPublicSetEntry_EarlyReturn)617 TEST_F(PopulatedGlobalFirstPartySetsTest, ForEachPublicSetEntry_EarlyReturn) {
618 int count = 0;
619 EXPECT_FALSE(global_sets().ForEachPublicSetEntry(
620 [&](const SchemefulSite& site, const FirstPartySetEntry& entry) {
621 ++count;
622 return count < 4;
623 }));
624 EXPECT_EQ(count, 4);
625 }
626
TEST_F(PopulatedGlobalFirstPartySetsTest,ForEachEffectiveSetEntry_PublicSetsOnly_FullIteration)627 TEST_F(PopulatedGlobalFirstPartySetsTest,
628 ForEachEffectiveSetEntry_PublicSetsOnly_FullIteration) {
629 EXPECT_THAT(
630 CollectEffectiveSetEntries(global_sets(), FirstPartySetsContextConfig()),
631 UnorderedElementsAre(
632 Pair(kAssociated1Cctld,
633 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)),
634 Pair(kAssociated1,
635 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)),
636 Pair(kAssociated2,
637 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 1)),
638 Pair(kAssociated3,
639 FirstPartySetEntry(kPrimary2, SiteType::kAssociated, 0)),
640 Pair(kPrimary,
641 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
642 Pair(kPrimary2,
643 FirstPartySetEntry(kPrimary2, SiteType::kPrimary, std::nullopt)),
644 Pair(kService, FirstPartySetEntry(kPrimary, SiteType::kService,
645 std::nullopt))));
646 }
647
TEST_F(PopulatedGlobalFirstPartySetsTest,ForEachEffectiveSetEntry_PublicSetsWithManualSet_FullIteration)648 TEST_F(PopulatedGlobalFirstPartySetsTest,
649 ForEachEffectiveSetEntry_PublicSetsWithManualSet_FullIteration) {
650 // Replace kPrimary's set (including the alias and service site) with just
651 // {kPrimary, kAssociated4}.
652 global_sets().ApplyManuallySpecifiedSet(LocalSetDeclaration(
653 /*set_entries=*/
654 {
655 {kPrimary,
656 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
657 {kAssociated4,
658 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
659 },
660 /*aliases=*/{}));
661
662 EXPECT_THAT(
663 CollectEffectiveSetEntries(global_sets(), FirstPartySetsContextConfig()),
664 UnorderedElementsAre(
665 Pair(kAssociated3,
666 FirstPartySetEntry(kPrimary2, SiteType::kAssociated, 0)),
667 Pair(kAssociated4,
668 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)),
669 Pair(kPrimary,
670 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
671 Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary,
672 std::nullopt))));
673 }
674
TEST_F(PopulatedGlobalFirstPartySetsTest,ForEachEffectiveSetEntry_PublicSetsWithConfig_FullIteration)675 TEST_F(PopulatedGlobalFirstPartySetsTest,
676 ForEachEffectiveSetEntry_PublicSetsWithConfig_FullIteration) {
677 // Modify kPrimary's set by removing kAssociated2 and adding kAssociated4, via
678 // policy.
679 FirstPartySetsContextConfig config = global_sets().ComputeConfig(SetsMutation(
680 /*replacement_sets=*/
681 {
682 {
683 {kPrimary,
684 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
685 {kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
686 std::nullopt)},
687 {kAssociated1Cctld,
688 FirstPartySetEntry(kPrimary, SiteType::kAssociated,
689 std::nullopt)},
690 {kAssociated4, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
691 std::nullopt)},
692 {kService,
693 FirstPartySetEntry(kPrimary, SiteType::kService, std::nullopt)},
694 },
695 },
696 /*addition_sets=*/{}));
697
698 EXPECT_THAT(
699 CollectEffectiveSetEntries(global_sets(), config),
700 UnorderedElementsAre(
701 Pair(kAssociated1Cctld,
702 FirstPartySetEntry(kPrimary, SiteType::kAssociated,
703 std::nullopt)),
704 Pair(kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
705 std::nullopt)),
706 Pair(kAssociated3,
707 FirstPartySetEntry(kPrimary2, SiteType::kAssociated, 0)),
708 Pair(kAssociated4, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
709 std::nullopt)),
710 Pair(kPrimary,
711 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
712 Pair(kPrimary2,
713 FirstPartySetEntry(kPrimary2, SiteType::kPrimary, std::nullopt)),
714 Pair(kService, FirstPartySetEntry(kPrimary, SiteType::kService,
715 std::nullopt))));
716 }
717
TEST_F(PopulatedGlobalFirstPartySetsTest,ForEachEffectiveSetEntry_PublicSetsWithManualSetAndConfig_FullIteration)718 TEST_F(
719 PopulatedGlobalFirstPartySetsTest,
720 ForEachEffectiveSetEntry_PublicSetsWithManualSetAndConfig_FullIteration) {
721 // Replace kPrimary's set (including the alias and service site) with just
722 // {kPrimary, kAssociated4, kAssociated5}.
723 global_sets().ApplyManuallySpecifiedSet(LocalSetDeclaration(
724 /*set_entries=*/
725 {
726 {kPrimary,
727 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
728 {kAssociated4,
729 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
730 {kAssociated5,
731 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 1)},
732 },
733 /*aliases=*/{}));
734
735 // Modify kPrimary's set by removing kAssociated2 and adding kAssociated4, via
736 // policy.
737 FirstPartySetsContextConfig config = global_sets().ComputeConfig(SetsMutation(
738 /*replacement_sets=*/
739 {
740 {
741 {kPrimary,
742 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
743 {kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
744 std::nullopt)},
745 {kAssociated1Cctld,
746 FirstPartySetEntry(kPrimary, SiteType::kAssociated,
747 std::nullopt)},
748 {kAssociated4, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
749 std::nullopt)},
750 {kService,
751 FirstPartySetEntry(kPrimary, SiteType::kService, std::nullopt)},
752 },
753 },
754 /*addition_sets=*/{}));
755
756 // Note that since the policy sets take precedence over the manual set,
757 // kAssociated5 is no longer in an FPS.
758 EXPECT_THAT(
759 CollectEffectiveSetEntries(global_sets(), config),
760 UnorderedElementsAre(
761 Pair(kAssociated1Cctld,
762 FirstPartySetEntry(kPrimary, SiteType::kAssociated,
763 std::nullopt)),
764 Pair(kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
765 std::nullopt)),
766 Pair(kAssociated3,
767 FirstPartySetEntry(kPrimary2, SiteType::kAssociated, 0)),
768 Pair(kAssociated4, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
769 std::nullopt)),
770 Pair(kPrimary,
771 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
772 Pair(kPrimary2,
773 FirstPartySetEntry(kPrimary2, SiteType::kPrimary, std::nullopt)),
774 Pair(kService, FirstPartySetEntry(kPrimary, SiteType::kService,
775 std::nullopt))));
776 }
777
TEST_F(PopulatedGlobalFirstPartySetsTest,ForEachEffectiveSetEntry_PublicSetsWithManualSetAndConfig_ManualAliasOverlap)778 TEST_F(
779 PopulatedGlobalFirstPartySetsTest,
780 ForEachEffectiveSetEntry_PublicSetsWithManualSetAndConfig_ManualAliasOverlap) {
781 global_sets().ApplyManuallySpecifiedSet(LocalSetDeclaration(
782 /*set_entries=*/
783 {
784 {kPrimary,
785 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
786 {kAssociated1,
787 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
788 },
789 /*aliases=*/{
790 {kAssociated1Cctld2, kAssociated1},
791 }));
792
793 FirstPartySetsContextConfig config = global_sets().ComputeConfig(SetsMutation(
794 /*replacement_sets=*/
795 {
796 {
797 {kPrimary2,
798 FirstPartySetEntry(kPrimary2, SiteType::kPrimary, std::nullopt)},
799 {kAssociated1,
800 FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
801 std::nullopt)},
802 },
803 },
804 /*addition_sets=*/{}));
805
806 EXPECT_THAT(
807 CollectEffectiveSetEntries(global_sets(), config),
808 UnorderedElementsAre(
809 Pair(kAssociated1,
810 FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
811 std::nullopt)),
812 Pair(kPrimary,
813 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
814 Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary,
815 std::nullopt))));
816 }
817
TEST_F(PopulatedGlobalFirstPartySetsTest,ComputeMetadata)818 TEST_F(PopulatedGlobalFirstPartySetsTest, ComputeMetadata) {
819 SchemefulSite nonmember(GURL("https://nonmember.test"));
820 SchemefulSite nonmember1(GURL("https://nonmember1.test"));
821 FirstPartySetEntry primary_entry(kPrimary, SiteType::kPrimary, std::nullopt);
822 FirstPartySetEntry associated_entry(kPrimary, SiteType::kAssociated, 0);
823
824 // Works as usual for sites that are in First-Party sets.
825 EXPECT_EQ(global_sets().ComputeMetadata(kAssociated1, &kAssociated1,
826 FirstPartySetsContextConfig()),
827 FirstPartySetMetadata(&associated_entry, &associated_entry));
828 EXPECT_EQ(global_sets().ComputeMetadata(kPrimary, &kAssociated1,
829 FirstPartySetsContextConfig()),
830 FirstPartySetMetadata(&primary_entry, &associated_entry));
831 EXPECT_EQ(global_sets().ComputeMetadata(kAssociated1, &kPrimary,
832 FirstPartySetsContextConfig()),
833 FirstPartySetMetadata(&associated_entry, &primary_entry));
834
835 EXPECT_EQ(global_sets().ComputeMetadata(nonmember, &kAssociated1,
836 FirstPartySetsContextConfig()),
837 FirstPartySetMetadata(nullptr, &associated_entry));
838 EXPECT_EQ(global_sets().ComputeMetadata(kAssociated1, &nonmember,
839 FirstPartySetsContextConfig()),
840 FirstPartySetMetadata(&associated_entry, nullptr));
841
842 EXPECT_EQ(global_sets().ComputeMetadata(nonmember, &nonmember,
843 FirstPartySetsContextConfig()),
844 FirstPartySetMetadata(nullptr, nullptr));
845 }
846
TEST_F(GlobalFirstPartySetsTest,ComputeConfig_Empty)847 TEST_F(GlobalFirstPartySetsTest, ComputeConfig_Empty) {
848 EXPECT_EQ(GlobalFirstPartySets(
849 kVersion,
850 /*entries=*/
851 {
852 {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary,
853 std::nullopt)},
854 {kAssociated1,
855 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
856 },
857 /*aliases=*/{})
858 .ComputeConfig(SetsMutation({}, {})),
859 FirstPartySetsContextConfig());
860 }
861
TEST_F(GlobalFirstPartySetsTest,ComputeConfig_Replacements_NoIntersection_NoRemoval)862 TEST_F(GlobalFirstPartySetsTest,
863 ComputeConfig_Replacements_NoIntersection_NoRemoval) {
864 GlobalFirstPartySets sets(
865 kVersion,
866 /*entries=*/
867 {
868 {kPrimary,
869 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
870 {kAssociated1,
871 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
872 },
873 /*aliases=*/{});
874 FirstPartySetsContextConfig config = sets.ComputeConfig(SetsMutation(
875 /*replacement_sets=*/
876 {
877 {
878 {kPrimary2,
879 FirstPartySetEntry(kPrimary2, SiteType::kPrimary, std::nullopt)},
880 {kAssociated2,
881 FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
882 std::nullopt)},
883 },
884 },
885 /*addition_sets=*/{}));
886 EXPECT_THAT(
887 sets.FindEntries({kAssociated2, kPrimary2}, config),
888 UnorderedElementsAre(
889 Pair(kAssociated2,
890 FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
891 std::nullopt)),
892 Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary,
893 std::nullopt))));
894 }
895
896 // The common associated site between the policy and existing set is removed
897 // from its previous set.
TEST_F(GlobalFirstPartySetsTest,ComputeConfig_Replacements_ReplacesExistingAssociatedSite_RemovedFromFormerSet)898 TEST_F(
899 GlobalFirstPartySetsTest,
900 ComputeConfig_Replacements_ReplacesExistingAssociatedSite_RemovedFromFormerSet) {
901 GlobalFirstPartySets sets(
902 kVersion,
903 /*entries=*/
904 {
905 {kPrimary,
906 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
907 {kAssociated1,
908 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
909 {kAssociated2,
910 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 1)},
911 },
912 /*aliases=*/{});
913 FirstPartySetsContextConfig config = sets.ComputeConfig(SetsMutation(
914 /*replacement_sets=*/
915 {
916 {
917 {kPrimary2,
918 FirstPartySetEntry(kPrimary2, SiteType::kPrimary, std::nullopt)},
919 {kAssociated2,
920 FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
921 std::nullopt)},
922 },
923 },
924 /*addition_sets=*/{}));
925 EXPECT_THAT(
926 sets.FindEntries({kPrimary2, kAssociated2}, config),
927 UnorderedElementsAre(
928 Pair(kAssociated2,
929 FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
930 std::nullopt)),
931 Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary,
932 std::nullopt))));
933 }
934
935 // The common primary between the policy and existing set is removed and its
936 // former associated sites are removed since they are now unowned.
TEST_F(GlobalFirstPartySetsTest,ComputeConfig_Replacements_ReplacesExistingPrimary_RemovesFormerAssociatedSites)937 TEST_F(
938 GlobalFirstPartySetsTest,
939 ComputeConfig_Replacements_ReplacesExistingPrimary_RemovesFormerAssociatedSites) {
940 GlobalFirstPartySets sets(
941 kVersion,
942 /*entries=*/
943 {
944 {kPrimary,
945 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
946 {kAssociated1,
947 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
948 {kAssociated2,
949 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 1)},
950 },
951 /*aliases=*/{});
952 FirstPartySetsContextConfig config = sets.ComputeConfig(SetsMutation(
953 /*replacement_sets=*/
954 {
955 {
956 {kPrimary,
957 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
958 {kAssociated3, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
959 std::nullopt)},
960 },
961 },
962 /*addition_sets=*/{}));
963 EXPECT_THAT(
964 sets.FindEntries({kAssociated3, kPrimary, kAssociated1, kAssociated2},
965 config),
966 UnorderedElementsAre(
967 Pair(kAssociated3, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
968 std::nullopt)),
969 Pair(kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary,
970 std::nullopt))));
971 }
972
973 // The common associated site between the policy and existing set is removed and
974 // any leftover singletons are deleted.
TEST_F(GlobalFirstPartySetsTest,ComputeConfig_Replacements_ReplacesExistingAssociatedSite_RemovesSingletons)975 TEST_F(
976 GlobalFirstPartySetsTest,
977 ComputeConfig_Replacements_ReplacesExistingAssociatedSite_RemovesSingletons) {
978 GlobalFirstPartySets sets(
979 kVersion,
980 /*entries=*/
981 {
982 {kPrimary,
983 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
984 {kAssociated1,
985 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
986 },
987 /*aliases=*/{});
988 FirstPartySetsContextConfig config = sets.ComputeConfig(SetsMutation(
989 /*replacement_sets=*/
990 {
991 {
992 {kPrimary3,
993 FirstPartySetEntry(kPrimary3, SiteType::kPrimary, std::nullopt)},
994 {kAssociated1,
995 FirstPartySetEntry(kPrimary3, SiteType::kAssociated,
996 std::nullopt)},
997 },
998 },
999 /*addition_sets=*/{}));
1000 EXPECT_THAT(
1001 sets.FindEntries({kAssociated1, kPrimary3, kPrimary}, config),
1002 UnorderedElementsAre(
1003 Pair(kAssociated1,
1004 FirstPartySetEntry(kPrimary3, SiteType::kAssociated,
1005 std::nullopt)),
1006 Pair(kPrimary3, FirstPartySetEntry(kPrimary3, SiteType::kPrimary,
1007 std::nullopt))));
1008 }
1009
1010 // The policy set and the existing set have nothing in common so the policy set
1011 // gets added in without updating the existing set.
TEST_F(GlobalFirstPartySetsTest,ComputeConfig_Additions_NoIntersection_AddsWithoutUpdating)1012 TEST_F(GlobalFirstPartySetsTest,
1013 ComputeConfig_Additions_NoIntersection_AddsWithoutUpdating) {
1014 GlobalFirstPartySets sets(
1015 kVersion,
1016 /*entries=*/
1017 {
1018 {kPrimary,
1019 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
1020 {kAssociated1,
1021 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
1022 },
1023 /*aliases=*/{});
1024 FirstPartySetsContextConfig config = sets.ComputeConfig(SetsMutation(
1025 /*replacement_sets=*/{},
1026 /*addition_sets=*/{
1027 {
1028 {kPrimary2,
1029 FirstPartySetEntry(kPrimary2, SiteType::kPrimary, std::nullopt)},
1030 {kAssociated2,
1031 FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
1032 std::nullopt)},
1033 },
1034 }));
1035 EXPECT_THAT(
1036 sets.FindEntries({kAssociated2, kPrimary2}, config),
1037 UnorderedElementsAre(
1038 Pair(kAssociated2,
1039 FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
1040 std::nullopt)),
1041 Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary,
1042 std::nullopt))));
1043 }
1044
1045 // The primary of a policy set is also an associated site in an existing set.
1046 // The policy set absorbs all sites in the existing set into its
1047 // associated sites.
TEST_F(GlobalFirstPartySetsTest,ComputeConfig_Additions_PolicyPrimaryIsExistingAssociatedSite_PolicySetAbsorbsExistingSet)1048 TEST_F(
1049 GlobalFirstPartySetsTest,
1050 ComputeConfig_Additions_PolicyPrimaryIsExistingAssociatedSite_PolicySetAbsorbsExistingSet) {
1051 GlobalFirstPartySets sets(
1052 kVersion,
1053 /*entries=*/
1054 {
1055 {kPrimary,
1056 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
1057 {kAssociated1,
1058 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
1059 },
1060 /*aliases=*/{});
1061 FirstPartySetsContextConfig config = sets.ComputeConfig(SetsMutation(
1062 /*replacement_sets=*/{},
1063 /*addition_sets=*/{
1064 {
1065 {kAssociated1,
1066 FirstPartySetEntry(kAssociated1, SiteType::kPrimary,
1067 std::nullopt)},
1068 {kAssociated2,
1069 FirstPartySetEntry(kAssociated1, SiteType::kAssociated,
1070 std::nullopt)},
1071 {kAssociated3,
1072 FirstPartySetEntry(kAssociated1, SiteType::kAssociated,
1073 std::nullopt)},
1074 },
1075 }));
1076 EXPECT_THAT(
1077 sets.FindEntries({kPrimary, kAssociated2, kAssociated3, kAssociated1},
1078 config),
1079 UnorderedElementsAre(
1080 Pair(kPrimary, FirstPartySetEntry(kAssociated1, SiteType::kAssociated,
1081 std::nullopt)),
1082 Pair(kAssociated2,
1083 FirstPartySetEntry(kAssociated1, SiteType::kAssociated,
1084 std::nullopt)),
1085 Pair(kAssociated3,
1086 FirstPartySetEntry(kAssociated1, SiteType::kAssociated,
1087 std::nullopt)),
1088 Pair(kAssociated1,
1089 FirstPartySetEntry(kAssociated1, SiteType::kPrimary,
1090 std::nullopt))));
1091 }
1092
1093 // The primary of a policy set is also a primary of an existing set.
1094 // The policy set absorbs all of its primary's existing associated sites into
1095 // its associated sites.
TEST_F(GlobalFirstPartySetsTest,ComputeConfig_Additions_PolicyPrimaryIsExistingPrimary_PolicySetAbsorbsExistingAssociatedSites)1096 TEST_F(
1097 GlobalFirstPartySetsTest,
1098 ComputeConfig_Additions_PolicyPrimaryIsExistingPrimary_PolicySetAbsorbsExistingAssociatedSites) {
1099 GlobalFirstPartySets sets(
1100 kVersion,
1101 /*entries=*/
1102 {
1103 {kPrimary,
1104 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
1105 {kAssociated1,
1106 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
1107 {kAssociated3,
1108 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 1)},
1109 },
1110 /*aliases=*/{});
1111 FirstPartySetsContextConfig config = sets.ComputeConfig(SetsMutation(
1112 /*replacement_sets=*/{},
1113 /*addition_sets=*/{{
1114 {kPrimary,
1115 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
1116 {kAssociated2,
1117 FirstPartySetEntry(kPrimary, SiteType::kAssociated, std::nullopt)},
1118 }}));
1119 EXPECT_THAT(
1120 sets.FindEntries({kAssociated1, kAssociated2, kAssociated3, kPrimary},
1121 config),
1122 UnorderedElementsAre(
1123 Pair(kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
1124 std::nullopt)),
1125 Pair(kAssociated2, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
1126 std::nullopt)),
1127 Pair(kAssociated3, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
1128 std::nullopt)),
1129 Pair(kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary,
1130 std::nullopt))));
1131 }
1132
1133 // Existing set overlaps with both replacement and addition set.
TEST_F(GlobalFirstPartySetsTest,ComputeConfig_ReplacementsAndAdditions_SetListsOverlapWithSameExistingSet)1134 TEST_F(
1135 GlobalFirstPartySetsTest,
1136 ComputeConfig_ReplacementsAndAdditions_SetListsOverlapWithSameExistingSet) {
1137 GlobalFirstPartySets sets(
1138 kVersion,
1139 /*entries=*/
1140 {
1141 {kPrimary,
1142 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
1143 {kAssociated1,
1144 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
1145 {kAssociated2,
1146 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 1)},
1147 },
1148 /*aliases=*/{});
1149 FirstPartySetsContextConfig config = sets.ComputeConfig(SetsMutation(
1150 /*replacement_sets=*/
1151 {
1152 {
1153 {kPrimary2,
1154 FirstPartySetEntry(kPrimary2, SiteType::kPrimary, std::nullopt)},
1155 {kAssociated1,
1156 FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
1157 std::nullopt)},
1158 },
1159 },
1160 /*addition_sets=*/{
1161 {
1162 {kPrimary,
1163 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
1164 {kAssociated3, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
1165 std::nullopt)},
1166 },
1167 }));
1168 EXPECT_THAT(
1169 sets.FindEntries(
1170 {kAssociated1, kAssociated2, kAssociated3, kPrimary, kPrimary2},
1171 config),
1172 UnorderedElementsAre(
1173 Pair(kAssociated1,
1174 FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
1175 std::nullopt)),
1176 Pair(kAssociated2, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
1177 std::nullopt)),
1178 Pair(kAssociated3, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
1179 std::nullopt)),
1180 Pair(kPrimary,
1181 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
1182 Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary,
1183 std::nullopt))));
1184 }
1185
TEST_F(GlobalFirstPartySetsTest,TransitiveOverlap_TwoCommonPrimaries)1186 TEST_F(GlobalFirstPartySetsTest, TransitiveOverlap_TwoCommonPrimaries) {
1187 SchemefulSite primary0(GURL("https://primary0.test"));
1188 SchemefulSite associated_site0(GURL("https://associatedsite0.test"));
1189 SchemefulSite primary1(GURL("https://primary1.test"));
1190 SchemefulSite associated_site1(GURL("https://associatedsite1.test"));
1191 SchemefulSite primary2(GURL("https://primary2.test"));
1192 SchemefulSite associated_site2(GURL("https://associatedsite2.test"));
1193 SchemefulSite primary42(GURL("https://primary42.test"));
1194 SchemefulSite associated_site42(GURL("https://associatedsite42.test"));
1195 // {primary1, {associated_site1}} and {primary2, {associated_site2}}
1196 // transitively overlap with the existing set. primary1 takes primaryship of
1197 // the normalized addition set since it was provided first. The other addition
1198 // sets are unaffected.
1199 GlobalFirstPartySets sets(
1200 kVersion,
1201 /*entries=*/
1202 {
1203 {primary1,
1204 FirstPartySetEntry(primary1, SiteType::kPrimary, std::nullopt)},
1205 {primary2, FirstPartySetEntry(primary1, SiteType::kAssociated, 0)},
1206 },
1207 /*aliases=*/{});
1208 FirstPartySetsContextConfig config = sets.ComputeConfig(SetsMutation(
1209 /*replacement_sets=*/{},
1210 /*addition_sets=*/{
1211 {{primary0,
1212 FirstPartySetEntry(primary0, SiteType::kPrimary, std::nullopt)},
1213 {associated_site0,
1214 FirstPartySetEntry(primary0, SiteType::kAssociated, std::nullopt)}},
1215 {{primary1,
1216 FirstPartySetEntry(primary1, SiteType::kPrimary, std::nullopt)},
1217 {associated_site1,
1218 FirstPartySetEntry(primary1, SiteType::kAssociated, std::nullopt)}},
1219 {{primary2,
1220 FirstPartySetEntry(primary2, SiteType::kPrimary, std::nullopt)},
1221 {associated_site2,
1222 FirstPartySetEntry(primary2, SiteType::kAssociated, std::nullopt)}},
1223 {{primary42,
1224 FirstPartySetEntry(primary42, SiteType::kPrimary, std::nullopt)},
1225 {associated_site42,
1226 FirstPartySetEntry(primary42, SiteType::kAssociated,
1227 std::nullopt)}},
1228 }));
1229 EXPECT_THAT(
1230 sets.FindEntries(
1231 {
1232 associated_site0,
1233 associated_site1,
1234 associated_site2,
1235 associated_site42,
1236 primary0,
1237 primary1,
1238 primary2,
1239 primary42,
1240 },
1241 config),
1242 UnorderedElementsAre(
1243 Pair(associated_site0,
1244 FirstPartySetEntry(primary0, SiteType::kAssociated,
1245 std::nullopt)),
1246 Pair(associated_site1,
1247 FirstPartySetEntry(primary1, SiteType::kAssociated,
1248 std::nullopt)),
1249 Pair(associated_site2,
1250 FirstPartySetEntry(primary1, SiteType::kAssociated,
1251 std::nullopt)),
1252 Pair(associated_site42,
1253 FirstPartySetEntry(primary42, SiteType::kAssociated,
1254 std::nullopt)),
1255 Pair(primary0,
1256 FirstPartySetEntry(primary0, SiteType::kPrimary, std::nullopt)),
1257 Pair(primary1,
1258 FirstPartySetEntry(primary1, SiteType::kPrimary, std::nullopt)),
1259 Pair(primary2, FirstPartySetEntry(primary1, SiteType::kAssociated,
1260 std::nullopt)),
1261 Pair(primary42, FirstPartySetEntry(primary42, SiteType::kPrimary,
1262 std::nullopt))));
1263 }
1264
TEST_F(GlobalFirstPartySetsTest,TransitiveOverlap_TwoCommonAssociatedSites)1265 TEST_F(GlobalFirstPartySetsTest, TransitiveOverlap_TwoCommonAssociatedSites) {
1266 SchemefulSite primary0(GURL("https://primary0.test"));
1267 SchemefulSite associated_site0(GURL("https://associatedsite0.test"));
1268 SchemefulSite primary1(GURL("https://primary1.test"));
1269 SchemefulSite associated_site1(GURL("https://associatedsite1.test"));
1270 SchemefulSite primary2(GURL("https://primary2.test"));
1271 SchemefulSite associated_site2(GURL("https://associatedsite2.test"));
1272 SchemefulSite primary42(GURL("https://primary42.test"));
1273 SchemefulSite associated_site42(GURL("https://associatedsite42.test"));
1274 // {primary1, {associated_site1}} and {primary2, {associated_site2}}
1275 // transitively overlap with the existing set. primary2 takes primaryship of
1276 // the normalized addition set since it was provided first. The other addition
1277 // sets are unaffected.
1278 GlobalFirstPartySets sets(
1279 kVersion,
1280 /*entries=*/
1281 {
1282 {primary2,
1283 FirstPartySetEntry(primary2, SiteType::kPrimary, std::nullopt)},
1284 {primary1, FirstPartySetEntry(primary2, SiteType::kAssociated, 0)},
1285 },
1286 /*aliases=*/{});
1287 FirstPartySetsContextConfig config = sets.ComputeConfig(SetsMutation(
1288 /*replacement_sets=*/{},
1289 /*addition_sets=*/{
1290 {{primary0,
1291 FirstPartySetEntry(primary0, SiteType::kPrimary, std::nullopt)},
1292 {associated_site0,
1293 FirstPartySetEntry(primary0, SiteType::kAssociated, std::nullopt)}},
1294 {{primary2,
1295 FirstPartySetEntry(primary2, SiteType::kPrimary, std::nullopt)},
1296 {associated_site2,
1297 FirstPartySetEntry(primary2, SiteType::kAssociated, std::nullopt)}},
1298 {{primary1,
1299 FirstPartySetEntry(primary1, SiteType::kPrimary, std::nullopt)},
1300 {associated_site1,
1301 FirstPartySetEntry(primary1, SiteType::kAssociated, std::nullopt)}},
1302 {{primary42,
1303 FirstPartySetEntry(primary42, SiteType::kPrimary, std::nullopt)},
1304 {associated_site42,
1305 FirstPartySetEntry(primary42, SiteType::kAssociated,
1306 std::nullopt)}},
1307 }));
1308 EXPECT_THAT(
1309 sets.FindEntries(
1310 {
1311 associated_site0,
1312 associated_site1,
1313 associated_site2,
1314 associated_site42,
1315 primary0,
1316 primary1,
1317 primary2,
1318 primary42,
1319 },
1320 config),
1321 UnorderedElementsAre(
1322 Pair(associated_site0,
1323 FirstPartySetEntry(primary0, SiteType::kAssociated,
1324 std::nullopt)),
1325 Pair(associated_site1,
1326 FirstPartySetEntry(primary2, SiteType::kAssociated,
1327 std::nullopt)),
1328 Pair(associated_site2,
1329 FirstPartySetEntry(primary2, SiteType::kAssociated,
1330 std::nullopt)),
1331 Pair(associated_site42,
1332 FirstPartySetEntry(primary42, SiteType::kAssociated,
1333 std::nullopt)),
1334 Pair(primary0,
1335 FirstPartySetEntry(primary0, SiteType::kPrimary, std::nullopt)),
1336 Pair(primary1, FirstPartySetEntry(primary2, SiteType::kAssociated,
1337 std::nullopt)),
1338 Pair(primary2,
1339 FirstPartySetEntry(primary2, SiteType::kPrimary, std::nullopt)),
1340 Pair(primary42, FirstPartySetEntry(primary42, SiteType::kPrimary,
1341 std::nullopt))));
1342 }
1343
TEST_F(GlobalFirstPartySetsTest,InvalidPublicSetsVersion_ComputeConfig)1344 TEST_F(GlobalFirstPartySetsTest, InvalidPublicSetsVersion_ComputeConfig) {
1345 const GlobalFirstPartySets sets(
1346 base::Version(), /*entries=*/
1347 {
1348 {kPrimary,
1349 FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
1350 {kAssociated1,
1351 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
1352 },
1353 /*aliases=*/{});
1354 ASSERT_TRUE(sets.empty());
1355
1356 FirstPartySetsContextConfig config = sets.ComputeConfig(SetsMutation(
1357 /*replacement_sets=*/
1358 {
1359 {
1360 {kPrimary2,
1361 FirstPartySetEntry(kPrimary2, SiteType::kPrimary, std::nullopt)},
1362 {kAssociated2,
1363 FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
1364 std::nullopt)},
1365 },
1366 },
1367 /*addition_sets=*/{}));
1368
1369 // The config should still be nonempty, even though the component was invalid.
1370 EXPECT_FALSE(config.empty());
1371
1372 EXPECT_THAT(
1373 sets.FindEntries(
1374 {
1375 kPrimary,
1376 kPrimary2,
1377 kAssociated1,
1378 kAssociated2,
1379 },
1380 config),
1381 UnorderedElementsAre(
1382 Pair(kAssociated2,
1383 FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
1384 std::nullopt)),
1385 Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary,
1386 std::nullopt))));
1387 }
1388
1389 class GlobalFirstPartySetsWithConfigTest
1390 : public PopulatedGlobalFirstPartySetsTest {
1391 public:
GlobalFirstPartySetsWithConfigTest()1392 GlobalFirstPartySetsWithConfigTest()
1393 : config_({
1394 // New entry:
1395 {kPrimary3, net::FirstPartySetEntryOverride(
1396 FirstPartySetEntry(kPrimary3,
1397 SiteType::kPrimary,
1398 std::nullopt))},
1399 // Removed entry:
1400 {kAssociated1, net::FirstPartySetEntryOverride()},
1401 // Remapped entry:
1402 {kAssociated3,
1403 net::FirstPartySetEntryOverride(
1404 FirstPartySetEntry(kPrimary3, SiteType::kAssociated, 0))},
1405 // Removed alias:
1406 {kAssociated1Cctld, net::FirstPartySetEntryOverride()},
1407 }) {}
1408
config()1409 FirstPartySetsContextConfig& config() { return config_; }
1410
1411 private:
1412 FirstPartySetsContextConfig config_;
1413 };
1414
TEST_F(GlobalFirstPartySetsWithConfigTest,ComputeMetadata)1415 TEST_F(GlobalFirstPartySetsWithConfigTest, ComputeMetadata) {
1416 FirstPartySetEntry example_primary_entry(kPrimary, SiteType::kPrimary,
1417 std::nullopt);
1418 FirstPartySetEntry foo_primary_entry(kPrimary3, SiteType::kPrimary,
1419 std::nullopt);
1420 FirstPartySetEntry foo_associated_entry(kPrimary3, SiteType::kAssociated, 0);
1421
1422 // kAssociated1 has been removed from its set.
1423 EXPECT_EQ(global_sets().ComputeMetadata(kAssociated1, &kPrimary, config()),
1424 FirstPartySetMetadata(nullptr, &example_primary_entry));
1425
1426 // kAssociated3 and kPrimary3 are sites in a new set.
1427 EXPECT_EQ(global_sets().ComputeMetadata(kAssociated3, &kPrimary3, config()),
1428 FirstPartySetMetadata(
1429
1430 &foo_associated_entry, &foo_primary_entry));
1431 }
1432
1433 } // namespace net
1434