363 lines
14 KiB
JavaScript
363 lines
14 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: script=third_party/cbor-js/cbor.js
|
|
// META: script=/common/subset-tests.js
|
|
// META: timeout=long
|
|
// META: variant=?1-5
|
|
// META: variant=?6-last
|
|
|
|
'use strict';
|
|
|
|
// The tests in this file focus on real time reporting.
|
|
|
|
const MAIN_PATH = '/.well-known/interest-group/real-time-report'
|
|
|
|
// Creates an AuctionConfig with component auctions.
|
|
function createMultiSellerAuctionConfig(
|
|
uuid, seller, decisionLogicURL, componentAuctions,
|
|
auctionConfigOverrides = {}) {
|
|
return {
|
|
seller: seller,
|
|
decisionLogicURL: decisionLogicURL,
|
|
interestGroupBuyers: [],
|
|
componentAuctions: componentAuctions,
|
|
...auctionConfigOverrides
|
|
};
|
|
}
|
|
|
|
// Creates a bidding script located on "origin". The generateBid() method calls
|
|
// real time reporting API and returns a bid of "bid".
|
|
// The reportWin() method is empty.
|
|
function createBiddingScriptURLForRealTimeReporting(origin = null, bid = 1) {
|
|
return createBiddingScriptURL({
|
|
origin: origin ? origin : new URL(BASE_URL).origin,
|
|
bid: bid,
|
|
allowComponentAuction: true,
|
|
generateBid: `
|
|
realTimeReporting.contributeToHistogram({ bucket: 20, priorityWeight: 1});`
|
|
});
|
|
}
|
|
|
|
// Creates a decision script that calls real time reporting API.
|
|
// The reportResult() method is empty.
|
|
function createDecisionScriptURLForRealTimeReporting(uuid, origin = null) {
|
|
return createDecisionScriptURL(uuid, {
|
|
origin: origin === null ? new URL(BASE_URL).origin : origin,
|
|
scoreAd: `
|
|
realTimeReporting.contributeToHistogram({ bucket: 200, priorityWeight: 1});`
|
|
});
|
|
}
|
|
|
|
// Delay method that waits for prescribed number of milliseconds.
|
|
const delay = ms => new Promise(resolve => step_timeout(resolve, ms));
|
|
|
|
// Polls the given `origin` to retrieve reports sent there. Once the reports are
|
|
// received, returns the list of reports. Returns null if the timeout is reached
|
|
// before a report is available.
|
|
const pollReports = async (origin, wait_for = 1, timeout = 5000 /*ms*/) => {
|
|
let startTime = performance.now();
|
|
let payloads = [];
|
|
while (performance.now() - startTime < timeout) {
|
|
const resp = await fetch(new URL(MAIN_PATH, origin));
|
|
const payload = await resp.arrayBuffer();
|
|
if (payload.byteLength > 0) {
|
|
payloads = payloads.concat(payload);
|
|
}
|
|
if (payloads.length >= wait_for) {
|
|
return payloads;
|
|
}
|
|
await delay(/*ms=*/ 100);
|
|
}
|
|
if (payloads.length > 0) {
|
|
return payloads;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
// Verifies that `reports` has 1 report in cbor, which has the expected three
|
|
// fields.
|
|
// `version` should be 1.
|
|
// `histogram` and `platformHistogram` should be objects that pass
|
|
// verifyHistogram().
|
|
const verifyReports = (reports) => {
|
|
assert_equals(reports.length, 1);
|
|
const report = CBOR.decode(reports[0]);
|
|
assert_own_property(report, 'version');
|
|
assert_equals(report.version, 1);
|
|
assert_own_property(report, 'histogram');
|
|
verifyHistogram(report.histogram, 128, 1024);
|
|
assert_own_property(report, 'platformHistogram');
|
|
verifyHistogram(report.platformHistogram, 1, 4);
|
|
assert_equals(Object.keys(report).length, 3);
|
|
};
|
|
|
|
// Verifies that a `histogram` has two fields: "buckets" and "length", where
|
|
// "buckets" field is a Uint8Array of `bucketSize`, and "length" field equals to
|
|
// `length`.
|
|
const verifyHistogram = (histogram, bucketSize, length) => {
|
|
assert_own_property(histogram, 'buckets');
|
|
assert_own_property(histogram, 'length');
|
|
assert_equals(Object.keys(histogram).length, 2);
|
|
assert_true(histogram.buckets instanceof Uint8Array);
|
|
assert_equals(histogram.buckets.length, bucketSize);
|
|
assert_equals(histogram.length, length);
|
|
};
|
|
|
|
// Method to clear the stash. Takes the URL as parameter.
|
|
const resetReports = url => {
|
|
// The view of the stash is path-specific
|
|
// (https://web-platform-tests.org/tools/wptserve/docs/stash.html), therefore
|
|
// the origin doesn't need to be specified.
|
|
url = `${url}?clear_stash=true`;
|
|
const options = {
|
|
method: 'POST',
|
|
};
|
|
return fetch(url, options);
|
|
};
|
|
|
|
subsetTest(promise_test, async test => {
|
|
const uuid = generateUuid(test);
|
|
await resetReports(MAIN_PATH);
|
|
await joinCrossOriginInterestGroup(test, uuid, OTHER_ORIGIN1, {
|
|
biddingLogicURL: createBiddingScriptURLForRealTimeReporting(OTHER_ORIGIN1)
|
|
});
|
|
await runBasicFledgeAuctionAndNavigate(test, uuid, {
|
|
decisionLogicURL: createDecisionScriptURLForRealTimeReporting(uuid),
|
|
interestGroupBuyers: [OTHER_ORIGIN1],
|
|
sellerRealTimeReportingConfig: {type: 'default-local-reporting'},
|
|
perBuyerRealTimeReportingConfig:
|
|
{[OTHER_ORIGIN1]: {type: 'default-local-reporting'}}
|
|
});
|
|
const sellerReports = await pollReports(location.origin);
|
|
verifyReports(sellerReports);
|
|
|
|
const buyerReports = await pollReports(OTHER_ORIGIN1);
|
|
verifyReports(buyerReports);
|
|
}, 'Real time reporting different buyer and seller both opted-in and called api.');
|
|
|
|
subsetTest(promise_test, async test => {
|
|
const uuid = generateUuid(test);
|
|
await resetReports(MAIN_PATH);
|
|
await joinCrossOriginInterestGroup(test, uuid, OTHER_ORIGIN1, {
|
|
biddingLogicURL: createBiddingScriptURLForRealTimeReporting(OTHER_ORIGIN1)
|
|
});
|
|
await runBasicFledgeAuctionAndNavigate(test, uuid, {
|
|
decisionLogicURL: createDecisionScriptURLForRealTimeReporting(uuid),
|
|
interestGroupBuyers: [OTHER_ORIGIN1],
|
|
perBuyerRealTimeReportingConfig:
|
|
{[OTHER_ORIGIN1]: {type: 'default-local-reporting'}}
|
|
});
|
|
|
|
const buyerReports = await pollReports(OTHER_ORIGIN1);
|
|
verifyReports(buyerReports);
|
|
|
|
// Seller called the RTR API, but didn't opt-in.
|
|
const sellerReports =
|
|
await pollReports(location.origin, /*wait_for=*/ 1, /*timeout=*/ 1000);
|
|
assert_equals(sellerReports, null);
|
|
}, 'Real time reporting buyer opted-in but not seller.');
|
|
|
|
subsetTest(promise_test, async test => {
|
|
const uuid = generateUuid(test);
|
|
await resetReports(MAIN_PATH);
|
|
await joinCrossOriginInterestGroup(test, uuid, OTHER_ORIGIN1, {
|
|
biddingLogicURL: createBiddingScriptURLForRealTimeReporting(OTHER_ORIGIN1)
|
|
});
|
|
await runBasicFledgeAuctionAndNavigate(test, uuid, {
|
|
decisionLogicURL: createDecisionScriptURLForRealTimeReporting(uuid),
|
|
interestGroupBuyers: [OTHER_ORIGIN1],
|
|
sellerRealTimeReportingConfig: {type: 'default-local-reporting'}
|
|
});
|
|
|
|
const sellerReports = await pollReports(location.origin);
|
|
verifyReports(sellerReports);
|
|
|
|
// Buyer called the RTR API, but didn't opt-in.
|
|
const buyerReports =
|
|
await pollReports(OTHER_ORIGIN1, /*wait_for=*/ 1, /*timeout=*/ 1000);
|
|
assert_equals(buyerReports, null);
|
|
}, 'Real time reporting seller opted-in but not buyer.');
|
|
|
|
subsetTest(promise_test, async test => {
|
|
const uuid = generateUuid(test);
|
|
await resetReports(MAIN_PATH);
|
|
await joinCrossOriginInterestGroup(
|
|
test, uuid, OTHER_ORIGIN1,
|
|
{biddingLogicURL: createBiddingScriptURL({origin: OTHER_ORIGIN1})});
|
|
await runBasicFledgeAuctionAndNavigate(test, uuid, {
|
|
decisionLogicURL: createDecisionScriptURL(uuid),
|
|
interestGroupBuyers: [OTHER_ORIGIN1],
|
|
sellerRealTimeReportingConfig: {type: 'default-local-reporting'},
|
|
perBuyerRealTimeReportingConfig:
|
|
{[OTHER_ORIGIN1]: {type: 'default-local-reporting'}}
|
|
});
|
|
const sellerReports = await pollReports(location.origin);
|
|
verifyReports(sellerReports);
|
|
|
|
const buyerReports = await pollReports(OTHER_ORIGIN1);
|
|
verifyReports(buyerReports);
|
|
}, 'Real time reporting different buyer and seller both opted-in but did not call api.');
|
|
|
|
subsetTest(promise_test, async test => {
|
|
const uuid = generateUuid(test);
|
|
await resetReports(MAIN_PATH);
|
|
await joinCrossOriginInterestGroup(test, uuid, OTHER_ORIGIN1, {
|
|
biddingLogicURL: createBiddingScriptURLForRealTimeReporting(OTHER_ORIGIN1)
|
|
});
|
|
await runBasicFledgeAuctionAndNavigate(test, uuid, {
|
|
decisionLogicURL: createDecisionScriptURLForRealTimeReporting(uuid),
|
|
interestGroupBuyers: [OTHER_ORIGIN1]
|
|
});
|
|
const sellerReports = await pollReports(location.origin);
|
|
assert_equals(sellerReports, null);
|
|
const buyerReports =
|
|
await pollReports(OTHER_ORIGIN1, /*wait_for=*/ 1, /*timeout=*/ 1000);
|
|
assert_equals(buyerReports, null);
|
|
}, 'Real time reporting both called api but did not opt-in.');
|
|
|
|
subsetTest(promise_test, async test => {
|
|
const uuid = generateUuid(test);
|
|
await resetReports(MAIN_PATH);
|
|
await joinInterestGroup(
|
|
test, uuid,
|
|
{biddingLogicURL: createBiddingScriptURLForRealTimeReporting()});
|
|
|
|
const origin = location.origin;
|
|
await runBasicFledgeAuctionAndNavigate(test, uuid, {
|
|
decisionLogicURL: createDecisionScriptURLForRealTimeReporting(uuid),
|
|
sellerRealTimeReportingConfig: {type: 'default-local-reporting'},
|
|
perBuyerRealTimeReportingConfig:
|
|
{[origin]: {type: 'default-local-reporting'}}
|
|
});
|
|
const reports = await pollReports(origin);
|
|
verifyReports(reports);
|
|
}, 'Real time reporting buyer and seller same origin.');
|
|
|
|
subsetTest(promise_test, async test => {
|
|
const uuid = generateUuid(test);
|
|
await resetReports(MAIN_PATH);
|
|
await joinCrossOriginInterestGroup(test, uuid, OTHER_ORIGIN1, {
|
|
biddingLogicURL: createBiddingScriptURLForRealTimeReporting(OTHER_ORIGIN1)
|
|
});
|
|
await joinCrossOriginInterestGroup(test, uuid, OTHER_ORIGIN2, {
|
|
biddingLogicURL:
|
|
createBiddingScriptURLForRealTimeReporting(OTHER_ORIGIN2, /*bid=*/ 100)
|
|
});
|
|
await runBasicFledgeAuctionAndNavigate(test, uuid, {
|
|
decisionLogicURL: createDecisionScriptURLForRealTimeReporting(uuid),
|
|
interestGroupBuyers: [OTHER_ORIGIN1, OTHER_ORIGIN2],
|
|
perBuyerRealTimeReportingConfig: {
|
|
[OTHER_ORIGIN1]: {type: 'default-local-reporting'},
|
|
[OTHER_ORIGIN2]: {type: 'default-local-reporting'}
|
|
}
|
|
});
|
|
const reports1 = await pollReports(OTHER_ORIGIN1);
|
|
verifyReports(reports1);
|
|
|
|
const reports2 = await pollReports(OTHER_ORIGIN2);
|
|
verifyReports(reports2);
|
|
}, 'Real time reporting both winning and losing buyers opted-in.');
|
|
|
|
subsetTest(promise_test, async test => {
|
|
const uuid = generateUuid(test);
|
|
await resetReports(MAIN_PATH);
|
|
await joinCrossOriginInterestGroup(test, uuid, OTHER_ORIGIN1, {
|
|
biddingLogicURL: createBiddingScriptURLForRealTimeReporting(OTHER_ORIGIN1)
|
|
});
|
|
await joinCrossOriginInterestGroup(test, uuid, OTHER_ORIGIN2, {
|
|
biddingLogicURL:
|
|
createBiddingScriptURLForRealTimeReporting(OTHER_ORIGIN2, /*bid=*/ 100)
|
|
});
|
|
await runBasicFledgeAuctionAndNavigate(test, uuid, {
|
|
decisionLogicURL: createDecisionScriptURLForRealTimeReporting(uuid),
|
|
interestGroupBuyers: [OTHER_ORIGIN1, OTHER_ORIGIN2],
|
|
perBuyerRealTimeReportingConfig:
|
|
{[OTHER_ORIGIN1]: {type: 'default-local-reporting'}}
|
|
});
|
|
const reports1 = await pollReports(OTHER_ORIGIN1);
|
|
verifyReports(reports1);
|
|
|
|
const reports2 =
|
|
await pollReports(OTHER_ORIGIN2, /*wait_for=*/ 1, /*timeout=*/ 1000);
|
|
assert_equals(reports2, null);
|
|
}, 'Real time reporting one buyer opted-in but not the other.');
|
|
|
|
subsetTest(promise_test, async test => {
|
|
const uuid = generateUuid(test);
|
|
await resetReports(MAIN_PATH);
|
|
await joinCrossOriginInterestGroup(test, uuid, OTHER_ORIGIN1, {
|
|
biddingLogicURL: createBiddingScriptURLForRealTimeReporting(OTHER_ORIGIN1)
|
|
});
|
|
await runBasicFledgeTestExpectingNoWinner(test, uuid, {
|
|
decisionLogicURL: createDecisionScriptURL(uuid, {
|
|
scoreAd: `
|
|
realTimeReporting.contributeToHistogram({ bucket: 200, priorityWeight: 1});
|
|
return -1;`
|
|
}),
|
|
interestGroupBuyers: [OTHER_ORIGIN1],
|
|
sellerRealTimeReportingConfig: {type: 'default-local-reporting'},
|
|
perBuyerRealTimeReportingConfig:
|
|
{[OTHER_ORIGIN1]: {type: 'default-local-reporting'}}
|
|
});
|
|
const sellerReports = await pollReports(location.origin);
|
|
verifyReports(sellerReports);
|
|
|
|
const buyerReports = await pollReports(OTHER_ORIGIN1);
|
|
verifyReports(buyerReports);
|
|
}, 'Real time reports are sent when all bids are rejected.');
|
|
|
|
// TODO(qingxinwu): script fetches failing cases.
|
|
|
|
subsetTest(promise_test, async test => {
|
|
const uuid = generateUuid(test);
|
|
|
|
let buyer = window.location.origin;
|
|
let componentSellerOptIn = OTHER_ORIGIN1;
|
|
let componentSellerNotOptIn = OTHER_ORIGIN2;
|
|
let topLevelSeller = OTHER_ORIGIN3;
|
|
await resetReports(MAIN_PATH);
|
|
await joinCrossOriginInterestGroup(
|
|
test, uuid, buyer,
|
|
{biddingLogicURL: createBiddingScriptURLForRealTimeReporting(buyer)});
|
|
|
|
const componentAuctions = [
|
|
{
|
|
seller: componentSellerOptIn,
|
|
interestGroupBuyers: [buyer],
|
|
decisionLogicURL: createDecisionScriptURLForRealTimeReporting(
|
|
uuid, componentSellerOptIn),
|
|
sellerRealTimeReportingConfig: {type: 'default-local-reporting'},
|
|
perBuyerRealTimeReportingConfig:
|
|
{[buyer]: {type: 'default-local-reporting'}}
|
|
},
|
|
{
|
|
seller: componentSellerNotOptIn,
|
|
interestGroupBuyers: [buyer],
|
|
decisionLogicURL: createDecisionScriptURLForRealTimeReporting(
|
|
uuid, componentSellerNotOptIn),
|
|
}
|
|
];
|
|
let auctionConfig = createMultiSellerAuctionConfig(
|
|
uuid, topLevelSeller,
|
|
createDecisionScriptURLForRealTimeReporting(uuid, topLevelSeller),
|
|
componentAuctions, {});
|
|
auctionConfig.sellerRealTimeReportingConfig = {
|
|
type: 'default-local-reporting'
|
|
};
|
|
|
|
await runBasicFledgeAuctionAndNavigate(test, uuid, auctionConfig);
|
|
const reportsBuyer = await pollReports(buyer);
|
|
verifyReports(reportsBuyer);
|
|
|
|
const reportsComponentSellerOptIn = await pollReports(componentSellerOptIn);
|
|
verifyReports(reportsComponentSellerOptIn);
|
|
|
|
const reportsTopLevelSeller = await pollReports(topLevelSeller);
|
|
verifyReports(reportsTopLevelSeller);
|
|
|
|
const reportsComponentSellerNotOptIn = await pollReports(
|
|
componentSellerOptIn, /*wait_for=*/ 1, /*timeout=*/ 1000);
|
|
assert_equals(reportsComponentSellerNotOptIn, null);
|
|
}, 'Real time reporting in a multi seller auction.');
|