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