UNPKG

mk9-prebid

Version:

Header Bidding Management Library

1,062 lines (946 loc) 39.3 kB
import { expect } from 'chai'; import { targeting as targetingInstance, filters, getHighestCpmBidsFromBidPool, sortByDealAndPriceBucketOrCpm } from 'src/targeting.js'; import { config } from 'src/config.js'; import { createBidReceived } from 'test/fixtures/fixtures.js'; import CONSTANTS from 'src/constants.json'; import { auctionManager } from 'src/auctionManager.js'; import * as utils from 'src/utils.js'; const bid1 = { 'bidderCode': 'rubicon', 'width': '300', 'height': '250', 'statusMessage': 'Bid available', 'adId': '148018fe5e', 'cpm': 0.537234, 'ad': 'markup', 'ad_id': '3163950', 'sizeId': '15', 'requestTimestamp': 1454535718610, 'responseTimestamp': 1454535724863, 'timeToRespond': 123, 'pbLg': '0.50', 'pbMg': '0.50', 'pbHg': '0.53', 'adUnitCode': '/123456/header-bid-tag-0', 'bidder': 'rubicon', 'size': '300x250', 'adserverTargeting': { 'foobar': '300x250', [CONSTANTS.TARGETING_KEYS.BIDDER]: 'rubicon', [CONSTANTS.TARGETING_KEYS.AD_ID]: '148018fe5e', [CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]: '0.53', [CONSTANTS.TARGETING_KEYS.DEAL]: '1234' }, 'dealId': '1234', 'netRevenue': true, 'currency': 'USD', 'ttl': 300 }; const bid2 = { 'bidderCode': 'rubicon', 'width': '300', 'height': '250', 'statusMessage': 'Bid available', 'adId': '5454545', 'cpm': 0.25, 'ad': 'markup', 'ad_id': '3163950', 'sizeId': '15', 'requestTimestamp': 1454535718610, 'responseTimestamp': 1454535724863, 'timeToRespond': 123, 'pbLg': '0.25', 'pbMg': '0.25', 'pbHg': '0.25', 'adUnitCode': '/123456/header-bid-tag-0', 'bidder': 'rubicon', 'size': '300x250', 'adserverTargeting': { 'foobar': '300x250', [CONSTANTS.TARGETING_KEYS.BIDDER]: 'rubicon', [CONSTANTS.TARGETING_KEYS.AD_ID]: '5454545', [CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]: '0.25' }, 'netRevenue': true, 'currency': 'USD', 'ttl': 300 }; const bid3 = { 'bidderCode': 'rubicon', 'width': '300', 'height': '600', 'statusMessage': 'Bid available', 'adId': '48747745', 'cpm': 0.75, 'ad': 'markup', 'ad_id': '3163950', 'sizeId': '15', 'requestTimestamp': 1454535718610, 'responseTimestamp': 1454535724863, 'timeToRespond': 123, 'pbLg': '0.75', 'pbMg': '0.75', 'pbHg': '0.75', 'adUnitCode': '/123456/header-bid-tag-1', 'bidder': 'rubicon', 'size': '300x600', 'adserverTargeting': { 'foobar': '300x600', [CONSTANTS.TARGETING_KEYS.BIDDER]: 'rubicon', [CONSTANTS.TARGETING_KEYS.AD_ID]: '48747745', [CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]: '0.75' }, 'netRevenue': true, 'currency': 'USD', 'ttl': 300 }; const nativeBid1 = { 'bidderCode': 'appnexus', 'width': 0, 'height': 0, 'statusMessage': 'Bid available', 'adId': '591e7c9354b633', 'requestId': '24aae81e32d6f6', 'mediaType': 'native', 'source': 'client', 'cpm': 10, 'creativeId': 97494403, 'currency': 'USD', 'netRevenue': true, 'ttl': 300, 'adUnitCode': '/19968336/prebid_native_example_1', 'appnexus': { 'buyerMemberId': 9325 }, 'meta': { 'advertiserId': 2529885 }, 'native': { 'title': 'This is a Prebid Native Creative', 'body': 'This is a Prebid Native Creative. There are many like it, but this one is mine.', 'sponsoredBy': 'Prebid.org', 'clickUrl': 'http://prebid.org/dev-docs/show-native-ads.html', 'clickTrackers': ['http://www.clickUrl.com/404'], 'impressionTrackers': ['http://imp.trackerUrl.com/it1'], 'javascriptTrackers': '<script>//js script here</script>', 'image': { 'url': 'http://vcdn.adnxs.com/p/creative-image/94/22/cd/0f/9422cd0f-f400-45d3-80f5-2b92629d9257.jpg', 'height': 2250, 'width': 3000 }, 'icon': { 'url': 'http://vcdn.adnxs.com/p/creative-image/bd/59/a6/c6/bd59a6c6-0851-411d-a16d-031475a51312.png', 'height': 83, 'width': 127 } }, 'auctionId': '72138a4a-b747-4192-9192-dcc41d675de8', 'responseTimestamp': 1565785219461, 'requestTimestamp': 1565785219405, 'bidder': 'appnexus', 'timeToRespond': 56, 'pbLg': '5.00', 'pbMg': '10.00', 'pbHg': '10.00', 'pbAg': '10.00', 'pbDg': '10.00', 'pbCg': '', 'size': '0x0', 'adserverTargeting': { [CONSTANTS.TARGETING_KEYS.BIDDER]: 'appnexus', [CONSTANTS.TARGETING_KEYS.AD_ID]: '591e7c9354b633', [CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]: '10.00', [CONSTANTS.TARGETING_KEYS.SIZE]: '0x0', [CONSTANTS.TARGETING_KEYS.SOURCE]: 'client', [CONSTANTS.TARGETING_KEYS.FORMAT]: 'native', [CONSTANTS.NATIVE_KEYS.title]: 'This is a Prebid Native Creative', [CONSTANTS.NATIVE_KEYS.body]: 'This is a Prebid Native Creative. There are many like it, but this one is mine.', [CONSTANTS.NATIVE_KEYS.sponsoredBy]: 'Prebid.org', [CONSTANTS.NATIVE_KEYS.clickUrl]: 'http://prebid.org/dev-docs/show-native-ads.html', [CONSTANTS.NATIVE_KEYS.image]: 'http://vcdn.adnxs.com/p/creative-image/94/22/cd/0f/9422cd0f-f400-45d3-80f5-2b92629d9257.jpg', [CONSTANTS.NATIVE_KEYS.icon]: 'http://vcdn.adnxs.com/p/creative-image/bd/59/a6/c6/bd59a6c6-0851-411d-a16d-031475a51312.png' } }; const nativeBid2 = { 'bidderCode': 'dgads', 'width': 0, 'height': 0, 'statusMessage': 'Bid available', 'adId': '6e0aba55ed54e5', 'requestId': '4de26ec83d9661', 'mediaType': 'native', 'source': 'client', 'cpm': 1.90909091, 'creativeId': 'xuidx6c901261b0x2b2', 'currency': 'JPY', 'netRevenue': true, 'ttl': 60, 'referrer': 'http://test.localhost:9999/integrationExamples/gpt/demo_native.html?pbjs_debug=true', 'native': { 'image': { 'url': 'https://ads-tr.bigmining.com/img/300x250.png', 'width': 300, 'height': 250 }, 'title': 'Test Title', 'body': 'Test Description', 'sponsoredBy': 'test.com', 'clickUrl': 'http://prebid.org/', 'clickTrackers': ['https://www.clickUrl.com/404'], 'impressionTrackers': [ 'http://imp.trackerUrl.com/it2' ] }, 'auctionId': '72138a4a-b747-4192-9192-dcc41d675de8', 'responseTimestamp': 1565785219607, 'requestTimestamp': 1565785219409, 'bidder': 'dgads', 'adUnitCode': '/19968336/prebid_native_example_1', 'timeToRespond': 198, 'pbLg': '1.50', 'pbMg': '1.90', 'pbHg': '1.90', 'pbAg': '1.90', 'pbDg': '1.90', 'pbCg': '', 'size': '0x0', 'adserverTargeting': { [CONSTANTS.TARGETING_KEYS.BIDDER]: 'dgads', [CONSTANTS.TARGETING_KEYS.AD_ID]: '6e0aba55ed54e5', [CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]: '1.90', [CONSTANTS.TARGETING_KEYS.SIZE]: '0x0', [CONSTANTS.TARGETING_KEYS.SOURCE]: 'client', [CONSTANTS.TARGETING_KEYS.FORMAT]: 'native', [CONSTANTS.NATIVE_KEYS.image]: 'https://ads-tr.bigmining.com/img/300x250.png', [CONSTANTS.NATIVE_KEYS.title]: 'Test Title', [CONSTANTS.NATIVE_KEYS.body]: 'Test Description', [CONSTANTS.NATIVE_KEYS.sponsoredBy]: 'test.com', [CONSTANTS.NATIVE_KEYS.clickUrl]: 'http://prebid.org/' } }; describe('targeting tests', function () { let sandbox; let enableSendAllBids = false; let useBidCache; beforeEach(function() { sandbox = sinon.sandbox.create(); useBidCache = true; let origGetConfig = config.getConfig; sandbox.stub(config, 'getConfig').callsFake(function (key) { if (key === 'enableSendAllBids') { return enableSendAllBids; } if (key === 'useBidCache') { return useBidCache; } return origGetConfig.apply(config, arguments); }); }); afterEach(function () { sandbox.restore(); }); describe('getAllTargeting', function () { let amBidsReceivedStub; let amGetAdUnitsStub; let bidExpiryStub; let logWarnStub; let logErrorStub; let bidsReceived; beforeEach(function () { bidsReceived = [bid1, bid2, bid3]; amBidsReceivedStub = sandbox.stub(auctionManager, 'getBidsReceived').callsFake(function() { return bidsReceived; }); amGetAdUnitsStub = sandbox.stub(auctionManager, 'getAdUnitCodes').callsFake(function() { return ['/123456/header-bid-tag-0']; }); bidExpiryStub = sandbox.stub(filters, 'isBidNotExpired').returns(true); logWarnStub = sinon.stub(utils, 'logWarn'); logErrorStub = sinon.stub(utils, 'logError'); }); afterEach(function() { config.resetConfig(); logWarnStub.restore(); logErrorStub.restore(); amBidsReceivedStub.restore(); amGetAdUnitsStub.restore(); bidExpiryStub.restore(); }); describe('when handling different adunit targeting value types', function () { const adUnitCode = '/123456/header-bid-tag-0'; const adServerTargeting = {}; let getAdUnitsStub; before(function() { getAdUnitsStub = sandbox.stub(auctionManager, 'getAdUnits').callsFake(function() { return [ { 'code': adUnitCode, [CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING]: adServerTargeting } ]; }); }); after(function() { getAdUnitsStub.restore(); }); afterEach(function() { delete adServerTargeting.test_type; }); const pairs = [ ['string', '2.3', '2.3'], ['number', 2.3, '2.3'], ['boolean', true, 'true'], ['string-separated', '2.3,4.5', '2.3, 4.5'], ['array-of-string', ['2.3', '4.5'], '2.3, 4.5'], ['array-of-number', [2.3, 4.5], '2.3, 4.5'], ['array-of-boolean', [true, false], 'true, false'] ]; pairs.forEach(([type, value, result]) => { it(`accepts ${type}`, function() { adServerTargeting.test_type = value; const targeting = targetingInstance.getAllTargeting([adUnitCode]); expect(targeting[adUnitCode].test_type).is.equal(result); }); }); }); describe('when hb_deal is present in bid.adserverTargeting', function () { let bid4; beforeEach(function() { bid4 = utils.deepClone(bid1); bid4.adserverTargeting['hb_bidder'] = bid4.bidder = bid4.bidderCode = 'appnexus'; bid4.cpm = 0; enableSendAllBids = true; bidsReceived.push(bid4); }); it('returns targeting with both hb_deal and hb_deal_{bidder_code}', function () { const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); // We should add both keys rather than one or the other expect(targeting['/123456/header-bid-tag-0']).to.contain.keys('hb_deal', `hb_deal_${bid1.bidderCode}`, `hb_deal_${bid4.bidderCode}`); // We should assign both keys the same value expect(targeting['/123456/header-bid-tag-0']['hb_deal']).to.deep.equal(targeting['/123456/header-bid-tag-0'][`hb_deal_${bid1.bidderCode}`]); }); }); it('will enforce a limit on the number of auction keys when auctionKeyMaxChars setting is active', function () { config.setConfig({ targetingControls: { auctionKeyMaxChars: 150 } }); const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0', '/123456/header-bid-tag-1']); expect(targeting['/123456/header-bid-tag-1']).to.deep.equal({}); expect(targeting['/123456/header-bid-tag-0']).to.contain.keys('hb_pb', 'hb_adid', 'hb_bidder', 'hb_deal'); expect(targeting['/123456/header-bid-tag-0']['hb_adid']).to.equal(bid1.adId); expect(logWarnStub.calledOnce).to.be.true; }); it('will return an error when auctionKeyMaxChars setting is set too low for any auction keys to be allowed', function () { config.setConfig({ targetingControls: { auctionKeyMaxChars: 50 } }); const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0', '/123456/header-bid-tag-1']); expect(targeting['/123456/header-bid-tag-1']).to.deep.equal({}); expect(targeting['/123456/header-bid-tag-0']).to.deep.equal({}); expect(logWarnStub.calledTwice).to.be.true; expect(logErrorStub.calledOnce).to.be.true; }); describe('when bidLimit is present in setConfig', function () { let bid4; beforeEach(function() { bid4 = utils.deepClone(bid1); bid4.adserverTargeting['hb_bidder'] = bid4.bidder = bid4.bidderCode = 'appnexus'; bid4.cpm = 2.25; bid4.adId = '8383838'; enableSendAllBids = true; bidsReceived.push(bid4); }); it('when sendBidsControl.bidLimit is set greater than 0 in getHighestCpmBidsFromBidPool', function () { config.setConfig({ sendBidsControl: { bidLimit: 2, dealPrioritization: true } }); const bids = getHighestCpmBidsFromBidPool(bidsReceived, utils.getHighestCpm, 2); expect(bids.length).to.equal(3); expect(bids[0].adId).to.equal('8383838'); expect(bids[1].adId).to.equal('148018fe5e'); expect(bids[2].adId).to.equal('48747745'); }); it('when sendBidsControl.bidLimit is set greater than 0 and deal priortization is false in getHighestCpmBidsFromBidPool', function () { config.setConfig({ sendBidsControl: { bidLimit: 2, dealPrioritization: false } }); const bids = getHighestCpmBidsFromBidPool(bidsReceived, utils.getHighestCpm, 2); expect(bids.length).to.equal(3); expect(bids[0].adId).to.equal('8383838'); expect(bids[1].adId).to.equal('148018fe5e'); expect(bids[2].adId).to.equal('48747745'); }); it('selects the top n number of bids when enableSendAllBids is true and and bitLimit is set', function () { config.setConfig({ sendBidsControl: { bidLimit: 1 } }); const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); let limitedBids = Object.keys(targeting['/123456/header-bid-tag-0']).filter(key => key.indexOf(CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_') != -1) expect(limitedBids.length).to.equal(1); }); it('sends all bids when enableSendAllBids is true and and bitLimit is above total number of bids received', function () { config.setConfig({ sendBidsControl: { bidLimit: 50 } }); const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); let limitedBids = Object.keys(targeting['/123456/header-bid-tag-0']).filter(key => key.indexOf(CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_') != -1) expect(limitedBids.length).to.equal(2); }); it('Sends all bids when enableSendAllBids is true and and bidLimit is set to 0', function () { config.setConfig({ sendBidsControl: { bidLimit: 0 } }); const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); let limitedBids = Object.keys(targeting['/123456/header-bid-tag-0']).filter(key => key.indexOf(CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_') != -1) expect(limitedBids.length).to.equal(2); }); }); describe('targetingControls.allowTargetingKeys', function () { let bid4; beforeEach(function() { bid4 = utils.deepClone(bid1); bid4.adserverTargeting = { hb_deal: '4321', hb_pb: '0.1', hb_adid: '567891011', hb_bidder: 'appnexus', }; bid4.bidder = bid4.bidderCode = 'appnexus'; bid4.cpm = 0.1; // losing bid so not included if enableSendAllBids === false bid4.dealId = '4321'; enableSendAllBids = true; config.setConfig({ targetingControls: { allowTargetingKeys: ['BIDDER', 'AD_ID', 'PRICE_BUCKET'] } }); bidsReceived.push(bid4); }); it('targeting should include custom keys', function () { const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); expect(targeting['/123456/header-bid-tag-0']).to.include.all.keys('foobar'); }); it('targeting should include keys prefixed by allowed default targeting keys', function () { const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); expect(targeting['/123456/header-bid-tag-0']).to.include.all.keys('hb_bidder_rubicon', 'hb_adid_rubicon', 'hb_pb_rubicon'); expect(targeting['/123456/header-bid-tag-0']).to.include.all.keys('hb_bidder_appnexus', 'hb_adid_appnexus', 'hb_pb_appnexus'); }); it('targeting should not include keys prefixed by disallowed default targeting keys', function () { const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); expect(targeting['/123456/header-bid-tag-0']).to.not.have.all.keys(['hb_deal_appnexus', 'hb_deal_rubicon']); }); }); describe('targetingControls.allowSendAllBidsTargetingKeys', function () { let bid4; beforeEach(function() { bid4 = utils.deepClone(bid1); bid4.adserverTargeting = { hb_deal: '4321', hb_pb: '0.1', hb_adid: '567891011', hb_bidder: 'appnexus', }; bid4.bidder = bid4.bidderCode = 'appnexus'; bid4.cpm = 0.1; // losing bid so not included if enableSendAllBids === false bid4.dealId = '4321'; enableSendAllBids = true; config.setConfig({ targetingControls: { allowTargetingKeys: ['BIDDER', 'AD_ID', 'PRICE_BUCKET'], allowSendAllBidsTargetingKeys: ['PRICE_BUCKET', 'AD_ID'] } }); bidsReceived.push(bid4); }); it('targeting should include custom keys', function () { const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); expect(targeting['/123456/header-bid-tag-0']).to.include.all.keys('foobar'); }); it('targeting should only include keys prefixed by allowed default send all bids targeting keys and standard keys', function () { const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); expect(targeting['/123456/header-bid-tag-0']).to.include.all.keys('hb_bidder', 'hb_adid', 'hb_pb'); expect(targeting['/123456/header-bid-tag-0']).to.include.all.keys('hb_adid_rubicon', 'hb_pb_rubicon'); expect(targeting['/123456/header-bid-tag-0']).to.include.all.keys('hb_bidder', 'hb_adid', 'hb_pb', 'hb_adid_appnexus', 'hb_pb_appnexus'); }); it('targeting should not include keys prefixed by disallowed default targeting keys and disallowed send all bid targeting keys', function () { const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); expect(targeting['/123456/header-bid-tag-0']).to.not.have.all.keys(['hb_deal', 'hb_bidder_rubicon', 'hb_bidder_appnexus', 'hb_deal_appnexus', 'hb_deal_rubicon']); }); }); describe('targetingControls.alwaysIncludeDeals', function () { let bid4; beforeEach(function() { bid4 = utils.deepClone(bid1); bid4.adserverTargeting = { hb_deal: '4321', hb_pb: '0.1', hb_adid: '567891011', hb_bidder: 'appnexus', }; bid4.bidder = bid4.bidderCode = 'appnexus'; bid4.cpm = 0.1; // losing bid so not included if enableSendAllBids === false bid4.dealId = '4321'; enableSendAllBids = false; bidsReceived.push(bid4); }); it('does not include losing deals when alwaysIncludeDeals not set', function () { const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); // Rubicon wins bid and has deal, but alwaysIncludeDeals is false, so only top bid plus deal_id // appnexus does not get sent since alwaysIncludeDeals is not defined expect(targeting['/123456/header-bid-tag-0']).to.deep.equal({ 'hb_deal_rubicon': '1234', 'hb_deal': '1234', 'hb_pb': '0.53', 'hb_adid': '148018fe5e', 'hb_bidder': 'rubicon', 'foobar': '300x250' }); }); it('does not include losing deals when alwaysIncludeDeals set to false', function () { config.setConfig({ targetingControls: { alwaysIncludeDeals: false } }); const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); // Rubicon wins bid and has deal, but alwaysIncludeDeals is false, so only top bid plus deal_id // appnexus does not get sent since alwaysIncludeDeals is false expect(targeting['/123456/header-bid-tag-0']).to.deep.equal({ 'hb_deal_rubicon': '1234', // This is just how it works before this PR, always added no matter what for winner if they have deal 'hb_deal': '1234', 'hb_pb': '0.53', 'hb_adid': '148018fe5e', 'hb_bidder': 'rubicon', 'foobar': '300x250' }); }); it('includes losing deals when alwaysIncludeDeals set to true and also winning deals bidder KVPs', function () { config.setConfig({ targetingControls: { alwaysIncludeDeals: true } }); const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); // Rubicon wins bid and has a deal, so all KVPs for them are passed (top plus bidder specific) // Appnexus had deal so passed through expect(targeting['/123456/header-bid-tag-0']).to.deep.equal({ 'hb_deal_rubicon': '1234', 'hb_deal': '1234', 'hb_pb': '0.53', 'hb_adid': '148018fe5e', 'hb_bidder': 'rubicon', 'foobar': '300x250', 'hb_pb_rubicon': '0.53', 'hb_adid_rubicon': '148018fe5e', 'hb_bidder_rubicon': 'rubicon', 'hb_deal_appnexus': '4321', 'hb_pb_appnexus': '0.1', 'hb_adid_appnexus': '567891011', 'hb_bidder_appnexus': 'appnexus' }); }); it('includes winning bid even when it is not a deal, plus other deal KVPs', function () { config.setConfig({ targetingControls: { alwaysIncludeDeals: true } }); let bid5 = utils.deepClone(bid4); bid5.adserverTargeting = { hb_pb: '3.0', hb_adid: '111111', hb_bidder: 'pubmatic', }; bid5.bidder = bid5.bidderCode = 'pubmatic'; bid5.cpm = 3.0; // winning bid! delete bid5.dealId; // no deal with winner bidsReceived.push(bid5); const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); // Pubmatic wins but no deal. So only top bid KVPs for them is sent // Rubicon has a dealId so passed through // Appnexus has a dealId so passed through expect(targeting['/123456/header-bid-tag-0']).to.deep.equal({ 'hb_bidder': 'pubmatic', 'hb_adid': '111111', 'hb_pb': '3.0', 'foobar': '300x250', 'hb_deal_rubicon': '1234', 'hb_pb_rubicon': '0.53', 'hb_adid_rubicon': '148018fe5e', 'hb_bidder_rubicon': 'rubicon', 'hb_deal_appnexus': '4321', 'hb_pb_appnexus': '0.1', 'hb_adid_appnexus': '567891011', 'hb_bidder_appnexus': 'appnexus' }); }); }); it('selects the top bid when enableSendAllBids true', function () { enableSendAllBids = true; let targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); // we should only get the targeting data for the one requested adunit expect(Object.keys(targeting).length).to.equal(1); let sendAllBidCpm = Object.keys(targeting['/123456/header-bid-tag-0']).filter(key => key.indexOf(CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_') != -1) // we shouldn't get more than 1 key for hb_pb_${bidder} expect(sendAllBidCpm.length).to.equal(1); // expect the winning CPM to be equal to the sendAllBidCPM expect(targeting['/123456/header-bid-tag-0'][CONSTANTS.TARGETING_KEYS.PRICE_BUCKET + '_rubicon']).to.deep.equal(targeting['/123456/header-bid-tag-0'][CONSTANTS.TARGETING_KEYS.PRICE_BUCKET]); }); it('ensures keys are properly generated when enableSendAllBids is true and multiple bidders use native', function() { const nativeAdUnitCode = '/19968336/prebid_native_example_1'; enableSendAllBids = true; // update mocks for this test to return native bids amBidsReceivedStub.callsFake(function() { return [nativeBid1, nativeBid2]; }); amGetAdUnitsStub.callsFake(function() { return [nativeAdUnitCode]; }); let targeting = targetingInstance.getAllTargeting([nativeAdUnitCode]); expect(targeting[nativeAdUnitCode].hb_native_image).to.equal(nativeBid1.native.image.url); expect(targeting[nativeAdUnitCode].hb_native_linkurl).to.equal(nativeBid1.native.clickUrl); expect(targeting[nativeAdUnitCode].hb_native_title).to.equal(nativeBid1.native.title); expect(targeting[nativeAdUnitCode].hb_native_image_dgad).to.exist.and.to.equal(nativeBid2.native.image.url); expect(targeting[nativeAdUnitCode].hb_pb_dgads).to.exist.and.to.equal(nativeBid2.pbMg); expect(targeting[nativeAdUnitCode].hb_native_body_appne).to.exist.and.to.equal(nativeBid1.native.body); }); it('does not include adpod type bids in the getBidsReceived results', function () { let adpodBid = utils.deepClone(bid1); adpodBid.video = { context: 'adpod', durationSeconds: 15, durationBucket: 15 }; adpodBid.cpm = 5; bidsReceived.push(adpodBid); const targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); expect(targeting['/123456/header-bid-tag-0']).to.contain.keys('hb_deal', 'hb_adid', 'hb_bidder'); expect(targeting['/123456/header-bid-tag-0']['hb_adid']).to.equal(bid1.adId); }); }); // end getAllTargeting tests describe('getAllTargeting without bids return empty object', function () { let amBidsReceivedStub; let amGetAdUnitsStub; let bidExpiryStub; beforeEach(function () { amBidsReceivedStub = sandbox.stub(auctionManager, 'getBidsReceived').callsFake(function() { return []; }); amGetAdUnitsStub = sandbox.stub(auctionManager, 'getAdUnitCodes').callsFake(function() { return ['/123456/header-bid-tag-0']; }); bidExpiryStub = sandbox.stub(filters, 'isBidNotExpired').returns(true); }); it('returns targetingSet correctly', function () { let targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); // we should only get the targeting data for the one requested adunit to at least exist even though it has no keys to set expect(Object.keys(targeting).length).to.equal(1); }); }); // end getAllTargeting without bids return empty object describe('Targeting in concurrent auctions', function () { describe('check getOldestBid', function () { let bidExpiryStub; let auctionManagerStub; beforeEach(function () { bidExpiryStub = sandbox.stub(filters, 'isBidNotExpired').returns(true); auctionManagerStub = sandbox.stub(auctionManager, 'getBidsReceived'); }); it('should use bids from pool to get Winning Bid', function () { let bidsReceived = [ createBidReceived({bidder: 'appnexus', cpm: 7, auctionId: 1, responseTimestamp: 100, adUnitCode: 'code-0', adId: 'adid-1'}), createBidReceived({bidder: 'rubicon', cpm: 6, auctionId: 1, responseTimestamp: 101, adUnitCode: 'code-1', adId: 'adid-2'}), createBidReceived({bidder: 'appnexus', cpm: 6, auctionId: 2, responseTimestamp: 102, adUnitCode: 'code-0', adId: 'adid-3'}), createBidReceived({bidder: 'rubicon', cpm: 6, auctionId: 2, responseTimestamp: 103, adUnitCode: 'code-1', adId: 'adid-4'}), ]; let adUnitCodes = ['code-0', 'code-1']; let bids = targetingInstance.getWinningBids(adUnitCodes, bidsReceived); expect(bids.length).to.equal(2); expect(bids[0].adId).to.equal('adid-1'); expect(bids[1].adId).to.equal('adid-2'); }); it('should honor useBidCache', function() { useBidCache = true; auctionManagerStub.returns([ createBidReceived({bidder: 'appnexus', cpm: 7, auctionId: 1, responseTimestamp: 100, adUnitCode: 'code-0', adId: 'adid-1'}), createBidReceived({bidder: 'appnexus', cpm: 5, auctionId: 2, responseTimestamp: 102, adUnitCode: 'code-0', adId: 'adid-2'}), ]); let adUnitCodes = ['code-0']; targetingInstance.setLatestAuctionForAdUnit('code-0', 2); let bids = targetingInstance.getWinningBids(adUnitCodes); expect(bids.length).to.equal(1); expect(bids[0].adId).to.equal('adid-1'); useBidCache = false; bids = targetingInstance.getWinningBids(adUnitCodes); expect(bids.length).to.equal(1); expect(bids[0].adId).to.equal('adid-2'); }); it('should not use rendered bid to get winning bid', function () { let bidsReceived = [ createBidReceived({bidder: 'appnexus', cpm: 8, auctionId: 1, responseTimestamp: 100, adUnitCode: 'code-0', adId: 'adid-1', status: 'rendered'}), createBidReceived({bidder: 'rubicon', cpm: 6, auctionId: 1, responseTimestamp: 101, adUnitCode: 'code-1', adId: 'adid-2'}), createBidReceived({bidder: 'appnexus', cpm: 7, auctionId: 2, responseTimestamp: 102, adUnitCode: 'code-0', adId: 'adid-3'}), createBidReceived({bidder: 'rubicon', cpm: 6, auctionId: 2, responseTimestamp: 103, adUnitCode: 'code-1', adId: 'adid-4'}), ]; auctionManagerStub.returns(bidsReceived); let adUnitCodes = ['code-0', 'code-1']; let bids = targetingInstance.getWinningBids(adUnitCodes); expect(bids.length).to.equal(2); expect(bids[0].adId).to.equal('adid-2'); expect(bids[1].adId).to.equal('adid-3'); }); it('should use highest cpm bid from bid pool to get winning bid', function () { // Pool is having 4 bids from 2 auctions. There are 2 bids from rubicon, #2 which is highest cpm bid will be selected to take part in auction. let bidsReceived = [ createBidReceived({bidder: 'appnexus', cpm: 8, auctionId: 1, responseTimestamp: 100, adUnitCode: 'code-0', adId: 'adid-1'}), createBidReceived({bidder: 'rubicon', cpm: 9, auctionId: 1, responseTimestamp: 101, adUnitCode: 'code-0', adId: 'adid-2'}), createBidReceived({bidder: 'appnexus', cpm: 7, auctionId: 2, responseTimestamp: 102, adUnitCode: 'code-0', adId: 'adid-3'}), createBidReceived({bidder: 'rubicon', cpm: 8, auctionId: 2, responseTimestamp: 103, adUnitCode: 'code-0', adId: 'adid-4'}), ]; auctionManagerStub.returns(bidsReceived); let adUnitCodes = ['code-0']; let bids = targetingInstance.getWinningBids(adUnitCodes); expect(bids.length).to.equal(1); expect(bids[0].adId).to.equal('adid-2'); }); }); describe('check bidExpiry', function () { let auctionManagerStub; let timestampStub; beforeEach(function () { auctionManagerStub = sandbox.stub(auctionManager, 'getBidsReceived'); timestampStub = sandbox.stub(utils, 'timestamp'); }); it('should not include expired bids in the auction', function () { timestampStub.returns(200000); // Pool is having 4 bids from 2 auctions. All the bids are expired and only bid #3 is passing the bidExpiry check. let bidsReceived = [ createBidReceived({bidder: 'appnexus', cpm: 18, auctionId: 1, responseTimestamp: 100, adUnitCode: 'code-0', adId: 'adid-1', ttl: 150}), createBidReceived({bidder: 'sampleBidder', cpm: 16, auctionId: 1, responseTimestamp: 101, adUnitCode: 'code-0', adId: 'adid-2', ttl: 100}), createBidReceived({bidder: 'appnexus', cpm: 7, auctionId: 2, responseTimestamp: 102, adUnitCode: 'code-0', adId: 'adid-3', ttl: 300}), createBidReceived({bidder: 'rubicon', cpm: 6, auctionId: 2, responseTimestamp: 103, adUnitCode: 'code-0', adId: 'adid-4', ttl: 50}), ]; auctionManagerStub.returns(bidsReceived); let adUnitCodes = ['code-0', 'code-1']; let bids = targetingInstance.getWinningBids(adUnitCodes); expect(bids.length).to.equal(1); expect(bids[0].adId).to.equal('adid-3'); }); }); }); describe('sortByDealAndPriceBucketOrCpm', function() { it('will properly sort bids when some bids have deals and some do not', function () { let bids = [{ adserverTargeting: { hb_adid: 'abc', hb_pb: '1.00', hb_deal: '1234' } }, { adserverTargeting: { hb_adid: 'def', hb_pb: '0.50', } }, { adserverTargeting: { hb_adid: 'ghi', hb_pb: '20.00', hb_deal: '4532' } }, { adserverTargeting: { hb_adid: 'jkl', hb_pb: '9.00', hb_deal: '9864' } }, { adserverTargeting: { hb_adid: 'mno', hb_pb: '50.00', } }, { adserverTargeting: { hb_adid: 'pqr', hb_pb: '100.00', } }]; bids.sort(sortByDealAndPriceBucketOrCpm()); expect(bids[0].adserverTargeting.hb_adid).to.equal('ghi'); expect(bids[1].adserverTargeting.hb_adid).to.equal('jkl'); expect(bids[2].adserverTargeting.hb_adid).to.equal('abc'); expect(bids[3].adserverTargeting.hb_adid).to.equal('pqr'); expect(bids[4].adserverTargeting.hb_adid).to.equal('mno'); expect(bids[5].adserverTargeting.hb_adid).to.equal('def'); }); it('will properly sort bids when all bids have deals', function () { let bids = [{ adserverTargeting: { hb_adid: 'abc', hb_pb: '1.00', hb_deal: '1234' } }, { adserverTargeting: { hb_adid: 'def', hb_pb: '0.50', hb_deal: '4321' } }, { adserverTargeting: { hb_adid: 'ghi', hb_pb: '2.50', hb_deal: '4532' } }, { adserverTargeting: { hb_adid: 'jkl', hb_pb: '2.00', hb_deal: '9864' } }]; bids.sort(sortByDealAndPriceBucketOrCpm()); expect(bids[0].adserverTargeting.hb_adid).to.equal('ghi'); expect(bids[1].adserverTargeting.hb_adid).to.equal('jkl'); expect(bids[2].adserverTargeting.hb_adid).to.equal('abc'); expect(bids[3].adserverTargeting.hb_adid).to.equal('def'); }); it('will properly sort bids when no bids have deals', function () { let bids = [{ adserverTargeting: { hb_adid: 'abc', hb_pb: '1.00' } }, { adserverTargeting: { hb_adid: 'def', hb_pb: '0.10' } }, { adserverTargeting: { hb_adid: 'ghi', hb_pb: '10.00' } }, { adserverTargeting: { hb_adid: 'jkl', hb_pb: '10.01' } }, { adserverTargeting: { hb_adid: 'mno', hb_pb: '1.00' } }, { adserverTargeting: { hb_adid: 'pqr', hb_pb: '100.00' } }]; bids.sort(sortByDealAndPriceBucketOrCpm()); expect(bids[0].adserverTargeting.hb_adid).to.equal('pqr'); expect(bids[1].adserverTargeting.hb_adid).to.equal('jkl'); expect(bids[2].adserverTargeting.hb_adid).to.equal('ghi'); expect(bids[3].adserverTargeting.hb_adid).to.equal('abc'); expect(bids[4].adserverTargeting.hb_adid).to.equal('mno'); expect(bids[5].adserverTargeting.hb_adid).to.equal('def'); }); it('will properly sort bids when some bids have deals and some do not and by cpm when flag is set to true', function () { let bids = [{ cpm: 1.04, adserverTargeting: { hb_adid: 'abc', hb_pb: '1.00', hb_deal: '1234' } }, { cpm: 0.50, adserverTargeting: { hb_adid: 'def', hb_pb: '0.50', hb_deal: '4532' } }, { cpm: 0.53, adserverTargeting: { hb_adid: 'ghi', hb_pb: '0.50', hb_deal: '4532' } }, { cpm: 9.04, adserverTargeting: { hb_adid: 'jkl', hb_pb: '9.00', hb_deal: '9864' } }, { cpm: 50.00, adserverTargeting: { hb_adid: 'mno', hb_pb: '50.00', } }, { cpm: 100.00, adserverTargeting: { hb_adid: 'pqr', hb_pb: '100.00', } }]; bids.sort(sortByDealAndPriceBucketOrCpm(true)); expect(bids[0].adserverTargeting.hb_adid).to.equal('jkl'); expect(bids[1].adserverTargeting.hb_adid).to.equal('abc'); expect(bids[2].adserverTargeting.hb_adid).to.equal('ghi'); expect(bids[3].adserverTargeting.hb_adid).to.equal('def'); expect(bids[4].adserverTargeting.hb_adid).to.equal('pqr'); expect(bids[5].adserverTargeting.hb_adid).to.equal('mno'); }); }); describe('setTargetingForAst', function () { let sandbox, apnTagStub; beforeEach(function() { sandbox = sinon.createSandbox(); sandbox.stub(targetingInstance, 'resetPresetTargetingAST'); apnTagStub = sandbox.stub(window.apntag, 'setKeywords'); }); afterEach(function () { sandbox.restore(); }); it('should set single addUnit code', function() { let adUnitCode = 'testdiv-abc-ad-123456-0'; sandbox.stub(targetingInstance, 'getAllTargeting').returns({ 'testdiv1-abc-ad-123456-0': {hb_bidder: 'appnexus'} }); targetingInstance.setTargetingForAst(adUnitCode); expect(targetingInstance.getAllTargeting.called).to.equal(true); expect(targetingInstance.resetPresetTargetingAST.called).to.equal(true); expect(apnTagStub.callCount).to.equal(1); expect(apnTagStub.getCall(0).args[0]).to.deep.equal('testdiv1-abc-ad-123456-0'); expect(apnTagStub.getCall(0).args[1]).to.deep.equal({HB_BIDDER: 'appnexus'}); }); it('should set array of addUnit codes', function() { let adUnitCodes = ['testdiv1-abc-ad-123456-0', 'testdiv2-abc-ad-123456-0'] sandbox.stub(targetingInstance, 'getAllTargeting').returns({ 'testdiv1-abc-ad-123456-0': {hb_bidder: 'appnexus'}, 'testdiv2-abc-ad-123456-0': {hb_bidder: 'appnexus'} }); targetingInstance.setTargetingForAst(adUnitCodes); expect(targetingInstance.getAllTargeting.called).to.equal(true); expect(targetingInstance.resetPresetTargetingAST.called).to.equal(true); expect(apnTagStub.callCount).to.equal(2); expect(apnTagStub.getCall(1).args[0]).to.deep.equal('testdiv2-abc-ad-123456-0'); expect(apnTagStub.getCall(1).args[1]).to.deep.equal({HB_BIDDER: 'appnexus'}); }); }); });