UNPKG

mk9-prebid

Version:

Header Bidding Management Library

655 lines (605 loc) 23.4 kB
import {expect} from 'chai'; import {spec} from 'modules/adkernelBidAdapter'; import * as utils from 'src/utils'; import {NATIVE, BANNER, VIDEO} from 'src/mediaTypes'; import {config} from 'src/config'; describe('Adkernel adapter', function () { const bid1_zone1 = { bidder: 'adkernel', params: {zoneId: 1, host: 'rtb.adkernel.com'}, adUnitCode: 'ad-unit-1', bidId: 'Bid_01', bidderRequestId: 'req-001', auctionId: 'auc-001', mediaTypes: { banner: { sizes: [[300, 250], [300, 200]] } } }, bid2_zone2 = { bidder: 'adkernel', params: {zoneId: 2, host: 'rtb.adkernel.com'}, adUnitCode: 'ad-unit-2', bidId: 'Bid_02', bidderRequestId: 'req-001', auctionId: 'auc-001', mediaTypes: { banner: { sizes: [[728, 90]] } }, userIdAsEids: [ { source: 'crwdcntrl.net', uids: [ {atype: 1, id: '97d09fbba28542b7acbb6317c9534945a702b74c5993c352f332cfe83f40cdd9'} ] } ] }, bid3_host2 = { bidder: 'adkernel', params: {zoneId: 1, host: 'rtb-private.adkernel.com'}, adUnitCode: 'ad-unit-2', bidId: 'Bid_02', bidderRequestId: 'req-001', auctionId: 'auc-001', mediaTypes: { banner: { sizes: [[728, 90]] } } }, bid_without_zone = { bidder: 'adkernel', params: {host: 'rtb-private.adkernel.com'}, adUnitCode: 'ad-unit-1', bidId: 'Bid_W', bidderRequestId: 'req-002', auctionId: 'auc-002', mediaTypes: { banner: { sizes: [[728, 90]] } } }, bid_without_host = { bidder: 'adkernel', params: {zoneId: 1}, adUnitCode: 'ad-unit-1', bidId: 'Bid_W', bidderRequestId: 'req-002', auctionId: 'auc-002', mediaTypes: { banner: { sizes: [[728, 90]] } } }, bid_with_wrong_zoneId = { bidder: 'adkernel', params: {zoneId: 'wrong id', host: 'rtb.adkernel.com'}, adUnitCode: 'ad-unit-2', bidId: 'Bid_02', bidderRequestId: 'req-002', auctionId: 'auc-002', mediaTypes: { banner: { sizes: [[728, 90]] } } }, bid_video = { bidder: 'adkernel', transactionId: '866394b8-5d37-4d49-803e-f1bdb595f73e', bidId: 'Bid_Video', bidderRequestId: '18b2a61ea5d9a7', auctionId: 'de45acf1-9109-4e52-8013-f2b7cf5f6766', params: { zoneId: 1, host: 'rtb.adkernel.com', video: {api: [1, 2]} }, mediaTypes: { video: { context: 'instream', playerSize: [[640, 480]] } }, adUnitCode: 'ad-unit-1' }, bid_multiformat = { bidder: 'adkernel', params: {zoneId: 1, host: 'rtb.adkernel.com'}, mediaTypes: { banner: {sizes: [[300, 250], [300, 200]]}, video: {context: 'instream', playerSize: [[640, 480]]} }, adUnitCode: 'ad-unit-1', transactionId: 'f82c64b8-c602-42a4-9791-4a268f6559ed', bidId: 'Bid_01', bidderRequestId: 'req-001', auctionId: 'auc-001' }, bid_native = { bidder: 'adkernel', params: {zoneId: 1, host: 'rtb.adkernel.com'}, mediaTypes: { native: { title: { required: true, len: 80 }, body: { required: true }, body2: { required: true }, icon: { required: true, aspect_ratios: [{min_width: 50, min_height: 50}] }, image: { required: true, sizes: [300, 200] }, clickUrl: { required: true }, rating: { required: false }, price: { required: false }, privacyLink: { required: false }, cta: { required: false }, sponsoredBy: { required: false }, displayUrl: { required: false } } }, adUnitCode: 'ad-unit-1', transactionId: 'f82c64b8-c602-42a4-9791-4a268f6559ed', bidId: 'Bid_01', bidderRequestId: 'req-001', auctionId: 'auc-001' }; const bidResponse1 = { id: 'bid1', seatbid: [{ bid: [{ id: '1', impid: 'Bid_01', crid: '100_001', price: 3.01, nurl: 'https://rtb.com/win?i=ZjKoPYSFI3Y_0', adm: '<!-- admarkup here -->', w: 300, h: 250, dealid: 'deal' }] }], ext: { adk_usersync: [{type: 1, url: 'https://adk.sync.com/sync'}] } }, videoBidResponse = { id: '47ce4badcf7482', seatbid: [{ bid: [{ id: 'sZSYq5zYMxo_0', impid: 'Bid_Video', crid: '100_003', price: 0.00145, adid: '158801', nurl: 'https://rtb.com/win?i=sZSYq5zYMxo_0&f=nurl', cid: '16855' }] }], }, usersyncOnlyResponse = { id: 'nobid1', ext: { adk_usersync: [{type: 2, url: 'https://adk.sync.com/sync'}] } }, nativeResponse = { id: '56fbc713-b737-4651-9050-13376aed9818', seatbid: [{ bid: [{ id: 'someid_01', impid: 'Bid_01', price: 2.25, adid: '4', adm: JSON.stringify({ native: { assets: [ {id: 0, title: {text: 'Title'}}, {id: 3, data: {value: 'Description'}}, {id: 4, data: {value: 'Additional description'}}, {id: 1, img: {url: 'http://rtb.com/thumbnail?i=pTuOlf5KHUo_0&imgt=icon', w: 50, h: 50}}, {id: 2, img: {url: 'http://rtb.com/thumbnail?i=pTuOlf5KHUo_0', w: 300, h: 200}}, {id: 5, data: {value: 'Sponsor.com'}}, {id: 14, data: {value: 'displayurl.com'}} ], link: {url: 'http://rtb.com/click?i=pTuOlf5KHUo_0'}, imptrackers: ['http://rtb.com/win?i=pTuOlf5KHUo_0&f=imp'] } }), adomain: ['displayurl.com'], cat: ['IAB1-4', 'IAB8-16', 'IAB25-5'], cid: '1', crid: '4', ext: { 'advertiser_id': 777, 'advertiser_name': 'advertiser', 'agency_name': 'agency' } }] }], bidid: 'pTuOlf5KHUo', cur: 'EUR' }; var sandbox; beforeEach(function () { sandbox = sinon.sandbox.create(); }); afterEach(function () { sandbox.restore(); config.resetConfig(); }); function buildBidderRequest(url = 'https://example.com/index.html', params = {}) { return Object.assign({}, params, {refererInfo: {referer: url, reachedTop: true}, timeout: 3000, bidderCode: 'adkernel'}); } const DEFAULT_BIDDER_REQUEST = buildBidderRequest(); function buildRequest(bidRequests, bidderRequest = DEFAULT_BIDDER_REQUEST, dnt = true) { let dntmock = sandbox.stub(utils, 'getDNT').callsFake(() => dnt); bidderRequest.bids = bidRequests; let pbRequests = spec.buildRequests(bidRequests, bidderRequest); dntmock.restore(); let rtbRequests = pbRequests.map(r => JSON.parse(r.data)); return [pbRequests, rtbRequests]; } describe('input parameters validation', function () { it('empty request shouldn\'t generate exception', function () { expect(spec.isBidRequestValid({ bidderCode: 'adkernel' })).to.be.equal(false); }); it('request without zone shouldn\'t issue a request', function () { expect(spec.isBidRequestValid(bid_without_zone)).to.be.equal(false); }); it('request without host shouldn\'t issue a request', function () { expect(spec.isBidRequestValid(bid_without_host)).to.be.equal(false); }); it('empty request shouldn\'t generate exception', function () { expect(spec.isBidRequestValid(bid_with_wrong_zoneId)).to.be.equal(false); }); it('valid native requests should pass', () => { expect(spec.isBidRequestValid(bid_native)).to.be.equal(true); }) }); describe('banner request building', function () { let bidRequest, bidRequests, _; before(function () { [_, bidRequests] = buildRequest([bid1_zone1]); bidRequest = bidRequests[0]; }); it('should be a first-price auction', function () { expect(bidRequest).to.have.property('at', 1); }); it('should have banner object', function () { expect(bidRequest.imp[0]).to.have.property('banner'); }); it('should have id', function () { expect(bidRequest.imp[0]).to.have.property('id'); expect(bidRequest.imp[0].id).to.be.eql('Bid_01'); }); it('should have w/h', function () { expect(bidRequest.imp[0].banner).to.have.property('format'); expect(bidRequest.imp[0].banner.format).to.be.eql([{w: 300, h: 250}, {w: 300, h: 200}]); }); it('should respect secure connection', function () { expect(bidRequest.imp[0]).to.have.property('secure', 1); }); it('should have tagid', function () { expect(bidRequest.imp[0]).to.have.property('tagid', 'ad-unit-1'); }); it('should create proper site block', function () { expect(bidRequest.site).to.have.property('domain', 'example.com'); expect(bidRequest.site).to.have.property('page', 'https://example.com/index.html'); }); it('should fill device with caller macro', function () { expect(bidRequest).to.have.property('device'); expect(bidRequest.device).to.have.property('ip', 'caller'); expect(bidRequest.device).to.have.property('ipv6', 'caller'); expect(bidRequest.device).to.have.property('ua', 'caller'); expect(bidRequest.device).to.have.property('dnt', 1); }); it('shouldn\'t contain gdpr nor ccpa information for default request', function () { let [_, bidRequests] = buildRequest([bid1_zone1]); expect(bidRequests[0]).to.not.have.property('regs'); expect(bidRequests[0]).to.not.have.property('user'); }); it('should contain gdpr-related information if consent is configured', function () { let [_, bidRequests] = buildRequest([bid1_zone1], buildBidderRequest('https://example.com/index.html', {gdprConsent: {gdprApplies: true, consentString: 'test-consent-string', vendorData: {}}, uspConsent: '1YNN'})); let bidRequest = bidRequests[0]; expect(bidRequest).to.have.property('regs'); expect(bidRequest.regs.ext).to.be.eql({'gdpr': 1, 'us_privacy': '1YNN'}); expect(bidRequest).to.have.property('user'); expect(bidRequest.user.ext).to.be.eql({'consent': 'test-consent-string'}); }); it('should contain coppa if configured', function () { config.setConfig({coppa: true}); let [_, bidRequests] = buildRequest([bid1_zone1]); let bidRequest = bidRequests[0]; expect(bidRequest).to.have.property('regs'); expect(bidRequest.regs).to.have.property('coppa', 1); }); it('should\'t contain consent string if gdpr isn\'t applied', function () { let [_, bidRequests] = buildRequest([bid1_zone1], buildBidderRequest('https://example.com/index.html', {gdprConsent: {gdprApplies: false}})); let bidRequest = bidRequests[0]; expect(bidRequest).to.have.property('regs'); expect(bidRequest.regs.ext).to.be.eql({'gdpr': 0}); expect(bidRequest).to.not.have.property('user'); }); it('should\'t pass dnt if state is unknown', function () { let [_, bidRequests] = buildRequest([bid1_zone1], DEFAULT_BIDDER_REQUEST, false); expect(bidRequests[0].device).to.not.have.property('dnt'); }); it('should forward default bidder timeout', function() { let [_, bidRequests] = buildRequest([bid1_zone1]); expect(bidRequests[0]).to.have.property('tmax', 3000); }); it('should set bidfloor if configured', function() { let bid = Object.assign({}, bid1_zone1); bid.getFloor = function() { return { currency: 'USD', floor: 0.145 } }; let [_, bidRequests] = buildRequest([bid]); expect(bidRequests[0].imp[0]).to.have.property('bidfloor', 0.145); }); it('should forward user ids if available', function() { let bid = Object.assign({}, bid2_zone2); let [_, bidRequests] = buildRequest([bid]); expect(bidRequests[0]).to.have.property('user'); expect(bidRequests[0].user).to.have.property('ext'); expect(bidRequests[0].user.ext).to.have.property('eids'); expect(bidRequests[0].user.ext.eids).to.be.an('array').that.is.not.empty; expect(bidRequests[0].user.ext.eids[0]).to.have.property('source'); expect(bidRequests[0].user.ext.eids[0]).to.have.property('uids'); }); }); describe('video request building', function () { let _, bidRequests; before(function () { [_, bidRequests] = buildRequest([bid_video]); }); it('should have video object', function () { expect(bidRequests[0].imp[0]).to.have.property('video'); }); it('should have h/w', function () { expect(bidRequests[0].imp[0].video).to.have.property('w', 640); expect(bidRequests[0].imp[0].video).to.have.property('h', 480); }); it('should have tagid', function () { expect(bidRequests[0].imp[0]).to.have.property('tagid', 'ad-unit-1'); }); it('should have openrtb video impression parameters', function() { expect(bidRequests[0].imp[0].video).to.have.property('api'); expect(bidRequests[0].imp[0].video.api).to.be.eql([1, 2]); }); }); describe('multiformat request building', function () { let _, bidRequests; before(function () { [_, bidRequests] = buildRequest([bid_multiformat]); }); it('should contain single request', function () { expect(bidRequests).to.have.length(1); expect(bidRequests[0].imp).to.have.length(1); }); it('should contain banner-only impression', function () { expect(bidRequests[0].imp).to.have.length(1); expect(bidRequests[0].imp[0]).to.have.property('banner'); expect(bidRequests[0].imp[0]).to.not.have.property('video'); }); }); describe('requests routing', function () { it('should issue a request for each host', function () { let [pbRequests, _] = buildRequest([bid1_zone1, bid3_host2]); expect(pbRequests).to.have.length(2); expect(pbRequests[0].url).to.have.string(`https://${bid1_zone1.params.host}/`); expect(pbRequests[1].url).to.have.string(`https://${bid3_host2.params.host}/`); }); it('should issue a request for each zone', function () { let [pbRequests, _] = buildRequest([bid1_zone1, bid2_zone2]); expect(pbRequests).to.have.length(2); expect(pbRequests[0].url).to.include(`zone=${bid1_zone1.params.zoneId}`); expect(pbRequests[1].url).to.include(`zone=${bid2_zone2.params.zoneId}`); }); }); describe('User sync request signals', function() { it('should respect syncEnabled option', function() { config.setConfig({ userSync: { syncEnabled: false, filterSettings: { all: { bidders: '*', filter: 'include' } } } }); let [pbRequests, bidRequests] = buildRequest([bid1_zone1]); expect(bidRequests).to.have.length(1); expect(bidRequests[0]).to.not.have.property('ext'); }); it('should respect all config node', function() { config.setConfig({ userSync: { syncEnabled: true, filterSettings: { all: { bidders: '*', filter: 'include' } } } }); let [pbRequests, bidRequests] = buildRequest([bid1_zone1]); expect(bidRequests).to.have.length(1); expect(bidRequests[0].ext).to.have.property('adk_usersync', 1); }); it('should respect exclude filter', function() { config.setConfig({ userSync: { syncEnabled: true, filterSettings: { image: { bidders: '*', filter: 'include' }, iframe: { bidders: ['adkernel'], filter: 'exclude' } } } }); let [pbRequests, bidRequests] = buildRequest([bid1_zone1]); expect(bidRequests).to.have.length(1); expect(bidRequests[0].ext).to.have.property('adk_usersync', 2); }); it('should respect total exclusion', function() { config.setConfig({ userSync: { syncEnabled: true, filterSettings: { image: { bidders: ['adkernel'], filter: 'exclude' }, iframe: { bidders: ['adkernel'], filter: 'exclude' } } } }); let [pbRequests, bidRequests] = buildRequest([bid1_zone1]); expect(bidRequests).to.have.length(1); expect(bidRequests[0]).to.not.have.property('ext'); }); }); describe('responses processing', function () { it('should return fully-initialized banner bid-response', function () { let [pbRequests, _] = buildRequest([bid1_zone1]); let resp = spec.interpretResponse({body: bidResponse1}, pbRequests[0])[0]; expect(resp).to.have.property('requestId', 'Bid_01'); expect(resp).to.have.property('cpm', 3.01); expect(resp).to.have.property('width', 300); expect(resp).to.have.property('height', 250); expect(resp).to.have.property('creativeId', '100_001'); expect(resp).to.have.property('currency'); expect(resp).to.have.property('ttl'); expect(resp).to.have.property('mediaType', BANNER); expect(resp).to.have.property('ad'); expect(resp).to.have.property('dealId', 'deal'); expect(resp.ad).to.have.string('<!-- admarkup here -->'); }); it('should return fully-initialized video bid-response', function () { let [pbRequests, _] = buildRequest([bid_video]); let resp = spec.interpretResponse({body: videoBidResponse}, pbRequests[0])[0]; expect(resp).to.have.property('requestId', 'Bid_Video'); expect(resp.mediaType).to.equal(VIDEO); expect(resp.cpm).to.equal(0.00145); expect(resp.vastUrl).to.equal('https://rtb.com/win?i=sZSYq5zYMxo_0&f=nurl'); expect(resp.width).to.equal(640); expect(resp.height).to.equal(480); }); it('should add nurl as pixel for banner response', function () { let [pbRequests, _] = buildRequest([bid1_zone1]); let resp = spec.interpretResponse({body: bidResponse1}, pbRequests[0])[0]; let expectedNurl = bidResponse1.seatbid[0].bid[0].nurl + '&px=1'; expect(resp.ad).to.have.string(expectedNurl); }); it('should handle bidresponse with user-sync only', function () { let [pbRequests, _] = buildRequest([bid1_zone1]); let resp = spec.interpretResponse({body: usersyncOnlyResponse}, pbRequests[0]); expect(resp).to.have.length(0); }); it('should perform usersync', function () { let syncs = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, []); expect(syncs).to.have.length(0); syncs = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}, [{body: bidResponse1}]); expect(syncs).to.have.length(0); syncs = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, [{body: bidResponse1}]); expect(syncs).to.have.length(1); expect(syncs[0]).to.have.property('type', 'iframe'); expect(syncs[0]).to.have.property('url', 'https://adk.sync.com/sync'); syncs = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{body: usersyncOnlyResponse}]); expect(syncs).to.have.length(1); expect(syncs[0]).to.have.property('type', 'image'); expect(syncs[0]).to.have.property('url', 'https://adk.sync.com/sync'); }); }); describe('adapter configuration', () => { it('should have aliases', () => { expect(spec.aliases).to.be.an('array').that.is.not.empty; }); }); describe('native support', () => { let _, bidRequests; before(function () { [_, bidRequests] = buildRequest([bid_native]); }); it('native request building', () => { expect(bidRequests[0].imp).to.have.length(1); expect(bidRequests[0].imp[0]).to.have.property('native'); expect(bidRequests[0].imp[0].native).to.have.property('request'); let request = JSON.parse(bidRequests[0].imp[0].native.request); expect(request).to.have.property('ver', '1.1'); expect(request.assets).to.have.length(10); expect(request.assets[0]).to.be.eql({id: 0, required: 1, title: {len: 80}}); expect(request.assets[1]).to.be.eql({id: 3, required: 1, data: {type: 2}}); expect(request.assets[2]).to.be.eql({id: 4, required: 1, data: {type: 10}}); expect(request.assets[3]).to.be.eql({id: 1, required: 1, img: {wmin: 50, hmin: 50, type: 1}}); expect(request.assets[4]).to.be.eql({id: 2, required: 1, img: {w: 300, h: 200, type: 3}}); expect(request.assets[5]).to.be.eql({id: 11, required: 0, data: {type: 3}}); expect(request.assets[6]).to.be.eql({id: 8, required: 0, data: {type: 6}}); expect(request.assets[7]).to.be.eql({id: 10, required: 0, data: {type: 12}}); expect(request.assets[8]).to.be.eql({id: 5, required: 0, data: {type: 1}}); expect(request.assets[9]).to.be.eql({id: 14, required: 0, data: {type: 11}}); }); it('native response processing', () => { let [pbRequests, _] = buildRequest([bid_native]); let resp = spec.interpretResponse({body: nativeResponse}, pbRequests[0])[0]; expect(resp).to.have.property('requestId', 'Bid_01'); expect(resp).to.have.property('cpm', 2.25); expect(resp).to.have.property('currency', 'EUR'); expect(resp).to.have.property('meta'); expect(resp.meta.advertiserId).to.be.eql(777); expect(resp.meta.advertiserName).to.be.eql('advertiser'); expect(resp.meta.agencyName).to.be.eql('agency'); expect(resp.meta.advertiserDomains).to.be.eql(['displayurl.com']); expect(resp.meta.secondaryCatIds).to.be.eql(['IAB1-4', 'IAB8-16', 'IAB25-5']); expect(resp).to.have.property('mediaType', NATIVE); expect(resp).to.have.property('native'); expect(resp.native).to.have.property('clickUrl', 'http://rtb.com/click?i=pTuOlf5KHUo_0'); expect(resp.native.impressionTrackers).to.be.eql(['http://rtb.com/win?i=pTuOlf5KHUo_0&f=imp']); expect(resp.native).to.have.property('title', 'Title'); expect(resp.native).to.have.property('body', 'Description'); expect(resp.native).to.have.property('body2', 'Additional description'); expect(resp.native.icon).to.be.eql({url: 'http://rtb.com/thumbnail?i=pTuOlf5KHUo_0&imgt=icon', width: 50, height: 50}); expect(resp.native.image).to.be.eql({url: 'http://rtb.com/thumbnail?i=pTuOlf5KHUo_0', width: 300, height: 200}); expect(resp.native).to.have.property('sponsoredBy', 'Sponsor.com'); expect(resp.native).to.have.property('displayUrl', 'displayurl.com'); }); }); });