diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:35:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:35:29 +0000 |
commit | 59203c63bb777a3bacec32fb8830fba33540e809 (patch) | |
tree | 58298e711c0ff0575818c30485b44a2f21bf28a0 /testing/web-platform/tests/fledge/tentative/interest-group-update.https.window.js | |
parent | Adding upstream version 126.0.1. (diff) | |
download | firefox-59203c63bb777a3bacec32fb8830fba33540e809.tar.xz firefox-59203c63bb777a3bacec32fb8830fba33540e809.zip |
Adding upstream version 127.0.upstream/127.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/fledge/tentative/interest-group-update.https.window.js')
-rw-r--r-- | testing/web-platform/tests/fledge/tentative/interest-group-update.https.window.js | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/testing/web-platform/tests/fledge/tentative/interest-group-update.https.window.js b/testing/web-platform/tests/fledge/tentative/interest-group-update.https.window.js new file mode 100644 index 0000000000..59b3736b09 --- /dev/null +++ b/testing/web-platform/tests/fledge/tentative/interest-group-update.https.window.js @@ -0,0 +1,406 @@ +// META: script=/resources/testdriver.js +// META: script=/common/utils.js +// META: script=resources/fledge-util.sub.js +// META: script=/common/subset-tests.js +// META: timeout=long +// META: variant=?1-4 +// META: variant=?5-9 +// META: variant=?10-14 +// META: variant=?15-19 +// META: variant=?20-last + +"use strict;" + +// This test repeatedly runs auctions to verify an update. A modified bidding script +// continuously throws errors until it detects the expected change in the interest group +// field. This update then stops the auction cycle. +const makeTestForUpdate = ({ + // Test name + name, + // fieldname that is getting updated + interestGroupFieldName, + // This is used to check if update has happened. + expectedValue, + // This is used to create the update response, by default it will always send + // back the `expectedValue`. Extra steps to make a deep copy. + responseOverride = expectedValue, + // Overrides to the interest group. + interestGroupOverrides = {}, + // Overrides to the auction config. + auctionConfigOverrides = {}, +}) => { + subsetTest(promise_test, async test => { + const uuid = generateUuid(test); + extraBiddingLogic = ``; + + let replacePlaceholders = (ads) => ads.forEach(element => { + element.renderURL = element.renderURL.replace(`UUID-PLACEHOLDER`, uuid); + }); + + // Testing 'ads' requires some additional setup due to it's reliance + // on createRenderURL, specifically the bidding script used checks to make + // sure the `uuid` is the correct one for the test. We use a renderURL + // with a placeholder 'UUID-PLACEHOLDER' and make sure to replace it + // before moving on to the test. + if (interestGroupFieldName === `ads`) { + if (interestGroupFieldName in interestGroupOverrides) { + replacePlaceholders(interestGroupOverrides[interestGroupFieldName]); + } + replacePlaceholders(responseOverride); + replacePlaceholders(expectedValue); + } + // When checking the render URL, both the deprecated 'renderUrl' and the updated 'renderURL' might exist + // in the interest group simultaneously, so this test deletes the 'renderUrl' to ensure a + // clean comparison with deepEquals. + if (interestGroupFieldName === `ads` || interestGroupFieldName === `adComponents`) { + extraBiddingLogic = ` + interestGroup.${interestGroupFieldName}.forEach(element => { + delete element.renderUrl; + });` + } + + let expectedValueJSON = JSON.stringify(expectedValue); + // When the update has not yet been seen, throw an error which will cause the + // auction not to have a result. + interestGroupOverrides.biddingLogicURL = createBiddingScriptURL({ + generateBid: ` + ${extraBiddingLogic} + if (!deepEquals(interestGroup.${interestGroupFieldName}, ${expectedValueJSON})) { + throw '${interestGroupFieldName} is ' + + JSON.stringify(interestGroup.${interestGroupFieldName}) + + ' instead of ' + '${expectedValueJSON}'; + }` + }); + + let responseBody = {}; + responseBody[interestGroupFieldName] = responseOverride; + let updateParams = { + body: JSON.stringify(responseBody), + uuid: uuid + }; + interestGroupOverrides.updateURL = createUpdateURL(updateParams); + await joinInterestGroup(test, uuid, interestGroupOverrides); + + // Run an auction until there's a winner, which means update occurred. + let auctionResult = await runBasicFledgeAuction(test, uuid, auctionConfigOverrides); + expectNoWinner(auctionResult); + while (!auctionResult) { + auctionResult = await runBasicFledgeAuction(test, uuid, auctionConfigOverrides); + } + }, name); +}; + +// In order to test the update process does not update certain fields, this test uses two interest groups: + +// * `failedUpdateGroup`: Receives an invalid update, and will continue to throw errors until the update +// occurs (which shouldn't happen). This group will have a high bid to ensure if +// there was ever a tie, it would win. +// * `successUpdateGroup`: A hard-coded interest group that receives a update and will signal the change +// by throwing an error. + +// By tracking render URLs, this test guarantees that only the URL associated with the correct update +// (`goodUpdateRenderURL`) is used, and the incorrect URL (`badUpdateRenderURL`) isn't. The test runs +// auctions repeatedly until the update in `successUpdateGroup` stops an auction from producing a winner. +// It then will run one final auction. If there's still no winner, it can infer that `failedUpdateGroup` +// would have received the update if it were propagating correctly. + +// If there was a bug in the implementation, a possible case can occur and manifest as a flaky test. +// In this scenerio with the current structure of the Protected Audience API, the `successUpdateGroup` +// updates, and so does the `failedUpdateGroup`, but the `failedUpdateGroup` update happens significantly +// after the `successUpdateGroup`'s update. In an effort to combat this, after the while loop we run +// another auction to ensure there is no winner (both cases should throw), but depending how slow the +// update takes, this flaky issue still can **possibly** occur. +const makeTestForNoUpdate = ({ + // Test name + name, + // fieldname that is should not be getting updated + interestGroupFieldName, + // this is used to create the update response and check if it did not happen. + responseOverride, + // Overrides to the auction config. + auctionConfigOverrides = {}, + // Overrides to the interest group. + failedUpdateGroup = {}, +}) => { + subsetTest(promise_test, async test => { + const uuid = generateUuid(test); + // successUpdateGroup + + // These are used in `successUpdateGroup` in order to get a proper update. + let successUpdateGroup = {}; + let successUpdateField = `userBiddingSignals`; + let successUpdateFieldExpectedValue = { 'test': 20 }; + + const goodUpdateRenderURL = createTrackerURL(window.location.origin, uuid, 'track_get', 'good_update'); + successUpdateGroup.ads = [{ 'renderURL': goodUpdateRenderURL }]; + successUpdateGroup.biddingLogicURL = createBiddingScriptURL({ + generateBid: ` + if (deepEquals(interestGroup.${successUpdateField}, ${JSON.stringify(successUpdateFieldExpectedValue)})){ + throw '${successUpdateField} has updated and is ' + + '${JSON.stringify(successUpdateFieldExpectedValue)}.' + }`, + bid: 5 + }); + + let successResponseBody = {}; + successResponseBody[successUpdateField] = successUpdateFieldExpectedValue; + let successUpdateParams = { + body: JSON.stringify(successResponseBody), + uuid: uuid + }; + successUpdateGroup.updateURL = createUpdateURL(successUpdateParams); + await joinInterestGroup(test, uuid, successUpdateGroup); + ///////////////////////// successUpdateGroup + + // failedUpdateGroup + const badUpdateRenderURL = createTrackerURL(window.location.origin, uuid, `track_get`, `bad_update`); + // Name needed so we don't have two IGs with same name. + failedUpdateGroup.name = failedUpdateGroup.name ? failedUpdateGroup.name : `IG name` + failedUpdateGroup.ads = [{ 'renderURL': badUpdateRenderURL }]; + failedUpdateGroup.biddingLogicURL = createBiddingScriptURL({ + generateBid: ` + if (!deepEquals(interestGroup.${interestGroupFieldName}, ${JSON.stringify(responseOverride)})){ + throw '${interestGroupFieldName} is as expected: '+ + JSON.stringify(interestGroup.${interestGroupFieldName}); + }`, + bid: 1000 + }); + let failedResponseBody = {}; + failedResponseBody[interestGroupFieldName] = responseOverride; + + let failedUpdateParams = { + body: JSON.stringify(failedResponseBody), + uuid: uuid + }; + + failedUpdateGroup.updateURL = createUpdateURL(failedUpdateParams); + await joinInterestGroup(test, uuid, failedUpdateGroup); + ///////////////////////// failedUpdateGroup + + // First result should be not be null, `successUpdateGroup` throws when update is detected so until then, + // run and observe the requests to ensure only `goodUpdateRenderURL` is fetched. + let auctionResult = await runBasicFledgeTestExpectingWinner(test, uuid, auctionConfigOverrides); + while (auctionResult) { + createAndNavigateFencedFrame(test, auctionResult); + await waitForObservedRequests( + uuid, + [goodUpdateRenderURL, createSellerReportURL(uuid)]); + await fetch(createCleanupURL(uuid)); + auctionResult = await runBasicFledgeAuction(test, uuid, auctionConfigOverrides); + } + // Re-run to ensure null because: + // `successUpdateGroup` should be throwing since update occurred. + // `failedUpdateGroup` should be throwing since update did not occur. + await runBasicFledgeTestExpectingNoWinner(test, uuid, auctionConfigOverrides); + }, name); +}; + +// Helper to eliminate rewriting a long call to createRenderURL(). +// Only thing to change would be signalParams to differentiate between URLs. +const createTempRenderURL = (signalsParams = null) => { + return createRenderURL(/*uuid=*/`UUID-PLACEHOLDER`,/*script=*/ null,/*signalParams=*/ signalsParams,/*origin=*/ null); +}; + +makeTestForUpdate({ + name: 'userBiddingSignals update overwrites everything in the field.', + interestGroupFieldName: 'userBiddingSignals', + expectedValue: { 'test': 20 }, + interestGroupOverrides: { + userBiddingSignals: { 'test': 10, 'extra_value': true }, + } +}); + +makeTestForUpdate({ + name: 'userBiddingSignals updated multi-type', + interestGroupFieldName: 'userBiddingSignals', + expectedValue: { 'test': 20, 5: [1, [false, false, true], 3, 'Hello'] }, + interestGroupOverrides: { + userBiddingSignals: { 'test': 10 }, + } +}); + +makeTestForUpdate({ + name: 'userBiddingSignals updated to non object', + interestGroupFieldName: 'userBiddingSignals', + expectedValue: 5, + interestGroupOverrides: { + userBiddingSignals: { 'test': 10 }, + } +}); + +makeTestForUpdate({ + name: 'userBiddingSignals updated to null', + interestGroupFieldName: 'userBiddingSignals', + expectedValue: null, + interestGroupOverrides: { + userBiddingSignals: { 'test': 10 }, + } +}); + +makeTestForUpdate({ + name: 'trustedBiddingSignalsKeys updated correctly', + interestGroupFieldName: 'trustedBiddingSignalsKeys', + expectedValue: ['new_key', 'old_key'], + interestGroupOverrides: { + trustedBiddingSignalsKeys: ['old_key'], + } +}); + +makeTestForUpdate({ + name: 'trustedBiddingSignalsKeys updated to empty array.', + interestGroupFieldName: 'trustedBiddingSignalsKeys', + expectedValue: [], + interestGroupOverrides: { + trustedBiddingSignalsKeys: ['old_key'], + } +}); + + +makeTestForUpdate({ + name: 'trustedBiddingSignalsSlotSizeMode updated to slot-size', + interestGroupFieldName: 'trustedBiddingSignalsSlotSizeMode', + expectedValue: 'slot-size', + interestGroupOverrides: { + trustedBiddingSignalsKeys: ['key'], + trustedBiddingSignalsSlotSizeMode: 'none', + } +}); + +makeTestForUpdate({ + name: 'trustedBiddingSignalsSlotSizeMode updated to all-slots-requested-sizes', + interestGroupFieldName: 'trustedBiddingSignalsSlotSizeMode', + expectedValue: 'all-slots-requested-sizes', + interestGroupOverrides: { + trustedBiddingSignalsKeys: ['key'], + trustedBiddingSignalsSlotSizeMode: 'slot-size', + } +}); + +makeTestForUpdate({ + name: 'trustedBiddingSignalsSlotSizeMode updated to none', + interestGroupFieldName: 'trustedBiddingSignalsSlotSizeMode', + expectedValue: 'none', + interestGroupOverrides: { + trustedBiddingSignalsKeys: ['key'], + trustedBiddingSignalsSlotSizeMode: 'slot-size', + } +}); + +makeTestForUpdate({ + name: 'trustedBiddingSignalsSlotSizeMode updated to unknown, defaults to none', + interestGroupFieldName: 'trustedBiddingSignalsSlotSizeMode', + expectedValue: 'none', + responseOverride: 'unknown-type', + interestGroupOverrides: { + trustedBiddingSignalsKeys: ['key'], + trustedBiddingSignalsSlotSizeMode: 'slot-size', + } +}); + +makeTestForUpdate({ + name: 'ads updated from 2 ads to 1.', + interestGroupFieldName: 'ads', + expectedValue: [ + { renderURL: createTempRenderURL('new_url1'), metadata: 'test1-new' }, + ], + interestGroupOverrides: { + ads: [{ renderURL: createTempRenderURL() }, + { renderURL: createTempRenderURL() }] + } +}); + +makeTestForUpdate({ + name: 'ads updated from 1 ad to 2.', + interestGroupFieldName: 'ads', + expectedValue: [{ renderURL: createTempRenderURL('new_url1'), metadata: 'test1-new' }, + { renderURL: createTempRenderURL('new_url2'), metadata: 'test2-new' }], + interestGroupOverrides: { + ads: [{ renderURL: createTempRenderURL() }] + } +}); + +makeTestForUpdate({ + name: 'adComponents updated from 1 adComponent to 2.', + interestGroupFieldName: 'adComponents', + expectedValue: [{ renderURL: createTempRenderURL('new_url1'), metadata: 'test1-new' }, + { renderURL: createTempRenderURL('new_url2'), metadata: 'test2' }], + interestGroupOverrides: { + adComponents: [{ renderURL: createTempRenderURL(), metadata: 'test1' }] + }, +}); + +makeTestForUpdate({ + name: 'adComponents updated from 2 adComponents to 1.', + interestGroupFieldName: 'adComponents', + expectedValue: [{ renderURL: createTempRenderURL('new_url1'), metadata: 'test1-new' }], + interestGroupOverrides: { + adComponents: [{ renderURL: createTempRenderURL() }, + { renderURL: createTempRenderURL() }] + }, +}); + +makeTestForUpdate({ + name: 'executionMode updated to frozen context', + interestGroupFieldName: 'executionMode', + expectedValue: 'frozen-context', + interestGroupOverrides: { + executionMode: 'compatibility', + } +}); + +makeTestForUpdate({ + name: 'executionMode updated to compatibility', + interestGroupFieldName: 'executionMode', + expectedValue: 'compatibility', + interestGroupOverrides: { + executionMode: 'frozen-context', + } +}); + +makeTestForUpdate({ + name: 'executionMode updated to group by origin', + interestGroupFieldName: 'executionMode', + expectedValue: 'group-by-origin', + interestGroupOverrides: { + executionMode: 'compatibility', + } +}); + +makeTestForNoUpdate({ + name: 'executionMode updated with invalid input', + interestGroupFieldName: 'executionMode', + responseOverride: 'unknown-type', +}); + +makeTestForNoUpdate({ + name: 'owner cannot be updated.', + interestGroupFieldName: 'owner', + responseOverride: OTHER_ORIGIN1, + auctionConfigOverrides: { + interestGroupBuyers: [OTHER_ORIGIN1, window.location.origin] + } +}); + +makeTestForNoUpdate({ + name: 'name cannot be updated.', + interestGroupFieldName: 'name', + responseOverride: 'new_name', + failedUpdateGroup: { name: 'name2' }, +}); + +makeTestForNoUpdate({ + name: 'executionMode not updated when unknown type.', + interestGroupFieldName: 'executionMode', + responseOverride: 'unknown-type', + failedUpdateGroup: { executionMode: 'compatibility' }, +}); + +makeTestForNoUpdate({ + name: 'trustedBiddingSignalsKeys not updated when bad value.', + interestGroupFieldName: 'trustedBiddingSignalsKeys', + responseOverride: 5, + failedUpdateGroup: { + trustedBiddingSignalsKeys: ['key'], + }, +}); + |