UNPKG

mk9-prebid

Version:

Header Bidding Management Library

463 lines (431 loc) 16.4 kB
import adapterManager from '../../../src/adapterManager.js'; import id5AnalyticsAdapter from '../../../modules/id5AnalyticsAdapter.js'; import { expect } from 'chai'; import sinon from 'sinon'; import events from '../../../src/events.js'; import constants from '../../../src/constants.json'; import { generateUUID } from '../../../src/utils.js'; const CONFIG_URL = 'https://api.id5-sync.com/analytics/12349/pbjs'; const INGEST_URL = 'https://test.me/ingest'; describe('ID5 analytics adapter', () => { let server; let config; beforeEach(() => { server = sinon.createFakeServer(); config = { options: { partnerId: 12349, } }; }); afterEach(() => { server.restore(); }); it('registers itself with the adapter manager', () => { const adapter = adapterManager.getAnalyticsAdapter('id5Analytics'); expect(adapter).to.exist; expect(adapter.gvlid).to.be.a('number'); expect(adapter.adapter).to.equal(id5AnalyticsAdapter); }); it('tolerates undefined or empty config', () => { id5AnalyticsAdapter.enableAnalytics(undefined); id5AnalyticsAdapter.enableAnalytics({}); }); it('calls configuration endpoint', () => { server.respondWith('GET', CONFIG_URL, [200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }, `{ "sampling": 0, "ingestUrl": "${INGEST_URL}" }` ]); id5AnalyticsAdapter.enableAnalytics(config); server.respond(); expect(server.requests).to.have.length(1); id5AnalyticsAdapter.disableAnalytics(); }); it('does not call configuration endpoint when partner id is missing', () => { id5AnalyticsAdapter.enableAnalytics({}); server.respond(); expect(server.requests).to.have.length(0); id5AnalyticsAdapter.disableAnalytics(); }); describe('after configuration', () => { let auction; beforeEach(() => { server.respondWith('GET', CONFIG_URL, [200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }, `{ "sampling": 1, "ingestUrl": "${INGEST_URL}" }` ]); server.respondWith('POST', INGEST_URL, [200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }, '' ]); auction = { auctionId: generateUUID(), adUnits: [{ 'code': 'user-728', mediaTypes: { banner: { sizes: [[300, 250], [300, 600], [728, 90]] } }, adUnitCodes: ['user-728'] }], }; }); afterEach(() => { id5AnalyticsAdapter.disableAnalytics(); }); it('sends auction end events to the backend', () => { id5AnalyticsAdapter.enableAnalytics(config); server.respond(); events.emit(constants.EVENTS.AUCTION_END, auction); server.respond(); // Why 3? 1: config, 2: tcfEnforcement, 3: auctionEnd // tcfEnforcement? yes, gdprEnforcement module emits in reaction to auctionEnd expect(server.requests).to.have.length(3); const body1 = JSON.parse(server.requests[1].requestBody); expect(body1.source).to.equal('pbjs'); expect(body1.event).to.equal('tcf2Enforcement'); expect(body1.partnerId).to.equal(12349); expect(body1.meta).to.be.a('object'); expect(body1.meta.pbjs).to.equal($$PREBID_GLOBAL$$.version); expect(body1.meta.sampling).to.equal(1); expect(body1.meta.tz).to.be.a('number'); const body2 = JSON.parse(server.requests[2].requestBody); expect(body2.source).to.equal('pbjs'); expect(body2.event).to.equal('auctionEnd'); expect(body2.partnerId).to.equal(12349); expect(body2.meta).to.be.a('object'); expect(body2.meta.pbjs).to.equal($$PREBID_GLOBAL$$.version); expect(body2.meta.sampling).to.equal(1); expect(body2.meta.tz).to.be.a('number'); expect(body2.payload).to.eql(auction); }); it('filters unwanted IDs from the events it sends', () => { auction.adUnits[0].bids = [{ 'bidder': 'appnexus', 'params': { 'placementId': '16618951' }, 'userId': { 'criteoId': '_h_y_19IMUhMZG1TOTRReHFNc29TekJ3TzQ3elhnRU81ayUyQjhiRkdJJTJGaTFXJTJCdDRnVmN4S0FETUhQbXdmQWg0M3g1NWtGbGolMkZXalclMkJvWjJDOXFDSk1HU3ZKaVElM0QlM0Q', 'id5id': { 'uid': 'ID5-ZHMOQ99ulpk687Fd9xVwzxMsYtkQIJnI-qm3iWdtww!ID5*FSycZQy7v7zWXiKbEpPEWoB3_UiWdPGzh554ncYDvOkAAA3rajiR0yNrFAU7oDTu', 'ext': { 'linkType': 1 } }, 'tdid': '888a6042-8f99-483b-aa26-23c44bc9166b' }, 'userIdAsEids': [{ 'source': 'criteo.com', 'uids': [{ 'id': '_h_y_19IMUhMZG1TOTRReHFNc29TekJ3TzQ3elhnRU81ayUyQjhiRkdJJTJGaTFXJTJCdDRnVmN4S0FETUhQbXdmQWg0M3g1NWtGbGolMkZXalclMkJvWjJDOXFDSk1HU3ZKaVElM0QlM0Q', 'atype': 1 }] }, { 'source': 'id5-sync.com', 'uids': [{ 'id': 'ID5-ZHMOQ99ulpk687Fd9xVwzxMsYtkQIJnI-qm3iWdtww!ID5*FSycZQy7v7zWXiKbEpPEWoB3_UiWdPGzh554ncYDvOkAAA3rajiR0yNrFAU7oDTu', 'atype': 1, 'ext': { 'linkType': 1 } }] }] }]; auction.bidderRequests = [{ 'bidderCode': 'appnexus', 'auctionId': 'e8d15df4-d89c-44c9-8b36-812f75cbf227', 'bidderRequestId': '1451a3c759c60359', 'bids': [ { 'bidder': 'appnexus', 'params': { 'placementId': '16824712' }, 'userId': { 'id5id': { 'uid': 'ID5-ZHMOQ99ulpk687Fd9xVwzxMsYtkQIJnI-qm3iWdtww!ID5*CmuuahP8jbPJGRCUDdT2VZ8wz0eJM8O8mNlKktlEjuYAABFEjc2c9faqDencf2hR', 'ext': { 'linkType': 1 } }, 'sharedid': { 'id': '01F6J4T72MRFYVWTN65WFA0H7N', 'third': '01F6J4T72MRFYVWTN65WFA0H7N' }, 'tdid': '0e45f56b-ad09-4c91-b090-8bd03e0d0754' }, 'userIdAsEids': [ { 'source': 'id5-sync.com', 'uids': [ { 'id': 'ID5-ZHMOQ99ulpk687Fd9xVwzxMsYtkQIJnI-qm3iWdtww!ID5*CmuuahP8jbPJGRCUDdT2VZ8wz0eJM8O8mNlKktlEjuYAABFEjc2c9faqDencf2hR', 'atype': 1, 'ext': { 'linkType': 1 } } ] }, { 'source': 'sharedid.org', 'uids': [ { 'id': '01F6J4T72MRFYVWTN65WFA0H7N', 'atype': 1, 'ext': { 'third': '01F6J4T72MRFYVWTN65WFA0H7N' } } ] }, { 'source': 'adserver.org', 'uids': [ { 'id': '0e45f56b-ad09-4c91-b090-8bd03e0d0754', 'atype': 1, 'ext': { 'rtiPartner': 'TDID' } } ] } ], 'ortb2Imp': { 'ext': { 'data': { 'adserver': { 'name': 'gam', 'adslot': '/6783/Kiwi/portail' }, 'pbadslot': '/6783/Kiwi/portail' } } }, 'adUnitCode': 'btf_leaderboard', 'transactionId': '3ce8216e-7898-4a22-86ba-01519b62bfce', 'sizes': [ [ 728, 90 ] ], 'bidId': '146661c05209a56e', 'bidderRequestId': '1451a3c759c60359', 'auctionId': 'e8d15df4-d89c-44c9-8b36-812f75cbf227', 'src': 'client', 'bidRequestsCount': 2, 'bidderRequestsCount': 2, 'bidderWinsCount': 0 } ], 'auctionStart': 1621959214757, 'timeout': 2000, 'refererInfo': { 'referer': 'https://www.blog.com/?pbjs_debug=true', 'reachedTop': true, 'isAmp': false, 'numIframes': 0, 'stack': [ 'https://www.blog.com/?pbjs_debug=true' ], 'canonicalUrl': null }, 'gdprConsent': { 'consentString': 'CPGw1WAPGw1WAAHABBENBbCsAP_AAH_AAAAAH3tf_X__b3_j-_59__t0eY1f9_7_v-0zjhfdt-8N2f_X_L8X42M7vF36pq4KuR4Eu3LBIQdlHOHcTUmw6okVrTPsbk2Mr7NKJ7PEmnMbe2dYGH9_n93TuZKY7__8___z__-v_v____f_r-3_3__59X---_e_V399zLv9__3__9gfaASYal8AF2JY4Mk0aVQogQhWEh0AoAKKAYWiawgZXBTsrgI9QQMAEJqAjAiBBiCjFgEAAgEASERASAHggEQBEAgABACpAQgAI2AQWAFgYBAAKAaFiBFAEIEhBkcFRymBARItFBPZWAJRd7GmEIZRYAUCj-iowEShBAsDISFg4AAA.f_gAD_gAAAAA', 'vendorData': { 'cmpId': 7, 'cmpVersion': 1, 'gdprApplies': true, 'tcfPolicyVersion': 2, 'eventStatus': 'useractioncomplete', 'cmpStatus': 'loaded', 'listenerId': 47, 'tcString': 'CPGw1WAPGw1WAAHABBENBbCsAP_AAH_AAAAAH3tf_X__b3_j-_59__t0eY1f9_7_v-0zjhfdt-8N2f_X_L8X42M7vF36pq4KuR4Eu3LBIQdlHOHcTUmw6okVrTPsbk2Mr7NKJ7PEmnMbe2dYGH9_n93TuZKY7__8___z__-v_v____f_r-3_3__59X---_e_V399zLv9__3__9gfaASYal8AF2JY4Mk0aVQogQhWEh0AoAKKAYWiawgZXBTsrgI9QQMAEJqAjAiBBiCjFgEAAgEASERASAHggEQBEAgABACpAQgAI2AQWAFgYBAAKAaFiBFAEIEhBkcFRymBARItFBPZWAJRd7GmEIZRYAUCj-iowEShBAsDISFg4AAA.f_gAD_gAAAAA', }, 'gdprApplies': true, 'addtlConsent': '1~7.12.35.62.66.70.89.93.108.122.144.149.153.162.167.184.196.221.241.253.259.272.311.317.323.326.338.348.350.415.440.448.449.482.486.491.494.495.540.571.574.585.587.588.590.725.733.780.817.839.864.867.932.938.981.986.1031.1033.1051.1092.1097.1126.1127.1170.1171.1186.1201.1204.1205.1211.1215.1230.1232.1236.1248.1276.1290.1301.1313.1344.1364.1365.1415.1419.1428.1449.1451.1509.1558.1564.1570.1577.1591.1651.1669.1712.1716.1720.1721.1725.1733.1753.1765.1799.1810.1834.1842.1870.1878.1889.1896.1911.1922.1929.2012.2072.2078.2079.2109.2177.2202.2253.2290.2299.2316.2357.2373.2526.2531.2571.2572.2575.2628.2663.2677.2776.2778.2779.2985.3033.3052.3154', 'apiVersion': 2 }, 'start': 1621959214763 }]; auction.bidsReceived = [{ 'bidderCode': 'appnexus', 'width': 728, 'height': 90, 'statusMessage': 'Bid available', 'adId': '99e7838aa7f1c4f', 'requestId': '21e0b32208ee9a', 'mediaType': 'banner', 'source': 'client', 'cpm': 0.020601, 'creativeId': 209272535, 'currency': 'USD', 'netRevenue': true, 'ttl': 300, 'adUnitCode': 'user-728', 'appnexus': { 'buyerMemberId': 11563 }, 'meta': { 'advertiserId': 4388779 }, 'ad': 'stuff i am not interested in', 'originalCpm': 0.020601, 'originalCurrency': 'USD', 'auctionId': 'c7694dbb-a583-4a73-a933-b16f1f821ba4', // Make sure cleanup is resilient 'someNullObject': null, 'someUndefinedProperty': undefined }]; id5AnalyticsAdapter.enableAnalytics(config); server.respond(); events.emit(constants.EVENTS.AUCTION_END, auction); server.respond(); expect(server.requests).to.have.length(3); const body = JSON.parse(server.requests[2].requestBody); expect(body.event).to.equal('auctionEnd'); expect(body.payload.adUnits[0].bids[0].userId).to.eql({ 'criteoId': '__ID5_REDACTED__', 'id5id': { 'uid': '__ID5_REDACTED__', 'ext': { 'linkType': 1 } }, 'tdid': '__ID5_REDACTED__' }); expect(body.payload.bidderRequests[0].bids[0].userId).to.eql({ 'sharedid': '__ID5_REDACTED__', 'id5id': { 'uid': '__ID5_REDACTED__', 'ext': { 'linkType': 1 } }, 'tdid': '__ID5_REDACTED__' }); body.payload.adUnits[0].bids[0].userIdAsEids.forEach((userId) => { expect(userId.uids[0].id).to.equal('__ID5_REDACTED__'); if (userId.uids[0].ext) { expect(userId.uids[0].ext).to.equal('__ID5_REDACTED__'); } }); body.payload.bidderRequests[0].bids[0].userIdAsEids.forEach((userId) => { expect(userId.uids[0].id).to.equal('__ID5_REDACTED__'); if (userId.uids[0].ext) { expect(userId.uids[0].ext).to.equal('__ID5_REDACTED__'); } }); expect(body.payload.bidsReceived[0].ad).to.equal(undefined); expect(body.payload.bidsReceived[0].requestId).to.equal('21e0b32208ee9a'); }); it('can override events to collect if configured to do so', () => { server.respondWith('GET', CONFIG_URL, [200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }, `{ "sampling": 1, "ingestUrl": "${INGEST_URL}", "eventsToTrack": ["tcf2Enforcement"] }` ]); id5AnalyticsAdapter.enableAnalytics(config); server.respond(); events.emit(constants.EVENTS.AUCTION_END, auction); server.respond(); expect(server.requests).to.have.length(2); const body1 = JSON.parse(server.requests[1].requestBody); expect(body1.event).to.equal('tcf2Enforcement'); }); it('can extend cleanup rules from server side', () => { auction.bidsReceived = [{ 'bidderCode': 'appnexus', 'width': 728, 'height': 90, 'statusMessage': 'Bid available', 'adId': '99e7838aa7f1c4f', 'requestId': '21e0b32208ee9a', 'mediaType': 'banner', 'source': 'client', 'cpm': 0.020601, 'creativeId': 209272535, 'currency': 'USD', 'netRevenue': true, 'ttl': 300, 'adUnitCode': 'user-728', 'appnexus': { 'buyerMemberId': 11563 }, 'meta': { 'advertiserId': 4388779 }, 'ad': 'stuff i am not interested in', 'originalCpm': 0.020601, 'originalCurrency': 'USD', 'auctionId': 'c7694dbb-a583-4a73-a933-b16f1f821ba4' }, { 'bidderCode': 'ix', 'width': 728, 'height': 90, 'statusMessage': 'Bid available', 'adId': '228f725de4a9ff09', 'requestId': '225a42b4a8ec7287', 'mediaType': 'banner', 'source': 'client', 'cpm': 0.06, 'netRevenue': true, 'currency': 'USD', 'creativeId': '8838044', 'ad': 'lots of HTML code', 'ttl': 300, 'meta': { 'networkId': 85, 'brandId': 822, 'brandName': 'Microsoft Brands', 'advertiserDomains': [ 'microsoftstore.com' ] }, 'originalCpm': 0.06, 'originalCurrency': 'USD', 'auctionId': 'fe28ce44-61bb-4ed8-be3c-3e801dfddcb9', 'responseTimestamp': 1621954632648, 'requestTimestamp': 1621954632498, 'bidder': 'ix', 'adUnitCode': 'sticky_footer', 'timeToRespond': 150, 'pbLg': '0.00', 'pbCg': '0.06', 'size': '728x90', 'adserverTargeting': { 'hb_bidder': 'ix', } }]; server.respondWith('GET', CONFIG_URL, [200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }, `{ "sampling": 1, "ingestUrl": "${INGEST_URL}", "additionalCleanupRules": {"auctionEnd": [{"match":["bidsReceived", "*", "requestId"],"apply":"erase"}]} }` ]); id5AnalyticsAdapter.enableAnalytics(config); server.respond(); events.emit(constants.EVENTS.AUCTION_END, auction); server.respond(); expect(server.requests).to.have.length(3); const body = JSON.parse(server.requests[2].requestBody); expect(body.event).to.equal('auctionEnd'); expect(body.payload.bidsReceived[0].requestId).to.equal(undefined); expect(body.payload.bidsReceived[1].requestId).to.equal(undefined); expect(body.payload.bidsReceived[0].bidderCode).to.equal('appnexus'); expect(body.payload.bidsReceived[1].bidderCode).to.equal('ix'); }); }); });