126 lines
5 KiB
JavaScript
126 lines
5 KiB
JavaScript
// META: script=/resources/testdriver.js
|
|
// META: script=/resources/testdriver-vendor.js
|
|
// META: script=/common/utils.js
|
|
// META: script=resources/fledge-util.sub.js
|
|
// META: timeout=long
|
|
|
|
"use strict";
|
|
|
|
// Runs one auction at a time using `auctionConfigOverrides` until the auction
|
|
// has a winner.
|
|
async function runAuctionsUntilWinner(test, uuid, auctionConfigOverrides) {
|
|
let fencedFrameConfig = null;
|
|
while (!fencedFrameConfig) {
|
|
fencedFrameConfig =
|
|
await runBasicFledgeAuction(test, uuid, auctionConfigOverrides);
|
|
}
|
|
return fencedFrameConfig;
|
|
}
|
|
|
|
// This tests the case of ties. The winner of an auction is normally checked
|
|
// by these tests by checking a report sent when the winner is loaded in a fenced
|
|
// frame. Unfortunately, that requires a lot of navigations, which can be slow.
|
|
//
|
|
// So instead, run a multi-seller auction. The inner auction has two bidders,
|
|
// which both bid, and the seller gives them the same score. For the first
|
|
// auction, the top-level seller just accepts the only bid it sees, and then
|
|
// as usual, we navigate a fenced frame, to learn which bidder won.
|
|
//
|
|
// The for subsequent auctions, the nested component auction is identical,
|
|
// but the top-level auction rejects bids from the bidder that won the
|
|
// first auction. So if we have a winner, we know that the other bidder
|
|
// won the tie. Auctions are run in parallel until this happens.
|
|
//
|
|
// The interest groups use "group-by-origin" execution mode, to potentially
|
|
// allow the auctions run in parallel to complete faster.
|
|
promise_test(async test => {
|
|
const uuid = generateUuid(test);
|
|
|
|
// Use different report URLs for each interest group, to identify
|
|
// which interest group has won an auction.
|
|
let reportURLs = [createBidderReportURL(uuid, /*id=*/'1'),
|
|
createBidderReportURL(uuid, /*id=*/'2')];
|
|
|
|
// Use different ad URLs for each auction. These need to be distinct
|
|
// so that the top-level seller can check the URL to check if the
|
|
// winning bid from the component auction has already won an
|
|
// auction.
|
|
let adURLs = [createRenderURL(uuid),
|
|
createRenderURL(uuid, /*script=*/';')];
|
|
|
|
await Promise.all(
|
|
[ joinInterestGroup(
|
|
test, uuid,
|
|
{ name: 'group 1',
|
|
ads: [{ renderURL: adURLs[0] }],
|
|
executionMode: 'group-by-origin',
|
|
biddingLogicURL: createBiddingScriptURL(
|
|
{ allowComponentAuction: true,
|
|
reportWin: `sendReportTo("${reportURLs[0]}");`})}),
|
|
joinInterestGroup(
|
|
test, uuid,
|
|
{ name: 'group 2',
|
|
ads: [{ renderURL: adURLs[1] }],
|
|
executionMode: 'group-by-origin',
|
|
biddingLogicURL: createBiddingScriptURL(
|
|
{ allowComponentAuction: true,
|
|
reportWin: `sendReportTo("${reportURLs[1]}");`})})
|
|
]
|
|
);
|
|
|
|
let componentAuctionConfig = {
|
|
seller: window.location.origin,
|
|
decisionLogicURL: createDecisionScriptURL(uuid),
|
|
interestGroupBuyers: [window.location.origin]
|
|
};
|
|
|
|
let auctionConfigOverrides = {
|
|
decisionLogicURL: createDecisionScriptURL(uuid),
|
|
interestGroupBuyers: [],
|
|
componentAuctions: [componentAuctionConfig]
|
|
};
|
|
|
|
await runBasicFledgeAuctionAndNavigate(test, uuid, auctionConfigOverrides);
|
|
|
|
// Waiting for the report URL of the winner should succeed, while waiting for
|
|
// the one of the loser should throw. Wait for both, see which succeeds, and
|
|
// set "winningAdURL" to the ad URL of the winner.
|
|
let winningAdURL = '';
|
|
try {
|
|
await waitForObservedRequests(uuid, [reportURLs[0]]);
|
|
winningAdURL = adURLs[0];
|
|
} catch (e) {
|
|
await waitForObservedRequests(uuid, [reportURLs[1]]);
|
|
winningAdURL = adURLs[1];
|
|
}
|
|
|
|
// Modify `auctionConfigOverrides` to only accept the ad from the interest
|
|
// group that didn't win the first auction.
|
|
auctionConfigOverrides.decisionLogicURL =
|
|
createDecisionScriptURL(
|
|
uuid,
|
|
{scoreAd: `if (browserSignals.renderURL === "${winningAdURL}")
|
|
return 0;`});
|
|
|
|
// Add an abort controller, so can cancel extra auctions.
|
|
let abortController = new AbortController();
|
|
auctionConfigOverrides.signal = abortController.signal;
|
|
|
|
// Run a bunch of auctions in parallel, until one has a winner.
|
|
let fencedFrameConfig = await Promise.any(
|
|
[ runAuctionsUntilWinner(test, uuid, auctionConfigOverrides),
|
|
runAuctionsUntilWinner(test, uuid, auctionConfigOverrides),
|
|
runAuctionsUntilWinner(test, uuid, auctionConfigOverrides),
|
|
runAuctionsUntilWinner(test, uuid, auctionConfigOverrides),
|
|
runAuctionsUntilWinner(test, uuid, auctionConfigOverrides),
|
|
runAuctionsUntilWinner(test, uuid, auctionConfigOverrides)
|
|
]
|
|
);
|
|
// Abort the other auctions.
|
|
abortController.abort('reason');
|
|
|
|
// Load the fencedFrameConfig in a fenced frame, and double-check that each
|
|
// interest group has won once.
|
|
createAndNavigateFencedFrame(test, fencedFrameConfig);
|
|
await waitForObservedRequests(uuid, [reportURLs[0], reportURLs[1]]);
|
|
}, 'runAdAuction tie.');
|