// META: script=/resources/testdriver.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) {
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.');