UNPKG

mk9-prebid

Version:

Header Bidding Management Library

1,649 lines (1,471 loc) 70.9 kB
import { expect } from 'chai'; import adapterManager, { allS2SBidders, clientTestAdapters, gdprDataHandler, coppaDataHandler } from 'src/adapterManager.js'; import { getAdUnits, getServerTestingConfig, getServerTestingsAds, getBidRequests } from 'test/fixtures/fixtures.js'; import CONSTANTS from 'src/constants.json'; import * as utils from 'src/utils.js'; import { config } from 'src/config.js'; import { registerBidder } from 'src/adapters/bidderFactory.js'; import { setSizeConfig } from 'src/sizeMapping.js'; import find from 'core-js-pure/features/array/find.js'; import includes from 'core-js-pure/features/array/includes.js'; import s2sTesting from 'modules/s2sTesting.js'; var events = require('../../../../src/events'); const CONFIG = { enabled: true, endpoint: CONSTANTS.S2S.DEFAULT_ENDPOINT, timeout: 1000, maxBids: 1, adapter: 'prebidServer', bidders: ['appnexus'], accountId: 'abc' }; const CONFIG2 = { enabled: true, endpoint: 'https://prebid-server.rubiconproject.com/openrtb2/auction', timeout: 1000, maxBids: 1, adapter: 'prebidServer', bidders: ['pubmatic'], accountId: 'def' } var prebidServerAdapterMock = { bidder: 'prebidServer', callBids: sinon.stub() }; var adequantAdapterMock = { bidder: 'adequant', callBids: sinon.stub() }; var appnexusAdapterMock = { bidder: 'appnexus', callBids: sinon.stub() }; var rubiconAdapterMock = { bidder: 'rubicon', callBids: sinon.stub() }; var pubmaticAdapterMock = { bidder: 'rubicon', callBids: sinon.stub() }; var badAdapterMock = { bidder: 'badBidder', callBids: sinon.stub().throws(Error('some fake error')) }; describe('adapterManager tests', function () { let orgAppnexusAdapter; let orgAdequantAdapter; let orgPrebidServerAdapter; let orgRubiconAdapter; let orgBadBidderAdapter; before(function () { orgAppnexusAdapter = adapterManager.bidderRegistry['appnexus']; orgAdequantAdapter = adapterManager.bidderRegistry['adequant']; orgPrebidServerAdapter = adapterManager.bidderRegistry['prebidServer']; orgRubiconAdapter = adapterManager.bidderRegistry['rubicon']; orgBadBidderAdapter = adapterManager.bidderRegistry['badBidder']; }); after(function () { adapterManager.bidderRegistry['appnexus'] = orgAppnexusAdapter; adapterManager.bidderRegistry['adequant'] = orgAdequantAdapter; adapterManager.bidderRegistry['prebidServer'] = orgPrebidServerAdapter; adapterManager.bidderRegistry['rubicon'] = orgRubiconAdapter; adapterManager.bidderRegistry['badBidder'] = orgBadBidderAdapter; config.setConfig({s2sConfig: { enabled: false }}); }); describe('callBids', function () { before(function () { config.setConfig({s2sConfig: { enabled: false }}); }); beforeEach(function () { sinon.stub(utils, 'logError'); appnexusAdapterMock.callBids.reset(); adapterManager.bidderRegistry['appnexus'] = appnexusAdapterMock; adapterManager.bidderRegistry['rubicon'] = rubiconAdapterMock; adapterManager.bidderRegistry['badBidder'] = badAdapterMock; adapterManager.bidderRegistry['badBidder'] = badAdapterMock; }); afterEach(function () { utils.logError.restore(); delete adapterManager.bidderRegistry['appnexus']; delete adapterManager.bidderRegistry['rubicon']; delete adapterManager.bidderRegistry['badBidder']; config.resetConfig(); }); it('should log an error if a bidder is used that does not exist', function () { const adUnits = [{ code: 'adUnit-code', sizes: [[728, 90]], bids: [ {bidder: 'appnexus', params: {placementId: 'id'}}, {bidder: 'fakeBidder', params: {placementId: 'id'}} ] }]; let bidRequests = adapterManager.makeBidRequests(adUnits, 1111, 2222, 1000); expect(bidRequests.length).to.equal(1); expect(bidRequests[0].bidderCode).to.equal('appnexus'); sinon.assert.called(utils.logError); }); it('should catch a bidder adapter thrown error and continue with other bidders', function () { const adUnits = [{ code: 'adUnit-code', sizes: [[728, 90]], bids: [ {bidder: 'appnexus', params: {placementId: 'id'}}, {bidder: 'badBidder', params: {placementId: 'id'}}, {bidder: 'rubicon', params: {account: 1111, site: 2222, zone: 3333}} ] }]; let bidRequests = adapterManager.makeBidRequests(adUnits, 1111, 2222, 1000); let doneBidders = []; function mockDoneCB() { doneBidders.push(this.bidderCode) } adapterManager.callBids(adUnits, bidRequests, () => {}, mockDoneCB); sinon.assert.calledOnce(appnexusAdapterMock.callBids); sinon.assert.calledOnce(badAdapterMock.callBids); sinon.assert.calledOnce(rubiconAdapterMock.callBids); expect(utils.logError.calledOnce).to.be.true; expect(utils.logError.calledWith( 'badBidder Bid Adapter emitted an uncaught error when parsing their bidRequest' )).to.be.true; // done should be called for our bidder! expect(doneBidders.indexOf('badBidder') === -1).to.be.false; }); it('should emit BID_REQUESTED event', function () { // function to count BID_REQUESTED events let cnt = 0; let count = () => cnt++; events.on(CONSTANTS.EVENTS.BID_REQUESTED, count); let bidRequests = [{ 'bidderCode': 'appnexus', 'auctionId': '1863e370099523', 'bidderRequestId': '2946b569352ef2', 'tid': '34566b569352ef2', 'bids': [ { 'bidder': 'appnexus', 'params': { 'placementId': '4799418', 'test': 'me' }, 'adUnitCode': '/19968336/header-bid-tag1', 'sizes': [[728, 90], [970, 70]], 'bidId': '392b5a6b05d648', 'bidderRequestId': '2946b569352ef2', 'auctionId': '1863e370099523', 'startTime': 1462918897462, 'status': 1, 'transactionId': 'fsafsa' }, ], 'start': 1462918897460 }]; let adUnits = [{ code: 'adUnit-code', bids: [ {bidder: 'appnexus', params: {placementId: 'id'}}, ] }]; adapterManager.callBids(adUnits, bidRequests, () => {}, () => {}); expect(cnt).to.equal(1); sinon.assert.calledOnce(appnexusAdapterMock.callBids); events.off(CONSTANTS.EVENTS.BID_REQUESTED, count); }); it('should give bidders access to bidder-specific config', function(done) { let mockBidders = ['rubicon', 'appnexus', 'pubmatic']; let bidderRequest = getBidRequests().filter(bidRequest => includes(mockBidders, bidRequest.bidderCode)); let adUnits = getAdUnits(); let bidders = {}; let results = {}; let cbCount = 0; function mock(bidder) { bidders[bidder] = adapterManager.bidderRegistry[bidder]; adapterManager.bidderRegistry[bidder] = { callBids: function(bidRequest, addBidResponse, done, ajax, timeout, configCallback) { let myResults = results[bidRequest.bidderCode] = []; myResults.push(config.getConfig('buildRequests')); myResults.push(config.getConfig('test1')); myResults.push(config.getConfig('test2')); // emulate ajax callback that would register bids setTimeout(configCallback(() => { myResults.push(config.getConfig('interpretResponse')); myResults.push(config.getConfig('afterInterpretResponse')); if (++cbCount === Object.keys(bidders).length) { assertions(); } }), 1); done(); } } } mockBidders.forEach(bidder => { mock(bidder); }); config.setConfig({ buildRequests: { data: 1 }, test1: { speedy: true, fun: { test: true } }, interpretResponse: 'baseInterpret', afterInterpretResponse: 'anotherBaseInterpret' }); config.setBidderConfig({ bidders: [ 'appnexus' ], config: { buildRequests: { test: 2 }, test1: { fun: { safe: true, cheap: false } }, interpretResponse: 'appnexusInterpret' } }); config.setBidderConfig({ bidders: [ 'rubicon' ], config: { buildRequests: 'rubiconBuild', interpretResponse: null } }); config.setBidderConfig({ bidders: [ 'appnexus', 'rubicon' ], config: { test2: { amazing: true } } }); adapterManager.callBids(adUnits, bidderRequest, () => {}, () => {}); function assertions() { expect(results).to.deep.equal({ 'appnexus': [ { data: 1, test: 2 }, { fun: { safe: true, cheap: false, test: true }, speedy: true }, { amazing: true }, 'appnexusInterpret', 'anotherBaseInterpret' ], 'pubmatic': [ { data: 1 }, { fun: { test: true }, speedy: true }, undefined, 'baseInterpret', 'anotherBaseInterpret' ], 'rubicon': [ 'rubiconBuild', { fun: { test: true }, speedy: true }, { amazing: true }, null, 'anotherBaseInterpret' ] }); // restore bid adapters Object.keys(bidders).forEach(bidder => { adapterManager.bidderRegistry[bidder] = bidders[bidder]; }); done(); } }); }); describe('callTimedOutBidders', function () { var criteoSpec = { onTimeout: sinon.stub() } var criteoAdapter = { bidder: 'criteo', getSpec: function() { return criteoSpec; } } before(function () { config.setConfig({s2sConfig: { enabled: false }}); }); beforeEach(function () { adapterManager.bidderRegistry['criteo'] = criteoAdapter; }); afterEach(function () { delete adapterManager.bidderRegistry['criteo']; }); it('should call spec\'s onTimeout callback when callTimedOutBidders is called', function () { const adUnits = [{ code: 'adUnit-code', sizes: [[728, 90]], bids: [ {bidder: 'criteo', params: {placementId: 'id'}}, ] }]; const timedOutBidders = [{ bidId: 'bidId', bidder: 'criteo', adUnitCode: adUnits[0].code, auctionId: 'auctionId', }]; adapterManager.callTimedOutBidders(adUnits, timedOutBidders, CONFIG.timeout); sinon.assert.called(criteoSpec.onTimeout); }); }); // end callTimedOutBidders describe('onBidWon', function () { var criteoSpec = { onBidWon: sinon.stub() } var criteoAdapter = { bidder: 'criteo', getSpec: function() { return criteoSpec; } } before(function () { config.setConfig({s2sConfig: { enabled: false }}); }); beforeEach(function () { adapterManager.bidderRegistry['criteo'] = criteoAdapter; }); afterEach(function () { delete adapterManager.bidderRegistry['criteo']; }); it('should call spec\'s onBidWon callback when a bid is won', function () { const bids = [ {bidder: 'criteo', params: {placementId: 'id'}}, ]; const adUnits = [{ code: 'adUnit-code', sizes: [[728, 90]], bids }]; adapterManager.callBidWonBidder(bids[0].bidder, bids[0], adUnits); sinon.assert.called(criteoSpec.onBidWon); }); }); // end onBidWon describe('onSetTargeting', function () { var criteoSpec = { onSetTargeting: sinon.stub() } var criteoAdapter = { bidder: 'criteo', getSpec: function() { return criteoSpec; } } before(function () { config.setConfig({s2sConfig: { enabled: false }}); }); beforeEach(function () { adapterManager.bidderRegistry['criteo'] = criteoAdapter; }); afterEach(function () { delete adapterManager.bidderRegistry['criteo']; }); it('should call spec\'s onSetTargeting callback when setTargeting is called', function () { const bids = [ {bidder: 'criteo', params: {placementId: 'id'}}, ]; const adUnits = [{ code: 'adUnit-code', sizes: [[728, 90]], bids }]; adapterManager.callSetTargetingBidder(bids[0].bidder, bids[0], adUnits); sinon.assert.called(criteoSpec.onSetTargeting); }); }); // end onSetTargeting describe('onBidViewable', function () { var criteoSpec = { onBidViewable: sinon.stub() } var criteoAdapter = { bidder: 'criteo', getSpec: function() { return criteoSpec; } } before(function () { config.setConfig({s2sConfig: { enabled: false }}); }); beforeEach(function () { adapterManager.bidderRegistry['criteo'] = criteoAdapter; }); afterEach(function () { delete adapterManager.bidderRegistry['criteo']; }); it('should call spec\'s onBidViewable callback when callBidViewableBidder is called', function () { const bids = [ {bidder: 'criteo', params: {placementId: 'id'}}, ]; const adUnits = [{ code: 'adUnit-code', sizes: [[728, 90]], bids }]; adapterManager.callBidViewableBidder(bids[0].bidder, bids[0]); sinon.assert.called(criteoSpec.onBidViewable); }); }); // end onBidViewable describe('S2S tests', function () { beforeEach(function () { config.setConfig({s2sConfig: CONFIG}); adapterManager.bidderRegistry['prebidServer'] = prebidServerAdapterMock; prebidServerAdapterMock.callBids.reset(); }); const bidRequests = [{ 'bidderCode': 'appnexus', 'auctionId': '1863e370099523', 'bidderRequestId': '2946b569352ef2', 'tid': '34566b569352ef2', 'timeout': 1000, 'src': 's2s', 'adUnitsS2SCopy': [ { 'code': '/19968336/header-bid-tag1', 'sizes': [ { 'w': 728, 'h': 90 }, { 'w': 970, 'h': 90 } ], 'bids': [ { 'bidder': 'appnexus', 'params': { 'placementId': '543221', 'test': 'me' }, 'adUnitCode': '/19968336/header-bid-tag1', 'sizes': [ [ 728, 90 ], [ 970, 90 ] ], 'bidId': '68136e1c47023d', 'bidderRequestId': '55e24a66bed717', 'auctionId': '1ff753bd4ae5cb', 'startTime': 1463510220995, 'status': 1, 'bid_id': '68136e1c47023d' } ] }, { 'code': '/19968336/header-bid-tag-0', 'sizes': [ { 'w': 300, 'h': 250 }, { 'w': 300, 'h': 600 } ], 'bids': [ { 'bidder': 'appnexus', 'params': { 'placementId': '5324321' }, 'adUnitCode': '/19968336/header-bid-tag-0', 'sizes': [ [ 300, 250 ], [ 300, 600 ] ], 'bidId': '7e5d6af25ed188', 'bidderRequestId': '55e24a66bed717', 'auctionId': '1ff753bd4ae5cb', 'startTime': 1463510220996, 'bid_id': '7e5d6af25ed188' } ] } ], 'bids': [ { 'bidder': 'appnexus', 'params': { 'placementId': '4799418', 'test': 'me' }, 'adUnitCode': '/19968336/header-bid-tag1', 'sizes': [ [ 728, 90 ], [ 970, 90 ] ], 'bidId': '392b5a6b05d648', 'bidderRequestId': '2946b569352ef2', 'auctionId': '1863e370099523', 'startTime': 1462918897462, 'status': 1, 'transactionId': 'fsafsa' }, { 'bidder': 'appnexus', 'params': { 'placementId': '4799418' }, 'adUnitCode': '/19968336/header-bid-tag-0', 'sizes': [ [ 300, 250 ], [ 300, 600 ] ], 'bidId': '4dccdc37746135', 'bidderRequestId': '2946b569352ef2', 'auctionId': '1863e370099523', 'startTime': 1462918897463, 'status': 1, 'transactionId': 'fsafsa' } ], 'start': 1462918897460 }]; it('invokes callBids on the S2S adapter', function () { adapterManager.callBids( getAdUnits(), bidRequests, () => {}, () => () => {} ); sinon.assert.calledOnce(prebidServerAdapterMock.callBids); }); // Enable this test when prebidServer adapter is made 1.0 compliant it('invokes callBids with only s2s bids', function () { const adUnits = getAdUnits(); // adUnit without appnexus bidder adUnits.push({ 'code': '123', 'sizes': [300, 250], 'bids': [ { 'bidder': 'adequant', 'params': { 'publisher_id': '1234567', 'bidfloor': 0.01 } } ] }); adapterManager.callBids( adUnits, bidRequests, () => {}, () => () => {} ); const requestObj = prebidServerAdapterMock.callBids.firstCall.args[0]; expect(requestObj.ad_units.length).to.equal(2); sinon.assert.calledOnce(prebidServerAdapterMock.callBids); }); describe('BID_REQUESTED event', function () { // function to count BID_REQUESTED events let cnt, count = () => cnt++; beforeEach(function () { prebidServerAdapterMock.callBids.reset(); cnt = 0; events.on(CONSTANTS.EVENTS.BID_REQUESTED, count); }); afterEach(function () { events.off(CONSTANTS.EVENTS.BID_REQUESTED, count); }); it('should fire for s2s requests', function () { let adUnits = utils.deepClone(getAdUnits()).map(adUnit => { adUnit.bids = adUnit.bids.filter(bid => includes(['appnexus'], bid.bidder)); return adUnit; }) let bidRequests = adapterManager.makeBidRequests(adUnits, 1111, 2222, 1000); adapterManager.callBids(adUnits, bidRequests, () => {}, () => {}); expect(cnt).to.equal(1); sinon.assert.calledOnce(prebidServerAdapterMock.callBids); }); it('should fire for simultaneous s2s and client requests', function () { adapterManager.bidderRegistry['adequant'] = adequantAdapterMock; let adUnits = utils.deepClone(getAdUnits()).map(adUnit => { adUnit.bids = adUnit.bids.filter(bid => includes(['adequant', 'appnexus'], bid.bidder)); return adUnit; }) let bidRequests = adapterManager.makeBidRequests(adUnits, 1111, 2222, 1000); adapterManager.callBids(adUnits, bidRequests, () => {}, () => {}); expect(cnt).to.equal(2); sinon.assert.calledOnce(prebidServerAdapterMock.callBids); sinon.assert.calledOnce(adequantAdapterMock.callBids); adequantAdapterMock.callBids.reset(); delete adapterManager.bidderRegistry['adequant']; }); }); }); // end s2s tests describe('Multiple S2S tests', function () { beforeEach(function () { config.setConfig({s2sConfig: [CONFIG, CONFIG2]}); adapterManager.bidderRegistry['prebidServer'] = prebidServerAdapterMock; prebidServerAdapterMock.callBids.reset(); }); afterEach(function () { allS2SBidders.length = 0; }); const bidRequests = [{ 'bidderCode': 'appnexus', 'auctionId': '1863e370099523', 'bidderRequestId': '2946b569352ef2', 'tid': '34566b569352ef2', 'timeout': 1000, 'src': 's2s', 'adUnitsS2SCopy': [ { 'code': '/19968336/header-bid-tag1', 'sizes': [ { 'w': 728, 'h': 90 }, { 'w': 970, 'h': 90 } ], 'bids': [ { 'bidder': 'appnexus', 'params': { 'placementId': '543221', 'test': 'me' }, 'adUnitCode': '/19968336/header-bid-tag1', 'sizes': [ [ 728, 90 ], [ 970, 90 ] ], 'bidId': '68136e1c47023d', 'bidderRequestId': '55e24a66bed717', 'auctionId': '1ff753bd4ae5cb', 'startTime': 1463510220995, 'status': 1, 'bid_id': '68136e1c47023d' } ] }, { 'code': '/19968336/header-bid-tag-0', 'sizes': [ { 'w': 300, 'h': 250 }, { 'w': 300, 'h': 600 } ], 'bids': [ { 'bidder': 'appnexus', 'params': { 'placementId': '5324321' }, 'adUnitCode': '/19968336/header-bid-tag-0', 'sizes': [ [ 300, 250 ], [ 300, 600 ] ], 'bidId': '7e5d6af25ed188', 'bidderRequestId': '55e24a66bed717', 'auctionId': '1ff753bd4ae5cb', 'startTime': 1463510220996, 'bid_id': '7e5d6af25ed188' } ] } ], 'bids': [ { 'bidder': 'appnexus', 'params': { 'placementId': '4799418', 'test': 'me' }, 'adUnitCode': '/19968336/header-bid-tag1', 'sizes': [ [ 728, 90 ], [ 970, 90 ] ], 'bidId': '392b5a6b05d648', 'bidderRequestId': '2946b569352ef2', 'auctionId': '1863e370099523', 'startTime': 1462918897462, 'status': 1, 'transactionId': 'fsafsa' }, { 'bidder': 'appnexus', 'params': { 'placementId': '4799418' }, 'adUnitCode': '/19968336/header-bid-tag-0', 'sizes': [ [ 300, 250 ], [ 300, 600 ] ], 'bidId': '4dccdc37746135', 'bidderRequestId': '2946b569352ef2', 'auctionId': '1863e370099523', 'startTime': 1462918897463, 'status': 1, 'transactionId': 'fsafsa' } ], 'start': 1462918897460 }, { 'bidderCode': 'pubmatic', 'auctionId': '1863e370099523', 'bidderRequestId': '2946b569352ef2', 'tid': '2342342342lfi23', 'timeout': 1000, 'src': 's2s', 'adUnitsS2SCopy': [ { 'code': '/19968336/header-bid-tag1', 'sizes': [ { 'w': 728, 'h': 90 }, { 'w': 970, 'h': 90 } ], 'bids': [ { 'bidder': 'pubmatic', 'params': { 'placementId': '543221', 'test': 'me' }, 'adUnitCode': '/19968336/header-bid-tag1', 'sizes': [ [ 728, 90 ], [ 970, 90 ] ], 'bidId': '68136e1c47023d', 'bidderRequestId': '55e24a66bed717', 'auctionId': '1ff753bd4ae5cb', 'startTime': 1463510220995, 'status': 1, 'bid_id': '68136e1c47023d' } ] }, { 'code': '/19968336/header-bid-tag-0', 'sizes': [ { 'w': 300, 'h': 250 }, { 'w': 300, 'h': 600 } ], 'bids': [ { 'bidder': 'pubmatic', 'params': { 'placementId': '5324321' }, 'adUnitCode': '/19968336/header-bid-tag-0', 'sizes': [ [ 300, 250 ], [ 300, 600 ] ], 'bidId': '7e5d6af25ed188', 'bidderRequestId': '55e24a66bed717', 'auctionId': '1ff753bd4ae5cb', 'startTime': 1463510220996, 'bid_id': '7e5d6af25ed188' } ] } ], 'bids': [ { 'bidder': 'pubmatic', 'params': { 'placementId': '4799418', 'test': 'me' }, 'adUnitCode': '/19968336/header-bid-tag1', 'sizes': [ [ 728, 90 ], [ 970, 90 ] ], 'bidId': '392b5a6b05d648', 'bidderRequestId': '2946b569352ef2', 'auctionId': '1863e370099523', 'startTime': 1462918897462, 'status': 1, 'transactionId': '4r42r23r23' }, { 'bidder': 'pubmatic', 'params': { 'placementId': '4799418' }, 'adUnitCode': '/19968336/header-bid-tag-0', 'sizes': [ [ 300, 250 ], [ 300, 600 ] ], 'bidId': '4dccdc37746135', 'bidderRequestId': '2946b569352ef2', 'auctionId': '1863e370099523', 'startTime': 1462918897463, 'status': 1, 'transactionId': '4r42r23r23' } ], 'start': 1462918897460 }]; it('invokes callBids on the S2S adapter', function () { adapterManager.callBids( getAdUnits(), bidRequests, () => {}, () => () => {} ); sinon.assert.calledTwice(prebidServerAdapterMock.callBids); }); // Enable this test when prebidServer adapter is made 1.0 compliant it('invokes callBids with only s2s bids', function () { const adUnits = getAdUnits(); // adUnit without appnexus bidder adUnits.push({ 'code': '123', 'sizes': [300, 250], 'bids': [ { 'bidder': 'adequant', 'params': { 'publisher_id': '1234567', 'bidfloor': 0.01 } } ] }); adapterManager.callBids( adUnits, bidRequests, () => {}, () => () => {} ); const requestObj = prebidServerAdapterMock.callBids.firstCall.args[0]; expect(requestObj.ad_units.length).to.equal(2); sinon.assert.calledTwice(prebidServerAdapterMock.callBids); }); describe('BID_REQUESTED event', function () { // function to count BID_REQUESTED events let cnt, count = () => cnt++; beforeEach(function () { prebidServerAdapterMock.callBids.reset(); cnt = 0; events.on(CONSTANTS.EVENTS.BID_REQUESTED, count); }); afterEach(function () { events.off(CONSTANTS.EVENTS.BID_REQUESTED, count); }); it('should fire for s2s requests', function () { let adUnits = utils.deepClone(getAdUnits()).map(adUnit => { adUnit.bids = adUnit.bids.filter(bid => includes(['appnexus', 'pubmatic'], bid.bidder)); return adUnit; }) let bidRequests = adapterManager.makeBidRequests(adUnits, 1111, 2222, 1000); adapterManager.callBids(adUnits, bidRequests, () => {}, () => {}); expect(cnt).to.equal(2); sinon.assert.calledTwice(prebidServerAdapterMock.callBids); }); it('should fire for simultaneous s2s and client requests', function () { adapterManager.bidderRegistry['adequant'] = adequantAdapterMock; let adUnits = utils.deepClone(getAdUnits()).map(adUnit => { adUnit.bids = adUnit.bids.filter(bid => includes(['adequant', 'appnexus', 'pubmatic'], bid.bidder)); return adUnit; }) let bidRequests = adapterManager.makeBidRequests(adUnits, 1111, 2222, 1000); adapterManager.callBids(adUnits, bidRequests, () => {}, () => {}); expect(cnt).to.equal(3); sinon.assert.calledTwice(prebidServerAdapterMock.callBids); sinon.assert.calledOnce(adequantAdapterMock.callBids); adequantAdapterMock.callBids.reset(); delete adapterManager.bidderRegistry['adequant']; }); }); }); // end multiple s2s tests describe('s2sTesting', function () { let doneStub = sinon.stub(); let ajaxStub = sinon.stub(); function getTestAdUnits() { // copy adUnits // return JSON.parse(JSON.stringify(getAdUnits())); return utils.deepClone(getAdUnits()).map(adUnit => { adUnit.bids = adUnit.bids.filter(bid => includes(['adequant', 'appnexus', 'rubicon'], bid.bidder)); return adUnit; }) } function callBids(adUnits = getTestAdUnits()) { let bidRequests = adapterManager.makeBidRequests(adUnits, 1111, 2222, 1000); adapterManager.callBids(adUnits, bidRequests, doneStub, ajaxStub); } function checkServerCalled(numAdUnits, numBids) { sinon.assert.calledOnce(prebidServerAdapterMock.callBids); let requestObj = prebidServerAdapterMock.callBids.firstCall.args[0]; expect(requestObj.ad_units.length).to.equal(numAdUnits); for (let i = 0; i < numAdUnits; i++) { expect(requestObj.ad_units[i].bids.filter((bid) => { return bid.bidder === 'appnexus' || bid.bidder === 'adequant'; }).length).to.equal(numBids); } } function checkClientCalled(adapter, numBids) { sinon.assert.calledOnce(adapter.callBids); expect(adapter.callBids.firstCall.args[0].bids.length).to.equal(numBids); } let TESTING_CONFIG = utils.deepClone(CONFIG); Object.assign(TESTING_CONFIG, { bidders: ['appnexus', 'adequant'], testing: true }); let stubGetSourceBidderMap; beforeEach(function () { config.setConfig({s2sConfig: TESTING_CONFIG}); adapterManager.bidderRegistry['prebidServer'] = prebidServerAdapterMock; adapterManager.bidderRegistry['adequant'] = adequantAdapterMock; adapterManager.bidderRegistry['appnexus'] = appnexusAdapterMock; adapterManager.bidderRegistry['rubicon'] = rubiconAdapterMock; stubGetSourceBidderMap = sinon.stub(s2sTesting, 'getSourceBidderMap'); prebidServerAdapterMock.callBids.reset(); adequantAdapterMock.callBids.reset(); appnexusAdapterMock.callBids.reset(); rubiconAdapterMock.callBids.reset(); }); afterEach(function () { s2sTesting.getSourceBidderMap.restore(); }); it('calls server adapter if no sources defined', function () { stubGetSourceBidderMap.returns({[s2sTesting.CLIENT]: [], [s2sTesting.SERVER]: []}); callBids(); // server adapter checkServerCalled(2, 2); // appnexus sinon.assert.notCalled(appnexusAdapterMock.callBids); // adequant sinon.assert.notCalled(adequantAdapterMock.callBids); }); it('calls client adapter if one client source defined', function () { stubGetSourceBidderMap.returns({[s2sTesting.CLIENT]: ['appnexus'], [s2sTesting.SERVER]: []}); callBids(); // server adapter checkServerCalled(2, 2); // appnexus checkClientCalled(appnexusAdapterMock, 2); // adequant sinon.assert.notCalled(adequantAdapterMock.callBids); }); it('calls client adapters if client sources defined', function () { stubGetSourceBidderMap.returns({[s2sTesting.CLIENT]: ['appnexus', 'adequant'], [s2sTesting.SERVER]: []}); callBids(); // server adapter checkServerCalled(2, 2); // appnexus checkClientCalled(appnexusAdapterMock, 2); // adequant checkClientCalled(adequantAdapterMock, 2); }); it('does not call server adapter for bidders that go to client', function () { stubGetSourceBidderMap.returns({[s2sTesting.CLIENT]: ['appnexus', 'adequant'], [s2sTesting.SERVER]: []}); var adUnits = getTestAdUnits(); adUnits[0].bids[0].finalSource = s2sTesting.CLIENT; adUnits[0].bids[1].finalSource = s2sTesting.CLIENT; adUnits[1].bids[0].finalSource = s2sTesting.CLIENT; adUnits[1].bids[1].finalSource = s2sTesting.CLIENT; callBids(adUnits); // server adapter sinon.assert.notCalled(prebidServerAdapterMock.callBids); // appnexus checkClientCalled(appnexusAdapterMock, 2); // adequant checkClientCalled(adequantAdapterMock, 2); }); it('does not call client adapters for bidders that go to server', function () { stubGetSourceBidderMap.returns({[s2sTesting.CLIENT]: ['appnexus', 'adequant'], [s2sTesting.SERVER]: []}); var adUnits = getTestAdUnits(); adUnits[0].bids[0].finalSource = s2sTesting.SERVER; adUnits[0].bids[1].finalSource = s2sTesting.SERVER; adUnits[1].bids[0].finalSource = s2sTesting.SERVER; adUnits[1].bids[1].finalSource = s2sTesting.SERVER; callBids(adUnits); // server adapter checkServerCalled(2, 2); // appnexus sinon.assert.notCalled(appnexusAdapterMock.callBids); // adequant sinon.assert.notCalled(adequantAdapterMock.callBids); }); it('calls client and server adapters for bidders that go to both', function () { stubGetSourceBidderMap.returns({[s2sTesting.CLIENT]: ['appnexus', 'adequant'], [s2sTesting.SERVER]: []}); var adUnits = getTestAdUnits(); // adUnits[0].bids[0].finalSource = s2sTesting.BOTH; // adUnits[0].bids[1].finalSource = s2sTesting.BOTH; // adUnits[1].bids[0].finalSource = s2sTesting.BOTH; // adUnits[1].bids[1].finalSource = s2sTesting.BOTH; callBids(adUnits); // server adapter checkServerCalled(2, 2); // appnexus checkClientCalled(appnexusAdapterMock, 2); // adequant checkClientCalled(adequantAdapterMock, 2); }); it('makes mixed client/server adapter calls for mixed bidder sources', function () { stubGetSourceBidderMap.returns({[s2sTesting.CLIENT]: ['appnexus', 'adequant'], [s2sTesting.SERVER]: []}); var adUnits = getTestAdUnits(); adUnits[0].bids[0].finalSource = s2sTesting.CLIENT; adUnits[0].bids[1].finalSource = s2sTesting.CLIENT; adUnits[1].bids[0].finalSource = s2sTesting.SERVER; adUnits[1].bids[1].finalSource = s2sTesting.SERVER; callBids(adUnits); // server adapter checkServerCalled(1, 2); // appnexus checkClientCalled(appnexusAdapterMock, 1); // adequant checkClientCalled(adequantAdapterMock, 1); }); }); describe('Multiple Server s2sTesting', function () { let doneStub = sinon.stub(); let ajaxStub = sinon.stub(); function getTestAdUnits() { // copy adUnits return utils.deepClone(getAdUnits()).map(adUnit => { adUnit.bids = adUnit.bids.filter(bid => { return includes(['adequant', 'appnexus', 'pubmatic', 'rubicon'], bid.bidder); }); return adUnit; }) } function callBids(adUnits = getTestAdUnits()) { let bidRequests = adapterManager.makeBidRequests(adUnits, 1111, 2222, 1000); adapterManager.callBids(adUnits, bidRequests, doneStub, ajaxStub); } function checkServerCalled(numAdUnits, firstConfigNumBids, secondConfigNumBids) { let requestObjects = []; let configBids; if (firstConfigNumBids === 0 || secondConfigNumBids === 0) { configBids = Math.max(firstConfigNumBids, secondConfigNumBids) sinon.assert.calledOnce(prebidServerAdapterMock.callBids); let requestObj1 = prebidServerAdapterMock.callBids.firstCall.args[0]; requestObjects.push(requestObj1) } else { sinon.assert.calledTwice(prebidServerAdapterMock.callBids); let requestObj1 = prebidServerAdapterMock.callBids.firstCall.args[0]; let requestObj2 = prebidServerAdapterMock.callBids.secondCall.args[0]; requestObjects.push(requestObj1, requestObj2); } requestObjects.forEach((requestObj, index) => { const numBids = configBids !== undefined ? configBids : index === 0 ? firstConfigNumBids : secondConfigNumBids expect(requestObj.ad_units.length).to.equal(numAdUnits); for (let i = 0; i < numAdUnits; i++) { expect(requestObj.ad_units[i].bids.filter((bid) => { return bid.bidder === 'appnexus' || bid.bidder === 'adequant' || bid.bidder === 'pubmatic'; }).length).to.equal(numBids); } }) } function checkClientCalled(adapter, numBids) { sinon.assert.calledOnce(adapter.callBids); expect(adapter.callBids.firstCall.args[0].bids.length).to.equal(numBids); } beforeEach(function () { allS2SBidders.length = 0; clientTestAdapters.length = 0 adapterManager.bidderRegistry['prebidServer'] = prebidServerAdapterMock; adapterManager.bidderRegistry['adequant'] = adequantAdapterMock; adapterManager.bidderRegistry['appnexus'] = appnexusAdapterMock; adapterManager.bidderRegistry['rubicon'] = rubiconAdapterMock; adapterManager.bidderRegistry['pubmatic'] = pubmaticAdapterMock; prebidServerAdapterMock.callBids.reset(); adequantAdapterMock.callBids.reset(); appnexusAdapterMock.callBids.reset(); rubiconAdapterMock.callBids.reset(); pubmaticAdapterMock.callBids.reset(); }); it('calls server adapter if no sources defined for config where testing is true, ' + 'calls client adapter for second config where testing is false', function () { let TEST_CONFIG = utils.deepClone(CONFIG); Object.assign(TEST_CONFIG, { bidders: ['appnexus', 'adequant'], testing: true, }); let TEST_CONFIG2 = utils.deepClone(CONFIG2); Object.assign(TEST_CONFIG2, { bidders: ['pubmatic'], testing: true }); config.setConfig({s2sConfig: [TEST_CONFIG, TEST_CONFIG2]}); callBids(); // server adapter checkServerCalled(2, 2, 1); // appnexus sinon.assert.notCalled(appnexusAdapterMock.callBids); // adequant sinon.assert.notCalled(adequantAdapterMock.callBids); // pubmatic sinon.assert.notCalled(pubmaticAdapterMock.callBids); // rubicon sinon.assert.called(rubiconAdapterMock.callBids); }); it('calls client adapter if one client source defined for config where testing is true, ' + 'calls client adapter for second config where testing is false', function () { let TEST_CONFIG = utils.deepClone(CONFIG); Object.assign(TEST_CONFIG, { bidders: ['appnexus', 'adequant'], bidderControl: { appnexus: { bidSource: { server: 0, client: 100 }, includeSourceKvp: true, }, }, testing: true, }); let TEST_CONFIG2 = utils.deepClone(CONFIG2); Object.assign(TEST_CONFIG2, { bidders: ['pubmatic'], testing: true }); config.setConfig({s2sConfig: [TEST_CONFIG, TEST_CONFIG2]}); callBids(); // server adapter checkServerCalled(2, 1, 1); // appnexus checkClientCalled(appnexusAdapterMock, 2); // adequant sinon.assert.notCalled(adequantAdapterMock.callBids); // pubmatic sinon.assert.notCalled(pubmaticAdapterMock.callBids); // rubicon checkClientCalled(rubiconAdapterMock, 1); }); it('calls client adapters if client sources defined in first config and server in second config', function () { let TEST_CONFIG = utils.deepClone(CONFIG); Object.assign(TEST_CONFIG, { bidders: ['appnexus', 'adequant'], bidderControl: { appnexus: { bidSource: { server: 0, client: 100 }, includeSourceKvp: true, }, adequant: { bidSource: { server: 0, client: 100 }, includeSourceKvp: true, }, }, testing: true, }); let TEST_CONFIG2 = utils.deepClone(CONFIG2); Object.assign(TEST_CONFIG2, { bidders: ['pubmatic'], testing: true }); config.setConfig({s2sConfig: [TEST_CONFIG, TEST_CONFIG2]}); callBids(); // server adapter checkServerCalled(2, 0, 1); // appnexus checkClientCalled(appnexusAdapterMock, 2); // adequant checkClientCalled(adequantAdapterMock, 2); // pubmatic sinon.assert.notCalled(pubmaticAdapterMock.callBids); // rubicon checkClientCalled(rubiconAdapterMock, 1); }); it('does not call server adapter for bidders that go to client when both configs are set to client', function () { let TEST_CONFIG = utils.deepClone(CONFIG); Object.assign(TEST_CONFIG, { bidders: ['appnexus', 'adequant'], bidderControl: { appnexus: { bidSource: { server: 0, client: 100 }, includeSourceKvp: true, }, adequant: { bidSource: { server: 0, client: 100 }, includeSourceKvp: true, }, }, testing: true, }); let TEST_CONFIG2 = utils.deepClone(CONFIG2); Object.assign(TEST_CONFIG2, { bidders: ['pubmatic'], bidderControl: { pubmatic: { bidSource: { server: 0, client: 100 }, includeSourceKvp: true, }, }, testing: true }); config.setConfig({s2sConfig: [TEST_CONFIG, TEST_CONFIG2]}); callBids(); sinon.assert.notCalled(prebidServerAdapterMock.callBids); // appnexus checkClientCalled(appnexusAdapterMock, 2); // adequant checkClientCalled(adequantAdapterMock, 2); // pubmatic checkClientCalled(pubmaticAdapterMock, 2); // rubicon checkClientCalled(rubiconAdapterMock, 1); }); it('does not call client adapters for bidders in either config when testServerOnly if true in first config', function () { let TEST_CONFIG = utils.deepClone(CONFIG); Object.assign(TEST_CONFIG, { bidders: ['appnexus', 'adequant'], testServerOnly: true, bidderControl: { appnexus: { bidSource: { server: 0, client: 100 }, includeSourceKvp: true, }, adequant: { bidSource: { server: 100, client: 0 }, includeSourceKvp: true, }, }, testing: true, }); let TEST_CONFIG2 = utils.deepClone(CONFIG2); Object.assign(TEST_CONFIG2, { bidders: ['pubmatic'], bidderControl: { pubmatic: { bidSource: { server: 0, client: 100 }, includeSourceKvp: true, } }, testing: true }); config.setConfig({s2sConfig: [TEST_CONFIG, TEST_CONFIG2]}); callBids(); // server adapter checkServerCalled(2, 1, 0); // appnexus sinon.assert.notCalled(appnexusAdapterMock.callBids); // adequant sinon.assert.notCalled(adequantAdapterMock.callBids); // pubmatic sinon.assert.notCalled(pubmaticAdapterMock.callBids); // rubicon sinon.assert.notCalled(rubiconAdapterMock.callBids); }); it('does not call client adapters for bidders in either config when testServerOnly if true in second config', function () { let TEST_CONFIG = utils.deepClone(CONFIG); Object.assign(TEST_CONFIG, { bidders: ['appnexus', 'adequant'], bidderControl: { appnexus: { bidSource: { server: 0, client: 100 }, includeSourceKvp: true, }, adequant: { bidSource: { server: 100, client: 0 }, includeSourceKvp: true, }, }, testing: true, }); let TEST_CONFIG2 = utils.deepClone(CONFIG2); Object.assign(TEST_CONFIG2, { bidders: ['pubmatic'], testServerOnly: true, bidderControl: { pubmatic: { bidSource: { server: 100, client: 0 }, includeSourceKvp: true, } }, testing: true }); config.setConfig({s2sConfig: [TEST_CONFIG, TEST_CONFIG2]}); callBids(); // server adapter checkServerCalled(2, 1, 1); // appnexus sinon.assert.notCalled(appnexusAdapterMock.callBids); // adequant sinon.assert.notCalled(adequantAdapterMock.callBids); // pubmatic sinon.assert.notCalled(pubmaticAdapterMock.callBids); // rubicon sinon.assert.notCalled(rubiconAdapterMock.callBids); }); }); describe('aliasBidderAdaptor', function() { const CODE = 'sampleBidder'; describe('using bidderFactory', function() { let spec; beforeEach(function () { spec = { code: CODE, isBidRequestValid: () => {}, buildRequests: () => {}, interpretResponse: () => {}, getUserSyncs: () => {} }; }); it('should add alias to registry when original adapter is using bidderFactory', function() { let thisSpec = Object.assign(spec, { supportedMediaTypes: ['video'] }); registerBidder(thisSpec); const alias = 'aliasBidder'; adapterManager.aliasBidAdapter(CODE, alias); expect(adapterManager.bidderRegistry).to.have.property(alias); expect(adapterManager.videoAdapters).to.include(alias); }); }); describe('special case for s2s-only bidders', function () { beforeEach(function () { sinon.stub(utils, 'logError'); }); afterEach(function () { config.resetConfig(); utils.logError.restore(); }); it('should allow an alias if alias is part of s2sConfig.bidders', function () { let testS2sConfig = utils.deepClone(CONFIG); testS2sConfig.bidders = ['s2sAlias']; config.setConfig({s2sConfig: testS2sConfig}); adapterManager.aliasBidAdapter('s2sBidder', 's2sAlias'); expect(adapterManager.aliasRegistry).to.have.property('s2sAlias'); }); it('should allow an alias if alias is part of s2sConfig.bidders for multiple s2sConfigs', function () { let testS2sConfig = utils.deepClone(CONFIG); testS2sConfig.bidders = ['s2sAlias']; config.setConfig({s2sConfig: [ testS2sConfig, { enabled: true, endpoint: 'rp-pbs-endpoint-test.com', timeout: 500, maxBids: 1, adapter: 'prebidServer', bidders: ['s2sRpAlias'], accountId: 'def' } ]}); adapterManager.aliasBidAdapter('s2sBidder', 's2sAlias'); expect(adapterManager.aliasRegistry).to.have.property('s2sAlias'); adapterManager.aliasBidAdapter('s2sBidder', 's2sRpAlias'); expect(adapterManager.aliasRegistry).to.have.property('s2sRpAlias'); }); it('should throw an error if alias + bidder are unknown and not part of s2sConfig.bidders', function () { let testS2sConfig = utils.deepClone(CONFIG); testS2sConfig.bidders = ['s2sAlias']; config.setConfig({s2sConfig: testS2sConfig}); adapterManager.aliasBidAdapter('s2sBidder1', 's2sAlias1'); sinon.assert.calledOnce(utils.logError); expect(adapterManager.aliasRegistry).to.not.have.property('s2sAlias1'); }); }); }); describe('makeBidRequests', function () { let adUnits; beforeEach(function () { allS2SBidders.length = 0 adUnits = utils.deepClone(getAdUnits()).map(adUnit => { adUnit.bids = adUnit.bids.filter(bid => includes(['appnexus', 'rubicon'], bid.bidder)); return adUnit; }) }); it('should make separate bidder request objects for each bidder', () => { adUnits = [utils.deepClone(getAdUnits()[0])]; let bidRequests = adapterManager.makeBidRequests( adUnits, Date.now(), utils.getUniqueIdentifierStr(), function callback() {}, [] ); let sizes1 = bidRequests[1].bids[0].sizes; let sizes2 = bidRequests[0].bids[0].sizes; // mutate array sizes1.splice(0, 1); expect(sizes1).not.to.deep.equal(sizes2); }); describe('setBidderSequence', function () { beforeEach(function () { sinon.spy(utils, 'shuffle'); }); afte