Field trial testing config for sampling debug reports.

Enable FledgeSampleDebugReports in field trial config. Add a WPT to test browserSignals.forDebuggingOnlyInCooldownOrLockout is set to true in generatedBid() and scoreAd() after an earlier auction made them under lockout or cooldown.

Bug: b/310944302

Change-Id: Ibb8129d1c61acfbda6fb2967404de89612c20c0d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5285079
Reviewed-by: Russ Hamilton <behamilton@google.com>
Commit-Queue: Qingxin Wu <qingxinwu@google.com>
Cr-Commit-Position: refs/heads/main@{#1259861}
diff --git a/fledge/tentative/auction-config.https.window.js b/fledge/tentative/auction-config.https.window.js
index 3b5814b..8fbdc95 100644
--- a/fledge/tentative/auction-config.https.window.js
+++ b/fledge/tentative/auction-config.https.window.js
@@ -390,3 +390,80 @@
                             [{width: '100', height: '100'},
                              {width: '200furlongs', height: '200'}]}
 });
+
+subsetTest(promise_test, async test => {
+  const uuid = generateUuid(test);
+
+  // The renderURL / report URLs for the first/second iterations of the auction.
+  let renderURL = createRenderURL(uuid);
+  let bidderReportURL1 = createBidderReportURL(uuid, /*id=*/ 1);
+  let bidderReportURL2 = createBidderReportURL(uuid, /*id=*/ 2);
+  let bidderDebugReportURL =
+      createBidderReportURL(uuid, /*id=*/ 'forDebuggingOnly');
+  let sellerReportURL1 = createSellerReportURL(uuid, /*id=*/ 1);
+  let sellerReportURL2 = createSellerReportURL(uuid, /*id=*/ 2);
+  let sellerDebugReportURL =
+      createSellerReportURL(uuid, /*id=*/ 'forDebuggingOnly');
+
+  // reportWin() sends "bidderReportURL1" if
+  // browserSignals.forDebuggingOnlyInCooldownOrLockout is true,
+  // "bidderReportURL2" otherwise.
+  await joinInterestGroup(test, uuid, {
+    ads: [{renderURL: renderURL}],
+    biddingLogicURL: createBiddingScriptURL({
+      generateBid: `
+        forDebuggingOnly.reportAdAuctionWin('${bidderDebugReportURL}');
+        if (!browserSignals.hasOwnProperty(
+          'forDebuggingOnlyInCooldownOrLockout')) {
+          throw "Missing forDebuggingOnlyInCooldownOrLockout in browserSignals";
+        }
+        let bid = browserSignals.forDebuggingOnlyInCooldownOrLockout ? 1 : 2;
+        return {bid: bid, render: '${renderURL}'};`,
+      reportWin: `
+        if (browserSignals.bid == 1)
+          sendReportTo('${bidderReportURL1}');
+        if (browserSignals.bid == 2)
+          sendReportTo('${bidderReportURL2}');`
+
+    })
+  });
+
+  // reportResult() sends "sellerReportURL1" if
+  // browserSignals.forDebuggingOnlyInCooldownOrLockout in scoreAd() is true,
+  // "sellerReportURL2" otherwise.
+  const auctionConfigOverrides = {
+    decisionLogicURL: createDecisionScriptURL(uuid, {
+      scoreAd: `
+        forDebuggingOnly.reportAdAuctionWin('${sellerDebugReportURL}');
+        if (!browserSignals.hasOwnProperty(
+          'forDebuggingOnlyInCooldownOrLockout')) {
+          throw "Missing forDebuggingOnlyInCooldownOrLockout in browserSignals";
+        }
+        let desirability =
+            browserSignals.forDebuggingOnlyInCooldownOrLockout ? 1 : 2;
+        return {desirability: desirability};`,
+      reportResult: `
+        if (browserSignals.desirability == 1)
+          sendReportTo('${sellerReportURL1}');
+        if (browserSignals.desirability == 2)
+          sendReportTo('${sellerReportURL2}');`
+    })
+  };
+
+  // In the first auction, browserSignals.forDebuggingOnlyInCooldownOrLockout in
+  // generateBid() and scoreAd() should both be false. After the auction,
+  // lockout and cooldowns should be updated.
+  await runBasicFledgeAuctionAndNavigate(test, uuid, auctionConfigOverrides);
+  await waitForObservedRequestsIgnoreDebugOnlyReports(
+      uuid, [bidderReportURL2, sellerReportURL2]);
+
+  // In the second auction, browserSignals.forDebuggingOnlyInCooldownOrLockout
+  // in generateBid() and scoreAd() should both be true, since both the buyer
+  // and seller called forDebuggingOnly API in the first auction, so they are in
+  // cooldowns at least (and also in lockout if a debug report is allowed to be
+  // sent).
+  await runBasicFledgeAuctionAndNavigate(test, uuid, auctionConfigOverrides);
+  await waitForObservedRequestsIgnoreDebugOnlyReports(
+    uuid,
+    [bidderReportURL2, sellerReportURL2, bidderReportURL1, sellerReportURL1]);
+}, `forDebuggingOnly lockout and cooldowns updating in one auction, read in another's.`);
diff --git a/fledge/tentative/component-auction.https.window.js b/fledge/tentative/component-auction.https.window.js
index 63771d4..c705320 100644
--- a/fledge/tentative/component-auction.https.window.js
+++ b/fledge/tentative/component-auction.https.window.js
@@ -671,7 +671,7 @@
                    browserSignals.prevWinsMs[0][1].renderURL === "${renderURL1}") {
                  return {bid: 1, allowComponentAuction: true, render: "${renderURL2}"};
                }
-               throw "Unexpected biddingSignals: " + JSON.stringify(browserSignals);`,
+               throw "Unexpected browserSignals: " + JSON.stringify(browserSignals);`,
             reportWin:
               `if (browserSignals.renderURL === "${renderURL1}")
                  sendReportTo("${bidderReportURL1}");
diff --git a/fledge/tentative/resources/fledge-util.sub.js b/fledge/tentative/resources/fledge-util.sub.js
index 69573d4..f64e97a 100644
--- a/fledge/tentative/resources/fledge-util.sub.js
+++ b/fledge/tentative/resources/fledge-util.sub.js
@@ -148,6 +148,35 @@
   }
 }
 
+
+// Similar to waitForObservedRequests, but ignore forDebuggingOnly reports.
+async function waitForObservedRequestsIgnoreDebugOnlyReports(
+  uuid, expectedRequests) {
+  // Sort array for easier comparison, as observed request order does not
+  // matter, and replace UUID to print consistent errors on failure.
+  expectedRequests =
+      expectedRequests.sort().map((url) => url.replace(uuid, '<uuid>'));
+
+  while (true) {
+    let numTrackedRequest = 0;
+    let trackedData = await fetchTrackedData(uuid);
+
+    // Clean up "trackedRequests" in same manner as "expectedRequests".
+    let trackedRequests = trackedData.trackedRequests.sort().map(
+        (url) => url.replace(uuid, '<uuid>'));
+
+    for (const trackedRequest of trackedRequests) {
+      // Ignore forDebuggingOnly reports, since their appearance is random.
+      if (!trackedRequest.includes('forDebuggingOnly')) {
+        assert_in_array(trackedRequest, expectedRequests);
+        numTrackedRequest++;
+      }
+    }
+
+    if (numTrackedRequest == expectedRequests.length) break;
+  }
+}
+
 // Creates a bidding script with the provided code in the method bodies. The
 // bidding script's generateBid() method will return a bid of 9 for the first
 // ad, after the passed in code in the "generateBid" input argument has been