Bug 1843584 [wpt PR 41045] - [FLEDGE] Add some WPTs for expiration logic, a=testonly
[gecko.git] / testing / web-platform / tests / fledge / tentative / join-leave-ad-interest-group.https.sub.window.js
blobd1d6d50e4b0bb0c42c5bdd1fc9ea2a46251f3db8
1 // META: script=/resources/testdriver.js
2 // META: script=/common/utils.js
3 // META: script=resources/fledge-util.js
4 // META: timeout=long
6 "use strict;"
8 // These tests are focused on joinAdInterestGroup() and leaveAdInterestGroup().
9 // Most join tests do not run auctions, but instead only check the result of
10 // the returned promise, since testing that interest groups are actually
11 // joined, and that each interestGroup field behaves as intended, are covered
12 // by other tests.
14 // Minimal fields needed for a valid interest group. Used in most test cases.
15 const BASE_INTEREST_GROUP = {
16   owner: window.location.origin,
17   name: 'default name',
20 // Each test case attempts to join and then leave an interest group, checking
21 // if any exceptions are thrown from either operation.
22 const SIMPLE_JOIN_LEAVE_TEST_CASES = [
23   { expectJoinSucces: false,
24     expectLeaveSucces: false,
25     interestGroup: null
26   },
27   { expectJoinSucces: false,
28     expectLeaveSucces: false,
29     interestGroup: {}
30   },
32   // Basic success test case.
33   { expectJoinSucces: true,
34     expectLeaveSucces: true,
35     interestGroup: BASE_INTEREST_GROUP
36   },
38   // "owner" tests
39   { expectJoinSucces: false,
40     expectLeaveSucces: false,
41     interestGroup: { name: 'default name' }
42   },
43   { expectJoinSucces: false,
44     expectLeaveSucces: false,
45     interestGroup: { ...BASE_INTEREST_GROUP,
46                      owner: null}
47   },
48   { expectJoinSucces: false,
49     expectLeaveSucces: false,
50     interestGroup: { ...BASE_INTEREST_GROUP,
51                      owner: window.location.origin.replace('https', 'http')}
52   },
53   { expectJoinSucces: false,
54     expectLeaveSucces: false,
55     interestGroup: { ...BASE_INTEREST_GROUP,
56                      owner: window.location.origin.replace('https', 'wss')}
57   },
58   // Cross-origin joins and leaves are not allowed without .well-known
59   // permissions.
60   { expectJoinSucces: false,
61     expectLeaveSucces: false,
62     interestGroup: { ...BASE_INTEREST_GROUP,
63                      owner: '{{hosts[][www]}}' }
64   },
66   // "name" tests
67   { expectJoinSucces: false,
68     expectLeaveSucces: false,
69     interestGroup: { owner: window.location.origin }
70   },
71   { expectJoinSucces: true,
72     expectLeaveSucces: true,
73     interestGroup: { ...BASE_INTEREST_GROUP,
74                      name: ''}
75   },
77   // "priority" tests
78   { expectJoinSucces: true,
79     expectLeaveSucces: true,
80     interestGroup: { ...BASE_INTEREST_GROUP,
81                      priority: 1}
82   },
83   { expectJoinSucces: true,
84     expectLeaveSucces: true,
85     interestGroup: { ...BASE_INTEREST_GROUP,
86                      priority: 0}
87   },
88   { expectJoinSucces: true,
89     expectLeaveSucces: true,
90     interestGroup: { ...BASE_INTEREST_GROUP,
91                      priority: -1.5}
92   },
94   // "priorityVector" tests
95   { expectJoinSucces: false,
96     expectLeaveSucces: true,
97     interestGroup: { ...BASE_INTEREST_GROUP,
98                      priorityVector: null}
99   },
100   { expectJoinSucces: false,
101     expectLeaveSucces: true,
102     interestGroup: { ...BASE_INTEREST_GROUP,
103                      priorityVector: 1}
104   },
105   { expectJoinSucces: false,
106     expectLeaveSucces: true,
107     interestGroup: { ...BASE_INTEREST_GROUP,
108                      priorityVector: {a: 'apple'}}
109   },
110   { expectJoinSucces: true,
111     expectLeaveSucces: true,
112     interestGroup: { ...BASE_INTEREST_GROUP,
113                      priorityVector: {}}
114   },
115   { expectJoinSucces: true,
116     expectLeaveSucces: true,
117     interestGroup: { ...BASE_INTEREST_GROUP,
118                      priorityVector: {a: 1}}
119   },
120   { expectJoinSucces: true,
121     expectLeaveSucces: true,
122     interestGroup: { ...BASE_INTEREST_GROUP,
123                      priorityVector: {'a': 1, 'b': -4.5, 'a.b': 0}}
124   },
126   // "prioritySignalsOverrides" tests
127   { expectJoinSucces: false,
128     expectLeaveSucces: true,
129     interestGroup: { ...BASE_INTEREST_GROUP,
130                      prioritySignalsOverrides: null}
131   },
132   { expectJoinSucces: false,
133     expectLeaveSucces: true,
134     interestGroup: { ...BASE_INTEREST_GROUP,
135                      prioritySignalsOverrides: 1}
136   },
137   { expectJoinSucces: false,
138     expectLeaveSucces: true,
139     interestGroup: { ...BASE_INTEREST_GROUP,
140                      prioritySignalsOverrides: {a: 'apple'}}
141   },
142   { expectJoinSucces: true,
143     expectLeaveSucces: true,
144     interestGroup: { ...BASE_INTEREST_GROUP,
145                      prioritySignalsOverrides: {}}
146   },
147   { expectJoinSucces: true,
148     expectLeaveSucces: true,
149     interestGroup: { ...BASE_INTEREST_GROUP,
150                      prioritySignalsOverrides: {a: 1}}
151   },
152   { expectJoinSucces: true,
153     expectLeaveSucces: true,
154     interestGroup: { ...BASE_INTEREST_GROUP,
155                      prioritySignalsOverrides: {'a': 1, 'b': -4.5, 'a.b': 0}}
156   },
158   // "enableBiddingSignalsPrioritization" tests
159   { expectJoinSucces: true,
160     expectLeaveSucces: true,
161     interestGroup: { ...BASE_INTEREST_GROUP,
162                      enableBiddingSignalsPrioritization: true}
163   },
164   { expectJoinSucces: true,
165     expectLeaveSucces: true,
166     interestGroup: { ...BASE_INTEREST_GROUP,
167                      enableBiddingSignalsPrioritization: false}
168   },
170   // "biddingLogicUrl" tests
171   { expectJoinSucces: true,
172     expectLeaveSucces: true,
173     interestGroup: { ...BASE_INTEREST_GROUP,
174                      biddingLogicUrl: null }
175   },
176   { expectJoinSucces: false,
177     expectLeaveSucces: true,
178     interestGroup: { ...BASE_INTEREST_GROUP,
179                      biddingLogicUrl: 'https://{{hosts[][www]}}/foo.js' }
180   },
181   { expectJoinSucces: false,
182     expectLeaveSucces: true,
183     interestGroup: { ...BASE_INTEREST_GROUP,
184                      biddingLogicUrl: 'data:text/javascript,Foo' }
185   },
186   { expectJoinSucces: true,
187     expectLeaveSucces: true,
188     interestGroup: { ...BASE_INTEREST_GROUP,
189                      biddingLogicUrl: `${window.location.origin}/foo.js`}
190   },
191   { expectJoinSucces: true,
192     expectLeaveSucces: true,
193     interestGroup: { ...BASE_INTEREST_GROUP,
194                      biddingLogicUrl: 'relative/path' }
195   },
197   // "biddingWasmHelperUrl" tests
198   { expectJoinSucces: true,
199     expectLeaveSucces: true,
200     interestGroup: { ...BASE_INTEREST_GROUP,
201                      biddingWasmHelperUrl: null }
202   },
203   { expectJoinSucces: false,
204     expectLeaveSucces: true,
205     interestGroup: { ...BASE_INTEREST_GROUP,
206                      biddingWasmHelperUrl: 'https://{{hosts[][www]}}/foo.js' }
207   },
208   { expectJoinSucces: false,
209     expectLeaveSucces: true,
210     interestGroup: { ...BASE_INTEREST_GROUP,
211                      biddingWasmHelperUrl: 'data:application/wasm,Foo' }
212   },
213   { expectJoinSucces: true,
214     expectLeaveSucces: true,
215     interestGroup: { ...BASE_INTEREST_GROUP,
216                      biddingWasmHelperUrl: `${window.location.origin}/foo.js`}
217   },
218   { expectJoinSucces: true,
219     expectLeaveSucces: true,
220     interestGroup: { ...BASE_INTEREST_GROUP,
221                      biddingWasmHelperUrl: 'relative/path' }
222   },
224   // "dailyUpdateUrl" tests
225   { expectJoinSucces: true,
226     expectLeaveSucces: true,
227     interestGroup: { ...BASE_INTEREST_GROUP,
228                      dailyUpdateUrl: null }
229   },
230   { expectJoinSucces: false,
231     expectLeaveSucces: true,
232     interestGroup: { ...BASE_INTEREST_GROUP,
233                      dailyUpdateUrl: 'https://{{hosts[][www]}}/foo.js' }
234   },
235   { expectJoinSucces: false,
236     expectLeaveSucces: true,
237     interestGroup: { ...BASE_INTEREST_GROUP,
238                      dailyUpdateUrl: 'data:application/wasm,Foo' }
239   },
240   { expectJoinSucces: true,
241     expectLeaveSucces: true,
242     interestGroup: { ...BASE_INTEREST_GROUP,
243                      dailyUpdateUrl: `${window.location.origin}/foo.js`}
244   },
245   { expectJoinSucces: true,
246     expectLeaveSucces: true,
247     interestGroup: { ...BASE_INTEREST_GROUP,
248                      dailyUpdateUrl: 'relative/path' }
249   },
251   // "executionMode" tests
252   { expectJoinSucces: true,
253     expectLeaveSucces: true,
254     interestGroup: { ...BASE_INTEREST_GROUP,
255                      executionMode: 'compatibility' }
256   },
257   { expectJoinSucces: true,
258     expectLeaveSucces: true,
259     interestGroup: { ...BASE_INTEREST_GROUP,
260                      executionMode: 'groupByOrigin' }
261   },
262   { expectJoinSucces: true,
263     expectLeaveSucces: true,
264     interestGroup: { ...BASE_INTEREST_GROUP,
265                      executionMode: 'unknownValuesAreValid' }
266   },
268   // "trustedBiddingSignalsUrl" tests
269   { expectJoinSucces: true,
270     expectLeaveSucces: true,
271     interestGroup: { ...BASE_INTEREST_GROUP,
272                      trustedBiddingSignalsUrl: null }
273   },
274   { expectJoinSucces: false,
275     expectLeaveSucces: true,
276     interestGroup: { ...BASE_INTEREST_GROUP,
277                      trustedBiddingSignalsUrl: 'https://{{hosts[][www]}}/foo.js' }
278   },
279   { expectJoinSucces: false,
280     expectLeaveSucces: true,
281     interestGroup: { ...BASE_INTEREST_GROUP,
282                      trustedBiddingSignalsUrl: 'data:application/json,{}' }
283   },
284   { expectJoinSucces: true,
285     expectLeaveSucces: true,
286     interestGroup: { ...BASE_INTEREST_GROUP,
287                      trustedBiddingSignalsUrl: `${window.location.origin}/foo.js`}
288   },
289   { expectJoinSucces: true,
290     expectLeaveSucces: true,
291     interestGroup: { ...BASE_INTEREST_GROUP,
292                      trustedBiddingSignalsUrl: 'relative/path' }
293   },
295   // "trustedBiddingSignalsKeys" tests
296   { expectJoinSucces: false,
297     expectLeaveSucces: true,
298     interestGroup: { ...BASE_INTEREST_GROUP,
299                      trustedBiddingSignalsKeys: null }
300   },
301   { expectJoinSucces: false,
302     expectLeaveSucces: true,
303     interestGroup: { ...BASE_INTEREST_GROUP,
304                      trustedBiddingSignalsKeys: {}}
305   },
306   { expectJoinSucces: true,
307     expectLeaveSucces: true,
308     interestGroup: { ...BASE_INTEREST_GROUP,
309                      trustedBiddingSignalsKeys: []}
310   },
311   { expectJoinSucces: true,
312     expectLeaveSucces: true,
313     interestGroup: { ...BASE_INTEREST_GROUP,
314                      trustedBiddingSignalsKeys: ['a', 4, 'Foo']}
315   },
317   // "userBiddingSignals" tests
318   { expectJoinSucces: true,
319     expectLeaveSucces: true,
320     interestGroup: { ...BASE_INTEREST_GROUP,
321                      userBiddingSignals: null }
322   },
323   { expectJoinSucces: true,
324     expectLeaveSucces: true,
325     interestGroup: { ...BASE_INTEREST_GROUP,
326                      userBiddingSignals: 'foo' }
327   },
328   { expectJoinSucces: true,
329     expectLeaveSucces: true,
330     interestGroup: { ...BASE_INTEREST_GROUP,
331                      userBiddingSignals: 15 }
332   },
333   { expectJoinSucces: true,
334     expectLeaveSucces: true,
335     interestGroup: { ...BASE_INTEREST_GROUP,
336                      userBiddingSignals: [5, 'foo', [-6.4, {a: 'b'}]] }
337   },
338   { expectJoinSucces: true,
339     expectLeaveSucces: true,
340     interestGroup: { ...BASE_INTEREST_GROUP,
341                      userBiddingSignals: {a: [5, 'foo', {b: -6.4}] }}
342   },
344   // "ads" tests
345   { expectJoinSucces: false,
346     expectLeaveSucces: true,
347     interestGroup: { ...BASE_INTEREST_GROUP,
348                      ads: null }
349   },
350   { expectJoinSucces: false,
351     expectLeaveSucces: true,
352     interestGroup: { ...BASE_INTEREST_GROUP,
353                      ads: 5 }
354   },
355   { expectJoinSucces: false,
356     expectLeaveSucces: true,
357     interestGroup: { ...BASE_INTEREST_GROUP,
358                      ads: {} }
359   },
360   { expectJoinSucces: true,
361     expectLeaveSucces: true,
362     interestGroup: { ...BASE_INTEREST_GROUP,
363                      ads: [] }
364   },
365   { expectJoinSucces: false,
366     expectLeaveSucces: true,
367     interestGroup: { ...BASE_INTEREST_GROUP,
368                      ads: [{}] }
369   },
370   { expectJoinSucces: false,
371     expectLeaveSucces: true,
372     interestGroup: { ...BASE_INTEREST_GROUP,
373                      ads: [{metadata: [{a:'b'}, 'c'], 1:[2,3]}] }
374   },
375   { expectJoinSucces: true,
376     expectLeaveSucces: true,
377     interestGroup: { ...BASE_INTEREST_GROUP,
378                      ads: [{renderUrl: 'https://somewhere.test/'}] }
379   },
380   { expectJoinSucces: true,
381     expectLeaveSucces: true,
382     interestGroup: { ...BASE_INTEREST_GROUP,
383                      ads: [{renderUrl: 'https://somewhere.test/'},
384                            {renderUrl: 'https://somewhere-else.test/'}] }
385   },
386   { expectJoinSucces: true,
387     expectLeaveSucces: true,
388     interestGroup: { ...BASE_INTEREST_GROUP,
389                      ads: [{renderUrl: 'https://somewhere.test/',
390                             metadata: null}] }
391   },
392   { expectJoinSucces: true,
393     expectLeaveSucces: true,
394     interestGroup: { ...BASE_INTEREST_GROUP,
395                      ads: [{renderUrl: 'https://somewhere.test/',
396                             metadata: null,
397                             someOtherField: 'foo'}] }
398   },
400   // "adComponents" tests
401   { expectJoinSucces: false,
402     expectLeaveSucces: true,
403     interestGroup: { ...BASE_INTEREST_GROUP,
404                      adComponents: null }
405   },
406   { expectJoinSucces: false,
407     expectLeaveSucces: true,
408     interestGroup: { ...BASE_INTEREST_GROUP,
409                      adComponents: 5 }
410   },
411   { expectJoinSucces: false,
412     expectLeaveSucces: true,
413     interestGroup: { ...BASE_INTEREST_GROUP,
414                      adComponents: {} }
415   },
416   { expectJoinSucces: true,
417     expectLeaveSucces: true,
418     interestGroup: { ...BASE_INTEREST_GROUP,
419                      adComponents: [] }
420   },
421   { expectJoinSucces: false,
422     expectLeaveSucces: true,
423     interestGroup: { ...BASE_INTEREST_GROUP,
424                      adComponents: [{}] }
425   },
426   { expectJoinSucces: false,
427     expectLeaveSucces: true,
428     interestGroup: { ...BASE_INTEREST_GROUP,
429                      adComponents: [{metadata: [{a:'b'}, 'c'], 1:[2,3]}] }
430   },
431   { expectJoinSucces: true,
432     expectLeaveSucces: true,
433     interestGroup: { ...BASE_INTEREST_GROUP,
434                      adComponents: [{renderUrl: 'https://somewhere.test/'}] }
435   },
436   { expectJoinSucces: true,
437     expectLeaveSucces: true,
438     interestGroup: { ...BASE_INTEREST_GROUP,
439                      adComponents: [{renderUrl: 'https://somewhere.test/'},
440                                     {renderUrl: 'https://elsewhere.test/'}] }
441   },
442   { expectJoinSucces: true,
443     expectLeaveSucces: true,
444     interestGroup: { ...BASE_INTEREST_GROUP,
445                      adComponents: [{renderUrl: 'https://somewhere.test/',
446                                      metadata: null}] }
447   },
448   { expectJoinSucces: true,
449     expectLeaveSucces: true,
450     interestGroup: { ...BASE_INTEREST_GROUP,
451                      adComponents: [{renderUrl: 'https://somewhere.test/',
452                                      metadata: null,
453                                      someOtherField: 'foo'}] }
454   },
456   // Miscellaneous tests.
457   { expectJoinSucces: true,
458     expectLeaveSucces: true,
459     interestGroup: { ...BASE_INTEREST_GROUP,
460                      extra: false,
461                      fields: {do:'not'},
462                      matter: 'at',
463                      all: [3,4,5] }
464   },
466   // Interest group dictionaries must be less than 1 MB (1048576 bytes), so
467   // test that here by using a large name on an otherwise valid interest group
468   // dictionary. The first case is the largest name value that still results in
469   // a valid dictionary, whereas the second test case produces a dictionary
470   // that's one byte too large.
471   { expectJoinSucces: true,
472     expectLeaveSucces: true,
473     interestGroup: { ...BASE_INTEREST_GROUP,
474                      name: 'a'.repeat(1048528) },
475     testCaseName: "Largest possible interest group dictionary",
476   },
477   { expectJoinSucces: false,
478     expectLeaveSucces: true,
479     interestGroup: { ...BASE_INTEREST_GROUP,
480                      name: 'a'.repeat(1048529) },
481     testCaseName: "Oversized interest group dictionary",
482   },
485 for (testCase of SIMPLE_JOIN_LEAVE_TEST_CASES) {
486   var test_name = 'Join and leave interest group: ';
487   if ('testCaseName' in testCase) {
488     test_name += testCase.testCaseName;
489   } else {
490     test_name += JSON.stringify(testCase);
491   }
493   promise_test((async (testCase) => {
494     const INTEREST_GROUP_LIFETIME_SECS = 1;
496     let join_promise = navigator.joinAdInterestGroup(testCase.interestGroup,
497                                                      INTEREST_GROUP_LIFETIME_SECS);
498     assert_true(join_promise instanceof Promise, "join should return a promise");
499     if (testCase.expectJoinSucces) {
500       assert_equals(await join_promise, undefined);
501     } else {
502       let joinExceptionThrown = false;
503       try {
504         await join_promise;
505       } catch (e) {
506         joinExceptionThrown = true;
507       }
508       assert_true(joinExceptionThrown, 'Exception not thrown on join.');
509     }
511     let leave_promise = navigator.leaveAdInterestGroup(testCase.interestGroup);
512     assert_true(leave_promise instanceof Promise, "leave should return a promise");
513     if (testCase.expectLeaveSucces) {
514       assert_equals(await leave_promise, undefined);
515     } else {
516       let leaveExceptionThrown = false;
517       try {
518         await leave_promise;
519       } catch (e) {
520         leaveExceptionThrown = true;
521       }
522       assert_true(leaveExceptionThrown, 'Exception not thrown on leave.');
523     }
524   }).bind(undefined, testCase), test_name);
527 promise_test(async test => {
528   const uuid = generateUuid(test);
530   // Joining an interest group without a bidding script and run an auction.
531   // There should be no winner.
532   await joinInterestGroup(test, uuid, { biddingLogicUrl: null });
533   assert_equals(null, await runBasicFledgeAuction(test, uuid),
534                 'Auction unexpectedly had a winner');
536   // Joining an interest group with a bidding script and the same owner/name as
537   // the previously joined interest group, and re-run the auction. There should
538   // be a winner this time.
539   await joinInterestGroup(test, uuid);
540   let config = await runBasicFledgeAuction(test, uuid);
541   assert_true(config instanceof FencedFrameConfig,
542               'Wrong value type returned from auction: ' +
543               config.constructor.name);
545   // Re-join the first interest group, and re-run the auction. The interest
546   // group should be overwritten again, and there should be no winner.
547   await joinInterestGroup(test, uuid, { biddingLogicUrl: null });
548   assert_equals(null, await runBasicFledgeAuction(test, uuid),
549                 'Auction unexpectedly had a winner');
550 }, 'Join same interest group overwrites old matching group.');
552 promise_test(async test => {
553   const uuid = generateUuid(test);
555   // Join an interest group, run an auction to make sure it was joined.
556   await joinInterestGroup(test, uuid);
557   let config = await runBasicFledgeAuction(test, uuid);
558   assert_true(config instanceof FencedFrameConfig,
559               'Wrong value type returned from auction: ' +
560               config.constructor.name);
562   // Leave the interest group, re-run the auction. There should be no winner.
563   await leaveInterestGroup();
564   assert_equals(null, await runBasicFledgeAuction(test, uuid),
565                 'Auction unexpectedly had a winner');
566 }, 'Leaving interest group actually leaves interest group.');
568 promise_test(async test => {
569   // This should not throw.
570   await leaveInterestGroup({ name: 'Never join group' });
571 }, 'Leave an interest group that was never joined.');
573 ///////////////////////////////////////////////////////////////////////////////
574 // Expiration tests
575 ///////////////////////////////////////////////////////////////////////////////
577 promise_test(async test => {
578   const uuid = generateUuid(test);
580   // Joins the default interest group, with a 0.2 second duration.
581   await joinInterestGroup(test, uuid, {}, 0.2);
583   // Keep on running auctions until interest group duration expires.
584   // Unfortunately, there's no duration that's guaranteed to be long enough to
585   // be be able to win an auction once, but short enough to prevent this test
586   // from running too long, so can't check the interest group won at least one
587   // auction.
588   while (await runBasicFledgeAuction(test, uuid) !== null);
589 }, 'Interest group duration.');
591 promise_test(async test => {
592   const uuid = generateUuid(test);
594   // Join interest group with a duration of -600. The interest group should
595   // immediately expire, and not be allowed to participate in auctions.
596   await joinInterestGroup(test, uuid, {}, -600);
597   assert_true(await runBasicFledgeAuction(test, uuid) === null);
598 }, 'Interest group duration of -600.');
600 promise_test(async test => {
601   const uuid = generateUuid(test);
603   // Join a long-lived interest group.
604   await joinInterestGroup(test, uuid, {}, 600);
606   // Make sure interest group with a non-default timeout was joined.
607   assert_true(await runBasicFledgeAuction(test, uuid) !== null);
609   // Re-join interest group with a duration value of 0.2 seconds.
610   await joinInterestGroup(test, uuid, {}, 0.2);
612   // Keep on running auctions until interest group expires.
613   while (await runBasicFledgeAuction(test, uuid) !== null);
614 }, 'Interest group test with overwritten duration.');
616 promise_test(async test => {
617   const uuid = generateUuid(test);
619   // Join a long-lived interest group.
620   await joinInterestGroup(test, uuid, {}, 600);
622   // Re-join interest group with a duration value of 0.2 seconds. The new
623   // duration should take precedence, and the interest group should immediately
624   // expire.
625   await joinInterestGroup(test, uuid, {}, -600);
626   assert_true(await runBasicFledgeAuction(test, uuid) === null);
627 }, 'Interest group test with overwritten duration of -600.');