tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

real-time-reporting.https.window.js (13925B)


      1 // META: script=/resources/testdriver.js
      2 // META: script=/resources/testdriver-vendor.js
      3 // META: script=/common/utils.js
      4 // META: script=resources/fledge-util.sub.js
      5 // META: script=third_party/cbor-js/cbor.js
      6 // META: script=/common/subset-tests.js
      7 // META: timeout=long
      8 // META: variant=?1-5
      9 // META: variant=?6-last
     10 
     11 'use strict';
     12 
     13 // The tests in this file focus on real time reporting.
     14 
     15 const MAIN_PATH = '/.well-known/interest-group/real-time-report'
     16 
     17 // Creates an AuctionConfig with component auctions.
     18 function createMultiSellerAuctionConfig(
     19    uuid, seller, decisionLogicURL, componentAuctions,
     20    auctionConfigOverrides = {}) {
     21  return {
     22    seller: seller,
     23    decisionLogicURL: decisionLogicURL,
     24    interestGroupBuyers: [],
     25    componentAuctions: componentAuctions,
     26    ...auctionConfigOverrides
     27  };
     28 }
     29 
     30 // Creates a bidding script located on "origin". The generateBid() method calls
     31 // real time reporting API and returns a bid of "bid".
     32 // The reportWin() method is empty.
     33 function createBiddingScriptURLForRealTimeReporting(origin = null, bid = 1) {
     34  return createBiddingScriptURL({
     35    origin: origin ? origin : new URL(BASE_URL).origin,
     36    bid: bid,
     37    allowComponentAuction: true,
     38    generateBid: `
     39      realTimeReporting.contributeToHistogram({ bucket: 20, priorityWeight: 1});`
     40  });
     41 }
     42 
     43 // Creates a decision script that calls real time reporting API.
     44 // The reportResult() method is empty.
     45 function createDecisionScriptURLForRealTimeReporting(uuid, origin = null) {
     46  return createDecisionScriptURL(uuid, {
     47    origin: origin === null ? new URL(BASE_URL).origin : origin,
     48    scoreAd: `
     49      realTimeReporting.contributeToHistogram({ bucket: 200, priorityWeight: 1});`
     50  });
     51 }
     52 
     53 // Delay method that waits for prescribed number of milliseconds.
     54 const delay = ms => new Promise(resolve => step_timeout(resolve, ms));
     55 
     56 // Polls the given `origin` to retrieve reports sent there. Once the reports are
     57 // received, returns the list of reports. Returns null if the timeout is reached
     58 // before a report is available.
     59 const pollReports = async (origin, wait_for = 1, timeout = 5000 /*ms*/) => {
     60  let startTime = performance.now();
     61  let payloads = [];
     62  while (performance.now() - startTime < timeout) {
     63    const resp = await fetch(new URL(MAIN_PATH, origin));
     64    const payload = await resp.arrayBuffer();
     65    if (payload.byteLength > 0) {
     66      payloads = payloads.concat(payload);
     67    }
     68    if (payloads.length >= wait_for) {
     69      return payloads;
     70    }
     71    await delay(/*ms=*/ 100);
     72  }
     73  if (payloads.length > 0) {
     74    return payloads;
     75  }
     76  return null;
     77 };
     78 
     79 // Verifies that `reports` has 1 report in cbor, which has the expected three
     80 // fields.
     81 // `version` should be 1.
     82 // `histogram` and `platformHistogram` should be objects that pass
     83 // verifyHistogram().
     84 const verifyReports = (reports) => {
     85  assert_equals(reports.length, 1);
     86  const report = CBOR.decode(reports[0]);
     87  assert_own_property(report, 'version');
     88  assert_equals(report.version, 1);
     89  assert_own_property(report, 'histogram');
     90  verifyHistogram(report.histogram, 128, 1024);
     91  assert_own_property(report, 'platformHistogram');
     92  verifyHistogram(report.platformHistogram, 1, 4);
     93  assert_equals(Object.keys(report).length, 3);
     94 };
     95 
     96 // Verifies that a `histogram` has two fields: "buckets" and "length", where
     97 // "buckets" field is a Uint8Array of `bucketSize`, and "length" field equals to
     98 // `length`.
     99 const verifyHistogram = (histogram, bucketSize, length) => {
    100  assert_own_property(histogram, 'buckets');
    101  assert_own_property(histogram, 'length');
    102  assert_equals(Object.keys(histogram).length, 2);
    103  assert_true(histogram.buckets instanceof Uint8Array);
    104  assert_equals(histogram.buckets.length, bucketSize);
    105  assert_equals(histogram.length, length);
    106 };
    107 
    108 // Method to clear the stash. Takes the URL as parameter.
    109 const resetReports = url => {
    110  // The view of the stash is path-specific
    111  // (https://web-platform-tests.org/tools/wptserve/docs/stash.html), therefore
    112  // the origin doesn't need to be specified.
    113  url = `${url}?clear_stash=true`;
    114  const options = {
    115    method: 'POST',
    116  };
    117  return fetch(url, options);
    118 };
    119 
    120 subsetTest(promise_test, async test => {
    121  const uuid = generateUuid(test);
    122  await resetReports(MAIN_PATH);
    123  await joinCrossOriginInterestGroup(test, uuid, OTHER_ORIGIN1, {
    124    biddingLogicURL: createBiddingScriptURLForRealTimeReporting(OTHER_ORIGIN1)
    125  });
    126  await runBasicFledgeAuctionAndNavigate(test, uuid, {
    127    decisionLogicURL: createDecisionScriptURLForRealTimeReporting(uuid),
    128    interestGroupBuyers: [OTHER_ORIGIN1],
    129    sellerRealTimeReportingConfig: {type: 'default-local-reporting'},
    130    perBuyerRealTimeReportingConfig:
    131        {[OTHER_ORIGIN1]: {type: 'default-local-reporting'}}
    132  });
    133  const sellerReports = await pollReports(location.origin);
    134  verifyReports(sellerReports);
    135 
    136  const buyerReports = await pollReports(OTHER_ORIGIN1);
    137  verifyReports(buyerReports);
    138 }, 'Real time reporting different buyer and seller both opted-in and called api.');
    139 
    140 subsetTest(promise_test, async test => {
    141  const uuid = generateUuid(test);
    142  await resetReports(MAIN_PATH);
    143  await joinCrossOriginInterestGroup(test, uuid, OTHER_ORIGIN1, {
    144    biddingLogicURL: createBiddingScriptURLForRealTimeReporting(OTHER_ORIGIN1)
    145  });
    146  await runBasicFledgeAuctionAndNavigate(test, uuid, {
    147    decisionLogicURL: createDecisionScriptURLForRealTimeReporting(uuid),
    148    interestGroupBuyers: [OTHER_ORIGIN1],
    149    perBuyerRealTimeReportingConfig:
    150        {[OTHER_ORIGIN1]: {type: 'default-local-reporting'}}
    151  });
    152 
    153  const buyerReports = await pollReports(OTHER_ORIGIN1);
    154  verifyReports(buyerReports);
    155 
    156  // Seller called the RTR API, but didn't opt-in.
    157  const sellerReports =
    158      await pollReports(location.origin, /*wait_for=*/ 1, /*timeout=*/ 1000);
    159  assert_equals(sellerReports, null);
    160 }, 'Real time reporting buyer opted-in but not seller.');
    161 
    162 subsetTest(promise_test, async test => {
    163  const uuid = generateUuid(test);
    164  await resetReports(MAIN_PATH);
    165  await joinCrossOriginInterestGroup(test, uuid, OTHER_ORIGIN1, {
    166    biddingLogicURL: createBiddingScriptURLForRealTimeReporting(OTHER_ORIGIN1)
    167  });
    168  await runBasicFledgeAuctionAndNavigate(test, uuid, {
    169    decisionLogicURL: createDecisionScriptURLForRealTimeReporting(uuid),
    170    interestGroupBuyers: [OTHER_ORIGIN1],
    171    sellerRealTimeReportingConfig: {type: 'default-local-reporting'}
    172  });
    173 
    174  const sellerReports = await pollReports(location.origin);
    175  verifyReports(sellerReports);
    176 
    177  // Buyer called the RTR API, but didn't opt-in.
    178  const buyerReports =
    179      await pollReports(OTHER_ORIGIN1, /*wait_for=*/ 1, /*timeout=*/ 1000);
    180  assert_equals(buyerReports, null);
    181 }, 'Real time reporting seller opted-in but not buyer.');
    182 
    183 subsetTest(promise_test, async test => {
    184  const uuid = generateUuid(test);
    185  await resetReports(MAIN_PATH);
    186  await joinCrossOriginInterestGroup(
    187      test, uuid, OTHER_ORIGIN1,
    188      {biddingLogicURL: createBiddingScriptURL({origin: OTHER_ORIGIN1})});
    189  await runBasicFledgeAuctionAndNavigate(test, uuid, {
    190    decisionLogicURL: createDecisionScriptURL(uuid),
    191    interestGroupBuyers: [OTHER_ORIGIN1],
    192    sellerRealTimeReportingConfig: {type: 'default-local-reporting'},
    193    perBuyerRealTimeReportingConfig:
    194        {[OTHER_ORIGIN1]: {type: 'default-local-reporting'}}
    195  });
    196  const sellerReports = await pollReports(location.origin);
    197  verifyReports(sellerReports);
    198 
    199  const buyerReports = await pollReports(OTHER_ORIGIN1);
    200  verifyReports(buyerReports);
    201 }, 'Real time reporting different buyer and seller both opted-in but did not call api.');
    202 
    203 subsetTest(promise_test, async test => {
    204  const uuid = generateUuid(test);
    205  await resetReports(MAIN_PATH);
    206  await joinCrossOriginInterestGroup(test, uuid, OTHER_ORIGIN1, {
    207    biddingLogicURL: createBiddingScriptURLForRealTimeReporting(OTHER_ORIGIN1)
    208  });
    209  await runBasicFledgeAuctionAndNavigate(test, uuid, {
    210    decisionLogicURL: createDecisionScriptURLForRealTimeReporting(uuid),
    211    interestGroupBuyers: [OTHER_ORIGIN1]
    212  });
    213  const sellerReports = await pollReports(location.origin);
    214  assert_equals(sellerReports, null);
    215  const buyerReports =
    216      await pollReports(OTHER_ORIGIN1, /*wait_for=*/ 1, /*timeout=*/ 1000);
    217  assert_equals(buyerReports, null);
    218 }, 'Real time reporting both called api but did not opt-in.');
    219 
    220 subsetTest(promise_test, async test => {
    221  const uuid = generateUuid(test);
    222  await resetReports(MAIN_PATH);
    223  await joinInterestGroup(
    224      test, uuid,
    225      {biddingLogicURL: createBiddingScriptURLForRealTimeReporting()});
    226 
    227  const origin = location.origin;
    228  await runBasicFledgeAuctionAndNavigate(test, uuid, {
    229    decisionLogicURL: createDecisionScriptURLForRealTimeReporting(uuid),
    230    sellerRealTimeReportingConfig: {type: 'default-local-reporting'},
    231    perBuyerRealTimeReportingConfig:
    232        {[origin]: {type: 'default-local-reporting'}}
    233  });
    234  const reports = await pollReports(origin);
    235  verifyReports(reports);
    236 }, 'Real time reporting buyer and seller same origin.');
    237 
    238 subsetTest(promise_test, async test => {
    239  const uuid = generateUuid(test);
    240  await resetReports(MAIN_PATH);
    241  await joinCrossOriginInterestGroup(test, uuid, OTHER_ORIGIN1, {
    242    biddingLogicURL: createBiddingScriptURLForRealTimeReporting(OTHER_ORIGIN1)
    243  });
    244  await joinCrossOriginInterestGroup(test, uuid, OTHER_ORIGIN2, {
    245    biddingLogicURL:
    246        createBiddingScriptURLForRealTimeReporting(OTHER_ORIGIN2, /*bid=*/ 100)
    247  });
    248  await runBasicFledgeAuctionAndNavigate(test, uuid, {
    249    decisionLogicURL: createDecisionScriptURLForRealTimeReporting(uuid),
    250    interestGroupBuyers: [OTHER_ORIGIN1, OTHER_ORIGIN2],
    251    perBuyerRealTimeReportingConfig: {
    252      [OTHER_ORIGIN1]: {type: 'default-local-reporting'},
    253      [OTHER_ORIGIN2]: {type: 'default-local-reporting'}
    254    }
    255  });
    256  const reports1 = await pollReports(OTHER_ORIGIN1);
    257  verifyReports(reports1);
    258 
    259  const reports2 = await pollReports(OTHER_ORIGIN2);
    260  verifyReports(reports2);
    261 }, 'Real time reporting both winning and losing buyers opted-in.');
    262 
    263 subsetTest(promise_test, async test => {
    264  const uuid = generateUuid(test);
    265  await resetReports(MAIN_PATH);
    266  await joinCrossOriginInterestGroup(test, uuid, OTHER_ORIGIN1, {
    267    biddingLogicURL: createBiddingScriptURLForRealTimeReporting(OTHER_ORIGIN1)
    268  });
    269  await joinCrossOriginInterestGroup(test, uuid, OTHER_ORIGIN2, {
    270    biddingLogicURL:
    271        createBiddingScriptURLForRealTimeReporting(OTHER_ORIGIN2, /*bid=*/ 100)
    272  });
    273  await runBasicFledgeAuctionAndNavigate(test, uuid, {
    274    decisionLogicURL: createDecisionScriptURLForRealTimeReporting(uuid),
    275    interestGroupBuyers: [OTHER_ORIGIN1, OTHER_ORIGIN2],
    276    perBuyerRealTimeReportingConfig:
    277        {[OTHER_ORIGIN1]: {type: 'default-local-reporting'}}
    278  });
    279  const reports1 = await pollReports(OTHER_ORIGIN1);
    280  verifyReports(reports1);
    281 
    282  const reports2 =
    283      await pollReports(OTHER_ORIGIN2, /*wait_for=*/ 1, /*timeout=*/ 1000);
    284  assert_equals(reports2, null);
    285 }, 'Real time reporting one buyer opted-in but not the other.');
    286 
    287 subsetTest(promise_test, async test => {
    288  const uuid = generateUuid(test);
    289  await resetReports(MAIN_PATH);
    290  await joinCrossOriginInterestGroup(test, uuid, OTHER_ORIGIN1, {
    291    biddingLogicURL: createBiddingScriptURLForRealTimeReporting(OTHER_ORIGIN1)
    292  });
    293  await runBasicFledgeTestExpectingNoWinner(test, uuid, {
    294    decisionLogicURL: createDecisionScriptURL(uuid, {
    295      scoreAd: `
    296        realTimeReporting.contributeToHistogram({ bucket: 200, priorityWeight: 1});
    297        return -1;`
    298    }),
    299    interestGroupBuyers: [OTHER_ORIGIN1],
    300    sellerRealTimeReportingConfig: {type: 'default-local-reporting'},
    301    perBuyerRealTimeReportingConfig:
    302        {[OTHER_ORIGIN1]: {type: 'default-local-reporting'}}
    303  });
    304  const sellerReports = await pollReports(location.origin);
    305  verifyReports(sellerReports);
    306 
    307  const buyerReports = await pollReports(OTHER_ORIGIN1);
    308  verifyReports(buyerReports);
    309 }, 'Real time reports are sent when all bids are rejected.');
    310 
    311 // TODO(qingxinwu): script fetches failing cases.
    312 
    313 subsetTest(promise_test, async test => {
    314  const uuid = generateUuid(test);
    315 
    316  let buyer = window.location.origin;
    317  let componentSellerOptIn = OTHER_ORIGIN1;
    318  let componentSellerNotOptIn = OTHER_ORIGIN2;
    319  let topLevelSeller = OTHER_ORIGIN3;
    320  await resetReports(MAIN_PATH);
    321  await joinCrossOriginInterestGroup(
    322      test, uuid, buyer,
    323      {biddingLogicURL: createBiddingScriptURLForRealTimeReporting(buyer)});
    324 
    325  const componentAuctions = [
    326    {
    327      seller: componentSellerOptIn,
    328      interestGroupBuyers: [buyer],
    329      decisionLogicURL: createDecisionScriptURLForRealTimeReporting(
    330          uuid, componentSellerOptIn),
    331      sellerRealTimeReportingConfig: {type: 'default-local-reporting'},
    332      perBuyerRealTimeReportingConfig:
    333          {[buyer]: {type: 'default-local-reporting'}}
    334    },
    335    {
    336      seller: componentSellerNotOptIn,
    337      interestGroupBuyers: [buyer],
    338      decisionLogicURL: createDecisionScriptURLForRealTimeReporting(
    339          uuid, componentSellerNotOptIn),
    340    }
    341  ];
    342  let auctionConfig = createMultiSellerAuctionConfig(
    343      uuid, topLevelSeller,
    344      createDecisionScriptURLForRealTimeReporting(uuid, topLevelSeller),
    345      componentAuctions, {});
    346  auctionConfig.sellerRealTimeReportingConfig = {
    347    type: 'default-local-reporting'
    348  };
    349 
    350  await runBasicFledgeAuctionAndNavigate(test, uuid, auctionConfig);
    351  const reportsBuyer = await pollReports(buyer);
    352  verifyReports(reportsBuyer);
    353 
    354  const reportsComponentSellerOptIn = await pollReports(componentSellerOptIn);
    355  verifyReports(reportsComponentSellerOptIn);
    356 
    357  const reportsTopLevelSeller = await pollReports(topLevelSeller);
    358  verifyReports(reportsTopLevelSeller);
    359 
    360  const reportsComponentSellerNotOptIn = await pollReports(
    361      componentSellerOptIn, /*wait_for=*/ 1, /*timeout=*/ 1000);
    362  assert_equals(reportsComponentSellerNotOptIn, null);
    363 }, 'Real time reporting in a multi seller auction.');