[FLEDGE]: Add trustedScoringSignalsUrl WPT tests.
This doesn't test the prioritization vector or componentRenderUrl
fields, which we'll add tests for when we test those specific features.
Bug: 1425952
Change-Id: Ida3ce4d22dfe52a72cabfaeda90e77845142ef79
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4762089
Reviewed-by: Maks Orlovich <morlovich@chromium.org>
Commit-Queue: Matt Menke <mmenke@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1181551}
diff --git a/fledge/tentative/TODO b/fledge/tentative/TODO
index 96654d2..fc2c296 100644
--- a/fledge/tentative/TODO
+++ b/fledge/tentative/TODO
@@ -18,8 +18,7 @@
loading URNs in fencedframes in other frames, loading component
ad URNs in fenced frames of other frames, etc)
* adAuctionConfig passed to reportResult().
-* trusted scoring signals.
-* Component ads.
+* Component ads (including scoring signals fetches).
* Component auctions.
* browserSignals fields in scoring/bidding methods.
* In reporting methods, browserSignals fields: dataVersion, topLevelSeller,
diff --git a/fledge/tentative/resources/fledge-util.js b/fledge/tentative/resources/fledge-util.js
index 7fef79c..f941d22 100644
--- a/fledge/tentative/resources/fledge-util.js
+++ b/fledge/tentative/resources/fledge-util.js
@@ -6,12 +6,15 @@
const DEFAULT_INTEREST_GROUP_NAME = 'default name';
-// Unlike other URLs, the trustedBiddingSignalsUrl can't have a query string
-// that's set by tests, since FLEDGE controls it entirely, so tests that
-// exercise it use a fixed URL string. Special keys and interest group names
-// control the response.
+// Unlike other URLs, trusted signals URLs can't have query strings
+// that are set by tests, since FLEDGE controls it entirely, so tests that
+// exercise them use a fixed URL string. Note that FLEDGE adds query
+// params when requesting these URLs, and the python scripts use these
+// to construct the response.
const TRUSTED_BIDDING_SIGNALS_URL =
`${BASE_URL}resources/trusted-bidding-signals.py`;
+const TRUSTED_SCORING_SIGNALS_URL =
+ `${BASE_URL}resources/trusted-scoring-signals.py`;
// Creates a URL that will be sent to the URL request tracker script.
// `uuid` is used to identify the stash shard to use.
@@ -159,11 +162,14 @@
// Creates a renderUrl for an ad that runs the passed in "script". "uuid" has
// no effect, beyond making the URL distinct between tests, and being verified
// by the decision logic script before accepting a bid. "uuid" is expected to
-// be last.
-function createRenderUrl(uuid, script) {
+// be last. "signalsParams" also has no effect, but is used by
+// trusted-scoring-signals.py to affect the response.
+function createRenderUrl(uuid, script, signalsParams) {
let url = new URL(`${BASE_URL}resources/fenced-frame.sub.py`);
if (script)
url.searchParams.append('script', script);
+ if (signalsParams)
+ url.searchParams.append('signalsParams', signalsParams);
url.searchParams.append('uuid', uuid);
return url.toString();
}
@@ -245,10 +251,10 @@
}
// Joins an interest group and runs an auction, expecting a winner to be
-// returned. "testConfig" can optionally modify the interest group or
+// returned. "testConfig" can optionally modify the uuid, interest group or
// auctionConfig.
async function runBasicFledgeTestExpectingWinner(test, testConfig = {}) {
- const uuid = generateUuid(test);
+ const uuid = testConfig.uuid ? testConfig.uuid : generateUuid(test);
await joinInterestGroup(test, uuid, testConfig.interestGroupOverrides);
let config = await runBasicFledgeAuction(
test, uuid, testConfig.auctionConfigOverrides);
@@ -257,10 +263,10 @@
}
// Joins an interest group and runs an auction, expecting no winner to be
-// returned. "testConfig" can optionally modify the interest group or
+// returned. "testConfig" can optionally modify the uuid, interest group or
// auctionConfig.
async function runBasicFledgeTestExpectingNoWinner(test, testConfig = {}) {
- const uuid = generateUuid(test);
+ const uuid = testConfig.uuid ? testConfig.uuid : generateUuid(test);
await joinInterestGroup(test, uuid, testConfig.interestGroupOverrides);
let result = await runBasicFledgeAuction(
test, uuid, testConfig.auctionConfigOverrides);
diff --git a/fledge/tentative/resources/trusted-scoring-signals.py b/fledge/tentative/resources/trusted-scoring-signals.py
new file mode 100644
index 0000000..6036d2e
--- /dev/null
+++ b/fledge/tentative/resources/trusted-scoring-signals.py
@@ -0,0 +1,127 @@
+import json
+from urllib.parse import unquote_plus, urlparse
+
+# Script to generate trusted scoring signals. The responses depends on the
+# query strings in the ads Urls - some result in entire response failures,
+# others affect only their own value. Each renderUrl potentially has a
+# signalsParam, which is a comma-delimited list of instructions that can
+# each affect either the value associated with the renderUrl, or the
+# response as a whole.
+def main(request, response):
+ hostname = None
+ renderUrls = None
+ adComponentRenderUrls = None
+
+ # Manually parse query params. Can't use request.GET because it unescapes as well as splitting,
+ # and commas mean very different things from escaped commas.
+ for param in request.url_parts.query.split("&"):
+ pair = param.split("=", 1)
+ if len(pair) != 2:
+ return fail(response, "Bad query parameter: " + param)
+ # Browsers should escape query params consistently.
+ if "%20" in pair[1]:
+ return fail(response, "Query parameter should escape using '+': " + param)
+
+ # Hostname can't be empty. The empty string can be a key or interest group name, though.
+ if pair[0] == "hostname" and hostname == None and len(pair[1]) > 0:
+ hostname = pair[1]
+ continue
+ if pair[0] == "renderUrls" and renderUrls == None:
+ renderUrls = list(map(unquote_plus, pair[1].split(",")))
+ continue
+ if pair[0] == "adComponentRenderUrls" and adComponentRenderUrls == None:
+ adComponentRenderUrls = list(map(unquote_plus, pair[1].split(",")))
+ continue
+ return fail(response, "Unexpected query parameter: " + param)
+
+ # "hostname" and "renderUrls" are mandatory.
+ if not hostname:
+ return fail(response, "hostname missing")
+ if not renderUrls:
+ return fail(response, "renderUrls missing")
+
+ response.status = (200, b"OK")
+
+ # The JSON representation of this is used as the response body.
+ responseBody = {"renderUrls": {}}
+
+ # Set when certain special keys are observed, used in place of the JSON
+ # representation of `responseBody`, when set.
+ body = None
+
+ contentType = "application/json"
+ adAuctionAllowed = "true"
+ dataVersion = None
+ if renderUrls:
+ for renderUrl in renderUrls:
+ value = "default value"
+
+ signalsParams = None
+ for param in urlparse(renderUrl).query.split("&"):
+ pair = param.split("=", 1)
+ if len(pair) != 2:
+ continue
+ if pair[0] == "signalsParams":
+ if signalsParams != None:
+ return fail(response, "renderUrl has multiple signalsParams: " + renderUrl)
+ signalsParams = pair[1]
+ if signalsParams != None:
+ signalsParams = unquote_plus(signalsParams)
+ for signalsParam in signalsParams.split(","):
+ if signalsParam == "close-connection":
+ # Close connection without writing anything, to simulate a
+ # network error. The write call is needed to avoid writing the
+ # default headers.
+ response.writer.write("")
+ response.close_connection = True
+ return
+ elif signalsParam.startswith("replace-body:"):
+ # Replace entire response body. Continue to run through other
+ # renderUrls, to allow them to modify request headers.
+ body = signalsParam.split(':', 1)[1]
+ elif signalsParam.startswith("data-version:"):
+ dataVersion = signalsParam.split(':', 1)[1]
+ elif signalsParam == "http-error":
+ response.status = (404, b"Not found")
+ elif signalsParam == "no-content-type":
+ contentType = None
+ elif signalsParam == "wrong-content-type":
+ contentType = 'text/plain'
+ elif signalsParam == "bad-ad-auction-allowed":
+ adAuctionAllowed = "sometimes"
+ elif signalsParam == "ad-auction-not-allowed":
+ adAuctionAllowed = "false"
+ elif signalsParam == "no-ad-auction-allow":
+ adAuctionAllowed = None
+ elif signalsParam == "no-value":
+ continue
+ elif signalsParam == "null-value":
+ value = None
+ elif signalsParam == "num-value":
+ value = 1
+ elif signalsParam == "string-value":
+ value = "1"
+ elif signalsParam == "array-value":
+ value = [1, "foo", None]
+ elif signalsParam == "object-value":
+ value = {"a":"b", "c":["d"]}
+ elif signalsParam == "hostname":
+ value = request.GET.first(b"hostname", b"not-found").decode("ASCII")
+ if value != None:
+ responseBody["renderUrls"][renderUrl] = value
+
+ if contentType:
+ response.headers.set("Content-Type", contentType)
+ if adAuctionAllowed:
+ response.headers.set("Ad-Auction-Allowed", adAuctionAllowed)
+ if dataVersion:
+ response.headers.set("Data-Version", dataVersion)
+
+ if body != None:
+ return body
+ return json.dumps(responseBody)
+
+def fail(response, body):
+ response.status = (400, "Bad Request")
+ response.headers.set(b"Content-Type", b"text/plain")
+ return body
diff --git a/fledge/tentative/trusted-scoring-signals.https.sub.window.js b/fledge/tentative/trusted-scoring-signals.https.sub.window.js
new file mode 100644
index 0000000..660ad04
--- /dev/null
+++ b/fledge/tentative/trusted-scoring-signals.https.sub.window.js
@@ -0,0 +1,409 @@
+// META: script=/resources/testdriver.js
+// META: script=/common/utils.js
+// META: script=resources/fledge-util.js
+// META: timeout=long
+
+"use strict";
+
+// These tests focus on trustedScoringSignals: Requesting them, handling network
+// errors, handling the renderURLs portion of the response, passing renderURLs
+// to worklet scripts, and handling the Data-Version header.
+
+// Helper for trusted scoring signals tests. Runs an auction with
+// TRUSTED_SCORING_SIGNALS_URL and a single interest group, failing the test
+// if there's no winner. "scoreAdCheck" is an expression that should be true
+// when evaluated in scoreAd(). "renderURL" can be used to control the response
+// given for TRUSTED_SCORING_SIGNALS_URL.
+async function runTrustedScoringSignalsTest(test, uuid, renderURL, scoreAdCheck) {
+ const auctionConfigOverrides = {
+ trustedScoringSignalsUrl: TRUSTED_SCORING_SIGNALS_URL,
+ decisionLogicUrl:
+ createDecisionScriptUrl(uuid, {
+ scoreAd: `if (!(${scoreAdCheck})) throw "error";` })};
+ await runBasicFledgeTestExpectingWinner(
+ test,
+ { uuid: uuid,
+ interestGroupOverrides: {ads: [{renderUrl: renderURL}]},
+ auctionConfigOverrides: auctionConfigOverrides
+ });
+}
+
+// Much like runTrustedScoringSignalsTest, but runs auctions through reporting
+// as well, and evaluates `check` both in scodeAd() and reportResult(). Also
+// makes sure browserSignals.dataVersion is undefined in generateBid() and
+// reportWin().
+async function runTrustedScoringSignalsDataVersionTest(
+ test, uuid, renderURL, check) {
+ const interestGroupOverrides = {
+ biddingLogicUrl :
+ createBiddingScriptUrl({
+ generateBid:
+ `if (browserSignals.dataVersion !== undefined)
+ throw "Bad browserSignals.dataVersion"`,
+ reportWin:
+ `if (browserSignals.dataVersion !== undefined)
+ sendReportTo('${createSellerReportUrl(uuid, 'error')}');
+ else
+ sendReportTo('${createSellerReportUrl(uuid)}');` }),
+ ads: [{renderUrl: renderURL}]};
+ await joinInterestGroup(test, uuid, interestGroupOverrides);
+
+ const auctionConfigOverrides = {
+ decisionLogicUrl: createDecisionScriptUrl(
+ uuid,
+ { scoreAd:
+ `if (!(${check})) return false;`,
+ reportResult:
+ `if (!(${check}))
+ sendReportTo('${createSellerReportUrl(uuid, 'error')}')
+ sendReportTo('${createSellerReportUrl(uuid)}')`,
+ }),
+ trustedScoringSignalsUrl: TRUSTED_SCORING_SIGNALS_URL
+ }
+ await runBasicFledgeAuctionAndNavigate(test, uuid, auctionConfigOverrides);
+ await waitForObservedRequests(
+ uuid, [createBidderReportUrl(uuid), createSellerReportUrl(uuid)]);
+}
+
+// Creates a render URL that, when sent to the trusted-scoring-signals.py,
+// results in a trusted scoring signals response with the provided response
+// body.
+function createScoringSignalsRenderUrlWithBody(uuid, responseBody) {
+ return createRenderUrl(uuid, /*script=*/null,
+ /*signalsParam=*/`replace-body:${responseBody}`);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Tests where no renderURL value is received for the passed in renderURL.
+/////////////////////////////////////////////////////////////////////////////
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const decsionLogicScriptUrl = createDecisionScriptUrl(
+ uuid,
+ { scoreAd: 'if (trustedScoringSignals !== null) throw "error";'});
+ await runBasicFledgeTestExpectingWinner(
+ test,
+ { uuid: uuid,
+ auctionConfigOverrides: { decisionLogicUrl: decsionLogicScriptUrl }
+ });
+}, 'No trustedScoringSignalsUrl.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, /*signalsParam=*/'close-connection');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ 'trustedScoringSignals === null');
+}, 'Trusted scoring signals closes the connection without sending anything.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, /*signalsParam=*/'http-error');
+ await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
+}, 'Trusted scoring signals response is HTTP 404 error.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, /*signalsParam=*/'no-content-type');
+ await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
+}, 'Trusted scoring signals response has no content-type.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, /*signalsParam=*/'wrong-content-type');
+ await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
+}, 'Trusted scoring signals response has wrong content-type.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, /*signalsParam=*/'ad-auction-not-allowed');
+ await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
+}, 'Trusted scoring signals response does not allow FLEDGE.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, /*signalsParam=*/'bad-ad-auction-allowed');
+ await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
+}, 'Trusted scoring signals response has wrong Ad-Auction-Allowed header.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, /*signalsParam=*/'no-ad-auction-allow');
+ await runTrustedScoringSignalsTest( test, uuid, renderURL, 'trustedScoringSignals === null');
+}, 'Trusted scoring signals response has no Ad-Auction-Allowed header.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createScoringSignalsRenderUrlWithBody(
+ uuid, /*responseBody=*/'');
+ await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
+}, 'Trusted scoring signals response has no body.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createScoringSignalsRenderUrlWithBody(
+ uuid, /*responseBody=*/'Not JSON');
+ await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
+}, 'Trusted scoring signals response is not JSON.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createScoringSignalsRenderUrlWithBody(
+ uuid, /*responseBody=*/'[]');
+ await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
+}, 'Trusted scoring signals response is a JSON array.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createScoringSignalsRenderUrlWithBody(
+ uuid, /*responseBody=*/'{JSON_keys_need_quotes: 1}');
+ await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
+}, 'Trusted scoring signals response is invalid JSON object.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createScoringSignalsRenderUrlWithBody(
+ uuid, /*responseBody=*/'{}');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ `trustedScoringSignals.renderURL["${renderURL}"] === null`);
+}, 'Trusted scoring signals response has no renderUrl object.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createScoringSignalsRenderUrlWithBody(
+ uuid, /*responseBody=*/'{"renderUrls":{}}');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ `trustedScoringSignals.renderURL["${renderURL}"] === null`);
+}, 'Trusted scoring signals response has no renderUrls.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createScoringSignalsRenderUrlWithBody(
+ uuid, /*responseBody=*/'{"renderUrls":{"https://wrong-url.test": 5}}');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ `trustedScoringSignals.renderURL["${renderURL}"] === null &&
+ trustedScoringSignals["https://wrong-url.test/"] === undefined`);
+}, 'Trusted scoring signals response has renderURL not in response.');
+
+/////////////////////////////////////////////////////////////////////////////
+// Tests where renderURL value is received for the passed in renderURL.
+/////////////////////////////////////////////////////////////////////////////
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, /*signalsParam=*/'null-value');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ `trustedScoringSignals.renderURL["${renderURL}"] === null`);
+}, 'Trusted scoring signals response has null value for renderURL.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, /*signalsParam=*/'num-value');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ `trustedScoringSignals.renderURL["${renderURL}"] === 1`);
+}, 'Trusted scoring signals response has a number value for renderURL.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null,
+ /*signalsParam=*/'string-value');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ `trustedScoringSignals.renderURL["${renderURL}"] === "1"`);
+}, 'Trusted scoring signals response has a string value for renderURL.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, /*signalsParam=*/'array-value');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ `JSON.stringify(trustedScoringSignals.renderURL["${renderURL}"]) === '[1,"foo",null]'`);
+}, 'Trusted scoring signals response has an array value for renderURL.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, /*signalsParam=*/'object-value');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ `Object.keys(trustedScoringSignals.renderURL["${renderURL}"]).length === 2 &&
+ trustedScoringSignals.renderURL["${renderURL}"]["a"] === "b" &&
+ JSON.stringify(trustedScoringSignals.renderURL["${renderURL}"]["c"]) === '["d"]'`);
+}, 'Trusted scoring signals response has an object value for renderURL.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, /*signalsParam=*/'+%20 \x00?,3#&');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ `trustedScoringSignals.renderURL["${renderURL}"] === "default value"`);
+}, 'Trusted scoring signals with escaped renderURL.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, /*signalsParam=*/'hostname');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ `trustedScoringSignals.renderURL["${renderURL}"] === "${window.location.hostname}"`);
+}, 'Trusted scoring signals receives hostname field.');
+
+// Joins two interest groups and makes sure the scoring signals for one are never leaked
+// to the seller script when scoring the other.
+//
+// There's no guarantee in this test that a single request to the server will be made with
+// render URLs from two different IGs, though that's the case this is trying to test -
+// browsers are not required to support batching, and even if they do, joining any two
+// particular requests may be racy.
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL1 = createRenderUrl(uuid, /*script=*/null, /*signalsParam=*/'num-value');
+ const renderURL2 = createRenderUrl(uuid, /*script=*/null, /*signalsParam=*/'string-value');
+ await joinInterestGroup(test, uuid, {ads: [{renderUrl: renderURL1}], name: '1'});
+ await joinInterestGroup(test, uuid, {ads: [{renderUrl: renderURL2}], name: '2'});
+ let auctionConfigOverrides = { trustedScoringSignalsUrl: TRUSTED_SCORING_SIGNALS_URL };
+
+ // scoreAd() only accepts the first IG's bid, validating its trustedScoringSignals.
+ auctionConfigOverrides.decisionLogicUrl =
+ createDecisionScriptUrl(uuid, {
+ scoreAd: `if (browserSignals.renderURL === "${renderURL1}" &&
+ trustedScoringSignals.renderURL["${renderURL1}"] !== 1 ||
+ trustedScoringSignals.renderURL["${renderURL2}"] !== undefined)
+ return;` });
+ let config = await runBasicFledgeAuction(
+ test, uuid, auctionConfigOverrides);
+ assert_true(config instanceof FencedFrameConfig,
+ `Wrong value type returned from first auction: ${config.constructor.type}`);
+
+ // scoreAd() only accepts the second IG's bid, validating its trustedScoringSignals.
+ auctionConfigOverrides.decisionLogicUrl =
+ createDecisionScriptUrl(uuid, {
+ scoreAd: `if (browserSignals.renderURL === "${renderURL2}" &&
+ trustedScoringSignals.renderURL["${renderURL1}"] !== undefined ||
+ trustedScoringSignals.renderURL["${renderURL2}"] !== '1')
+ return;` });
+ config = await runBasicFledgeAuction(
+ test, uuid, auctionConfigOverrides);
+ assert_true(config instanceof FencedFrameConfig,
+ `Wrong value type returned from second auction: ${config.constructor.type}`);
+}, 'Trusted scoring signals multiple renderURLs.');
+
+/////////////////////////////////////////////////////////////////////////////
+// Data-Version tests
+/////////////////////////////////////////////////////////////////////////////
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid);
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ 'browserSignals.dataVersion === undefined');
+}, 'Trusted scoring signals response has no data-version.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, 'data-version:3');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ 'browserSignals.dataVersion === 3');
+}, 'Trusted scoring signals response has valid data-version.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, 'data-version:0');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ 'browserSignals.dataVersion === 0');
+}, 'Trusted scoring signals response has min data-version.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, 'data-version:4294967295');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ 'browserSignals.dataVersion === 4294967295');
+}, 'Trusted scoring signals response has max data-version.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, 'data-version:4294967296');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ 'browserSignals.dataVersion === undefined');
+}, 'Trusted scoring signals response has too large data-version.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, 'data-version:03');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ 'browserSignals.dataVersion === undefined');
+}, 'Trusted scoring signals response has data-version with leading 0.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, 'data-version:-1');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ 'browserSignals.dataVersion === undefined');
+}, 'Trusted scoring signals response has negative data-version.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, 'data-version:1.3');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ 'browserSignals.dataVersion === undefined');
+}, 'Trusted scoring signals response has decimal in data-version.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, 'data-version:2 2');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ 'browserSignals.dataVersion === undefined');
+}, 'Trusted scoring signals response has space in data-version.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, 'data-version:0x4');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ 'browserSignals.dataVersion === undefined');
+}, 'Trusted scoring signals response has space hax data-version.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, 'data-version:3,replace-body:');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ 'browserSignals.dataVersion === undefined');
+}, 'Trusted scoring signals response has data-version and empty body.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, 'data-version:3,replace-body:[]');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ 'browserSignals.dataVersion === undefined');
+}, 'Trusted scoring signals response has data-version and JSON array body.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, 'data-version:3,replace-body:{} {}');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ 'browserSignals.dataVersion === undefined');
+}, 'Trusted scoring signals response has data-version and double JSON object body.');
+
+promise_test(async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderUrl(uuid, /*script=*/null, 'data-version:3,replace-body:{}');
+ await runTrustedScoringSignalsTest(
+ test, uuid, renderURL,
+ 'browserSignals.dataVersion === 3');
+}, 'Trusted scoring signals response has data-version and no renderURLs.');