summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/fledge
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/fledge')
-rw-r--r--testing/web-platform/tests/fledge/tentative/TODO8
-rw-r--r--testing/web-platform/tests/fledge/tentative/auction-config-passed-to-worklets.https.window.js12
-rw-r--r--testing/web-platform/tests/fledge/tentative/auction-config.https.window.js115
-rw-r--r--testing/web-platform/tests/fledge/tentative/component-ads.https.window.js59
-rw-r--r--testing/web-platform/tests/fledge/tentative/component-auction.https.window.js124
-rw-r--r--testing/web-platform/tests/fledge/tentative/deprecated-render-url-replacements.https.window.js196
-rw-r--r--testing/web-platform/tests/fledge/tentative/interest-group-update.https.window.js406
-rw-r--r--testing/web-platform/tests/fledge/tentative/resources/fledge-util.sub.js43
-rw-r--r--testing/web-platform/tests/fledge/tentative/resources/trusted-bidding-signals.py20
-rw-r--r--testing/web-platform/tests/fledge/tentative/resources/update-url.py6
-rw-r--r--testing/web-platform/tests/fledge/tentative/score-ad-browser-signals.https.window.js57
-rw-r--r--testing/web-platform/tests/fledge/tentative/trusted-bidding-signals.https.window.js45
12 files changed, 1068 insertions, 23 deletions
diff --git a/testing/web-platform/tests/fledge/tentative/TODO b/testing/web-platform/tests/fledge/tentative/TODO
index 6fd378c035..8760e59d21 100644
--- a/testing/web-platform/tests/fledge/tentative/TODO
+++ b/testing/web-platform/tests/fledge/tentative/TODO
@@ -79,7 +79,13 @@ Need tests for (likely not a complete list):
origins, and between generateBid() and reportWin().
* Test Content-Type headers allowed in responess for script/wasm/JSON fetches.
* Test WASM support, updating createBiddingWasmHelperURL().
-
+* Remaining interest group updates.
+ * Check that an update with one valid field and one invalid one fails.
+ * Test that an update works if owner and/or name match those in the interest group.
+ * Test updating the update URL and bidding script URL so they are all the same origin (requires updating test fixture to handle multiple updates).
+ * Test when Ads is null.
+ * Test updating a cross origin interest group.
+ * Test fields that are updatable but do not make it to 'generateBid'.
If possible:
* Aggregate reporting.
* Join/leave permission delegation via .well-known files
diff --git a/testing/web-platform/tests/fledge/tentative/auction-config-passed-to-worklets.https.window.js b/testing/web-platform/tests/fledge/tentative/auction-config-passed-to-worklets.https.window.js
index 9b12d077ba..7780957739 100644
--- a/testing/web-platform/tests/fledge/tentative/auction-config-passed-to-worklets.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/auction-config-passed-to-worklets.https.window.js
@@ -81,6 +81,18 @@ makeTest({
});
makeTest({
+ name: 'AuctionConfig.deprecatedRenderURLReplacements with brackets.',
+ fieldName: 'deprecatedRenderURLReplacements',
+ fieldValue: {'${EXAMPLE_MACRO}': 'SSP'},
+});
+
+makeTest({
+ name: 'AuctionConfig.deprecatedRenderURLReplacements with percents.',
+ fieldName: 'deprecatedRenderURLReplacements',
+ fieldValue: {'%%EXAMPLE_MACRO%%': 'SSP'},
+});
+
+makeTest({
name: 'AuctionConfig.seller is URL.',
fieldName: 'seller',
fieldValue: OTHER_ORIGIN1,
diff --git a/testing/web-platform/tests/fledge/tentative/auction-config.https.window.js b/testing/web-platform/tests/fledge/tentative/auction-config.https.window.js
index 5fa4fa252f..057b4d7f78 100644
--- a/testing/web-platform/tests/fledge/tentative/auction-config.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/auction-config.https.window.js
@@ -12,7 +12,10 @@
// META: variant=?31-35
// META: variant=?36-40
// META: variant=?40-45
-// META: variant=?46-last
+// META: variant=?46-50
+// META: variant=?51-55
+// META: variant=?56-60
+// META: variant=?61-last
"use strict;"
@@ -110,6 +113,62 @@ const EXPECT_PROMISE_ERROR = auctionResult => {
}
makeTest({
+ name: 'deprecatedRenderURLReplacements without end bracket is invalid.',
+ expect: EXPECT_PROMISE_ERROR,
+ expectPromiseError: EXPECT_EXCEPTION(TypeError),
+ auctionConfigOverrides: {deprecatedRenderURLReplacements: {'${No_End_Bracket': 'SSP'}}
+});
+
+makeTest({
+ name: 'deprecatedRenderURLReplacements without percents and brackets.',
+ expect: EXPECT_PROMISE_ERROR,
+ expectPromiseError: EXPECT_EXCEPTION(TypeError),
+ auctionConfigOverrides: {deprecatedRenderURLReplacements: {'No_Wrapper': 'SSP'}}
+});
+
+makeTest({
+ name: 'deprecatedRenderURLReplacements without dollar sign.',
+ expect: EXPECT_PROMISE_ERROR,
+ expectPromiseError: EXPECT_EXCEPTION(TypeError),
+ auctionConfigOverrides: {deprecatedRenderURLReplacements: {'{No_Dollar_Sign}': 'SSP'}}
+});
+
+makeTest({
+ name: 'deprecatedRenderURLReplacements without start bracket is invalid.',
+ expect: EXPECT_PROMISE_ERROR,
+ expectPromiseError: EXPECT_EXCEPTION(TypeError),
+ auctionConfigOverrides: {deprecatedRenderURLReplacements: {'$No_Start_Bracket}': 'SSP'}}
+});
+
+makeTest({
+ name: 'deprecatedRenderURLReplacements mix and match is invalid.',
+ expect: EXPECT_PROMISE_ERROR,
+ expectPromiseError: EXPECT_EXCEPTION(TypeError),
+ auctionConfigOverrides: {deprecatedRenderURLReplacements: {'${Bracket_And_Percent%%': 'SSP'}}
+});
+
+makeTest({
+ name: 'deprecatedRenderURLReplacements missing start percent is invalid.',
+ expect: EXPECT_PROMISE_ERROR,
+ expectPromiseError: EXPECT_EXCEPTION(TypeError),
+ auctionConfigOverrides: {deprecatedRenderURLReplacements: {'%Missing_Start_Percents%%': 'SSP'}}
+});
+
+makeTest({
+ name: 'deprecatedRenderURLReplacements single percents is invalid.',
+ expect: EXPECT_PROMISE_ERROR,
+ expectPromiseError: EXPECT_EXCEPTION(TypeError),
+ auctionConfigOverrides: {deprecatedRenderURLReplacements: {'%Single_Percents%': 'SSP'}}
+});
+
+makeTest({
+ name: 'deprecatedRenderURLReplacements without end percents is invalid.',
+ expect: EXPECT_PROMISE_ERROR,
+ expectPromiseError: EXPECT_EXCEPTION(TypeError),
+ auctionConfigOverrides: {deprecatedRenderURLReplacements: {'%%No_End_Percents': 'SSP'}}
+});
+
+makeTest({
name: 'no buyers => no winners',
expect: EXPECT_NO_WINNER,
auctionConfigOverrides: {interestGroupBuyers: []},
@@ -445,6 +504,60 @@ makeTest({
{width: '200furlongs', height: '200'}]}
});
+makeTest({
+ name: 'sellerRealTimeReportingConfig has default local reporting type',
+ expect: EXPECT_WINNER,
+ auctionConfigOverrides: {sellerRealTimeReportingConfig:
+ {type: 'default-local-reporting'}}
+});
+
+makeTest({
+ name: 'sellerRealTimeReportingConfig has no type',
+ expect: EXPECT_EXCEPTION(TypeError),
+ auctionConfigOverrides: {sellerRealTimeReportingConfig:
+ {notType: 'default-local-reporting'}}
+});
+
+makeTest({
+ name: 'sellerRealTimeReportingConfig has unknown type',
+ expect: EXPECT_WINNER,
+ auctionConfigOverrides: {sellerRealTimeReportingConfig: {type: 'unknown type'}}
+});
+
+makeTest({
+ name: 'perBuyerRealTimeReportingConfig',
+ expect: EXPECT_WINNER,
+ auctionConfigOverrides: {perBuyerRealTimeReportingConfig:
+ {"https://example.com": {type: 'default-local-reporting'}}}
+});
+
+makeTest({
+ name: 'perBuyerRealTimeReportingConfig has no entry',
+ expect: EXPECT_WINNER,
+ auctionConfigOverrides: {perBuyerRealTimeReportingConfig: {}}
+});
+
+makeTest({
+ name: 'perBuyerRealTimeReportingConfig has invalid buyer',
+ expect: EXPECT_EXCEPTION(TypeError),
+ auctionConfigOverrides: {perBuyerRealTimeReportingConfig:
+ {"http://example.com": {type: 'default-local-reporting'}}}
+});
+
+makeTest({
+ name: 'perBuyerRealTimeReportingConfig has no type',
+ expect: EXPECT_EXCEPTION(TypeError),
+ auctionConfigOverrides: {perBuyerRealTimeReportingConfig:
+ {"https://example.com": {notType: 'default-local-reporting'}}}
+});
+
+makeTest({
+ name: 'perBuyerRealTimeReportingConfig has unknown type',
+ expect: EXPECT_WINNER,
+ auctionConfigOverrides: {perBuyerRealTimeReportingConfig:
+ {"https://example.com": {type: 'unknown type'}}}
+});
+
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
diff --git a/testing/web-platform/tests/fledge/tentative/component-ads.https.window.js b/testing/web-platform/tests/fledge/tentative/component-ads.https.window.js
index 6b22585d57..8493025429 100644
--- a/testing/web-platform/tests/fledge/tentative/component-ads.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/component-ads.https.window.js
@@ -44,13 +44,25 @@ function createComponentAdRenderURL(uuid, id) {
//
// If "adMetadata" is true, metadata is added to each component ad. Only integer metadata
// is used, relying on renderURL tests to cover other types of renderURL metadata.
+//
+// If "deprecatedRenderURLReplacements" is passed, the matches and replacements will be
+// used in the trackingURLs and the object will be passed into the auctionConfig, to
+// replace matching macros within the renderURLs.
async function runComponentAdLoadingTest(test, uuid, numComponentAdsInInterestGroup,
- componentAdsInBid, componentAdsToLoad,
- adMetadata = false) {
+ componentAdsInBid, componentAdsToLoad,
+ adMetadata = false, deprecatedRenderURLReplacements = null) {
let interestGroupAdComponents = [];
+ // These are used within the URLs for deprecatedRenderURLReplacement tests.
+ const renderURLReplacementsStrings = createStringBeforeAndAfterReplacements(deprecatedRenderURLReplacements);
+ const beforeReplacementsString= renderURLReplacementsStrings.beforeReplacements;
+ const afterReplacementsString = renderURLReplacementsStrings.afterReplacements;
+
for (let i = 0; i < numComponentAdsInInterestGroup; ++i) {
- const componentRenderURL = createComponentAdRenderURL(uuid, i);
- let adComponent = {renderURL: componentRenderURL};
+ let componentRenderURL = createComponentAdRenderURL(uuid, i);
+ if (deprecatedRenderURLReplacements !== null) {
+ componentRenderURL = createTrackerURL(window.location.origin, uuid, 'track_get', beforeReplacementsString);
+ }
+ let adComponent = { renderURL: componentRenderURL };
if (adMetadata)
adComponent.metadata = i;
interestGroupAdComponents.push(adComponent);
@@ -74,7 +86,7 @@ async function runComponentAdLoadingTest(test, uuid, numComponentAdsInInterestGr
eventData: status,
destination: ["buyer"]});`);
- let bid = {bid:1, render: renderURL};
+ let bid = {bid:1, render:renderURL};
if (componentAdsInBid) {
bid.adComponents = [];
for (let index of componentAdsInBid) {
@@ -89,8 +101,13 @@ async function runComponentAdLoadingTest(test, uuid, numComponentAdsInInterestGr
// to "expectedTrackerURLs".
if (componentAdsToLoad && bid.adComponents) {
for (let index of componentAdsToLoad) {
+ let expectedURL = createComponentAdTrackerURL(uuid, componentAdsInBid[index]);
+ if (deprecatedRenderURLReplacements != null) {
+ expectedURL = createTrackerURL(window.location.origin, uuid, 'track_get',
+ afterReplacementsString);
+ }
if (index < componentAdsInBid.length)
- expectedTrackerURLs.push(createComponentAdTrackerURL(uuid, componentAdsInBid[index]));
+ expectedTrackerURLs.push(expectedURL);
}
}
@@ -127,11 +144,13 @@ async function runComponentAdLoadingTest(test, uuid, numComponentAdsInInterestGr
test, uuid,
{decisionLogicURL: createDecisionScriptURL(
uuid,
- { scoreAd:
+ { scoreAd:
`if (JSON.stringify(browserSignals.adComponents) !==
'${JSON.stringify(bid.adComponents)}') {
throw "Unexpected adComponents: " + JSON.stringify(browserSignals.adComponents);
- }`})});
+ }`}),
+ deprecatedRenderURLReplacements: deprecatedRenderURLReplacements
+ });
}
await waitForObservedRequests(uuid, expectedTrackerURLs);
@@ -447,3 +466,27 @@ subsetTest(promise_test, async test => {
}, 'Reports not sent from component ad.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ await runComponentAdLoadingTest(test, uuid, /*numComponentAdsInInterestGroup=*/1,
+ /*componentAdsInBid=*/[0], /*componentAdsToLoad=*/[0], false, { '%%EXAMPLE-MACRO%%': 'SSP' });
+}, 'component ad with render url replacements with percents.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ await runComponentAdLoadingTest(test, uuid, /*numComponentAdsInInterestGroup=*/1,
+ /*componentAdsInBid=*/[0], /*componentAdsToLoad=*/[0], false, { '${EXAMPLE-MACRO}': 'SSP' });
+}, 'component ad with render url replacements with brackets.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ await runComponentAdLoadingTest(test, uuid, /*numComponentAdsInInterestGroup=*/1,
+ /*componentAdsInBid=*/[0], /*componentAdsToLoad=*/[0], false, { '${EXAMPLE-MACRO-1}': 'SSP-1', '%%EXAMPLE-MACRO-2%%': 'SSP-2' });
+}, 'component ad with render url replacements with multiple replacements.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ await runComponentAdLoadingTest(test, uuid, /*numComponentAdsInInterestGroup=*/3,
+ /*componentAdsInBid=*/[0,1,2], /*componentAdsToLoad=*/[0,1,2], false, { '${EXAMPLE-MACRO-1}': 'SSP-1', '%%EXAMPLE-MACRO-2%%': 'SSP-2' });
+}, 'component ad with render url replacements with multiple replacements, and multiple component ads.');
diff --git a/testing/web-platform/tests/fledge/tentative/component-auction.https.window.js b/testing/web-platform/tests/fledge/tentative/component-auction.https.window.js
index 015c20a5c2..bf804e6857 100644
--- a/testing/web-platform/tests/fledge/tentative/component-auction.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/component-auction.https.window.js
@@ -11,18 +11,21 @@
"use strict";
// Creates an AuctionConfig with a single component auction.
-function createComponentAuctionConfig(uuid) {
+function createComponentAuctionConfig(uuid, auctionConfigOverrides = {},
+ deprecatedRenderURLReplacements = {}) {
let componentAuctionConfig = {
seller: window.location.origin,
decisionLogicURL: createDecisionScriptURL(uuid),
- interestGroupBuyers: [window.location.origin]
+ interestGroupBuyers: [window.location.origin],
+ deprecatedRenderURLReplacements: deprecatedRenderURLReplacements
};
return {
seller: window.location.origin,
decisionLogicURL: createDecisionScriptURL(uuid),
interestGroupBuyers: [],
- componentAuctions: [componentAuctionConfig]
+ componentAuctions: [componentAuctionConfig],
+ ...auctionConfigOverrides
};
}
@@ -717,3 +720,118 @@ subsetTest(promise_test, async test => {
uuid,
[bidderReportURL1, seller1ReportURL, bidderReportURL2, seller2ReportURL]);
}, `Component auction prevWinsMs and numBids updating in one component seller's auction, read in another's.`);
+
+
+const makeDeprecatedRenderURLReplacementTest = ({
+ name,
+ deprecatedRenderURLReplacements,
+}) => {
+ subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ let bidderReportURL = createBidderReportURL(uuid);
+ let componentSellerReportURL = createSellerReportURL(uuid, /*id=*/"component");
+ let topLevelSellerReportURL = createSellerReportURL(uuid, /*id=*/"top");
+
+ // These are used within the URLs for deprecatedRenderURLReplacement tests.
+ const renderURLReplacementsStrings = createStringBeforeAndAfterReplacements(deprecatedRenderURLReplacements);
+ const beforeReplacementsString = renderURLReplacementsStrings.beforeReplacements;
+ const afterReplacementsString = renderURLReplacementsStrings.afterReplacements;
+ const renderURLBeforeReplacements = createTrackerURL(window.location.origin, uuid, 'track_get', beforeReplacementsString);
+ const renderURLAfterReplacements = createTrackerURL(window.location.origin, uuid, 'track_get', afterReplacementsString);
+
+ await joinInterestGroup(
+ test, uuid,
+ {
+ ads: [{ renderURL: renderURLBeforeReplacements }],
+ biddingLogicURL: createBiddingScriptURL(
+ {
+ allowComponentAuction: true,
+ bid: 5,
+ reportWin:
+ `if (browserSignals.bid !== 5)
+ throw "Unexpected bid: " + browserSignals.bid;
+ sendReportTo("${bidderReportURL}");`
+ })
+ });
+
+ let auctionConfig = createComponentAuctionConfig(uuid, {}, deprecatedRenderURLReplacements);
+
+ auctionConfig.componentAuctions[0].decisionLogicURL =
+ createDecisionScriptURL(
+ uuid,
+ {
+ scoreAd:
+ `if (bid !== 5)
+ throw "Unexpected component bid: " + bid`,
+ reportResult:
+ `if (browserSignals.bid !== 5)
+ throw "Unexpected component bid: " + browserSignals.bid;
+ if (browserSignals.modifiedBid !== undefined)
+ throw "Unexpected component modifiedBid: " + browserSignals.modifiedBid;
+ sendReportTo("${componentSellerReportURL}");`
+ });
+
+ auctionConfig.decisionLogicURL =
+ createDecisionScriptURL(
+ uuid,
+ {
+ scoreAd:
+ `if (bid !== 5)
+ throw "Unexpected top-level bid: " + bid`,
+ reportResult:
+ `if (browserSignals.bid !== 5)
+ throw "Unexpected top-level bid: " + browserSignals.bid;
+ if (browserSignals.modifiedBid !== undefined)
+ throw "Unexpected top-level modifiedBid: " + browserSignals.modifiedBid;
+ sendReportTo("${topLevelSellerReportURL}");`
+ });
+
+ await runBasicFledgeAuctionAndNavigate(test, uuid, auctionConfig);
+ await waitForObservedRequests(
+ uuid,
+ [bidderReportURL, componentSellerReportURL, topLevelSellerReportURL, renderURLAfterReplacements]);
+ }, name);
+};
+
+makeDeprecatedRenderURLReplacementTest({
+ name: 'Replacements with brackets.',
+ deprecatedRenderURLReplacements: { '${EXAMPLE-MACRO}': 'SSP' }
+});
+
+makeDeprecatedRenderURLReplacementTest({
+ name: 'Replacements with percents.',
+ deprecatedRenderURLReplacements: { '%%EXAMPLE-MACRO%%': 'SSP' }
+});
+
+makeDeprecatedRenderURLReplacementTest({
+ name: 'Replacements with multiple replacements.',
+ deprecatedRenderURLReplacements: { '${EXAMPLE-MACRO1}': 'SSP1', '%%EXAMPLE-MACRO2%%': 'SSP2' }
+});
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ let deprecatedRenderURLReplacements = { '${EXAMPLE-MACRO1}': 'SSP1', '%%EXAMPLE-MACRO2%%': 'SSP2' };
+ const renderURLReplacementsStrings = createStringBeforeAndAfterReplacements(deprecatedRenderURLReplacements);
+ let beforeReplacementsString = renderURLReplacementsStrings.beforeReplacements;
+
+ await joinInterestGroup(
+ test, uuid,
+ {
+ ads: [{ renderURL: createTrackerURL(window.location.origin, uuid, 'track_get', beforeReplacementsString) }],
+ biddingLogicURL: createBiddingScriptURL({allowComponentAuction: true})
+ });
+ let auctionConfigOverride = {deprecatedRenderURLReplacements: deprecatedRenderURLReplacements }
+ let auctionConfig = createComponentAuctionConfig(uuid,/*auctionConfigOverride=*/auctionConfigOverride,
+ /*deprecatedRenderURLReplacements=*/deprecatedRenderURLReplacements);
+
+ auctionConfig.componentAuctions[0].decisionLogicURL = createDecisionScriptURL(uuid);
+
+ try {
+ await runBasicFledgeAuction(test, uuid, auctionConfig);
+ } catch (exception) {
+ assert_true(exception instanceof TypeError, "did not get expected error: " + exception);
+ return;
+ }
+ throw 'Exception unexpectedly not thrown.'
+}, "deprecatedRenderURLReplacements cause error if passed in top level auction and component auction.");
diff --git a/testing/web-platform/tests/fledge/tentative/deprecated-render-url-replacements.https.window.js b/testing/web-platform/tests/fledge/tentative/deprecated-render-url-replacements.https.window.js
new file mode 100644
index 0000000000..4f8bc1cc7f
--- /dev/null
+++ b/testing/web-platform/tests/fledge/tentative/deprecated-render-url-replacements.https.window.js
@@ -0,0 +1,196 @@
+// META: script=/resources/testdriver.js
+// META: script=/common/utils.js
+// META: script=resources/fledge-util.sub.js
+// META: script=/common/subset-tests.js
+// META: timeout=long
+// META: variant=?1-5
+// META: variant=?6-10
+// META: variant=?11-15
+// META: variant=?16-last
+
+
+"use strict;"
+
+// This test ensures proper handling of deprecatedRenderURLReplacements within auctionConfigOverrides.
+// It validates that these replacements are correctly applied to the winning bid's renderURL by
+// injecting a URL with matching macros into an interest group and ensuring that a new url with
+// the replacements in it, is tracked and observed.
+const makeTest = ({
+ // Test name
+ name,
+ // Overrides to the interest group.
+ interestGroupOverrides = {},
+ // Overrides to the auction config.
+ auctionConfigOverrides = {},
+ // This is what goes into the renderURL and is expected to be replaced.
+ beforeReplacements,
+ // This is what's expected when 'beforeReplacements' is replaced.
+ afterReplacements,
+}) => {
+ subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ let urlBeforeReplacements = createTrackerURL(window.location.origin, uuid, 'track_get', beforeReplacements);
+ let urlAfterReplacements = createTrackerURL(window.location.origin, uuid, 'track_get', afterReplacements);
+ interestGroupOverrides.ads = [{ renderURL: urlBeforeReplacements }];
+ await joinInterestGroup(test, uuid, interestGroupOverrides);
+
+ await runBasicFledgeAuctionAndNavigate(test, uuid, auctionConfigOverrides);
+ await waitForObservedRequests(
+ uuid,
+ [urlAfterReplacements, createSellerReportURL(uuid), createBidderReportURL(uuid)]);
+ }, name);
+};
+
+makeTest({
+ name: 'Replacements with brackets.',
+ auctionConfigOverrides: {
+ deprecatedRenderURLReplacements: { '${EXAMPLE-MACRO}': 'SSP' }
+ },
+ beforeReplacements: "${EXAMPLE-MACRO}",
+ afterReplacements: 'SSP',
+
+});
+
+makeTest({
+ name: 'Replacements with percents.',
+ auctionConfigOverrides: {
+ deprecatedRenderURLReplacements: { '%%EXAMPLE-MACRO%%': 'SSP' }
+ },
+ beforeReplacements: "%%EXAMPLE-MACRO%%",
+ afterReplacements: 'SSP',
+});
+
+makeTest({
+ name: 'Multiple replacements within a URL.',
+ auctionConfigOverrides: {
+ deprecatedRenderURLReplacements: { '${EXAMPLE-MACRO1}': 'SSP1', '%%EXAMPLE-MACRO2%%': 'SSP2' }
+ },
+ beforeReplacements: "${EXAMPLE-MACRO1}/%%EXAMPLE-MACRO2%%",
+ afterReplacements: 'SSP1/SSP2',
+});
+
+makeTest({
+ name: 'Recursive and reduce size with brackets.',
+ auctionConfigOverrides: {
+ deprecatedRenderURLReplacements: { '${1}': '1' }
+ },
+ beforeReplacements: "${${${1}}}",
+ afterReplacements: "${${1}}"
+});
+
+makeTest({
+ name: 'Recursive and increase size with brackets.',
+ auctionConfigOverrides: {
+ deprecatedRenderURLReplacements: { '${1}': '${${1}}' }
+ },
+ beforeReplacements: "${1}",
+ afterReplacements: "${${1}}"
+});
+
+makeTest({
+ name: 'Replacements use a single pass with brackets.',
+ auctionConfigOverrides: {
+ deprecatedRenderURLReplacements: { '${1}': '${2}', '${2}': '${1}' }
+ },
+ beforeReplacements: "${1}${2}",
+ afterReplacements: "${2}${1}"
+});
+
+makeTest({
+ name: 'Multiple instances of same substitution string with brackets.',
+ auctionConfigOverrides: {
+ deprecatedRenderURLReplacements: { '${1}': '${2}' }
+ },
+ beforeReplacements: "{${1}${1}}",
+ afterReplacements: "{${2}${2}}"
+});
+
+makeTest({
+ name: 'Mismatched replacement with brackets.',
+ auctionConfigOverrides: {
+ deprecatedRenderURLReplacements: { '${2}': '${1}' }
+ },
+ beforeReplacements: "${1}",
+ afterReplacements: "${1}"
+});
+
+makeTest({
+ name: 'Recursive and reduce size with percents.',
+ auctionConfigOverrides: {
+ deprecatedRenderURLReplacements: { '%%1%%': '1' }
+ },
+ beforeReplacements: "%%%%1%%%%",
+ afterReplacements: "%%1%%"
+});
+
+makeTest({
+ name: 'Recursive and increase size with percents.',
+ auctionConfigOverrides: {
+ deprecatedRenderURLReplacements: { '%%1%%': '%%%%1%%%%' }
+ },
+ beforeReplacements: "%%1%%",
+ afterReplacements: "%%%%1%%%%"
+});
+
+makeTest({
+ name: 'Replacements use a single pass with percents.',
+ auctionConfigOverrides: {
+ deprecatedRenderURLReplacements: { '%%1%%': '%%2%%', '%%2%%': '%%1%%' }
+ },
+ beforeReplacements: "%%1%%%%2%%",
+ afterReplacements: "%%2%%%%1%%"
+});
+
+makeTest({
+ name: 'Multiple instances of same substitution string with percents.',
+ auctionConfigOverrides: {
+ deprecatedRenderURLReplacements: { '%%1%%': '%%2%%' }
+ },
+ beforeReplacements: "%%1%%%%1%%",
+ afterReplacements: "%%2%%%%2%%"
+});
+
+makeTest({
+ name: 'Mismatched replacement with percents.',
+ auctionConfigOverrides: {
+ deprecatedRenderURLReplacements: { '%%2%%': '%%1%%' }
+ },
+ beforeReplacements: "%%1%%",
+ afterReplacements: "%%1%%"
+});
+
+makeTest({
+ name: 'Case sensativity.',
+ auctionConfigOverrides: {
+ deprecatedRenderURLReplacements: { '%%foo%%': '%%bar%%' }
+ },
+ beforeReplacements: "%%FOO%%%%foo%%",
+ afterReplacements: "%%FOO%%%%bar%%"
+});
+
+makeTest({
+ name: 'Super macro, a macro with a macro inside it basically, with percents.',
+ auctionConfigOverrides: {
+ deprecatedRenderURLReplacements: { '%%%%foo%%%%': 'foo' }
+ },
+ beforeReplacements: "%%%%foo%%%%",
+ afterReplacements: "foo"
+});
+
+makeTest({
+ name: 'Super macro, with brackets.',
+ auctionConfigOverrides: {
+ deprecatedRenderURLReplacements: { '${${foo}}': 'foo' }
+ },
+ beforeReplacements: "${${foo}}",
+ afterReplacements: "foo"
+});
+
+makeTest({
+ name: 'Super macro, with both.',
+ auctionConfigOverrides: {
+ deprecatedRenderURLReplacements: { '${%%foo%%}': 'foo', '%%${bar}%%':'bar' }
+ },
+ beforeReplacements: "${%%foo%%}%%${bar}%%",
+ afterReplacements: "foobar"
+});
diff --git a/testing/web-platform/tests/fledge/tentative/interest-group-update.https.window.js b/testing/web-platform/tests/fledge/tentative/interest-group-update.https.window.js
new file mode 100644
index 0000000000..59b3736b09
--- /dev/null
+++ b/testing/web-platform/tests/fledge/tentative/interest-group-update.https.window.js
@@ -0,0 +1,406 @@
+// META: script=/resources/testdriver.js
+// META: script=/common/utils.js
+// META: script=resources/fledge-util.sub.js
+// META: script=/common/subset-tests.js
+// META: timeout=long
+// META: variant=?1-4
+// META: variant=?5-9
+// META: variant=?10-14
+// META: variant=?15-19
+// META: variant=?20-last
+
+"use strict;"
+
+// This test repeatedly runs auctions to verify an update. A modified bidding script
+// continuously throws errors until it detects the expected change in the interest group
+// field. This update then stops the auction cycle.
+const makeTestForUpdate = ({
+ // Test name
+ name,
+ // fieldname that is getting updated
+ interestGroupFieldName,
+ // This is used to check if update has happened.
+ expectedValue,
+ // This is used to create the update response, by default it will always send
+ // back the `expectedValue`. Extra steps to make a deep copy.
+ responseOverride = expectedValue,
+ // Overrides to the interest group.
+ interestGroupOverrides = {},
+ // Overrides to the auction config.
+ auctionConfigOverrides = {},
+}) => {
+ subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ extraBiddingLogic = ``;
+
+ let replacePlaceholders = (ads) => ads.forEach(element => {
+ element.renderURL = element.renderURL.replace(`UUID-PLACEHOLDER`, uuid);
+ });
+
+ // Testing 'ads' requires some additional setup due to it's reliance
+ // on createRenderURL, specifically the bidding script used checks to make
+ // sure the `uuid` is the correct one for the test. We use a renderURL
+ // with a placeholder 'UUID-PLACEHOLDER' and make sure to replace it
+ // before moving on to the test.
+ if (interestGroupFieldName === `ads`) {
+ if (interestGroupFieldName in interestGroupOverrides) {
+ replacePlaceholders(interestGroupOverrides[interestGroupFieldName]);
+ }
+ replacePlaceholders(responseOverride);
+ replacePlaceholders(expectedValue);
+ }
+ // When checking the render URL, both the deprecated 'renderUrl' and the updated 'renderURL' might exist
+ // in the interest group simultaneously, so this test deletes the 'renderUrl' to ensure a
+ // clean comparison with deepEquals.
+ if (interestGroupFieldName === `ads` || interestGroupFieldName === `adComponents`) {
+ extraBiddingLogic = `
+ interestGroup.${interestGroupFieldName}.forEach(element => {
+ delete element.renderUrl;
+ });`
+ }
+
+ let expectedValueJSON = JSON.stringify(expectedValue);
+ // When the update has not yet been seen, throw an error which will cause the
+ // auction not to have a result.
+ interestGroupOverrides.biddingLogicURL = createBiddingScriptURL({
+ generateBid: `
+ ${extraBiddingLogic}
+ if (!deepEquals(interestGroup.${interestGroupFieldName}, ${expectedValueJSON})) {
+ throw '${interestGroupFieldName} is ' +
+ JSON.stringify(interestGroup.${interestGroupFieldName}) +
+ ' instead of ' + '${expectedValueJSON}';
+ }`
+ });
+
+ let responseBody = {};
+ responseBody[interestGroupFieldName] = responseOverride;
+ let updateParams = {
+ body: JSON.stringify(responseBody),
+ uuid: uuid
+ };
+ interestGroupOverrides.updateURL = createUpdateURL(updateParams);
+ await joinInterestGroup(test, uuid, interestGroupOverrides);
+
+ // Run an auction until there's a winner, which means update occurred.
+ let auctionResult = await runBasicFledgeAuction(test, uuid, auctionConfigOverrides);
+ expectNoWinner(auctionResult);
+ while (!auctionResult) {
+ auctionResult = await runBasicFledgeAuction(test, uuid, auctionConfigOverrides);
+ }
+ }, name);
+};
+
+// In order to test the update process does not update certain fields, this test uses two interest groups:
+
+// * `failedUpdateGroup`: Receives an invalid update, and will continue to throw errors until the update
+// occurs (which shouldn't happen). This group will have a high bid to ensure if
+// there was ever a tie, it would win.
+// * `successUpdateGroup`: A hard-coded interest group that receives a update and will signal the change
+// by throwing an error.
+
+// By tracking render URLs, this test guarantees that only the URL associated with the correct update
+// (`goodUpdateRenderURL`) is used, and the incorrect URL (`badUpdateRenderURL`) isn't. The test runs
+// auctions repeatedly until the update in `successUpdateGroup` stops an auction from producing a winner.
+// It then will run one final auction. If there's still no winner, it can infer that `failedUpdateGroup`
+// would have received the update if it were propagating correctly.
+
+// If there was a bug in the implementation, a possible case can occur and manifest as a flaky test.
+// In this scenerio with the current structure of the Protected Audience API, the `successUpdateGroup`
+// updates, and so does the `failedUpdateGroup`, but the `failedUpdateGroup` update happens significantly
+// after the `successUpdateGroup`'s update. In an effort to combat this, after the while loop we run
+// another auction to ensure there is no winner (both cases should throw), but depending how slow the
+// update takes, this flaky issue still can **possibly** occur.
+const makeTestForNoUpdate = ({
+ // Test name
+ name,
+ // fieldname that is should not be getting updated
+ interestGroupFieldName,
+ // this is used to create the update response and check if it did not happen.
+ responseOverride,
+ // Overrides to the auction config.
+ auctionConfigOverrides = {},
+ // Overrides to the interest group.
+ failedUpdateGroup = {},
+}) => {
+ subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ // successUpdateGroup
+
+ // These are used in `successUpdateGroup` in order to get a proper update.
+ let successUpdateGroup = {};
+ let successUpdateField = `userBiddingSignals`;
+ let successUpdateFieldExpectedValue = { 'test': 20 };
+
+ const goodUpdateRenderURL = createTrackerURL(window.location.origin, uuid, 'track_get', 'good_update');
+ successUpdateGroup.ads = [{ 'renderURL': goodUpdateRenderURL }];
+ successUpdateGroup.biddingLogicURL = createBiddingScriptURL({
+ generateBid: `
+ if (deepEquals(interestGroup.${successUpdateField}, ${JSON.stringify(successUpdateFieldExpectedValue)})){
+ throw '${successUpdateField} has updated and is ' +
+ '${JSON.stringify(successUpdateFieldExpectedValue)}.'
+ }`,
+ bid: 5
+ });
+
+ let successResponseBody = {};
+ successResponseBody[successUpdateField] = successUpdateFieldExpectedValue;
+ let successUpdateParams = {
+ body: JSON.stringify(successResponseBody),
+ uuid: uuid
+ };
+ successUpdateGroup.updateURL = createUpdateURL(successUpdateParams);
+ await joinInterestGroup(test, uuid, successUpdateGroup);
+ ///////////////////////// successUpdateGroup
+
+ // failedUpdateGroup
+ const badUpdateRenderURL = createTrackerURL(window.location.origin, uuid, `track_get`, `bad_update`);
+ // Name needed so we don't have two IGs with same name.
+ failedUpdateGroup.name = failedUpdateGroup.name ? failedUpdateGroup.name : `IG name`
+ failedUpdateGroup.ads = [{ 'renderURL': badUpdateRenderURL }];
+ failedUpdateGroup.biddingLogicURL = createBiddingScriptURL({
+ generateBid: `
+ if (!deepEquals(interestGroup.${interestGroupFieldName}, ${JSON.stringify(responseOverride)})){
+ throw '${interestGroupFieldName} is as expected: '+
+ JSON.stringify(interestGroup.${interestGroupFieldName});
+ }`,
+ bid: 1000
+ });
+ let failedResponseBody = {};
+ failedResponseBody[interestGroupFieldName] = responseOverride;
+
+ let failedUpdateParams = {
+ body: JSON.stringify(failedResponseBody),
+ uuid: uuid
+ };
+
+ failedUpdateGroup.updateURL = createUpdateURL(failedUpdateParams);
+ await joinInterestGroup(test, uuid, failedUpdateGroup);
+ ///////////////////////// failedUpdateGroup
+
+ // First result should be not be null, `successUpdateGroup` throws when update is detected so until then,
+ // run and observe the requests to ensure only `goodUpdateRenderURL` is fetched.
+ let auctionResult = await runBasicFledgeTestExpectingWinner(test, uuid, auctionConfigOverrides);
+ while (auctionResult) {
+ createAndNavigateFencedFrame(test, auctionResult);
+ await waitForObservedRequests(
+ uuid,
+ [goodUpdateRenderURL, createSellerReportURL(uuid)]);
+ await fetch(createCleanupURL(uuid));
+ auctionResult = await runBasicFledgeAuction(test, uuid, auctionConfigOverrides);
+ }
+ // Re-run to ensure null because:
+ // `successUpdateGroup` should be throwing since update occurred.
+ // `failedUpdateGroup` should be throwing since update did not occur.
+ await runBasicFledgeTestExpectingNoWinner(test, uuid, auctionConfigOverrides);
+ }, name);
+};
+
+// Helper to eliminate rewriting a long call to createRenderURL().
+// Only thing to change would be signalParams to differentiate between URLs.
+const createTempRenderURL = (signalsParams = null) => {
+ return createRenderURL(/*uuid=*/`UUID-PLACEHOLDER`,/*script=*/ null,/*signalParams=*/ signalsParams,/*origin=*/ null);
+};
+
+makeTestForUpdate({
+ name: 'userBiddingSignals update overwrites everything in the field.',
+ interestGroupFieldName: 'userBiddingSignals',
+ expectedValue: { 'test': 20 },
+ interestGroupOverrides: {
+ userBiddingSignals: { 'test': 10, 'extra_value': true },
+ }
+});
+
+makeTestForUpdate({
+ name: 'userBiddingSignals updated multi-type',
+ interestGroupFieldName: 'userBiddingSignals',
+ expectedValue: { 'test': 20, 5: [1, [false, false, true], 3, 'Hello'] },
+ interestGroupOverrides: {
+ userBiddingSignals: { 'test': 10 },
+ }
+});
+
+makeTestForUpdate({
+ name: 'userBiddingSignals updated to non object',
+ interestGroupFieldName: 'userBiddingSignals',
+ expectedValue: 5,
+ interestGroupOverrides: {
+ userBiddingSignals: { 'test': 10 },
+ }
+});
+
+makeTestForUpdate({
+ name: 'userBiddingSignals updated to null',
+ interestGroupFieldName: 'userBiddingSignals',
+ expectedValue: null,
+ interestGroupOverrides: {
+ userBiddingSignals: { 'test': 10 },
+ }
+});
+
+makeTestForUpdate({
+ name: 'trustedBiddingSignalsKeys updated correctly',
+ interestGroupFieldName: 'trustedBiddingSignalsKeys',
+ expectedValue: ['new_key', 'old_key'],
+ interestGroupOverrides: {
+ trustedBiddingSignalsKeys: ['old_key'],
+ }
+});
+
+makeTestForUpdate({
+ name: 'trustedBiddingSignalsKeys updated to empty array.',
+ interestGroupFieldName: 'trustedBiddingSignalsKeys',
+ expectedValue: [],
+ interestGroupOverrides: {
+ trustedBiddingSignalsKeys: ['old_key'],
+ }
+});
+
+
+makeTestForUpdate({
+ name: 'trustedBiddingSignalsSlotSizeMode updated to slot-size',
+ interestGroupFieldName: 'trustedBiddingSignalsSlotSizeMode',
+ expectedValue: 'slot-size',
+ interestGroupOverrides: {
+ trustedBiddingSignalsKeys: ['key'],
+ trustedBiddingSignalsSlotSizeMode: 'none',
+ }
+});
+
+makeTestForUpdate({
+ name: 'trustedBiddingSignalsSlotSizeMode updated to all-slots-requested-sizes',
+ interestGroupFieldName: 'trustedBiddingSignalsSlotSizeMode',
+ expectedValue: 'all-slots-requested-sizes',
+ interestGroupOverrides: {
+ trustedBiddingSignalsKeys: ['key'],
+ trustedBiddingSignalsSlotSizeMode: 'slot-size',
+ }
+});
+
+makeTestForUpdate({
+ name: 'trustedBiddingSignalsSlotSizeMode updated to none',
+ interestGroupFieldName: 'trustedBiddingSignalsSlotSizeMode',
+ expectedValue: 'none',
+ interestGroupOverrides: {
+ trustedBiddingSignalsKeys: ['key'],
+ trustedBiddingSignalsSlotSizeMode: 'slot-size',
+ }
+});
+
+makeTestForUpdate({
+ name: 'trustedBiddingSignalsSlotSizeMode updated to unknown, defaults to none',
+ interestGroupFieldName: 'trustedBiddingSignalsSlotSizeMode',
+ expectedValue: 'none',
+ responseOverride: 'unknown-type',
+ interestGroupOverrides: {
+ trustedBiddingSignalsKeys: ['key'],
+ trustedBiddingSignalsSlotSizeMode: 'slot-size',
+ }
+});
+
+makeTestForUpdate({
+ name: 'ads updated from 2 ads to 1.',
+ interestGroupFieldName: 'ads',
+ expectedValue: [
+ { renderURL: createTempRenderURL('new_url1'), metadata: 'test1-new' },
+ ],
+ interestGroupOverrides: {
+ ads: [{ renderURL: createTempRenderURL() },
+ { renderURL: createTempRenderURL() }]
+ }
+});
+
+makeTestForUpdate({
+ name: 'ads updated from 1 ad to 2.',
+ interestGroupFieldName: 'ads',
+ expectedValue: [{ renderURL: createTempRenderURL('new_url1'), metadata: 'test1-new' },
+ { renderURL: createTempRenderURL('new_url2'), metadata: 'test2-new' }],
+ interestGroupOverrides: {
+ ads: [{ renderURL: createTempRenderURL() }]
+ }
+});
+
+makeTestForUpdate({
+ name: 'adComponents updated from 1 adComponent to 2.',
+ interestGroupFieldName: 'adComponents',
+ expectedValue: [{ renderURL: createTempRenderURL('new_url1'), metadata: 'test1-new' },
+ { renderURL: createTempRenderURL('new_url2'), metadata: 'test2' }],
+ interestGroupOverrides: {
+ adComponents: [{ renderURL: createTempRenderURL(), metadata: 'test1' }]
+ },
+});
+
+makeTestForUpdate({
+ name: 'adComponents updated from 2 adComponents to 1.',
+ interestGroupFieldName: 'adComponents',
+ expectedValue: [{ renderURL: createTempRenderURL('new_url1'), metadata: 'test1-new' }],
+ interestGroupOverrides: {
+ adComponents: [{ renderURL: createTempRenderURL() },
+ { renderURL: createTempRenderURL() }]
+ },
+});
+
+makeTestForUpdate({
+ name: 'executionMode updated to frozen context',
+ interestGroupFieldName: 'executionMode',
+ expectedValue: 'frozen-context',
+ interestGroupOverrides: {
+ executionMode: 'compatibility',
+ }
+});
+
+makeTestForUpdate({
+ name: 'executionMode updated to compatibility',
+ interestGroupFieldName: 'executionMode',
+ expectedValue: 'compatibility',
+ interestGroupOverrides: {
+ executionMode: 'frozen-context',
+ }
+});
+
+makeTestForUpdate({
+ name: 'executionMode updated to group by origin',
+ interestGroupFieldName: 'executionMode',
+ expectedValue: 'group-by-origin',
+ interestGroupOverrides: {
+ executionMode: 'compatibility',
+ }
+});
+
+makeTestForNoUpdate({
+ name: 'executionMode updated with invalid input',
+ interestGroupFieldName: 'executionMode',
+ responseOverride: 'unknown-type',
+});
+
+makeTestForNoUpdate({
+ name: 'owner cannot be updated.',
+ interestGroupFieldName: 'owner',
+ responseOverride: OTHER_ORIGIN1,
+ auctionConfigOverrides: {
+ interestGroupBuyers: [OTHER_ORIGIN1, window.location.origin]
+ }
+});
+
+makeTestForNoUpdate({
+ name: 'name cannot be updated.',
+ interestGroupFieldName: 'name',
+ responseOverride: 'new_name',
+ failedUpdateGroup: { name: 'name2' },
+});
+
+makeTestForNoUpdate({
+ name: 'executionMode not updated when unknown type.',
+ interestGroupFieldName: 'executionMode',
+ responseOverride: 'unknown-type',
+ failedUpdateGroup: { executionMode: 'compatibility' },
+});
+
+makeTestForNoUpdate({
+ name: 'trustedBiddingSignalsKeys not updated when bad value.',
+ interestGroupFieldName: 'trustedBiddingSignalsKeys',
+ responseOverride: 5,
+ failedUpdateGroup: {
+ trustedBiddingSignalsKeys: ['key'],
+ },
+});
+
diff --git a/testing/web-platform/tests/fledge/tentative/resources/fledge-util.sub.js b/testing/web-platform/tests/fledge/tentative/resources/fledge-util.sub.js
index 7be02e34ff..a7d0f63830 100644
--- a/testing/web-platform/tests/fledge/tentative/resources/fledge-util.sub.js
+++ b/testing/web-platform/tests/fledge/tentative/resources/fledge-util.sub.js
@@ -77,6 +77,15 @@ function createDirectFromSellerSignalsURL(origin = window.location.origin) {
return url.toString();
}
+function createUpdateURL(params = {}) {
+ let origin = window.location.origin;
+ let url = new URL(`${origin}${RESOURCE_PATH}update-url.py`);
+ url.searchParams.append('body', params.body);
+ url.searchParams.append('uuid', params.uuid);
+
+ return url.toString();
+}
+
// Generates a UUID and registers a cleanup method with the test fixture to
// request a URL from the request tracking script that clears all data
// associated with the generated uuid when requested.
@@ -142,19 +151,21 @@ async function waitForObservedRequests(uuid, expectedRequests, filter) {
trackedRequests = trackedRequests.filter(filter);
}
- // If expected number of requests have been observed, compare with list of
- // all expected requests and exit.
- if (trackedRequests.length >= expectedRequests.length) {
- assert_array_equals(trackedRequests, expectedRequests);
- break;
- }
-
// If fewer than total number of expected requests have been observed,
// compare what's been received so far, to have a greater chance to fail
// rather than hang on error.
for (const trackedRequest of trackedRequests) {
assert_in_array(trackedRequest, expectedRequests);
}
+
+ // If expected number of requests have been observed, compare with list of
+ // all expected requests and exit. This check was previously before the for loop,
+ // but was swapped in order to avoid flakiness with failing tests and their
+ // respective *-expected.txt.
+ if (trackedRequests.length >= expectedRequests.length) {
+ assert_array_equals(trackedRequests, expectedRequests);
+ break;
+ }
}
}
@@ -830,3 +841,21 @@ let additionalBidHelper = function() {
fetchAdditionalBids: fetchAdditionalBids
};
}();
+
+
+// DeprecatedRenderURLReplacements helper function.
+// Returns an object containing sample strings both before and after the
+// replacements in 'replacements' have been applied by
+// deprecatedRenderURLReplacements. All substitution strings will appear
+// only once in the output strings.
+function createStringBeforeAndAfterReplacements(deprecatedRenderURLReplacements) {
+ let beforeReplacements = '';
+ let afterReplacements = '';
+ if(deprecatedRenderURLReplacements){
+ for (const [match, replacement] of Object.entries(deprecatedRenderURLReplacements)) {
+ beforeReplacements += match + "/";
+ afterReplacements += replacement + "/";
+ }
+ }
+ return { beforeReplacements, afterReplacements };
+}
diff --git a/testing/web-platform/tests/fledge/tentative/resources/trusted-bidding-signals.py b/testing/web-platform/tests/fledge/tentative/resources/trusted-bidding-signals.py
index f9ca9031f1..955a7c0bdf 100644
--- a/testing/web-platform/tests/fledge/tentative/resources/trusted-bidding-signals.py
+++ b/testing/web-platform/tests/fledge/tentative/resources/trusted-bidding-signals.py
@@ -1,3 +1,4 @@
+import collections
import json
from urllib.parse import unquote_plus
@@ -47,7 +48,8 @@ def main(request, response):
response.status = (200, b"OK")
# The JSON representation of this is used as the response body. This does
- # not currently include a "perInterestGroupData" object.
+ # not currently include a "perInterestGroupData" object except for
+ # updateIfOlderThanMs.
responseBody = {"keys": {}}
# Set when certain special keys are observed, used in place of the JSON
@@ -117,6 +119,22 @@ def main(request, response):
if "data-version" in interestGroupNames:
dataVersion = "4"
+ per_interest_group_data = collections.defaultdict(dict)
+ for name in interestGroupNames:
+ if name == "use-update-if-older-than-ms":
+ # One hour in milliseconds.
+ per_interest_group_data[name]["updateIfOlderThanMs"] = 3_600_000
+ elif name == "use-update-if-older-than-ms-small":
+ # A value less than the minimum of 10 minutes.
+ per_interest_group_data[name]["updateIfOlderThanMs"] = 1
+ elif name == "use-update-if-older-than-ms-zero":
+ per_interest_group_data[name]["updateIfOlderThanMs"] = 0
+ elif name == "use-update-if-older-than-ms-negative":
+ per_interest_group_data[name]["updateIfOlderThanMs"] = -1
+
+ if per_interest_group_data:
+ responseBody["perInterestGroupData"] = dict(per_interest_group_data)
+
if contentType:
response.headers.set("Content-Type", contentType)
if adAuctionAllowed:
diff --git a/testing/web-platform/tests/fledge/tentative/resources/update-url.py b/testing/web-platform/tests/fledge/tentative/resources/update-url.py
new file mode 100644
index 0000000000..7de89e0f8f
--- /dev/null
+++ b/testing/web-platform/tests/fledge/tentative/resources/update-url.py
@@ -0,0 +1,6 @@
+def main(request, response):
+ response.status = (200, b"OK")
+ response.headers.set(b"Ad-Auction-Allowed", b"true")
+ response.headers.set(b"Content-Type", b"application/json")
+ body = request.GET.first(b"body", None)
+ return body \ No newline at end of file
diff --git a/testing/web-platform/tests/fledge/tentative/score-ad-browser-signals.https.window.js b/testing/web-platform/tests/fledge/tentative/score-ad-browser-signals.https.window.js
new file mode 100644
index 0000000000..f20412cfc7
--- /dev/null
+++ b/testing/web-platform/tests/fledge/tentative/score-ad-browser-signals.https.window.js
@@ -0,0 +1,57 @@
+// META: script=/resources/testdriver.js
+// META: script=/common/utils.js
+// META: script=resources/fledge-util.sub.js
+// META: script=/common/subset-tests.js
+// META: timeout=long
+
+"use strict;"
+
+// These tests focus on the browserSignals argument passed to scoreAd().
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+
+ let biddingLogicURL = createBiddingScriptURL(
+ {
+ generateBid:
+ `
+ return {
+ bid: 1,
+ render: { url: interestGroup.ads[0].renderURL,
+ width: '100sw',
+ height: '50px' }
+ };
+ `
+ });
+
+ let decisionLogicURL = createDecisionScriptURL(uuid,
+ {
+ scoreAd:
+ `
+ if (!browserSignals.hasOwnProperty('renderSize')) {
+ throw 'Missing renderSize member in browserSignals.';
+ }
+ if (browserSignals.renderSize.width !== '100sw' ||
+ browserSignals.renderSize.height !== '50px') {
+ throw 'Incorrect renderSize width or height.';
+ }
+ `
+ }
+ );
+
+ await joinGroupAndRunBasicFledgeTestExpectingWinner(
+ test,
+ {
+ uuid: uuid,
+ interestGroupOverrides: {
+ name: uuid,
+ biddingLogicURL: biddingLogicURL,
+ ads: [{ renderURL: createRenderURL(uuid), sizeGroup: 'group1' }],
+ adSizes: { 'size1': { width: '100sw', height: '50px' } },
+ sizeGroups: { 'group1': ['size1'] }
+ },
+ auctionConfigOverrides: {
+ decisionLogicURL: decisionLogicURL
+ }
+ });
+}, 'ScoreAd browserSignals renderSize test.');
diff --git a/testing/web-platform/tests/fledge/tentative/trusted-bidding-signals.https.window.js b/testing/web-platform/tests/fledge/tentative/trusted-bidding-signals.https.window.js
index d0b9a82086..905abf8381 100644
--- a/testing/web-platform/tests/fledge/tentative/trusted-bidding-signals.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/trusted-bidding-signals.https.window.js
@@ -17,7 +17,8 @@
// META: variant=?56-60
// META: variant=?61-65
// META: variant=?66-70
-// META: variant=?71-last
+// META: variant=?71-75
+// META: variant=?76-last
"use strict";
@@ -938,4 +939,44 @@ subsetTest(promise_test, async test => {
]
);
runBasicFledgeTestExpectingWinner(test, uuid);
-}, 'Trusted bidding signals splits the request if the combined URL length exceeds the limit of small value.'); \ No newline at end of file
+}, 'Trusted bidding signals splits the request if the combined URL length exceeds the limit of small value.');
+
+/////////////////////////////////////////////////////////////////////////////
+// updateIfOlderThanMs tests
+//
+// NOTE: Due to the lack of mock time in wpt, these test just exercise the code
+// paths and ensure that no crash occurs -- they don't otherwise verify
+// behavior.
+/////////////////////////////////////////////////////////////////////////////
+
+subsetTest(promise_test, async test => {
+ await runTrustedBiddingSignalsTest(
+ test,
+ 'true',
+ { name: 'use-update-if-older-than-ms',
+ trustedBiddingSignalsURL: TRUSTED_BIDDING_SIGNALS_URL });
+}, 'Trusted bidding signals response has updateIfOlderThanMs > 10 min.');
+
+subsetTest(promise_test, async test => {
+ await runTrustedBiddingSignalsTest(
+ test,
+ 'true',
+ { name: 'use-update-if-older-than-ms-small',
+ trustedBiddingSignalsURL: TRUSTED_BIDDING_SIGNALS_URL });
+}, 'Trusted bidding signals response has updateIfOlderThanMs == 1 ms.');
+
+subsetTest(promise_test, async test => {
+ await runTrustedBiddingSignalsTest(
+ test,
+ 'true',
+ { name: 'use-update-if-older-than-ms-zero',
+ trustedBiddingSignalsURL: TRUSTED_BIDDING_SIGNALS_URL });
+}, 'Trusted bidding signals response has updateIfOlderThanMs == 0 ms.');
+
+subsetTest(promise_test, async test => {
+ await runTrustedBiddingSignalsTest(
+ test,
+ 'true',
+ { name: 'use-update-if-older-than-ms-negative',
+ trustedBiddingSignalsURL: TRUSTED_BIDDING_SIGNALS_URL });
+}, 'Trusted bidding signals response has updateIfOlderThanMs == -1 ms.');