tor-browser

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

commit 5f45348f92aff96aeb7dc58601476fe39f8e380d
parent a7974ab2f8a7a189137ccf14107b9202a450b2c6
Author: Alexandru Marc <amarc@mozilla.com>
Date:   Thu,  2 Oct 2025 23:43:09 +0300

Revert "Bug 1974059 - Attribution service with specific to Newtab r=mconley,home-newtab-reviewers" for causing build bustages

This reverts commit e79b03ef1a42878332895a48e501649ce6725030.

Diffstat:
Dbrowser/extensions/newtab/lib/NewtabAttributionService.sys.mjs | 379-------------------------------------------------------------------------------
Dbrowser/extensions/newtab/test/xpcshell/test_NewtabAttributionService.js | 612-------------------------------------------------------------------------------
Mbrowser/extensions/newtab/test/xpcshell/xpcshell.toml | 2--
3 files changed, 0 insertions(+), 993 deletions(-)

diff --git a/browser/extensions/newtab/lib/NewtabAttributionService.sys.mjs b/browser/extensions/newtab/lib/NewtabAttributionService.sys.mjs @@ -1,379 +0,0 @@ -/* vim: set ts=2 sw=2 sts=2 et tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const lazy = {}; - -ChromeUtils.defineESModuleGetters(lazy, { - IndexedDB: "resource://gre/modules/IndexedDB.sys.mjs", - DAPTelemetrySender: "resource://gre/modules/DAPTelemetrySender.sys.mjs", -}); - -const MAX_CONVERSIONS = 5; -const MAX_LOOKBACK_DAYS = 30; -const DAY_IN_MILLI = 1000 * 60 * 60 * 24; -const CONVERSION_RESET_MILLI = 7 * DAY_IN_MILLI; - -/** - * - */ -export class NewTabAttributionService { - /** - * @typedef { 'view' | 'click' | 'default' } matchType - Available matching methodologies for conversion events. - * - * @typedef { 'view' | 'click' } eventType - A subset of matchType values that Newtab will register events. - * - * @typedef {object} task - DAP task settings. - * @property {string} id - task id. - * @property {string} vdaf - vdaf type. - * @property {number} bits - datatype size. - * @property {number} length - number of buckets. - * @property {number} time_precision - time precision. - * - * @typedef {object} allocatedTask - * @property {task} task - DAP task settings. - * @property {number} defaultMeasurement - Measurement value used if budget is exceeded. - * @property {number} index - Measurement value used if budget is not exceeded. - * - * @typedef {object} impression - stored event. - * @property {allocatedTask} conversion - DAP task settings for conversion attribution. - * @property {number} lastImpression - Timestamp in milliseconds for last touch matching. - * @property {number} lastView - Timestamp in milliseconds for last view matching. - * @property {number} lastClick - Timestamp in milliseconds for last click matching. - * - * @typedef {object} budget - stored budget. - * @property {number} conversions - Number of conversions that have occurred in the budget period. - * @property {number} nextReset - Timestamp in milliseconds for the end of the period this budget applies to. - */ - #dapTelemetrySenderInternal; - #dateProvider; - // eslint-disable-next-line no-unused-private-class-members - #testDapOptions; - - constructor({ dapTelemetrySender, dateProvider, testDapOptions } = {}) { - this.#dapTelemetrySenderInternal = dapTelemetrySender; - this.#dateProvider = dateProvider ?? Date; - this.#testDapOptions = testDapOptions; - - this.dbName = "NewTabAttribution"; - this.impressionStoreName = "impressions"; - this.budgetStoreName = "budgets"; - this.storeNames = [this.impressionStoreName, this.budgetStoreName]; - this.dbVersion = 1; - this.models = { - default: "lastImpression", - view: "lastView", - click: "lastClick", - }; - } - - get #dapTelemetrySender() { - return this.#dapTelemetrySenderInternal || lazy.DAPTelemetrySender; - } - - #now() { - return this.#dateProvider.now(); - } - - /** - * onAttributionEvent stores an event locally for an attributable interaction on Newtab. - * - * @param {eventType} type - The type of event. - * @param {*} params - Attribution task details & partner, to enable attribution matching - * with this event and submission to DAP. - */ - async onAttributionEvent(type, params) { - try { - const now = this.#now(); - - const impressionStore = await this.#getImpressionStore(); - - if (!params || !params.conversion) { - return; - } - - const impression = await this.#getImpression( - impressionStore, - params.partner_id, - { - conversion: { - task: { - id: params.conversion.task_id, - vdaf: params.conversion.vdaf, - bits: params.conversion.bits, - length: params.conversion.length, - time_precision: params.conversion.time_precision, - }, - defaultMeasurement: params.conversion.default_measurement, - index: params.conversion.index, - }, - } - ); - - const prop = this.#getModelProp(type); - impression.lastImpression = now; - impression[prop] = now; - - await this.#updateImpression( - impressionStore, - params.partner_id, - impression - ); - } catch (e) { - console.error(e); - } - } - - /** - * onAttributionClear - */ - async onAttributionClear() {} - - /** - * onAttributionReset - */ - async onAttributionReset() {} - - /** - * onAttributionConversion checks for eligible Newtab events and submits - * a DAP report. - * - * @param {string} partnerId - The partner that the conversion occured for. Compared against - * local events to see if any of them are eligible. - * @param {number} lookbackDays - The number of days prior to now that an event can be for it - * to be eligible. - * @param {matchType} impressionType - How the matching of events is determined. - * 'view': attributes the most recent eligible view event. - * 'click': attributes the most recent eligible click event. - * 'default': attributes the most recent eligible event of any type. - */ - async onAttributionConversion(partnerId, lookbackDays, impressionType) { - try { - if (lookbackDays > MAX_LOOKBACK_DAYS) { - return; - } - - const now = this.#now(); - - const budget = await this.#getBudget(partnerId, now); - const impression = await this.#findImpression( - partnerId, - lookbackDays, - impressionType, - now - ); - - let conversion = impression?.conversion; - if (!conversion) { - // retreive "conversion" for conversions with no found impression - // conversion = await this.#getUnattributedTask(partnerId); - if (!conversion) { - return; - } - } - - let measurement = conversion.defaultMeasurement; - let budgetSpend = 0; - if (budget.conversions < MAX_CONVERSIONS && conversion) { - budgetSpend = 1; - if (conversion.task && conversion.task.length > conversion.index) { - measurement = conversion.index; - } - } - - await this.#updateBudget(budget, budgetSpend, partnerId); - await this.#dapTelemetrySender.sendDAPMeasurement( - conversion.task, - measurement, - {} - ); - } catch (e) { - console.error(e); - } - } - - /** - * findImpression queries the local events to find an attributable event. - * @param {string} partnerId - Partner the event must be associated with. - * @param {number} lookbackDays - Maximum number of days ago that the event occurred for it to - * be eligible. - * @param {matchType} impressionType - How the matching of events is determined. Determines what - * timestamp property to compare against. - * @param {number} now - Timestamp in milliseconds when the conversion event was triggered - * @returns {Promise<impression|undefined>} - The impression that most recently occurred matching the - * search criteria. - */ - async #findImpression(partnerId, lookbackDays, impressionType, now) { - // Get impressions for the partner - const impressionStore = await this.#getImpressionStore(); - const impressions = await this.#getPartnerImpressions( - impressionStore, - partnerId - ); - - // Determine what timestamp to compare against for the matching methodology - const prop = this.#getModelProp(impressionType); - - // Find the most relevant impression - const lookbackWindow = now - lookbackDays * DAY_IN_MILLI; - return ( - impressions - // Filter by lookback days - .filter(impression => impression[prop] >= lookbackWindow) - // Get the impression with the most recent interaction - .reduce( - (cur, impression) => - !cur || impression[prop] > cur[prop] ? impression : cur, - null - ) - ); - } - - /** - * getImpression searches existing events for the partner and retuns the event - * if it is found, defaulting to the passed in impression if there are none. This - * enables timestamp fields of the stored event to be updated or carried forward. - * @param {ObjectStore} impressionStore - Promise-based wrapped IDBObjectStore. - * @param {string} partnerId - partner this event is associated with. - * @param {impression} defaultImpression - event to use if it has not been seen previously. - * @returns {Promise<impression>} - */ - async #getImpression(impressionStore, partnerId, defaultImpression) { - const impressions = await this.#getPartnerImpressions( - impressionStore, - partnerId - ); - const impression = impressions.find(r => - this.#compareImpression(r, defaultImpression) - ); - - return impression ?? defaultImpression; - } - - /** - * updateImpression stores the passed event, either updating the record - * if this event was already seen, or appending to the list of events if it is new. - * @param {ObjectStore} impressionStore - Promise-based wrapped IDBObjectStore. - * @param {string} partnerId - partner this event is associated with. - * @param {impression} impression - event to update. - */ - async #updateImpression(impressionStore, partnerId, impression) { - let impressions = await this.#getPartnerImpressions( - impressionStore, - partnerId - ); - - const i = impressions.findIndex(r => - this.#compareImpression(r, impression) - ); - if (i < 0) { - impressions.push(impression); - } else { - impressions[i] = impression; - } - - await impressionStore.put(impressions, partnerId); - } - - /** - * @param {impression} cur - * @param {impression} impression - * @returns {boolean} true if cur and impression have the same DAP allocation, else false. - */ - #compareImpression(cur, impression) { - return ( - cur.conversion.task.id === impression.conversion.task.id && - cur.conversion.index === impression.conversion.index - ); - } - - /** - * getBudget returns the current budget available for the partner. - * - * @param {string} partnerId - partner to look up budget for. - * @param {number} now - Timestamp in milliseconds. - * @returns {Promise<budget>} the current budget for the partner. - */ - async #getBudget(partnerId, now) { - const budgetStore = await this.#getBudgetStore(); - const budget = await budgetStore.get(partnerId); - - if (!budget || now > budget.nextReset) { - return { - conversions: 0, - nextReset: now + CONVERSION_RESET_MILLI, - }; - } - - return budget; - } - - /** - * updateBudget updates the stored budget to indicate some has been used. - * @param {budget} budget - current budget to be modified. - * @param {number} value - amount of budget that has been used. - * @param {string} partnerId - partner this budget is for. - */ - async #updateBudget(budget, value, partnerId) { - const budgetStore = await this.#getBudgetStore(); - budget.conversions += value; - await budgetStore.put(budget, partnerId); - } - - /** - * @param {ObjectStore} impressionStore - Promise-based wrapped IDBObjectStore. - * @param {string} partnerId - partner to look up impressions for. - * @returns {Promise<Array<impression>>} impressions associated with the partner. - */ - async #getPartnerImpressions(impressionStore, partnerId) { - const impressions = (await impressionStore.get(partnerId)) ?? []; - return impressions; - } - - async #getImpressionStore() { - return await this.#getStore(this.impressionStoreName); - } - - async #getBudgetStore() { - return await this.#getStore(this.budgetStoreName); - } - - async #getStore(storeName) { - return (await this.#db).objectStore(storeName, "readwrite"); - } - - get #db() { - return this._db || (this._db = this.#createOrOpenDb()); - } - - async #createOrOpenDb() { - try { - return await this.#openDatabase(); - } catch { - await lazy.IndexedDB.deleteDatabase(this.dbName); - return this.#openDatabase(); - } - } - - async #openDatabase() { - return await lazy.IndexedDB.open(this.dbName, this.dbVersion, db => { - this.storeNames.forEach(store => { - if (!db.objectStoreNames.contains(store)) { - db.createObjectStore(store); - } - }); - }); - } - - /** - * getModelProp returns the property name associated with a given matching - * methodology. - * - * @param {matchType} type - * @returns {string} The name of the timestamp property to check against. - */ - #getModelProp(type) { - return this.models[type] ?? this.models.default; - } -} diff --git a/browser/extensions/newtab/test/xpcshell/test_NewtabAttributionService.js b/browser/extensions/newtab/test/xpcshell/test_NewtabAttributionService.js @@ -1,612 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. -https://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -ChromeUtils.defineESModuleGetters(this, { - NewTabAttributionService: - "resource://newtab/lib/NewTabAttributionService.sys.mjs", -}); - -const { HttpServer } = ChromeUtils.importESModule( - "resource://testing-common/httpd.sys.mjs" -); - -const BinaryInputStream = Components.Constructor( - "@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", - "setInputStream" -); - -const PREF_LEADER = "toolkit.telemetry.dap.leader.url"; -const PREF_HELPER = "toolkit.telemetry.dap.helper.url"; -const TASK_ID = "DSZGMFh26hBYXNaKvhL_N4AHA3P5lDn19on1vFPBxJM"; -const MAX_CONVERSIONS = 5; -const DAY_IN_MILLI = 1000 * 60 * 60 * 24; -const LOOKBACK_DAYS = 1; -const MAX_LOOKBACK_DAYS = 30; -const HISTOGRAM_SIZE = 5; - -class MockDateProvider { - constructor() { - this._now = Date.now(); - } - - now() { - return this._now; - } - - add(interval_ms) { - this._now += interval_ms; - } -} - -class MockDAPTelemetrySender { - constructor() { - this.receivedMeasurements = []; - } - - async sendDAPMeasurement(task, measurement, _options) { - this.receivedMeasurements.push({ - task, - measurement, - }); - } -} - -class MockServer { - constructor() { - this.receivedReports = []; - - const server = new HttpServer(); - - server.registerPrefixHandler( - "/leader_endpoint/tasks/", - this.uploadHandler.bind(this) - ); - - this._server = server; - } - - start() { - this._server.start(-1); - - this.orig_leader = Services.prefs.getStringPref(PREF_LEADER); - this.orig_helper = Services.prefs.getStringPref(PREF_HELPER); - - const i = this._server.identity; - const serverAddr = `${i.primaryScheme}://${i.primaryHost}:${i.primaryPort}`; - Services.prefs.setStringPref(PREF_LEADER, `${serverAddr}/leader_endpoint`); - Services.prefs.setStringPref(PREF_HELPER, `${serverAddr}/helper_endpoint`); - } - - async stop() { - Services.prefs.setStringPref(PREF_LEADER, this.orig_leader); - Services.prefs.setStringPref(PREF_HELPER, this.orig_helper); - - await this._server.stop(); - } - - uploadHandler(request, response) { - let body = new BinaryInputStream(request.bodyInputStream); - - this.receivedReports.push({ - contentType: request.getHeader("Content-Type"), - size: body.available(), - }); - - response.setStatusLine(request.httpVersion, 200); - } -} - -add_setup(async function () { - do_get_profile(); -}); - -add_task(async function testSuccessfulConversion() { - const mockSender = new MockDAPTelemetrySender(); - const privateAttribution = new NewTabAttributionService({ - dapTelemetrySender: mockSender, - }); - - const partnerIdentifier = "partner_identifier"; - const conversionSettings = { - task_id: TASK_ID, - vdaf: "histogram", - bits: 1, - length: HISTOGRAM_SIZE, - time_precision: 60, - default_measurement: 0, - index: 1, - }; - - await privateAttribution.onAttributionEvent("view", { - partner_id: partnerIdentifier, - conversion: conversionSettings, - }); - - await privateAttribution.onAttributionEvent("click", { - partner_id: partnerIdentifier, - conversion: conversionSettings, - }); - - await privateAttribution.onAttributionConversion( - partnerIdentifier, - LOOKBACK_DAYS, - "view" - ); - - const expectedMeasurement = { - task: { - id: conversionSettings.task_id, - vdaf: conversionSettings.vdaf, - bits: conversionSettings.bits, - length: conversionSettings.length, - time_precision: conversionSettings.time_precision, - }, - measurement: conversionSettings.index, - }; - - const receivedMeasurement = mockSender.receivedMeasurements.pop(); - Assert.deepEqual(receivedMeasurement, expectedMeasurement); - Assert.equal(mockSender.receivedMeasurements.length, 0); -}); - -add_task(async function testConversionWithoutImpression() { - const mockSender = new MockDAPTelemetrySender(); - const privateAttribution = new NewTabAttributionService({ - dapTelemetrySender: mockSender, - }); - - const partnerIdentifier = "partner_identifier_no_impression"; - - await privateAttribution.onAttributionConversion( - partnerIdentifier, - LOOKBACK_DAYS, - "view" - ); - - Assert.equal(mockSender.receivedMeasurements.length, 0); -}); - -add_task(async function testConversionWithInvalidLookbackDays() { - const mockSender = new MockDAPTelemetrySender(); - const privateAttribution = new NewTabAttributionService({ - dapTelemetrySender: mockSender, - }); - - const partnerIdentifier = "partner_identifier"; - const conversionSettings = { - task_id: TASK_ID, - vdaf: "histogram", - bits: 1, - length: HISTOGRAM_SIZE, - time_precision: 60, - default_measurement: 0, - index: 1, - }; - - await privateAttribution.onAttributionEvent("view", { - partner_id: partnerIdentifier, - conversion: conversionSettings, - }); - - await privateAttribution.onAttributionConversion( - partnerIdentifier, - MAX_LOOKBACK_DAYS + 1, - "view" - ); - - Assert.equal(mockSender.receivedMeasurements.length, 0); -}); - -add_task(async function testSelectionByLastView() { - const mockSender = new MockDAPTelemetrySender(); - const mockDateProvider = new MockDateProvider(); - const privateAttribution = new NewTabAttributionService({ - dapTelemetrySender: mockSender, - dateProvider: mockDateProvider, - }); - - const partnerIdentifier = "partner_identifier_last_view"; - const conversionSettings = { - task_id: TASK_ID, - vdaf: "histogram", - bits: 1, - length: HISTOGRAM_SIZE, - time_precision: 60, - default_measurement: 0, - index: 1, - }; - const selectedViewIndex = 1; - const ignoredViewIndex = 2; - const clickIndex = 3; - - // View event that will be ignored, as a more recent view will exist - await privateAttribution.onAttributionEvent("view", { - partner_id: partnerIdentifier, - conversion: { - ...conversionSettings, - index: ignoredViewIndex, - }, - }); - - // step forward time - mockDateProvider.add(10); - - // View event that will be selected, as no more recent view exists - await privateAttribution.onAttributionEvent("view", { - partner_id: partnerIdentifier, - conversion: { - ...conversionSettings, - index: selectedViewIndex, - }, - }); - - // step forward time - mockDateProvider.add(10); - - // Click event that will be ignored because the match type is "view" - await privateAttribution.onAttributionEvent("click", { - partner_id: partnerIdentifier, - conversion: { - ...conversionSettings, - index: clickIndex, - }, - }); - - // Conversion filtering for "view" finds the view event - await privateAttribution.onAttributionConversion( - partnerIdentifier, - LOOKBACK_DAYS, - "view" - ); - - let receivedMeasurement = mockSender.receivedMeasurements.pop(); - Assert.deepEqual(receivedMeasurement.measurement, selectedViewIndex); - Assert.equal(mockSender.receivedMeasurements.length, 0); -}); - -add_task(async function testSelectionByLastClick() { - const mockSender = new MockDAPTelemetrySender(); - const mockDateProvider = new MockDateProvider(); - const privateAttribution = new NewTabAttributionService({ - dapTelemetrySender: mockSender, - dateProvider: mockDateProvider, - }); - - const partnerIdentifier = "partner_identifier_last_click"; - const conversionSettings = { - task_id: TASK_ID, - vdaf: "histogram", - bits: 1, - length: HISTOGRAM_SIZE, - time_precision: 60, - default_measurement: 0, - index: 1, - }; - const viewIndex = 1; - const ignoredClickIndex = 2; - const selectedClickIndex = 3; - - // Click event that will be ignored, as a more recent click will exist - await privateAttribution.onAttributionEvent("click", { - partner_id: partnerIdentifier, - conversion: { - ...conversionSettings, - index: ignoredClickIndex, - }, - }); - - // step forward time - mockDateProvider.add(10); - - // Click event that will be selected, as no more recent click exists - await privateAttribution.onAttributionEvent("click", { - partner_id: partnerIdentifier, - conversion: { - ...conversionSettings, - index: selectedClickIndex, - }, - }); - - // step forward time - mockDateProvider.add(10); - - // View event that will be ignored because the match type is "click" - await privateAttribution.onAttributionEvent("view", { - partner_id: partnerIdentifier, - conversion: { - ...conversionSettings, - index: viewIndex, - }, - }); - - // Conversion filtering for "click" finds the click event - await privateAttribution.onAttributionConversion( - partnerIdentifier, - LOOKBACK_DAYS, - "click" - ); - - let receivedMeasurement = mockSender.receivedMeasurements.pop(); - Assert.deepEqual(receivedMeasurement.measurement, selectedClickIndex); - Assert.equal(mockSender.receivedMeasurements.length, 0); -}); - -add_task(async function testSelectionByLastTouch() { - const mockSender = new MockDAPTelemetrySender(); - const mockDateProvider = new MockDateProvider(); - const privateAttribution = new NewTabAttributionService({ - dapTelemetrySender: mockSender, - dateProvider: mockDateProvider, - }); - - const partnerIdentifier = "partner_identifier_last_touch"; - const conversionSettings = { - task_id: TASK_ID, - vdaf: "histogram", - bits: 1, - length: HISTOGRAM_SIZE, - time_precision: 60, - default_measurement: 0, - index: 1, - }; - const viewIndex = 1; - const clickIndex = 2; - - // Click at clickIndex - await privateAttribution.onAttributionEvent("click", { - partner_id: partnerIdentifier, - conversion: { - ...conversionSettings, - index: clickIndex, - }, - }); - - // step forward time so the view event occurs most recently - mockDateProvider.add(10); - - // View at viewIndex - await privateAttribution.onAttributionEvent("view", { - partner_id: partnerIdentifier, - conversion: { - ...conversionSettings, - index: viewIndex, - }, - }); - - // Conversion filtering for "default" finds the view event - await privateAttribution.onAttributionConversion( - partnerIdentifier, - LOOKBACK_DAYS, - "default" - ); - - let receivedMeasurement = mockSender.receivedMeasurements.pop(); - Assert.deepEqual(receivedMeasurement.measurement, viewIndex); - Assert.equal(mockSender.receivedMeasurements.length, 0); -}); - -add_task(async function testSelectionByPartnerId() { - const mockSender = new MockDAPTelemetrySender(); - const mockDateProvider = new MockDateProvider(); - const privateAttribution = new NewTabAttributionService({ - dapTelemetrySender: mockSender, - dateProvider: mockDateProvider, - }); - - const partnerIdentifier1 = "partner_identifier_1"; - const partnerIdentifier2 = "partner_identifier_2"; - const conversionSettings = { - task_id: TASK_ID, - vdaf: "histogram", - bits: 1, - length: HISTOGRAM_SIZE, - time_precision: 60, - default_measurement: 0, - index: 1, - }; - const partner1Index = 1; - const partner2Index = 2; - - // view event associated with partner 1 - await privateAttribution.onAttributionEvent("view", { - partner_id: partnerIdentifier1, - conversion: { - ...conversionSettings, - index: partner1Index, - }, - }); - - // step forward time so the partner 2 event occurs most recently - mockDateProvider.add(10); - - // view event associated with partner 2 - await privateAttribution.onAttributionEvent("view", { - partner_id: partnerIdentifier2, - conversion: { - ...conversionSettings, - index: partner2Index, - }, - }); - - // Conversion filtering for "default" finds the correct view event - await privateAttribution.onAttributionConversion( - partnerIdentifier1, - LOOKBACK_DAYS, - "default" - ); - - let receivedMeasurement = mockSender.receivedMeasurements.pop(); - Assert.deepEqual(receivedMeasurement.measurement, partner1Index); - Assert.equal(mockSender.receivedMeasurements.length, 0); -}); - -add_task(async function testExpiredImpressions() { - const mockSender = new MockDAPTelemetrySender(); - const mockDateProvider = new MockDateProvider(); - const privateAttribution = new NewTabAttributionService({ - dapTelemetrySender: mockSender, - dateProvider: mockDateProvider, - }); - - const partnerIdentifier = "partner_identifier"; - const conversionSettings = { - task_id: TASK_ID, - vdaf: "histogram", - bits: 1, - length: HISTOGRAM_SIZE, - time_precision: 60, - default_measurement: 0, - index: 1, - }; - - // Register impression - await privateAttribution.onAttributionEvent("view", { - partner_id: partnerIdentifier, - conversion: conversionSettings, - }); - - // Fast-forward time by LOOKBACK_DAYS days + 1 ms - mockDateProvider.add(LOOKBACK_DAYS * DAY_IN_MILLI + 1); - - // Conversion doesn't match expired impression - await privateAttribution.onAttributionConversion( - partnerIdentifier, - LOOKBACK_DAYS, - "view" - ); - - Assert.equal(mockSender.receivedMeasurements.length, 0); -}); - -add_task(async function testConversionBudget() { - const mockSender = new MockDAPTelemetrySender(); - const privateAttribution = new NewTabAttributionService({ - dapTelemetrySender: mockSender, - }); - - const partnerIdentifier = "partner_identifier_budget"; - const conversionSettings = { - task_id: TASK_ID, - vdaf: "histogram", - bits: 1, - length: HISTOGRAM_SIZE, - time_precision: 60, - default_measurement: 0, - index: 1, - }; - - await privateAttribution.onAttributionEvent("view", { - partner_id: partnerIdentifier, - conversion: conversionSettings, - }); - - // Measurements uploaded for conversions up to MAX_CONVERSIONS - for (let i = 0; i < MAX_CONVERSIONS; i++) { - await privateAttribution.onAttributionConversion( - partnerIdentifier, - LOOKBACK_DAYS, - "view" - ); - - const receivedMeasurement = mockSender.receivedMeasurements.pop(); - Assert.deepEqual(receivedMeasurement.measurement, conversionSettings.index); - Assert.equal(mockSender.receivedMeasurements.length, 0); - } - - // default report uploaded on subsequent conversions - await privateAttribution.onAttributionConversion( - partnerIdentifier, - LOOKBACK_DAYS, - "view" - ); - - const receivedMeasurement = mockSender.receivedMeasurements.pop(); - Assert.deepEqual( - receivedMeasurement.measurement, - conversionSettings.default_measurement - ); - Assert.equal(mockSender.receivedMeasurements.length, 0); -}); - -add_task(async function testHistogramSize() { - const mockSender = new MockDAPTelemetrySender(); - const privateAttribution = new NewTabAttributionService({ - dapTelemetrySender: mockSender, - }); - - const partnerIdentifier = "partner_identifier_bad_settings"; - const conversionSettings = { - task_id: TASK_ID, - vdaf: "histogram", - bits: 1, - length: HISTOGRAM_SIZE, - time_precision: 60, - default_measurement: 0, - // Zero-based index equal to histogram size is out of bounds - index: HISTOGRAM_SIZE, - }; - - await privateAttribution.onAttributionEvent("view", { - partner_id: partnerIdentifier, - conversion: conversionSettings, - }); - - await privateAttribution.onAttributionConversion( - partnerIdentifier, - LOOKBACK_DAYS, - "view" - ); - - const receivedMeasurement = mockSender.receivedMeasurements.pop(); - Assert.deepEqual( - receivedMeasurement.measurement, - conversionSettings.default_measurement - ); - Assert.equal(mockSender.receivedMeasurements.length, 0); -}); - -add_task(async function testWithRealDAPSender() { - // Omit mocking DAP telemetry sender in this test to defend against mock - // sender getting out of sync - const mockServer = new MockServer(); - mockServer.start(); - - const privateAttribution = new NewTabAttributionService({}); - - const partnerIdentifier = "partner_identifier_real_dap"; - const conversionSettings = { - task_id: TASK_ID, - vdaf: "histogram", - bits: 1, - length: HISTOGRAM_SIZE, - time_precision: 60, - default_measurement: 0, - index: 1, - }; - - await privateAttribution.onAttributionEvent("view", { - partner_id: partnerIdentifier, - conversion: conversionSettings, - }); - - await privateAttribution.onAttributionConversion( - partnerIdentifier, - LOOKBACK_DAYS, - "view" - ); - - await mockServer.stop(); - - Assert.equal(mockServer.receivedReports.length, 1); - - const expectedReport = { - contentType: "application/dap-report", - size: 502, - }; - - const receivedReport = mockServer.receivedReports.pop(); - Assert.deepEqual(receivedReport, expectedReport); -}); diff --git a/browser/extensions/newtab/test/xpcshell/xpcshell.toml b/browser/extensions/newtab/test/xpcshell/xpcshell.toml @@ -23,8 +23,6 @@ support-files = ["topstories.json"] ["test_LocalInferredRanking.js"] -["test_NewTabAttributionService.js"] - ["test_NewTabContentPing.js"] ["test_NewTabGleanUtils.js"]