Create new PAA test cases for protected audience call flow.

The tests are referred from the FLEDGE tests and only covered
successful cases. Due to the protected audience call flow does not
check the response of PAA, there is no way to test the failure
cases right now and it needs to be updated in the future.

Bug: 1456401
Change-Id: I5bd39e647717aaa5cb944f25aca1129fa49c17a7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4621708
Reviewed-by: Alex Turner <alexmt@chromium.org>
Commit-Queue: Tianyang Xu <xtlsheep@google.com>
Cr-Commit-Position: refs/heads/main@{#1161276}
diff --git a/private-aggregation/protected-audience-surface-success.https.html b/private-aggregation/protected-audience-surface-success.https.html
new file mode 100644
index 0000000..417a6b1
--- /dev/null
+++ b/private-aggregation/protected-audience-surface-success.https.html
@@ -0,0 +1,89 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/protected-audience-helper-module.js"></script>
+
+<body>
+  <script>
+    'use strict';
+
+    promise_test(async test => {
+      const uuid = generateUuid(test);
+      const contribution = '{ bucket: 1n, value: 2 }';
+
+      await runReportTest(
+        test, uuid,
+        // reportResult:
+        `privateAggregation.contributeToHistogram(${contribution});`,
+        // reportWin:
+        `privateAggregation.contributeToHistogram(${contribution});`
+      );
+    }, 'basic contributeToHistogram() test');
+
+  promise_test(async test => {
+    const uuid = generateUuid(test);
+    const contribution = '{ bucket: 0n, value: 2 }';
+
+    await runReportTest(
+      test, uuid,
+      // reportResult:
+      `privateAggregation.contributeToHistogram(${contribution});`,
+      // reportWin:
+      `privateAggregation.contributeToHistogram(${contribution});`
+    );
+  }, 'contributeToHistogram() with a zero bucket');
+
+  promise_test(async test => {
+    const uuid = generateUuid(test);
+    const contribution = '{ bucket: 1n, value: 0 }';
+
+    await runReportTest(
+      test, uuid,
+      // reportResult:
+      `privateAggregation.contributeToHistogram(${contribution});`,
+      // reportWin:
+      `privateAggregation.contributeToHistogram(${contribution});`
+    );
+  }, 'contributeToHistogram() with a zero value');
+
+  promise_test(async test => {
+    const uuid = generateUuid(test);
+    const contribution = '{ bucket: 18446744073709551616n, value: 5 }';
+
+    await runReportTest(
+      test, uuid,
+      // reportResult:
+      `privateAggregation.contributeToHistogram(${contribution});`,
+      // reportWin:
+      `privateAggregation.contributeToHistogram(${contribution});`
+    );
+  }, 'contributeToHistogram() with a large bucket');
+
+  promise_test(async test => {
+    const uuid = generateUuid(test);
+    const contribution = '{ bucket: 340282366920938463463374607431768211455n, value: 5 }';
+
+    await runReportTest(
+      test, uuid,
+      // reportResult:
+      `privateAggregation.contributeToHistogram(${contribution});`,
+      // reportWin:
+      `privateAggregation.contributeToHistogram(${contribution});`
+    );
+  }, 'contributeToHistogram() with a max bucket');
+
+  promise_test(async test => {
+    const uuid = generateUuid(test);
+    const contribution = '{ bucket: 1n, value: 2.3 }';
+
+    await runReportTest(
+      test, uuid,
+      // reportResult:
+      `privateAggregation.contributeToHistogram(${contribution});`,
+      // reportWin:
+      `privateAggregation.contributeToHistogram(${contribution});`
+    );
+  }, 'contributeToHistogram() with a non-integer value');
+  </script>
+</body>
diff --git a/private-aggregation/resources/protected-audience-helper-module.js b/private-aggregation/resources/protected-audience-helper-module.js
new file mode 100644
index 0000000..0cf9e89
--- /dev/null
+++ b/private-aggregation/resources/protected-audience-helper-module.js
@@ -0,0 +1,154 @@
+// This file is adapted from /fledge/tentative/resources/fledge-util.js,
+// removing unnecessary logic and modifying to allow it to be run in the
+// private-aggregation directory.
+
+"use strict;"
+
+// Directory of fledge
+const FLEDGE_DIR = '/fledge/tentative/';
+const FULL_URL = window.location.href;
+let BASE_URL = FULL_URL.substring(0, FULL_URL.lastIndexOf('/') + 1)
+const BASE_PATH = (new URL(BASE_URL)).pathname;
+const DEFAULT_INTEREST_GROUP_NAME = 'default name';
+
+// Use python source files under fledge directory
+BASE_URL = BASE_URL.replace(BASE_PATH, FLEDGE_DIR);
+
+// Generates a UUID by token.
+function generateUuid(test) {
+  let uuid = token();
+  return uuid;
+}
+
+// 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
+// run, unless it returns something or throws.
+//
+// The default reportWin() method is empty.
+function createBiddingScriptUrl(params = {}) {
+  let url = new URL(`${BASE_URL}resources/bidding-logic.sub.py`);
+  if (params.generateBid)
+    url.searchParams.append('generateBid', params.generateBid);
+  if (params.reportWin)
+    url.searchParams.append('reportWin', params.reportWin);
+  if (params.error)
+    url.searchParams.append('error', params.error);
+  if (params.bid)
+    url.searchParams.append('bid', params.bid);
+  return url.toString();
+}
+
+// Creates a decision script with the provided code in the method bodies. The
+// decision script's scoreAd() method will reject ads with renderUrls that
+// don't ends with "uuid", and will return a score equal to the bid, after the
+// passed in code in the "scoreAd" input argument has been run, unless it
+// returns something or throws.
+//
+// The default reportResult() method is empty.
+function createDecisionScriptUrl(uuid, params = {}) {
+  let url = new URL(`${BASE_URL}resources/decision-logic.sub.py`);
+  url.searchParams.append('uuid', uuid);
+  if (params.scoreAd)
+    url.searchParams.append('scoreAd', params.scoreAd);
+  if (params.reportResult)
+    url.searchParams.append('reportResult', params.reportResult);
+  if (params.error)
+    url.searchParams.append('error', params.error);
+  return url.toString();
+}
+
+// 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) {
+  let url = new URL(`${BASE_URL}resources/fenced-frame.sub.py`);
+  if (script)
+    url.searchParams.append('script', script);
+  url.searchParams.append('uuid', uuid);
+  return url.toString();
+}
+
+// Joins an interest group that, by default, is owned by the current frame's
+// origin, is named DEFAULT_INTEREST_GROUP_NAME, has a bidding script that
+// issues a bid of 9 with a renderUrl of "https://not.checked.test/${uuid}".
+// `interestGroupOverrides` is required to override fields in the joined
+// interest group.
+async function joinInterestGroup(test, uuid, interestGroupOverrides) {
+  const INTEREST_GROUP_LIFETIME_SECS = 60;
+
+  let interestGroup = {
+    owner: window.location.origin,
+    name: DEFAULT_INTEREST_GROUP_NAME,
+    ads: [{renderUrl: createRenderUrl(uuid)}],
+    ...interestGroupOverrides
+  };
+
+  await navigator.joinAdInterestGroup(interestGroup,
+                                      INTEREST_GROUP_LIFETIME_SECS);
+  test.add_cleanup(
+      async () => {await navigator.leaveAdInterestGroup(interestGroup)});
+}
+
+// Runs a FLEDGE auction and returns the result. `auctionConfigOverrides` is
+// required to override fields in the auction configuration.
+async function runBasicFledgeAuction(test, uuid, auctionConfigOverrides) {
+  let auctionConfig = {
+    seller: window.location.origin,
+    interestGroupBuyers: [window.location.origin],
+    resolveToConfig: true,
+    ...auctionConfigOverrides
+  };
+  return await navigator.runAdAuction(auctionConfig);
+}
+
+// Calls runBasicFledgeAuction(), expecting the auction to have a winner.
+// Creates a fenced frame that will be destroyed on completion of "test", and
+// navigates it to the URN URL returned by the auction. Does not wait for the
+// fenced frame to finish loading, since there's no API that can do that.
+async function runBasicFledgeAuctionAndNavigate(test, uuid,
+  auctionConfigOverrides) {
+  let config = await runBasicFledgeAuction(test, uuid, auctionConfigOverrides);
+  assert_true(config instanceof FencedFrameConfig,
+      `Wrong value type returned from auction: ${config.constructor.type}`);
+
+  let fencedFrame = document.createElement('fencedframe');
+  fencedFrame.mode = 'opaque-ads';
+  fencedFrame.config = config;
+  document.body.appendChild(fencedFrame);
+  test.add_cleanup(() => { document.body.removeChild(fencedFrame); });
+}
+
+// Joins an interest group and runs an auction, expecting no winner to be
+// returned. "testConfig" can optionally modify the interest group or
+// auctionConfig.
+async function runBasicFledgeTestExpectingNoWinner(test, testConfig) {
+  const uuid = generateUuid(test);
+  await joinInterestGroup(test, uuid, testConfig.interestGroupOverrides);
+  let result = await runBasicFledgeAuction(
+      test, uuid, testConfig.auctionConfigOverrides);
+  assert_true(result === null, 'Auction unexpectedly had a winner');
+}
+
+// Test helper for report phase of auctions that lets the caller specify the
+// body of reportResult() and reportWin().
+//
+// Passing worklets in null will cause the test fail.
+//
+// Null worklets test cases are handled under
+// fledge.
+async function runReportTest(test, uuid, reportResult, reportWin) {
+  assert_not_equals(reportResult, null)
+  assert_not_equals(reportWin, null)
+
+  let interestGroupOverrides =
+    { biddingLogicUrl: createBiddingScriptUrl({ reportWin }) };
+
+  await joinInterestGroup(test, uuid, interestGroupOverrides);
+  await runBasicFledgeAuctionAndNavigate(
+      test, uuid,
+      { decisionLogicUrl: createDecisionScriptUrl(
+        uuid, { reportResult })
+    });
+}
diff --git a/private-aggregation/resources/private-aggregation-helper-module.js b/private-aggregation/resources/shared-storage-helper-module.js
similarity index 100%
rename from private-aggregation/resources/private-aggregation-helper-module.js
rename to private-aggregation/resources/shared-storage-helper-module.js
diff --git a/private-aggregation/resources/util.js b/private-aggregation/resources/util.js
index 7b3a2c5..24e1564 100644
--- a/private-aggregation/resources/util.js
+++ b/private-aggregation/resources/util.js
@@ -7,7 +7,7 @@
   let url1 = generateURL("/shared-storage/resources/frame1.html",
                          [ancestor_key]);
 
-  await addModuleOnce("/private-aggregation/resources/private-aggregation-helper-module.js");
+  await addModuleOnce("/private-aggregation/resources/shared-storage-helper-module.js");
 
   let select_url_result = await sharedStorage.selectURL(
     "contribute-to-histogram", [{url: url0}, {url: url1}],