mk9-prebid
Version:
Header Bidding Management Library
541 lines (492 loc) • 17.1 kB
JavaScript
import {
malltvAnalyticsAdapter, parseBidderCode, parseAdUnitCode,
ANALYTICS_VERSION, BIDDER_STATUS, DEFAULT_SERVER
} from 'modules/malltvAnalyticsAdapter.js'
import { expect } from 'chai'
import { getCpmInEur } from '../../../modules/malltvAnalyticsAdapter'
import events from 'src/events'
import constants from 'src/constants.json'
const auctionId = 'b0b39610-b941-4659-a87c-de9f62d3e13e'
const propertyId = '123456'
const server = 'https://analytics.server.url/v1'
describe('Malltv Prebid AnalyticsAdapter Testing', function () {
describe('event tracking and message cache manager', function () {
beforeEach(function () {
const configOptions = { propertyId }
sinon.stub(events, 'getEvents').returns([])
malltvAnalyticsAdapter.enableAnalytics({
provider: 'malltvAnalytics',
options: configOptions
})
})
afterEach(function () {
malltvAnalyticsAdapter.disableAnalytics()
events.getEvents.restore()
})
describe('#getCpmInEur()', function() {
it('should get bid cpm as currency is EUR', function() {
const receivedBids = [
{
auctionId: auctionId,
adUnitCode: 'adunit_1',
bidder: 'malltv',
bidderCode: 'MALLTV',
requestId: 'a1b2c3d4',
timeToRespond: 72,
cpm: 0.1,
currency: 'EUR',
originalCpm: 0.1,
originalCurrency: 'EUR',
ad: '<html>fake ad1</html>'
},
]
const result = getCpmInEur(receivedBids[0])
expect(result).to.equal(0.1)
})
})
describe('#parseBidderCode()', function() {
it('should get lower case bidder code from bidderCode field value', function() {
const receivedBids = [
{
auctionId: auctionId,
adUnitCode: 'adunit_1',
bidder: 'malltv',
bidderCode: 'MALLTV',
requestId: 'a1b2c3d4',
timeToRespond: 72,
cpm: 0.1,
currency: 'EUR',
originalCpm: 0.1,
originalCurrency: 'EUR',
ad: '<html>fake ad1</html>'
},
]
const result = parseBidderCode(receivedBids[0])
expect(result).to.equal('malltv')
})
it('should get lower case bidder code from bidder field value as bidderCode field is missing', function() {
const receivedBids = [
{
auctionId: auctionId,
adUnitCode: 'adunit_1',
bidder: 'MALLTV',
bidderCode: '',
requestId: 'a1b2c3d4',
timeToRespond: 72,
cpm: 0.1,
currency: 'EUR',
originalCpm: 0.1,
originalCurrency: 'EUR',
ad: '<html>fake ad1</html>'
},
]
const result = parseBidderCode(receivedBids[0])
expect(result).to.equal('malltv')
})
})
describe('#parseAdUnitCode()', function() {
it('should get lower case adUnit code from adUnitCode field value', function() {
const receivedBids = [
{
auctionId: auctionId,
adUnitCode: 'ADUNIT',
bidder: 'malltv',
bidderCode: 'MALLTV',
requestId: 'a1b2c3d4',
timeToRespond: 72,
cpm: 0.1,
currency: 'EUR',
originalCpm: 0.1,
originalCurrency: 'EUR',
ad: '<html>fake ad1</html>'
},
]
const result = parseAdUnitCode(receivedBids[0])
expect(result).to.equal('adunit')
})
})
describe('#getCachedAuction()', function() {
const existing = {timeoutBids: [{}]}
malltvAnalyticsAdapter.cachedAuctions['test_auction_id'] = existing
it('should get the existing cached object if it exists', function() {
const result = malltvAnalyticsAdapter.getCachedAuction('test_auction_id')
expect(result).to.equal(existing)
})
it('should create a new object and store it in the cache on cache miss', function() {
const result = malltvAnalyticsAdapter.getCachedAuction('no_such_id')
expect(result).to.deep.include({
timeoutBids: [],
})
})
})
describe('when formatting JSON payload sent to backend', function() {
const receivedBids = [
{
auctionId: auctionId,
adUnitCode: 'adunit_1',
bidder: 'malltv',
bidderCode: 'malltv',
requestId: 'a1b2c3d4',
timeToRespond: 72,
cpm: 0.1,
currency: 'EUR',
originalCpm: 0.1,
originalCurrency: 'EUR',
ad: '<html>fake ad1</html>',
vastUrl: null
},
{
auctionId: auctionId,
adUnitCode: 'adunit_1',
bidder: 'gjirafa',
bidderCode: 'gjirafa',
requestId: 'b2c3d4e5',
timeToRespond: 100,
cpm: 0.08,
currency: 'EUR',
originalCpm: 0.08,
originalCurrency: 'EUR',
ad: '<html>fake ad2</html>',
vastUrl: null
},
{
auctionId: auctionId,
adUnitCode: 'adunit_2',
bidder: 'malltv',
bidderCode: 'malltv',
requestId: 'c3d4e5f6',
timeToRespond: 120,
cpm: 0.09,
currency: 'EUR',
originalCpm: 0.09,
originalCurrency: 'EUR',
ad: '<html>fake ad3</html>',
vastUrl: null
},
]
const highestCpmBids = [
{
auctionId: auctionId,
adUnitCode: 'adunit_1',
bidder: 'malltv',
bidderCode: 'malltv',
// No requestId
timeToRespond: 72,
cpm: 0.1,
currency: 'EUR',
originalCpm: 0.1,
originalCurrency: 'EUR',
ad: '<html>fake ad1</html>',
vastUrl: null
}
]
const noBids = [
{
auctionId: auctionId,
adUnitCode: 'adunit_2',
bidder: 'malltv',
bidderCode: 'malltv',
bidId: 'a1b2c3d4',
}
]
const timeoutBids = [
{
auctionId: auctionId,
adUnitCode: 'adunit_2',
bidder: 'gjirafa',
bidderCode: 'gjirafa',
bidId: '00123d4c',
}
]
const withoutOriginalCpmBids = [
{
auctionId: auctionId,
adUnitCode: 'adunit_2',
bidder: 'malltv',
bidderCode: 'malltv',
requestId: 'c3d4e5f6',
timeToRespond: 120,
cpm: 0.29,
currency: 'EUR',
originalCpm: '',
originalCurrency: 'EUR',
ad: '<html>fake ad3</html>'
},
]
const withoutOriginalCurrencyBids = [
{
auctionId: auctionId,
adUnitCode: 'adunit_2',
bidder: 'malltv',
bidderCode: 'malltv',
requestId: 'c3d4e5f6',
timeToRespond: 120,
cpm: 0.09,
currency: 'EUR',
originalCpm: 0.09,
originalCurrency: '',
ad: '<html>fake ad3</html>'
},
]
function assertHavingRequiredMessageFields(message) {
expect(message).to.include({
analyticsVersion: ANALYTICS_VERSION,
auctionId: auctionId,
propertyId: propertyId,
prebidVersion: '$prebid.version$',
})
}
describe('#createCommonMessage', function() {
it('should correctly serialize some common fields', function() {
const message = malltvAnalyticsAdapter.createCommonMessage(auctionId)
assertHavingRequiredMessageFields(message)
})
})
describe('#serializeBidResponse', function() {
it('should handle BID properly and serialize bid price related fields', function() {
const result = malltvAnalyticsAdapter.serializeBidResponse(receivedBids[0], BIDDER_STATUS.BID)
expect(result).to.include({
prebidWon: false,
isTimeout: false,
status: BIDDER_STATUS.BID,
time: 72,
cpm: 0.1,
currency: 'EUR',
originalCpm: 0.1,
originalCurrency: 'EUR',
cpmEur: 0.1,
})
})
it('should handle NO_BID properly and set status to noBid', function() {
const result = malltvAnalyticsAdapter.serializeBidResponse(noBids[0], BIDDER_STATUS.NO_BID)
expect(result).to.include({
prebidWon: false,
isTimeout: false,
status: BIDDER_STATUS.NO_BID,
})
})
it('should handle BID_WON properly and serialize bid price related fields', function() {
const result = malltvAnalyticsAdapter.serializeBidResponse(receivedBids[0], BIDDER_STATUS.BID_WON)
expect(result).to.include({
prebidWon: true,
isTimeout: false,
status: BIDDER_STATUS.BID_WON,
time: 72,
cpm: 0.1,
currency: 'EUR',
originalCpm: 0.1,
originalCurrency: 'EUR',
cpmEur: 0.1,
})
})
it('should handle TIMEOUT properly and set status to timeout and isTimeout to true', function() {
const result = malltvAnalyticsAdapter.serializeBidResponse(noBids[0], BIDDER_STATUS.TIMEOUT)
expect(result).to.include({
prebidWon: false,
isTimeout: true,
status: BIDDER_STATUS.TIMEOUT,
})
})
it('should handle BID_WON properly and fill originalCpm field with cpm in missing originalCpm case', function() {
const result = malltvAnalyticsAdapter.serializeBidResponse(withoutOriginalCpmBids[0], BIDDER_STATUS.BID_WON)
expect(result).to.include({
prebidWon: true,
isTimeout: false,
status: BIDDER_STATUS.BID_WON,
time: 120,
cpm: 0.29,
currency: 'EUR',
originalCpm: 0.29,
originalCurrency: 'EUR',
cpmEur: 0.29,
})
})
it('should handle BID_WON properly and fill originalCurrency field with currency in missing originalCurrency case', function() {
const result = malltvAnalyticsAdapter.serializeBidResponse(withoutOriginalCurrencyBids[0], BIDDER_STATUS.BID_WON)
expect(result).to.include({
prebidWon: true,
isTimeout: false,
status: BIDDER_STATUS.BID_WON,
time: 120,
cpm: 0.09,
currency: 'EUR',
originalCpm: 0.09,
originalCurrency: 'EUR',
cpmEur: 0.09,
})
})
})
describe('#addBidResponseToMessage()', function() {
it('should add a bid response in the output message, grouped by adunit_id and bidder', function() {
const message = {
adUnits: {}
}
malltvAnalyticsAdapter.addBidResponseToMessage(message, noBids[0], BIDDER_STATUS.NO_BID)
expect(message.adUnits).to.deep.include({
'adunit_2': {
'malltv': {
prebidWon: false,
isTimeout: false,
status: BIDDER_STATUS.NO_BID,
}
}
})
})
})
describe('#createBidMessage()', function() {
it('should format auction message sent to the backend', function() {
const args = {
auctionId: auctionId,
timestamp: 1234567890,
timeout: 3000,
auctionEnd: 1234567990,
adUnitCodes: ['adunit_1', 'adunit_2'],
bidsReceived: receivedBids,
noBids: noBids
}
const result = malltvAnalyticsAdapter.createBidMessage(args, highestCpmBids, timeoutBids)
assertHavingRequiredMessageFields(result)
expect(result).to.deep.include({
auctionElapsed: 100,
timeout: 3000,
adUnits: {
'adunit_1': {
'malltv': {
prebidWon: true,
isTimeout: false,
status: BIDDER_STATUS.BID,
time: 72,
cpm: 0.1,
currency: 'EUR',
originalCpm: 0.1,
originalCurrency: 'EUR',
cpmEur: 0.1,
vastUrl: null
},
'gjirafa': {
prebidWon: false,
isTimeout: false,
status: BIDDER_STATUS.BID,
time: 100,
cpm: 0.08,
currency: 'EUR',
originalCpm: 0.08,
originalCurrency: 'EUR',
cpmEur: 0.08,
vastUrl: null
}
},
'adunit_2': {
// this bid result exists in both bid and noBid arrays and should be treated as bid
'malltv': {
prebidWon: false,
isTimeout: false,
time: 120,
cpm: 0.09,
currency: 'EUR',
originalCpm: 0.09,
originalCurrency: 'EUR',
cpmEur: 0.09,
status: BIDDER_STATUS.BID,
vastUrl: null
},
'gjirafa': {
prebidWon: false,
isTimeout: true,
status: BIDDER_STATUS.TIMEOUT,
}
}
}
})
})
})
describe('#handleBidTimeout()', function() {
it('should cached the timeout bid as BID_TIMEOUT event was triggered', function() {
malltvAnalyticsAdapter.cachedAuctions['test_timeout_auction_id'] = { 'timeoutBids': [] }
const args = [{
auctionId: 'test_timeout_auction_id',
timestamp: 1234567890,
timeout: 3000,
auctionEnd: 1234567990,
bidsReceived: receivedBids,
noBids: noBids
}]
malltvAnalyticsAdapter.handleBidTimeout(args)
const result = malltvAnalyticsAdapter.getCachedAuction('test_timeout_auction_id')
expect(result).to.deep.include({
timeoutBids: [{
auctionId: 'test_timeout_auction_id',
timestamp: 1234567890,
timeout: 3000,
auctionEnd: 1234567990,
bidsReceived: receivedBids,
noBids: noBids
}]
})
})
})
})
})
describe('Malltv Analytics Adapter track handler ', function () {
const configOptions = { propertyId }
beforeEach(function () {
sinon.stub(events, 'getEvents').returns([])
malltvAnalyticsAdapter.enableAnalytics({
provider: 'malltvAnalytics',
options: configOptions
})
})
afterEach(function () {
malltvAnalyticsAdapter.disableAnalytics()
events.getEvents.restore()
})
it('should call handleBidTimeout as BID_TIMEOUT trigger event', function() {
sinon.spy(malltvAnalyticsAdapter, 'handleBidTimeout')
events.emit(constants.EVENTS.BID_TIMEOUT, {})
sinon.assert.callCount(malltvAnalyticsAdapter.handleBidTimeout, 1)
malltvAnalyticsAdapter.handleBidTimeout.restore()
})
it('should call handleAuctionEnd as AUCTION_END trigger event', function() {
sinon.spy(malltvAnalyticsAdapter, 'handleAuctionEnd')
events.emit(constants.EVENTS.AUCTION_END, {})
sinon.assert.callCount(malltvAnalyticsAdapter.handleAuctionEnd, 1)
malltvAnalyticsAdapter.handleAuctionEnd.restore()
})
})
describe('enableAnalytics and config parser', function () {
const configOptions = { propertyId, server }
beforeEach(function () {
sinon.stub(events, 'getEvents').returns([])
malltvAnalyticsAdapter.enableAnalytics({
provider: 'malltvAnalytics',
options: configOptions
})
})
afterEach(function () {
malltvAnalyticsAdapter.disableAnalytics()
events.getEvents.restore()
})
it('should parse config correctly with optional values', function () {
const { options, propertyId, server } = malltvAnalyticsAdapter.getAnalyticsOptions()
expect(options).to.deep.equal(configOptions)
expect(propertyId).to.equal(configOptions.propertyId)
expect(server).to.equal(configOptions.server)
})
it('should not enable Analytics when propertyId is missing', function() {
const configOptions = {
options: { }
}
const isConfigValid = malltvAnalyticsAdapter.initConfig(configOptions)
expect(isConfigValid).to.equal(false)
})
it('should use DEFAULT_SERVER when server is missing', function () {
const configOptions = {
options: {
propertyId
}
}
malltvAnalyticsAdapter.initConfig(configOptions)
expect(malltvAnalyticsAdapter.getAnalyticsOptions().server).to.equal(DEFAULT_SERVER)
})
})
})