UNPKG

mk9-prebid

Version:

Header Bidding Management Library

1,126 lines (1,003 loc) 39.8 kB
import { deviceAccessHook, setEnforcementConfig, userSyncHook, userIdHook, makeBidRequestsHook, validateRules, enforcementRules, purpose1Rule, purpose2Rule, enableAnalyticsHook, getGvlid, internal } from 'modules/gdprEnforcement.js'; import { config } from 'src/config.js'; import adapterManager, { gdprDataHandler } from 'src/adapterManager.js'; import * as utils from 'src/utils.js'; import { validateStorageEnforcement } from 'src/storageManager.js'; import events from 'src/events.js'; describe('gdpr enforcement', function () { let nextFnSpy; let logWarnSpy; let gdprDataHandlerStub; let staticConfig = { cmpApi: 'static', timeout: 7500, allowAuctionWithoutConsent: false, consentData: { getTCData: { 'tcString': 'COuqj-POu90rDBcBkBENAZCgAPzAAAPAACiQFwwBAABAA1ADEAbQC4YAYAAgAxAG0A', 'cmpId': 92, 'cmpVersion': 100, 'tcfPolicyVersion': 2, 'gdprApplies': true, 'isServiceSpecific': true, 'useNonStandardStacks': false, 'purposeOneTreatment': false, 'publisherCC': 'US', 'cmpStatus': 'loaded', 'eventStatus': 'tcloaded', 'outOfBand': { 'allowedVendors': {}, 'discloseVendors': {} }, 'purpose': { 'consents': { '1': true, '2': true, '3': true, '7': true }, 'legitimateInterests': { '1': false, '2': true, '3': false } }, 'vendor': { 'consents': { '1': true, '2': true, '3': false, '4': true, '5': false }, 'legitimateInterests': { '1': false, '2': true, '3': false, '4': false, '5': false } }, 'specialFeatureOptins': { '1': false, '2': false }, 'restrictions': {}, 'publisher': { 'consents': { '1': false, '2': false, '3': false }, 'legitimateInterests': { '1': false, '2': false, '3': false }, 'customPurpose': { 'consents': {}, 'legitimateInterests': {} } } } } }; after(function () { validateStorageEnforcement.getHooks({ hook: deviceAccessHook }).remove(); $$PREBID_GLOBAL$$.requestBids.getHooks().remove(); adapterManager.makeBidRequests.getHooks({ hook: makeBidRequestsHook }).remove(); }) describe('deviceAccessHook', function () { let adapterManagerStub; function getBidderSpec(gvlid) { return { getSpec: () => { return { gvlid } } } } beforeEach(function () { nextFnSpy = sinon.spy(); gdprDataHandlerStub = sinon.stub(gdprDataHandler, 'getConsentData'); logWarnSpy = sinon.spy(utils, 'logWarn'); adapterManagerStub = sinon.stub(adapterManager, 'getBidAdapter'); }); afterEach(function () { config.resetConfig(); gdprDataHandler.getConsentData.restore(); logWarnSpy.restore(); adapterManagerStub.restore(); }); it('should not allow device access when device access flag is set to false', function () { config.setConfig({ deviceAccess: false, consentManagement: { gdpr: { rules: [{ purpose: 'storage', enforcePurpose: false, enforceVendor: false, vendorExceptions: ['appnexus', 'rubicon'] }] } } }); deviceAccessHook(nextFnSpy); expect(nextFnSpy.calledOnce).to.equal(true); let result = { hasEnforcementHook: true, valid: false } sinon.assert.calledWith(nextFnSpy, undefined, undefined, result); }); it('should only check for consent for vendor exceptions when enforcePurpose and enforceVendor are false', function () { adapterManagerStub.withArgs('appnexus').returns(getBidderSpec(1)); adapterManagerStub.withArgs('rubicon').returns(getBidderSpec(5)); setEnforcementConfig({ gdpr: { rules: [{ purpose: 'storage', enforcePurpose: false, enforceVendor: false, vendorExceptions: ['appnexus'] }] } }); let consentData = {} consentData.vendorData = staticConfig.consentData.getTCData; consentData.gdprApplies = true; consentData.apiVersion = 2; gdprDataHandlerStub.returns(consentData); deviceAccessHook(nextFnSpy, 1, 'appnexus'); deviceAccessHook(nextFnSpy, 5, 'rubicon'); expect(logWarnSpy.callCount).to.equal(0); }); it('should check consent for all vendors when enforcePurpose and enforceVendor are true', function () { adapterManagerStub.withArgs('appnexus').returns(getBidderSpec(1)); adapterManagerStub.withArgs('rubicon').returns(getBidderSpec(3)); setEnforcementConfig({ gdpr: { rules: [{ purpose: 'storage', enforcePurpose: true, enforceVendor: true, }] } }); let consentData = {} consentData.vendorData = staticConfig.consentData.getTCData; consentData.gdprApplies = true; consentData.apiVersion = 2; gdprDataHandlerStub.returns(consentData); deviceAccessHook(nextFnSpy, 1, 'appnexus'); deviceAccessHook(nextFnSpy, 3, 'rubicon'); expect(logWarnSpy.callCount).to.equal(1); }); it('should allow device access when gdprApplies is false and hasDeviceAccess flag is true', function () { adapterManagerStub.withArgs('appnexus').returns(getBidderSpec(1)); setEnforcementConfig({ gdpr: { rules: [{ purpose: 'storage', enforcePurpose: true, enforceVendor: true, vendorExceptions: [] }] } }); let consentData = {} consentData.vendorData = staticConfig.consentData.getTCData; consentData.gdprApplies = false; consentData.apiVersion = 2; gdprDataHandlerStub.returns(consentData); deviceAccessHook(nextFnSpy, 1, 'appnexus'); expect(nextFnSpy.calledOnce).to.equal(true); let result = { hasEnforcementHook: true, valid: true } sinon.assert.calledWith(nextFnSpy, 1, 'appnexus', result); }); it('should use gvlMapping set by publisher', function() { config.setConfig({ 'gvlMapping': { 'appnexus': 4 } }); setEnforcementConfig({ gdpr: { rules: [{ purpose: 'storage', enforcePurpose: true, enforceVendor: true, vendorExceptions: [] }] } }); let consentData = {} consentData.vendorData = staticConfig.consentData.getTCData; consentData.gdprApplies = true; consentData.apiVersion = 2; gdprDataHandlerStub.returns(consentData); deviceAccessHook(nextFnSpy, 1, 'appnexus'); expect(nextFnSpy.calledOnce).to.equal(true); let result = { hasEnforcementHook: true, valid: true } sinon.assert.calledWith(nextFnSpy, 4, 'appnexus', result); config.resetConfig(); }); it('should use gvl id of alias and not of parent', function() { let curBidderStub = sinon.stub(config, 'getCurrentBidder'); curBidderStub.returns('appnexus-alias'); adapterManager.aliasBidAdapter('appnexus', 'appnexus-alias'); config.setConfig({ 'gvlMapping': { 'appnexus-alias': 4 } }); setEnforcementConfig({ gdpr: { rules: [{ purpose: 'storage', enforcePurpose: true, enforceVendor: true, vendorExceptions: [] }] } }); let consentData = {} consentData.vendorData = staticConfig.consentData.getTCData; consentData.gdprApplies = true; consentData.apiVersion = 2; gdprDataHandlerStub.returns(consentData); deviceAccessHook(nextFnSpy, 1, 'appnexus'); expect(nextFnSpy.calledOnce).to.equal(true); let result = { hasEnforcementHook: true, valid: true } sinon.assert.calledWith(nextFnSpy, 4, 'appnexus', result); config.resetConfig(); curBidderStub.restore(); }); }); describe('userSyncHook', function () { let curBidderStub; let adapterManagerStub; beforeEach(function () { gdprDataHandlerStub = sinon.stub(gdprDataHandler, 'getConsentData'); logWarnSpy = sinon.spy(utils, 'logWarn'); curBidderStub = sinon.stub(config, 'getCurrentBidder'); adapterManagerStub = sinon.stub(adapterManager, 'getBidAdapter'); nextFnSpy = sinon.spy(); }); afterEach(function () { config.getCurrentBidder.restore(); config.resetConfig(); gdprDataHandler.getConsentData.restore(); adapterManager.getBidAdapter.restore(); logWarnSpy.restore(); }); it('should allow bidder to do user sync if consent is true', function () { setEnforcementConfig({ gdpr: { rules: [{ purpose: 'storage', enforcePurpose: false, enforceVendor: true, vendorExceptions: ['sampleBidder2'] }] } }); let consentData = {} consentData.vendorData = staticConfig.consentData.getTCData; consentData.gdprApplies = true; consentData.apiVersion = 2; gdprDataHandlerStub.returns(consentData); curBidderStub.returns('sampleBidder1'); adapterManagerStub.withArgs('sampleBidder1').returns({ getSpec: function () { return { 'gvlid': 1 } } }); userSyncHook(nextFnSpy); curBidderStub.returns('sampleBidder2'); adapterManagerStub.withArgs('sampleBidder2').returns({ getSpec: function () { return { 'gvlid': 3 } } }); userSyncHook(nextFnSpy); expect(nextFnSpy.calledTwice).to.equal(true); }); it('should not allow bidder to do user sync if user has denied consent', function () { setEnforcementConfig({ gdpr: { rules: [{ purpose: 'storage', enforcePurpose: false, enforceVendor: true, vendorExceptions: [] }] } }); let consentData = {} consentData.vendorData = staticConfig.consentData.getTCData; consentData.apiVersion = 2; consentData.gdprApplies = true; gdprDataHandlerStub.returns(consentData); curBidderStub.returns('sampleBidder1'); adapterManagerStub.withArgs('sampleBidder1').returns({ getSpec: function () { return { 'gvlid': 1 } } }); userSyncHook(nextFnSpy); curBidderStub.returns('sampleBidder2'); adapterManagerStub.withArgs('sampleBidder2').returns({ getSpec: function () { return { 'gvlid': 3 } } }); userSyncHook(nextFnSpy); expect(nextFnSpy.calledOnce).to.equal(true); expect(logWarnSpy.callCount).to.equal(1); }); it('should not check vendor consent when enforceVendor is false', function () { setEnforcementConfig({ gdpr: { rules: [{ purpose: 'storage', enforcePurpose: true, enforceVendor: false, vendorExceptions: ['sampleBidder1'] }] } }); let consentData = {} consentData.vendorData = staticConfig.consentData.getTCData; consentData.apiVersion = 2; consentData.gdprApplies = true; gdprDataHandlerStub.returns(consentData); curBidderStub.returns('sampleBidder1'); adapterManagerStub.withArgs('sampleBidder1').returns({ getSpec: function () { return { 'gvlid': 1 } } }); userSyncHook(nextFnSpy); curBidderStub.returns('sampleBidder2'); adapterManagerStub.withArgs('sampleBidder2').returns({ getSpec: function () { return { 'gvlid': 3 } } }); userSyncHook(nextFnSpy); expect(nextFnSpy.calledTwice).to.equal(true); expect(logWarnSpy.callCount).to.equal(0); }); }); describe('userIdHook', function () { beforeEach(function () { logWarnSpy = sinon.spy(utils, 'logWarn'); nextFnSpy = sinon.spy(); }); afterEach(function () { config.resetConfig(); logWarnSpy.restore(); }); it('should allow user id module if consent is given', function () { setEnforcementConfig({ gdpr: { rules: [{ purpose: 'storage', enforcePurpose: false, enforceVendor: true, vendorExceptions: [] }] } }); let consentData = {} consentData.vendorData = staticConfig.consentData.getTCData; consentData.apiVersion = 2; consentData.gdprApplies = true; let submodules = [{ submodule: { gvlid: 1, name: 'sampleUserId' } }] userIdHook(nextFnSpy, submodules, consentData); // Should pass back hasValidated flag since version 2 const args = nextFnSpy.getCalls()[0].args; expect(args[1].hasValidated).to.be.true; expect(nextFnSpy.calledOnce).to.equal(true); sinon.assert.calledWith(nextFnSpy, submodules, { ...consentData, hasValidated: true }); }); it('should allow userId module if gdpr not in scope', function () { let submodules = [{ submodule: { gvlid: 1, name: 'sampleUserId' } }]; let consentData = null; userIdHook(nextFnSpy, submodules, consentData); // Should not pass back hasValidated flag since version 2 const args = nextFnSpy.getCalls()[0].args; expect(args[1]).to.be.null; expect(nextFnSpy.calledOnce).to.equal(true); sinon.assert.calledWith(nextFnSpy, submodules, consentData); }); it('should not allow user id module if user denied consent', function () { setEnforcementConfig({ gdpr: { rules: [{ purpose: 'storage', enforcePurpose: false, enforceVendor: true, vendorExceptions: [] }] } }); let consentData = {} consentData.vendorData = staticConfig.consentData.getTCData; consentData.apiVersion = 2; consentData.gdprApplies = true; let submodules = [{ submodule: { gvlid: 1, name: 'sampleUserId' } }, { submodule: { gvlid: 3, name: 'sampleUserId1' } }] userIdHook(nextFnSpy, submodules, consentData); expect(logWarnSpy.callCount).to.equal(1); let expectedSubmodules = [{ submodule: { gvlid: 1, name: 'sampleUserId' } }] sinon.assert.calledWith(nextFnSpy, expectedSubmodules, { ...consentData, hasValidated: true }); }); }); describe('makeBidRequestsHook', function () { let sandbox; let adapterManagerStub; let emitEventSpy; const MOCK_AD_UNITS = [{ code: 'ad-unit-1', mediaTypes: {}, bids: [{ bidder: 'bidder_1' // has consent }, { bidder: 'bidder_2' // doesn't have consent, but liTransparency is true. Bidder remains active. }] }, { code: 'ad-unit-2', mediaTypes: {}, bids: [{ bidder: 'bidder_2' }, { bidder: 'bidder_3' }] }]; beforeEach(function () { sandbox = sinon.createSandbox(); gdprDataHandlerStub = sandbox.stub(gdprDataHandler, 'getConsentData'); adapterManagerStub = sandbox.stub(adapterManager, 'getBidAdapter'); logWarnSpy = sandbox.spy(utils, 'logWarn'); nextFnSpy = sandbox.spy(); emitEventSpy = sandbox.spy(events, 'emit'); }); afterEach(function () { config.resetConfig(); sandbox.restore(); }); it('should block bidder which does not have consent and allow bidder which has consent (liTransparency is established)', function () { setEnforcementConfig({ gdpr: { rules: [{ purpose: 'basicAds', enforcePurpose: true, enforceVendor: true, vendorExceptions: [] }] } }); const consentData = {}; consentData.vendorData = staticConfig.consentData.getTCData; consentData.apiVersion = 2; consentData.gdprApplies = true; gdprDataHandlerStub.returns(consentData); adapterManagerStub.withArgs('bidder_1').returns({ getSpec: function () { return { 'gvlid': 4 } } }); adapterManagerStub.withArgs('bidder_2').returns({ getSpec: function () { return { 'gvlid': 5 } } }); adapterManagerStub.withArgs('bidder_3').returns({ getSpec: function () { return { 'gvlid': undefined } } }); makeBidRequestsHook(nextFnSpy, MOCK_AD_UNITS, []); // Assertions expect(nextFnSpy.calledOnce).to.equal(true); sinon.assert.calledWith(nextFnSpy, [{ code: 'ad-unit-1', mediaTypes: {}, bids: [ sinon.match({ bidder: 'bidder_1' }), sinon.match({ bidder: 'bidder_2' }) ] }, { code: 'ad-unit-2', mediaTypes: {}, bids: [ sinon.match({ bidder: 'bidder_2' }), sinon.match({ bidder: 'bidder_3' }) // should be allowed even though it's doesn't have a gvlId because liTransparency is established. ] }], []); }); it('should block bidder which does not have consent and allow bidder which has consent (liTransparency is NOT established)', function() { setEnforcementConfig({ gdpr: { rules: [{ purpose: 'basicAds', enforcePurpose: true, enforceVendor: true, vendorExceptions: ['bidder_3'] }] } }); const consentData = {}; // set li for purpose 2 to false const newConsentData = utils.deepClone(staticConfig); newConsentData.consentData.getTCData.purpose.legitimateInterests['2'] = false; consentData.vendorData = newConsentData.consentData.getTCData; consentData.apiVersion = 2; consentData.gdprApplies = true; gdprDataHandlerStub.returns(consentData); adapterManagerStub.withArgs('bidder_1').returns({ getSpec: function () { return { 'gvlid': 4 } } }); adapterManagerStub.withArgs('bidder_2').returns({ getSpec: function () { return { 'gvlid': 5 } } }); adapterManagerStub.withArgs('bidder_3').returns({ getSpec: function () { return { 'gvlid': undefined } } }); makeBidRequestsHook(nextFnSpy, MOCK_AD_UNITS, []); // Assertions expect(nextFnSpy.calledOnce).to.equal(true); sinon.assert.calledWith(nextFnSpy, [{ code: 'ad-unit-1', mediaTypes: {}, bids: [ sinon.match({ bidder: 'bidder_1' }), // 'bidder_2' is not present because it doesn't have vendorConsent ] }, { code: 'ad-unit-2', mediaTypes: {}, bids: [ sinon.match({ bidder: 'bidder_3' }), // 'bidder_3' is allowed despite gvlId being undefined because it's part of vendorExceptions ] }], []); expect(logWarnSpy.calledOnce).to.equal(true); }); it('should skip validation checks if GDPR version is not equal to "2"', function () { setEnforcementConfig({ gdpr: { rules: [{ purpose: 'storage', enforePurpose: false, enforceVendor: false, vendorExceptions: [] }] } }); const consentData = {}; consentData.vendorData = staticConfig.consentData.getTCData; consentData.apiVersion = 1; consentData.gdprApplies = true; gdprDataHandlerStub.returns(consentData); makeBidRequestsHook(nextFnSpy, MOCK_AD_UNITS, []); // Assertions expect(nextFnSpy.calledOnce).to.equal(true); sinon.assert.calledWith(nextFnSpy, sinon.match.array.deepEquals(MOCK_AD_UNITS), []); expect(emitEventSpy.notCalled).to.equal(true); expect(logWarnSpy.notCalled).to.equal(true); }); }); describe('enableAnalyticsHook', function () { let sandbox; let adapterManagerStub; const MOCK_ANALYTICS_ADAPTER_CONFIG = [{ provider: 'analyticsAdapter_A', options: {} }, { provider: 'analyticsAdapter_B', options: {} }, { provider: 'analyticsAdapter_C', options: {} }]; beforeEach(function () { sandbox = sinon.createSandbox(); gdprDataHandlerStub = sandbox.stub(gdprDataHandler, 'getConsentData'); adapterManagerStub = sandbox.stub(adapterManager, 'getAnalyticsAdapter'); logWarnSpy = sandbox.spy(utils, 'logWarn'); nextFnSpy = sandbox.spy(); }); afterEach(function() { config.resetConfig(); sandbox.restore(); }); it('should block analytics adapter which does not have consent and allow the one(s) which have consent', function() { setEnforcementConfig({ gdpr: { rules: [{ purpose: 'measurement', enforcePurpose: true, enforceVendor: true, vendorExceptions: ['analyticsAdapter_B'] }] } }); const consentData = {}; consentData.vendorData = staticConfig.consentData.getTCData; consentData.apiVersion = 2; consentData.gdprApplies = true; gdprDataHandlerStub.returns(consentData); adapterManagerStub.withArgs('analyticsAdapter_A').returns({ gvlid: 3 }); adapterManagerStub.withArgs('analyticsAdapter_B').returns({ gvlid: 5 }); adapterManagerStub.withArgs('analyticsAdapter_C').returns({ gvlid: 1 }); enableAnalyticsHook(nextFnSpy, MOCK_ANALYTICS_ADAPTER_CONFIG); // Assertions expect(nextFnSpy.calledOnce).to.equal(true); sinon.assert.calledWith(nextFnSpy, [{ provider: 'analyticsAdapter_B', options: {} }, { provider: 'analyticsAdapter_C', options: {} }]); expect(logWarnSpy.calledOnce).to.equal(true); }); }); describe('validateRules', function () { const createGdprRule = (purposeName = 'storage', enforcePurpose = true, enforceVendor = true, vendorExceptions = []) => ({ purpose: purposeName, enforcePurpose: enforcePurpose, enforceVendor: enforceVendor, vendorExceptions: vendorExceptions }); const consentData = { vendorData: staticConfig.consentData.getTCData, apiVersion: 2, gdprApplies: true }; // Bidder - 'bidderA' has vendorConsent const vendorAllowedModule = 'bidderA'; const vendorAllowedGvlId = 1; // Bidder = 'bidderB' doesn't have vendorConsent const vendorBlockedModule = 'bidderB'; const vendorBlockedGvlId = 3; const consentDataWithPurposeConsentFalse = utils.deepClone(consentData); consentDataWithPurposeConsentFalse.vendorData.purpose.consents['1'] = false; it('should return true when enforcePurpose=true AND purposeConsent[p]==true AND enforceVendor[p,v]==true AND vendorConsent[v]==true', function () { // 'enforcePurpose' and 'enforceVendor' both are 'true' const gdprRule = createGdprRule('storage', true, true, []); // case 1 - Both purpose consent and vendor consent is 'true'. validateRules must return 'true' let isAllowed = validateRules(gdprRule, consentData, vendorAllowedModule, vendorAllowedGvlId); expect(isAllowed).to.equal(true); // case 2 - Purpose consent is 'true' but vendor consent is 'false'. validateRules must return 'false' isAllowed = validateRules(gdprRule, consentData, vendorBlockedModule, vendorBlockedGvlId); expect(isAllowed).to.equal(false); // case 3 - Purpose consent is 'false' but vendor consent is 'true'. validateRules must return 'false' isAllowed = validateRules(gdprRule, consentDataWithPurposeConsentFalse, vendorAllowedModule, vendorAllowedGvlId); expect(isAllowed).to.equal(false); // case 4 - Both purpose consent and vendor consent is 'false'. validateRules must return 'false' isAllowed = validateRules(gdprRule, consentDataWithPurposeConsentFalse, vendorBlockedModule, vendorBlockedGvlId); expect(isAllowed).to.equal(false); }); it('should return true when enforcePurpose=true AND purposeConsent[p]==true AND enforceVendor[p,v]==false', function () { // 'enforcePurpose' is 'true' and 'enforceVendor' is 'false' const gdprRule = createGdprRule('storage', true, false, []); // case 1 - Both purpose consent and vendor consent is 'true'. validateRules must return 'true' let isAllowed = validateRules(gdprRule, consentData, vendorAllowedModule, vendorAllowedGvlId); expect(isAllowed).to.equal(true); // case 2 - Purpose consent is 'true' but vendor consent is 'false'. validateRules must return 'true' because vendorConsent doens't matter isAllowed = validateRules(gdprRule, consentData, vendorBlockedModule, vendorBlockedGvlId); expect(isAllowed).to.equal(true); // case 3 - Purpose consent is 'false' but vendor consent is 'true'. validateRules must return 'false' because vendorConsent doesn't matter isAllowed = validateRules(gdprRule, consentDataWithPurposeConsentFalse, vendorAllowedModule, vendorAllowedGvlId); expect(isAllowed).to.equal(false); // case 4 - Both purpose consent and vendor consent is 'false'. validateRules must return 'false' and vendorConsent doesn't matter isAllowed = validateRules(gdprRule, consentDataWithPurposeConsentFalse, vendorBlockedModule, vendorAllowedGvlId); expect(isAllowed).to.equal(false); }); it('should return true when enforcePurpose=false AND enforceVendor[p,v]==true AND vendorConsent[v]==true', function () { // 'enforcePurpose' is 'false' and 'enforceVendor' is 'true' const gdprRule = createGdprRule('storage', false, true, []); // case 1 - Both purpose consent and vendor consent is 'true'. validateRules must return 'true' let isAllowed = validateRules(gdprRule, consentData, vendorAllowedModule, vendorAllowedGvlId); expect(isAllowed).to.equal(true); // case 2 - Purpose consent is 'true' but vendor consent is 'false'. validateRules must return 'false' because purposeConsent doesn't matter isAllowed = validateRules(gdprRule, consentData, vendorBlockedModule, vendorBlockedGvlId); expect(isAllowed).to.equal(false); // case 3 - urpose consent is 'false' but vendor consent is 'true'. validateRules must return 'true' because purposeConsent doesn't matter isAllowed = validateRules(gdprRule, consentDataWithPurposeConsentFalse, vendorAllowedModule, vendorAllowedGvlId); expect(isAllowed).to.equal(true); // case 4 - Both purpose consent and vendor consent is 'false'. validateRules must return 'false' and purposeConsent doesn't matter isAllowed = validateRules(gdprRule, consentDataWithPurposeConsentFalse, vendorBlockedModule, vendorBlockedGvlId); expect(isAllowed).to.equal(false); }); it('should return true when enforcePurpose=false AND enforceVendor[p,v]==false', function () { // 'enforcePurpose' is 'false' and 'enforceVendor' is 'false' const gdprRule = createGdprRule('storage', false, false, []); // case 1 - Both purpose consent and vendor consent is 'true'. validateRules must return 'true', both the consents do not matter. let isAllowed = validateRules(gdprRule, consentData, vendorAllowedModule, vendorAllowedGvlId); expect(isAllowed).to.equal(true); // case 2 - Purpose consent is 'true' but vendor consent is 'false'. validateRules must return 'true', both the consents do not matter. isAllowed = validateRules(gdprRule, consentData, vendorBlockedModule, vendorBlockedGvlId); expect(isAllowed).to.equal(true); // case 3 - urpose consent is 'false' but vendor consent is 'true'. validateRules must return 'true', both the consents do not matter. isAllowed = validateRules(gdprRule, consentDataWithPurposeConsentFalse, vendorAllowedModule, vendorAllowedGvlId); expect(isAllowed).to.equal(true); // case 4 - Both purpose consent and vendor consent is 'false'. validateRules must return 'true', both the consents do not matter. isAllowed = validateRules(gdprRule, consentDataWithPurposeConsentFalse, vendorBlockedModule, vendorBlockedGvlId); expect(isAllowed).to.equal(true); }); it('should return true when "vendorExceptions" contains the name of the vendor under test', function () { // 'vendorExceptions' contains 'bidderB' which doesn't have vendor consent. const gdprRule = createGdprRule('storage', false, true, [vendorBlockedModule]); /* 'bidderB' gets a free pass since it's included in the 'vendorExceptions' array. validateRules must disregard user's choice for purpose and vendor consent and return 'true' for this bidder(s) */ const isAllowed = validateRules(gdprRule, consentData, vendorBlockedModule, vendorBlockedGvlId); expect(isAllowed).to.equal(true); }); describe('Purpose 2 special case', function () { const consentDataWithLIFalse = utils.deepClone(consentData); consentDataWithLIFalse.vendorData.purpose.legitimateInterests['2'] = false; const consentDataWithPurposeConsentFalse = utils.deepClone(consentData); consentDataWithPurposeConsentFalse.vendorData.purpose.consents['2'] = false; const consentDataWithPurposeConsentFalseAndLIFalse = utils.deepClone(consentData); consentDataWithPurposeConsentFalseAndLIFalse.vendorData.purpose.legitimateInterests['2'] = false; consentDataWithPurposeConsentFalseAndLIFalse.vendorData.purpose.consents['2'] = false; it('should return true when (enforcePurpose=true AND purposeConsent[p]===true AND enforceVendor[p.v]===true AND vendorConsent[v]===true) OR (purposesLITransparency[p]===true)', function () { // both 'enforcePurpose' and 'enforceVendor' is 'true' const gdprRule = createGdprRule('basicAds', true, true, []); // case 1 - Both purpose consent and vendor consent is 'true', but legitimateInterests for purpose 2 is 'false'. validateRules must return 'true'. let isAllowed = validateRules(gdprRule, consentDataWithLIFalse, vendorAllowedModule, vendorAllowedGvlId); expect(isAllowed).to.equal(true); // case 2 - Purpose consent is 'true' but vendor consent is 'false', but legitimateInterests for purpose 2 is 'true'. validateRules must return 'true'. isAllowed = validateRules(gdprRule, consentData, vendorBlockedModule, vendorBlockedGvlId); expect(isAllowed).to.equal(true); // case 3 - Purpose consent is 'true' and vendor consent is 'true', as well as legitimateInterests for purpose 2 is 'true'. validateRules must return 'true'. isAllowed = validateRules(gdprRule, consentData, vendorAllowedModule, vendorAllowedGvlId); expect(isAllowed).to.equal(true); // case 4 - Purpose consent is 'true' and vendor consent is 'false', and legitimateInterests for purpose 2 is 'false'. validateRules must return 'false'. isAllowed = validateRules(gdprRule, consentDataWithLIFalse, vendorBlockedModule, vendorBlockedGvlId); expect(isAllowed).to.equal(false); }); it('should return true when (enforcePurpose=true AND purposeConsent[p]===true AND enforceVendor[p.v]===false) OR (purposesLITransparency[p]===true)', function () { // 'enforcePurpose' is 'true' and 'enforceVendor' is 'false' const gdprRule = createGdprRule('basicAds', true, false, []); // case 1 - Purpose consent is 'true', vendor consent doesn't matter and legitimateInterests for purpose 2 is 'true'. validateRules must return 'true'. let isAllowed = validateRules(gdprRule, consentData, vendorBlockedModule, vendorBlockedGvlId); expect(isAllowed).to.equal(true); // case 2 - Purpose consent is 'false', vendor consent doesn't matter and legitimateInterests for purpose 2 is 'true'. validateRules must return 'true'. isAllowed = validateRules(gdprRule, consentDataWithPurposeConsentFalse, vendorAllowedModule, vendorAllowedGvlId); expect(isAllowed).to.equal(true); // case 3 - Purpose consent is 'false', vendor consent doesn't matter and legitimateInterests for purpose 2 is 'false'. validateRules must return 'false'. isAllowed = validateRules(gdprRule, consentDataWithPurposeConsentFalseAndLIFalse, vendorAllowedModule, vendorAllowedGvlId); expect(isAllowed).to.equal(false); }); it('should return true when (enforcePurpose=false AND enforceVendor[p,v]===true AND vendorConsent[v]===true) OR (purposesLITransparency[p]===true)', function () { // 'enforcePurpose' is 'false' and 'enforceVendor' is 'true' const gdprRule = createGdprRule('basicAds', false, true, []); // case - 1 Vendor consent is 'true', purpose consent doesn't matter and legitimateInterests for purpose 2 is 'true'. validateRules must return 'true'. let isAllowed = validateRules(gdprRule, consentData, vendorAllowedModule, vendorAllowedGvlId); expect(isAllowed).to.equal(true); // case 2 - Vendor consent is 'false', purpose consent doesn't matter and legitimateInterests for purpose 2 is 'true'. validateRules must return 'true'. isAllowed = validateRules(gdprRule, consentData, vendorBlockedModule, vendorBlockedGvlId); expect(isAllowed).to.equal(true); // case 3 - Vendor consent is 'false', purpose consent doesn't matter and legitimateInterests for purpose 2 is 'false'. validateRules must return 'false'. isAllowed = validateRules(gdprRule, consentDataWithLIFalse, vendorBlockedModule, vendorBlockedGvlId); expect(isAllowed).to.equal(false); }); }); }) describe('setEnforcementConfig', function () { let sandbox; const DEFAULT_RULES = [{ purpose: 'storage', enforcePurpose: true, enforceVendor: true, vendorExceptions: [] }, { purpose: 'basicAds', enforcePurpose: true, enforceVendor: true, vendorExceptions: [] }]; beforeEach(function () { sandbox = sinon.createSandbox(); logWarnSpy = sandbox.spy(utils, 'logWarn'); }); afterEach(function () { config.resetConfig(); sandbox.restore(); }); it('should enforce TCF2 Purpose1 and Purpose 2 if no "rules" found in the config', function () { setEnforcementConfig({ gdpr: { cmpApi: 'iab', allowAuctionWithoutConsent: true, timeout: 5000 } }); expect(logWarnSpy.calledOnce).to.equal(true); expect(enforcementRules).to.deep.equal(DEFAULT_RULES); }); it('should enforce TCF2 Purpose 2 also if only Purpose 1 is defined in "rules"', function () { const purpose1RuleDefinedInConfig = { purpose: 'storage', enforcePurpose: false, enforceVendor: true, vendorExceptions: ['bidderA'] } setEnforcementConfig({ gdpr: { rules: [purpose1RuleDefinedInConfig] } }); expect(purpose1Rule).to.deep.equal(purpose1RuleDefinedInConfig); expect(purpose2Rule).to.deep.equal(DEFAULT_RULES[1]); }); it('should enforce TCF2 Purpose 1 also if only Purpose 2 is defined in "rules"', function () { const purpose2RuleDefinedInConfig = { purpose: 'basicAds', enforcePurpose: false, enforceVendor: true, vendorExceptions: ['bidderA'] } setEnforcementConfig({ gdpr: { rules: [purpose2RuleDefinedInConfig] } }); expect(purpose1Rule).to.deep.equal(DEFAULT_RULES[0]); expect(purpose2Rule).to.deep.equal(purpose2RuleDefinedInConfig); }); it('should use the "rules" defined in config if a definition found', function() { const rules = [{ purpose: 'storage', enforcePurpose: false, enforceVendor: false }, { purpose: 'basicAds', enforcePurpose: false, enforceVendor: false }] setEnforcementConfig({gdpr: { rules }}); expect(enforcementRules).to.deep.equal(rules); }); }); describe('TCF2FinalResults', function() { let sandbox; beforeEach(function() { sandbox = sinon.createSandbox(); sandbox.spy(events, 'emit'); }); afterEach(function() { config.resetConfig(); sandbox.restore(); }); it('should emit TCF2 enforcement data on auction end', function() { const rules = [{ purpose: 'storage', enforcePurpose: false, enforceVendor: false }, { purpose: 'basicAds', enforcePurpose: false, enforceVendor: false }] setEnforcementConfig({gdpr: { rules }}); events.emit('auctionEnd', {}) // Assertions sinon.assert.calledWith(events.emit.getCall(1), 'tcf2Enforcement', sinon.match.object); }) }); describe('getGvlid', function() { let sandbox; let getGvlidForBidAdapterStub; let getGvlidForUserIdModuleStub; let getGvlidForAnalyticsAdapterStub; beforeEach(function() { sandbox = sinon.createSandbox(); getGvlidForBidAdapterStub = sandbox.stub(internal, 'getGvlidForBidAdapter'); getGvlidForUserIdModuleStub = sandbox.stub(internal, 'getGvlidForUserIdModule'); getGvlidForAnalyticsAdapterStub = sandbox.stub(internal, 'getGvlidForAnalyticsAdapter'); }); afterEach(function() { sandbox.restore(); config.resetConfig(); }); it('should return "null" if called without passing any argument', function() { const gvlid = getGvlid(); expect(gvlid).to.equal(null); }); it('should return "null" if GVL ID is not defined for any of these modules: Bid adapter, UserId submodule and Analytics adapter', function() { getGvlidForBidAdapterStub.withArgs('moduleA').returns(null); getGvlidForUserIdModuleStub.withArgs('moduleA').returns(null); getGvlidForAnalyticsAdapterStub.withArgs('moduleA').returns(null); const gvlid = getGvlid('moduleA'); expect(gvlid).to.equal(null); }); it('should return the GVL ID from gvlMapping if it is defined in setConfig', function() { config.setConfig({ gvlMapping: { moduleA: 1 } }); // Actual GVL ID for moduleA is 2, as defined on its the bidAdapter.js file. getGvlidForBidAdapterStub.withArgs('moduleA').returns(2); const gvlid = getGvlid('moduleA'); expect(gvlid).to.equal(1); }); it('should return the GVL ID by calling getGvlidForBidAdapter -> getGvlidForUserIdModule -> getGvlidForAnalyticsAdapter in sequence', function() { getGvlidForBidAdapterStub.withArgs('moduleA').returns(null); getGvlidForUserIdModuleStub.withArgs('moduleA').returns(null); getGvlidForAnalyticsAdapterStub.withArgs('moduleA').returns(7); expect(getGvlid('moduleA')).to.equal(7); }); }); });