From 0eb76d7620c3bfccd254340fbc189985373fda07 Mon Sep 17 00:00:00 2001 From: Adish Rao <30475159+adish1997@users.noreply.github.com> Date: Mon, 27 Mar 2023 22:32:49 +0530 Subject: [PATCH] added fix and support for multibid module (#9602) Co-authored-by: adish --- modules/medianetAnalyticsAdapter.js | 176 ++++++++++++------ .../modules/medianetAnalyticsAdapter_spec.js | 28 ++- 2 files changed, 147 insertions(+), 57 deletions(-) diff --git a/modules/medianetAnalyticsAdapter.js b/modules/medianetAnalyticsAdapter.js index d2d3d2bf888..647a02fe7dc 100644 --- a/modules/medianetAnalyticsAdapter.js +++ b/modules/medianetAnalyticsAdapter.js @@ -8,8 +8,7 @@ import { logError, logInfo, triggerPixel, - uniques, - getHighestCpm + uniques } from '../src/utils.js'; import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; import adapterManager from '../src/adapterManager.js'; @@ -41,6 +40,7 @@ const PREBID_VERSION = $$PREBID_GLOBAL$$.version; const ERROR_CONFIG_JSON_PARSE = 'analytics_config_parse_fail'; const ERROR_CONFIG_FETCH = 'analytics_config_ajax_fail'; const ERROR_WINNING_BID_ABSENT = 'winning_bid_absent'; +const ERROR_WINNING_AUCTION_MISSING = 'winning_auction_missing'; const BID_SUCCESS = 1; const BID_NOBID = 2; const BID_TIMEOUT = 3; @@ -72,7 +72,7 @@ class ErrorLogger { this.evtid = 'projectevents'; this.project = 'prebidanalytics'; this.dn = pageDetails.domain || ''; - this.requrl = pageDetails.requrl || ''; + this.requrl = pageDetails.topmostLocation || ''; this.pbversion = PREBID_VERSION; this.cid = config.cid || ''; this.rd = additionalData; @@ -270,6 +270,52 @@ class AdSlot { } } +class BidWrapper { + constructor() { + this.bidReqs = []; + this.bidObjs = []; + } + + findReqBid(bidId) { + return this.bidReqs.find(bid => { + return bid['bidId'] === bidId + }); + } + + findBidObj(key, value) { + return this.bidObjs.find(bid => { + return bid[key] === value + }); + } + + addBidReq(bidRequest) { + this.bidReqs.push(bidRequest) + } + + addBidObj(bidObj) { + if (!(bidObj instanceof Bid)) { + bidObj = Bid.getInstance(bidObj); + } + const bidReq = this.findReqBid(bidObj.bidId); + if (bidReq instanceof Bid) { + bidReq.used = true; + } + this.bidObjs.push(bidObj); + } + + getAdSlotBids(adSlot) { + const bidResponses = this.getAdSlotBidObjs(adSlot); + return bidResponses.map((bid) => bid.getLoggingData()); + } + + getAdSlotBidObjs(adSlot) { + const bidResponses = this.bidObjs + .filter((bid) => bid.adUnitCode === adSlot); + const remResponses = this.bidReqs.filter(bid => !bid.used && bid.adUnitCode === adSlot); + return [...bidResponses, ...remResponses]; + } +} + class Bid { constructor(bidId, bidder, src, start, adUnitCode, mediaType, allMediaTypeSizes) { this.bidId = bidId; @@ -299,6 +345,9 @@ class Bid { this.floorPrice = undefined; this.floorRule = undefined; this.serverLatencyMillis = undefined; + this.used = false; + this.originalRequestId = bidId; + this.requestId = undefined; } get size() { @@ -308,8 +357,15 @@ class Bid { return this.width + 'x' + this.height; } + static getInstance(bidProps) { + const bidObj = new Bid(); + return bidProps && Object.assign(bidObj, bidProps); + } + getLoggingData() { return { + reqId: this.requestId || this.bidId, + ogReqId: this.originalRequestId, adid: this.adId, pvnm: this.bidder, src: this.src, @@ -341,7 +397,7 @@ class Auction { constructor(acid) { this.acid = acid; this.status = AUCTION_IN_PROGRESS; - this.bids = []; + this.bidWrapper = new BidWrapper(); this.adSlots = {}; this.auctionInitTime = undefined; this.auctionStartTime = undefined; @@ -378,24 +434,31 @@ class Auction { addSlot({ adUnitCode, supplyAdCode, mediaTypes, allMediaTypeSizes, tmax, adext, context }) { if (adUnitCode && this.adSlots[adUnitCode] === undefined) { this.adSlots[adUnitCode] = new AdSlot(tmax, supplyAdCode, context, adext); - this.addBid(new Bid('-1', DUMMY_BIDDER, 'client', '-1', adUnitCode, mediaTypes, allMediaTypeSizes)); + this.addBidObj(new Bid('-1', DUMMY_BIDDER, 'client', Date.now(), adUnitCode, mediaTypes, allMediaTypeSizes)); } } addBid(bid) { - this.bids.push(bid); + this.bidWrapper.addBidReq(bid); } - findBid(key, value) { - return this.bids.filter(bid => { - return bid[key] === value - })[0]; + addBidObj(bidObj) { + this.bidWrapper.addBidObj(bidObj) } - getAdslotBids(adslot) { - return this.bids - .filter((bid) => bid.adUnitCode === adslot) - .map((bid) => bid.getLoggingData()); + findReqBid(bidId) { + return this.bidWrapper.findReqBid(bidId) + } + + findBidObj(key, value) { + return this.bidWrapper.findBidObj(key, value) + } + + getAdSlotBids(adSlot) { + return this.bidWrapper.getAdSlotBids(adSlot); + } + getAdSlotBidObjs(adSlot) { + return this.bidWrapper.getAdSlotBidObjs(adSlot); } _mergeFieldsToLog(objParams) { @@ -494,41 +557,26 @@ function _getSizes(mediaTypes, sizes) { } } -/* - - The code is used to determine if the current bid is higher than the previous bid. - - If it is, then the code will return true and if not, it will return false. - */ -function canSelectCurrentBid(previousBid, currentBid) { - if (!(previousBid instanceof Bid)) return false; - - // For first bid response the previous bid will be containing bid request obj - // in which the cpm would be undefined so the current bid can directly be selected. - const isFirstBidResponse = previousBid.cpm === undefined && currentBid.cpm !== undefined; - if (isFirstBidResponse) return true; - - // if there are 2 bids, get the highest bid - const selectedBid = getHighestCpm(previousBid, currentBid); - - // Return true if selectedBid is currentBid, - // The timeToRespond field is used as an identifier for distinguishing - // between the current iterating bid and the previous bid. - return selectedBid.timeToRespond === currentBid.timeToRespond; -} - function bidResponseHandler(bid) { - const { width, height, mediaType, cpm, requestId, timeToRespond, auctionId, dealId } = bid; - const {originalCpm, bidderCode, creativeId, adId, currency} = bid; + const { width, height, mediaType, cpm, requestId, timeToRespond, auctionId, dealId, originalRequestId, bidder } = bid; + const {originalCpm, creativeId, adId, currency} = bid; if (!(auctions[auctionId] instanceof Auction)) { return; } - let bidObj = auctions[auctionId].findBid('bidId', requestId); - if (!canSelectCurrentBid(bidObj, bid)) { - return; + const reqId = originalRequestId || requestId; + const bidReq = auctions[auctionId].findReqBid(reqId); + + if (!(bidReq instanceof Bid)) return; + + let bidObj = auctions[auctionId].findBidObj('bidId', requestId); + let isBidOverridden = true; + if (!bidObj || bidObj.status === BID_SUCCESS) { + bidObj = {}; + isBidOverridden = false; } - Object.assign( - bidObj, - { cpm, width, height, mediaType, timeToRespond, dealId, creativeId }, + Object.assign(bidObj, bidReq, + { cpm, width, height, mediaType, timeToRespond, dealId, creativeId, originalRequestId, requestId }, { adId, currency } ); bidObj.floorPrice = deepAccess(bid, 'floorData.floorValue'); @@ -547,7 +595,7 @@ function bidResponseHandler(bid) { bidObj.status = BID_SUCCESS; } - if (bidderCode === MEDIANET_BIDDER_CODE && bid.ext instanceof Object) { + if (bidder === MEDIANET_BIDDER_CODE && bid.ext instanceof Object) { Object.assign( bidObj, { 'ext': bid.ext }, @@ -558,6 +606,7 @@ function bidResponseHandler(bid) { if (typeof bid.serverResponseTimeMs !== 'undefined') { bidObj.serverLatencyMillis = bid.serverResponseTimeMs; } + !isBidOverridden && auctions[auctionId].addBidObj(bidObj); } function noBidResponseHandler({ auctionId, bidId }) { @@ -567,11 +616,13 @@ function noBidResponseHandler({ auctionId, bidId }) { if (auctions[auctionId].hasEnded()) { return; } - let bidObj = auctions[auctionId].findBid('bidId', bidId); - if (!(bidObj instanceof Bid)) { + const bidReq = auctions[auctionId].findReqBid(bidId); + if (!(bidReq instanceof Bid) || bidReq.used) { return; } + const bidObj = {...bidReq}; bidObj.status = BID_NOBID; + auctions[auctionId].addBidObj(bidObj); } function bidTimeoutHandler(timedOutBids) { @@ -579,11 +630,13 @@ function bidTimeoutHandler(timedOutBids) { if (!(auctions[auctionId] instanceof Auction)) { return; } - let bidObj = auctions[auctionId].findBid('bidId', bidId); - if (!(bidObj instanceof Bid)) { + const bidReq = auctions[auctionId].findReqBid('bidId', bidId); + if (!(bidReq instanceof Bid) || bidReq.used) { return; } + const bidObj = {...bidReq}; bidObj.status = BID_TIMEOUT; + auctions[auctionId].addBidObj(bidObj); }) } @@ -614,13 +667,13 @@ function setTargetingHandler(params) { const winnerAdId = params[adunit][CONSTANTS.TARGETING_KEYS.AD_ID]; let winningBid; let bidAdIds = Object.keys(targetingObj).map(k => targetingObj[k]); - auctionObj.bids.filter((bid) => bidAdIds.indexOf(bid.adId) !== -1).map(function(bid) { + auctionObj.bidWrapper.bidObjs.filter((bid) => bidAdIds.indexOf(bid.adId) !== -1).map(function(bid) { bid.iwb = 1; if (bid.adId === winnerAdId) { winningBid = bid; } }); - auctionObj.bids.forEach(bid => { + auctionObj.bidWrapper.bidObjs.forEach(bid => { if (bid.bidder === DUMMY_BIDDER && bid.adUnitCode === adunit) { bid.iwb = bidAdIds.length === 0 ? 0 : 1; bid.width = deepAccess(winningBid, 'width'); @@ -633,16 +686,27 @@ function setTargetingHandler(params) { } function bidWonHandler(bid) { - const { auctionId, adUnitCode, adId } = bid; + const { auctionId, adUnitCode, adId, bidder, requestId, originalRequestId } = bid; if (!(auctions[auctionId] instanceof Auction)) { + new ErrorLogger(ERROR_WINNING_AUCTION_MISSING, { + adId, + auctionId, + adUnitCode, + bidder, + requestId, + originalRequestId + }).send(); return; } - let bidObj = auctions[auctionId].findBid('adId', adId); + let bidObj = auctions[auctionId].findBidObj('adId', adId); if (!(bidObj instanceof Bid)) { new ErrorLogger(ERROR_WINNING_BID_ABSENT, { - adId: adId, - acid: auctionId, + adId, + auctionId, adUnitCode, + bidder, + requestId, + originalRequestId }).send(); return; } @@ -696,13 +760,13 @@ function fireAuctionLog(acid, adtag, logType, adId) { let bidParams; if (logType === LOG_TYPE.RA) { - const winningBidObj = auctions[acid].findBid('adId', adId); + const winningBidObj = auctions[acid].findBidObj('adId', adId); if (!winningBidObj) return; const winLogData = winningBidObj.getLoggingData(); bidParams = [winLogData]; commonParams.lper = 1; } else { - bidParams = auctions[acid].getAdslotBids(adtag).map(({winner, ...restParams}) => restParams); + bidParams = auctions[acid].getAdSlotBids(adtag).map(({winner, ...restParams}) => restParams); delete commonParams.wts; } let mnetPresent = bidParams.filter(b => b.pvnm === MEDIANET_BIDDER_CODE).length > 0; diff --git a/test/spec/modules/medianetAnalyticsAdapter_spec.js b/test/spec/modules/medianetAnalyticsAdapter_spec.js index c408f23c4f4..e19c27cc2d3 100644 --- a/test/spec/modules/medianetAnalyticsAdapter_spec.js +++ b/test/spec/modules/medianetAnalyticsAdapter_spec.js @@ -30,9 +30,11 @@ const MOCK = { NO_BID: {'bidder': 'medianet', 'params': {'cid': 'test123', 'crid': '451466393', 'site': {}}, 'mediaTypes': {'banner': {'sizes': [[300, 250]], 'ext': ['asdads']}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '303fa0c6-682f-4aea-8e4a-dc68f0d5c7d5', 'sizes': [[300, 250], [300, 600]], 'bidId': '28248b0e6aece2', 'bidderRequestId': '13fccf3809fe43', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'src': 'client'}, BID_TIMEOUT: [{'bidId': '28248b0e6aece2', 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'params': [{'cid': 'test123', 'crid': '451466393', 'site': {}}, {'cid': '8CUX0H51P', 'crid': '451466393', 'site': {}}], 'timeout': 6}], BIDS_SAME_REQ_DIFF_CPM: [{'bidderCode': 'medianet', 'width': 300, 'height': 250, 'adId': '3e6e4bce5c8fb3', 'requestId': '28248b0e6aece2', 'mediaType': 'banner', 'source': 'client', 'ext': {'pvid': 123, 'crid': '321'}, 'no_bid': false, 'cpm': 2.299, 'ad': 'AD_CODE', 'ttl': 180, 'creativeId': 'Test1', 'netRevenue': true, 'currency': 'USD', 'dfp_id': 'div-gpt-ad-1460505748561-0', 'originalCpm': 1.1495, 'originalCurrency': 'USD', 'floorData': {'floorValue': 1.10, 'floorRule': 'banner'}, 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'responseTimestamp': 1584563606009, 'requestTimestamp': 1584563605743, 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'timeToRespond': 266, 'pbLg': '2.00', 'pbMg': '2.20', 'pbHg': '2.29', 'pbAg': '2.25', 'pbDg': '2.29', 'pbCg': '2.00', 'size': '300x250', 'adserverTargeting': {'hb_bidder': 'medianet', 'hb_adid': '3e6e4bce5c8fb3', 'hb_pb': '2.00', 'hb_size': '300x250', 'hb_source': 'client', 'hb_format': 'banner', 'prebid_test': 1}, 'status': 'rendered', 'params': [{'cid': 'test123', 'crid': '451466393'}]}, {'bidderCode': 'medianet', 'width': 300, 'height': 250, 'adId': '3e6e4bce5c8fb4', 'requestId': '28248b0e6aece2', 'mediaType': 'banner', 'source': 'client', 'ext': {'pvid': 123, 'crid': '321'}, 'no_bid': false, 'cpm': 1.299, 'ad': 'AD_CODE', 'ttl': 180, 'creativeId': 'Test1', 'netRevenue': true, 'currency': 'USD', 'dfp_id': 'div-gpt-ad-1460505748561-0', 'originalCpm': 1.1495, 'originalCurrency': 'USD', 'floorData': {'floorValue': 1.10, 'floorRule': 'banner'}, 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'responseTimestamp': 1584563606009, 'requestTimestamp': 1584563605743, 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'timeToRespond': 278, 'pbLg': '1.00', 'pbMg': '1.20', 'pbHg': '1.29', 'pbAg': '1.25', 'pbDg': '1.29', 'pbCg': '1.00', 'size': '300x250', 'adserverTargeting': {'hb_bidder': 'medianet', 'hb_adid': '3e6e4bce5c8fb3', 'hb_pb': '1.00', 'hb_size': '300x250', 'hb_source': 'client', 'hb_format': 'banner', 'prebid_test': 1}, 'status': 'rendered', 'params': [{'cid': 'test123', 'crid': '451466393'}]}], + BIDS_SAME_REQ_DIFF_CPM_SAME_TIME: [{'bidderCode': 'medianet', 'width': 300, 'height': 250, 'adId': '3e6e4bce5c8fb4', 'requestId': '28248b0e6aece2', 'mediaType': 'banner', 'source': 'client', 'ext': {'pvid': 123, 'crid': '321'}, 'no_bid': false, 'cpm': 1.299, 'ad': 'AD_CODE', 'ttl': 180, 'creativeId': 'Test1', 'netRevenue': true, 'currency': 'USD', 'dfp_id': 'div-gpt-ad-1460505748561-0', 'originalCpm': 1.1495, 'originalCurrency': 'USD', 'floorData': {'floorValue': 1.10, 'floorRule': 'banner'}, 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'responseTimestamp': 1584563606009, 'requestTimestamp': 1584563605743, 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'timeToRespond': 266, 'pbLg': '1.00', 'pbMg': '1.20', 'pbHg': '1.29', 'pbAg': '1.25', 'pbDg': '1.29', 'pbCg': '1.00', 'size': '300x250', 'adserverTargeting': {'hb_bidder': 'medianet', 'hb_adid': '3e6e4bce5c8fb3', 'hb_pb': '1.00', 'hb_size': '300x250', 'hb_source': 'client', 'hb_format': 'banner', 'prebid_test': 1}, 'status': 'rendered', 'params': [{'cid': 'test123', 'crid': '451466393'}]}, {'bidderCode': 'medianet', 'width': 300, 'height': 250, 'adId': '3e6e4bce5c8fb3', 'requestId': '28248b0e6aece2', 'mediaType': 'banner', 'source': 'client', 'ext': {'pvid': 123, 'crid': '321'}, 'no_bid': false, 'cpm': 2.299, 'ad': 'AD_CODE', 'ttl': 180, 'creativeId': 'Test1', 'netRevenue': true, 'currency': 'USD', 'dfp_id': 'div-gpt-ad-1460505748561-0', 'originalCpm': 1.1495, 'originalCurrency': 'USD', 'floorData': {'floorValue': 1.10, 'floorRule': 'banner'}, 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'responseTimestamp': 1584563606009, 'requestTimestamp': 1584563605743, 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'timeToRespond': 266, 'pbLg': '2.00', 'pbMg': '2.20', 'pbHg': '2.29', 'pbAg': '2.25', 'pbDg': '2.29', 'pbCg': '2.00', 'size': '300x250', 'adserverTargeting': {'hb_bidder': 'medianet', 'hb_adid': '3e6e4bce5c8fb3', 'hb_pb': '2.00', 'hb_size': '300x250', 'hb_source': 'client', 'hb_format': 'banner', 'prebid_test': 1}, 'status': 'rendered', 'params': [{'cid': 'test123', 'crid': '451466393'}]}], BIDS_SAME_REQ_EQUAL_CPM: [{'bidderCode': 'medianet', 'width': 300, 'height': 250, 'adId': '3e6e4bce5c8fb3', 'requestId': '28248b0e6aece2', 'mediaType': 'banner', 'source': 'client', 'ext': {'pvid': 123, 'crid': '321'}, 'no_bid': false, 'cpm': 2.299, 'ad': 'AD_CODE', 'ttl': 180, 'creativeId': 'Test1', 'netRevenue': true, 'currency': 'USD', 'dfp_id': 'div-gpt-ad-1460505748561-0', 'originalCpm': 1.1495, 'originalCurrency': 'USD', 'floorData': {'floorValue': 1.1, 'floorRule': 'banner'}, 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'responseTimestamp': 1584563606009, 'requestTimestamp': 1584563605743, 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'timeToRespond': 266, 'pbLg': '2.00', 'pbMg': '2.20', 'pbHg': '2.29', 'pbAg': '2.25', 'pbDg': '2.29', 'pbCg': '2.00', 'size': '300x250', 'adserverTargeting': {'hb_bidder': 'medianet', 'hb_adid': '3e6e4bce5c8fb3', 'hb_pb': '2.00', 'hb_size': '300x250', 'hb_source': 'client', 'hb_format': 'banner', 'prebid_test': 1}, 'status': 'rendered', 'params': [{'cid': 'test123', 'crid': '451466393'}]}, {'bidderCode': 'medianet', 'width': 300, 'height': 250, 'adId': '3e6e4bce5c8fb4', 'requestId': '28248b0e6aece2', 'mediaType': 'banner', 'source': 'client', 'ext': {'pvid': 123, 'crid': '321'}, 'no_bid': false, 'cpm': 2.299, 'ad': 'AD_CODE', 'ttl': 180, 'creativeId': 'Test1', 'netRevenue': true, 'currency': 'USD', 'dfp_id': 'div-gpt-ad-1460505748561-0', 'originalCpm': 1.1495, 'originalCurrency': 'USD', 'floorData': {'floorValue': 1.1, 'floorRule': 'banner'}, 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'responseTimestamp': 1584563606009, 'requestTimestamp': 1584563605743, 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'timeToRespond': 286, 'pbLg': '2.00', 'pbMg': '2.20', 'pbHg': '2.29', 'pbAg': '2.25', 'pbDg': '2.29', 'pbCg': '2.00', 'size': '300x250', 'adserverTargeting': {'hb_bidder': 'medianet', 'hb_adid': '3e6e4bce5c8fb3', 'hb_pb': '2.00', 'hb_size': '300x250', 'hb_source': 'client', 'hb_format': 'banner', 'prebid_test': 1}, 'status': 'rendered', 'params': [{'cid': 'test123', 'crid': '451466393'}]}], BID_RESPONSES: [{'bidderCode': 'medianet', 'width': 300, 'height': 250, 'adId': '3e6e4bce5c8fb3', 'requestId': '28248b0e6aece2', 'mediaType': 'banner', 'source': 'client', 'ext': {'pvid': 123, 'crid': '321'}, 'no_bid': false, 'cpm': 2.299, 'ad': 'AD_CODE', 'ttl': 180, 'creativeId': 'Test1', 'netRevenue': true, 'currency': 'USD', 'dfp_id': 'div-gpt-ad-1460505748561-0', 'originalCpm': 1.1495, 'originalCurrency': 'USD', 'floorData': {'floorValue': 1.1, 'floorRule': 'banner'}, 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'responseTimestamp': 1584563606009, 'requestTimestamp': 1584563605743, 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'timeToRespond': 266, 'pbLg': '2.00', 'pbMg': '2.20', 'pbHg': '2.29', 'pbAg': '2.25', 'pbDg': '2.29', 'pbCg': '2.00', 'size': '300x250', 'adserverTargeting': {'hb_bidder': 'medianet', 'hb_adid': '3e6e4bce5c8fb3', 'hb_pb': '2.00', 'hb_size': '300x250', 'hb_source': 'client', 'hb_format': 'banner', 'prebid_test': 1}, 'params': [{'cid': 'test123', 'crid': '451466393'}]}, {'bidderCode': 'appnexus', 'width': 300, 'height': 250, 'adId': '3e6e4bce5c8fb4', 'requestId': '28248b0e6aecd5', 'mediaType': 'banner', 'source': 'client', 'ext': {'pvid': 123, 'crid': '321'}, 'no_bid': false, 'cpm': 1.299, 'ad': 'AD_CODE', 'ttl': 180, 'creativeId': 'Test1', 'netRevenue': true, 'currency': 'USD', 'dfp_id': 'div-gpt-ad-1460505748561-0', 'originalCpm': 1.1495, 'originalCurrency': 'USD', 'floorData': {'floorValue': 1.1, 'floorRule': 'banner'}, 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'responseTimestamp': 1584563606009, 'requestTimestamp': 1584563605743, 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'timeToRespond': 278, 'pbLg': '1.00', 'pbMg': '1.20', 'pbHg': '1.29', 'pbAg': '1.25', 'pbDg': '1.29', 'pbCg': '1.00', 'size': '300x250', 'adserverTargeting': {'hb_bidder': 'appnexus', 'hb_adid': '3e6e4bce5c8fb4', 'hb_pb': '1.00', 'hb_size': '300x250', 'hb_source': 'client', 'hb_format': 'banner', 'prebid_test': 1}, 'params': [{'publisherId': 'test123', 'placementId': '451466393'}]}], - BID_REQUESTS: [{'bidderCode': 'medianet', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'bids': [{'bidder': 'medianet', 'params': {'cid': 'TEST_CID', 'crid': '451466393'}, 'mediaTypes': {'banner': {'sizes': [[300, 250]], 'ext': ['asdads']}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'sizes': [[300, 250]], 'bidId': '28248b0e6aece2', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'src': 'client'}], 'auctionStart': 1584563605739, 'timeout': 6000, 'uspConsent': '1YY', 'start': 1584563605743}, {'bidderCode': 'appnexus', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'bids': [{'bidder': 'appnexus', 'params': {'publisherId': 'TEST_CID', 'placementId': '451466393'}, 'mediaTypes': {'banner': {'sizes': [[300, 250]], 'ext': ['asdads']}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'sizes': [[300, 250]], 'bidId': '28248b0e6aecd5', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'src': 'client'}], 'auctionStart': 1584563605739, 'timeout': 6000, 'uspConsent': '1YY', 'start': 1584563605743}] + BID_REQUESTS: [{'bidderCode': 'medianet', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'bids': [{'bidder': 'medianet', 'params': {'cid': 'TEST_CID', 'crid': '451466393'}, 'mediaTypes': {'banner': {'sizes': [[300, 250]], 'ext': ['asdads']}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'sizes': [[300, 250]], 'bidId': '28248b0e6aece2', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'src': 'client'}], 'auctionStart': 1584563605739, 'timeout': 6000, 'uspConsent': '1YY', 'start': 1584563605743}, {'bidderCode': 'appnexus', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'bids': [{'bidder': 'appnexus', 'params': {'publisherId': 'TEST_CID', 'placementId': '451466393'}, 'mediaTypes': {'banner': {'sizes': [[300, 250]], 'ext': ['asdads']}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'sizes': [[300, 250]], 'bidId': '28248b0e6aecd5', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'src': 'client'}], 'auctionStart': 1584563605739, 'timeout': 6000, 'uspConsent': '1YY', 'start': 1584563605743}], + MULTI_BID_RESPONSES: [{'bidderCode': 'medianet', 'width': 300, 'height': 250, 'adId': '3e6e4bce5c8fb3', 'requestId': '28248b0e6aece2', 'mediaType': 'banner', 'source': 'client', 'ext': {'pvid': 123, 'crid': '321'}, 'no_bid': false, 'cpm': 2.299, 'ad': 'AD_CODE', 'ttl': 180, 'creativeId': 'Test1', 'netRevenue': true, 'currency': 'USD', 'dfp_id': 'div-gpt-ad-1460505748561-0', 'originalCpm': 1.1495, 'originalCurrency': 'USD', 'floorData': {'floorValue': 1.10, 'floorRule': 'banner'}, 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'responseTimestamp': 1584563606009, 'requestTimestamp': 1584563605743, 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'timeToRespond': 266, 'pbLg': '2.00', 'pbMg': '2.20', 'pbHg': '2.29', 'pbAg': '2.25', 'pbDg': '2.29', 'pbCg': '2.00', 'size': '300x250', 'adserverTargeting': {'hb_bidder': 'medianet', 'hb_adid': '3e6e4bce5c8fb3', 'hb_pb': '2.00', 'hb_size': '300x250', 'hb_source': 'client', 'hb_format': 'banner', 'prebid_test': 1}, 'status': 'rendered', 'params': [{'cid': 'test123', 'crid': '451466393'}]}, {'bidderCode': 'bidA2', 'originalBidder': 'medianet', 'width': 300, 'height': 250, 'adId': '3e6e4bce5c8fb4', 'requestId': '28248b0e6aebecc', 'originalRequestId': '28248b0e6aece2', 'mediaType': 'banner', 'source': 'client', 'ext': {'pvid': 123, 'crid': '321'}, 'no_bid': false, 'cpm': 3.299, 'ad': 'AD_CODE', 'ttl': 180, 'creativeId': 'Test1', 'netRevenue': true, 'currency': 'USD', 'dfp_id': 'div-gpt-ad-1460505748561-0', 'originalCpm': 3.1495, 'originalCurrency': 'USD', 'floorData': {'floorValue': 1.10, 'floorRule': 'banner'}, 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'responseTimestamp': 1584563606009, 'requestTimestamp': 1584563605743, 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'timeToRespond': 266, 'pbLg': '3.00', 'pbMg': '3.20', 'pbHg': '3.29', 'pbAg': '3.25', 'pbDg': '3.29', 'pbCg': '3.00', 'size': '300x250', 'adserverTargeting': {'hb_bidder': 'medianet', 'hb_adid': '3e6e4bce5c8fb3', 'hb_pb': '3.00', 'hb_size': '300x250', 'hb_source': 'client', 'hb_format': 'banner', 'prebid_test': 1}, 'status': 'rendered', 'params': [{'cid': 'test123', 'crid': '451466393'}]}] }; function performAuctionWithFloorConfig() { @@ -102,6 +104,14 @@ function performStandardAuctionMultiBidResponseNoWin() { events.emit(SET_TARGETING, MOCK.SET_TARGETING); } +function performMultiBidAuction() { + events.emit(AUCTION_INIT, Object.assign({}, MOCK.AUCTION_INIT, {adUnits: MOCK.Ad_Units})); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + MOCK.MULTI_BID_RESPONSES.forEach(bidResp => events.emit(BID_RESPONSE, bidResp)); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, MOCK.SET_TARGETING); +} + function getQueryData(url, decode = false) { const queryArgs = url.split('?')[1].split('&'); return queryArgs.reduce((data, arg) => { @@ -309,6 +319,13 @@ describe('Media.net Analytics Adapter', function() { expect(winningBid.adid).equals('3e6e4bce5c8fb3'); }); + it('should pick winning bid if multibids with same request id and same time to respond', function() { + performStandardAuctionMultiBidWithSameRequestId(MOCK.BIDS_SAME_REQ_DIFF_CPM_SAME_TIME); + let winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner)[0]; + expect(winningBid.adid).equals('3e6e4bce5c8fb3'); + medianetAnalytics.clearlogsQueue(); + }); + it('should pick winning bid if multibids with same request id and equal cpm', function() { performStandardAuctionMultiBidWithSameRequestId(MOCK.BIDS_SAME_REQ_EQUAL_CPM); let winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner)[0]; @@ -348,5 +365,14 @@ describe('Media.net Analytics Adapter', function() { expect(errors.length).equals(1); expect(errors[0].event).equals(ERROR_WINNING_BID_ABSENT); }); + + it('can handle multi bid module', function () { + performMultiBidAuction(); + const queue = medianetAnalytics.getlogsQueue(); + expect(queue.length).equals(1); + const multiBidLog = queue.map((log) => getQueryData(log, true))[0]; + expect(multiBidLog.pvnm).to.have.ordered.members(['-2', 'medianet', 'medianet']); + expect(multiBidLog.status).to.have.ordered.members(['1', '1', '1']); + }) }); });