diff --git a/src/targeting.js b/src/targeting.js index 1903524984b..3e275ba4e16 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -219,7 +219,7 @@ export function newTargeting(auctionManager) { if (enableSendAllBids || (deals && bid.dealId)) { const targetingValue = getTargetingMap(bid, standardKeys.filter( key => typeof bid.adserverTargeting[key] !== 'undefined' && - (deals || allowedSendAllBidTargeting.indexOf(key) !== -1))); + (deals || allowedSendAllBidTargeting.indexOf(key) !== -1))); if (targetingValue) { result.push({[bid.adUnitCode]: targetingValue}) @@ -290,7 +290,7 @@ export function newTargeting(auctionManager) { const adUnitBidLimit = (sendAllBids && (bidLimit || bidLimitConfigValue)) || 0; const { customKeysByUnit, filteredBids } = getfilteredBidsAndCustomKeys(adUnitCodes, bidsReceived); const bidsSorted = getHighestCpmBidsFromBidPool(filteredBids, winReducer, adUnitBidLimit, undefined, winSorter); - let targeting = getTargetingLevels(bidsSorted, customKeysByUnit); + let targeting = getTargetingLevels(bidsSorted, customKeysByUnit, adUnitCodes); const defaultKeys = Object.keys(Object.assign({}, DEFAULT_TARGETING_KEYS, NATIVE_KEYS)); let allowedKeys = config.getConfig(CFG_ALLOW_TARGETING_KEYS); @@ -337,8 +337,8 @@ export function newTargeting(auctionManager) { }); } - function getTargetingLevels(bidsSorted, customKeysByUnit) { - const targeting = getWinningBidTargeting(bidsSorted) + function getTargetingLevels(bidsSorted, customKeysByUnit, adUnitCodes) { + const targeting = getWinningBidTargeting(bidsSorted, adUnitCodes) .concat(getCustomBidTargeting(bidsSorted, customKeysByUnit)) .concat(getBidderTargeting(bidsSorted)) .concat(getAdUnitTargeting()); @@ -568,25 +568,17 @@ export function newTargeting(auctionManager) { * @return {Array} - An array of winning bids. */ targeting.getWinningBids = function(adUnitCode, bids, winReducer = getHighestCpm, winSorter = sortByHighestCpm) { - const usedCodes = []; const bidsReceived = bids || getBidsReceived(winReducer, winSorter); const adUnitCodes = getAdUnitCodes(adUnitCode); return bidsReceived - .reduce((result, bid) => { - const code = bid.adUnitCode; - const cpmEligible = bidderSettings.get(code, 'allowZeroCpmBids') === true ? bid.cpm >= 0 : bid.cpm > 0; - const isPreferredDeal = config.getConfig('targetingControls.alwaysIncludeDeals') && bid.dealId; - const eligible = includes(adUnitCodes, code) && - !includes(usedCodes, code) && - (isPreferredDeal || cpmEligible) - if (eligible) { - result.push(bid); - usedCodes.push(code); - } - - return result; - }, []); + .filter(bid => includes(adUnitCodes, bid.adUnitCode)) + .filter(bid => (bidderSettings.get(bid.bidderCode, 'allowZeroCpmBids') === true) ? bid.cpm >= 0 : bid.cpm > 0) + .map(bid => bid.adUnitCode) + .filter(uniques) + .map(adUnitCode => bidsReceived + .filter(bid => bid.adUnitCode === adUnitCode ? bid : null) + .reduce(getHighestCpm)); }; /** @@ -624,19 +616,11 @@ export function newTargeting(auctionManager) { /** * Get targeting key value pairs for winning bid. * @param {Array} bidsReceived code array + * @param {string[]} adUnitCodes code array * @return {targetingArray} winning bids targeting */ - function getWinningBidTargeting(bidsReceived) { - let usedAdUnitCodes = []; - let winners = bidsReceived - .reduce((bids, bid) => { - if (!includes(usedAdUnitCodes, bid.adUnitCode)) { - bids.push(bid); - usedAdUnitCodes.push(bid.adUnitCode); - } - return bids; - }, []); - + function getWinningBidTargeting(bidsReceived, adUnitCodes) { + let winners = targeting.getWinningBids(adUnitCodes, bidsReceived); let standardKeys = getStandardKeys(); winners = winners.map(winner => { diff --git a/test/spec/unit/core/targeting_spec.js b/test/spec/unit/core/targeting_spec.js index 2296379ca43..01f9f93477e 100644 --- a/test/spec/unit/core/targeting_spec.js +++ b/test/spec/unit/core/targeting_spec.js @@ -13,7 +13,7 @@ import {auctionManager} from 'src/auctionManager.js'; import * as utils from 'src/utils.js'; import {deepClone} from 'src/utils.js'; import {createBid} from '../../../../src/bidfactory.js'; -import {hook} from '../../../../src/hook.js'; +import { hook, setupBeforeHookFnOnce } from '../../../../src/hook.js'; import {getHighestCpm} from '../../../../src/utils/reducers.js'; function mkBid(bid, status = STATUS.GOOD) { @@ -956,6 +956,40 @@ describe('targeting tests', function () { }); }); // end getAllTargeting tests + describe('getAllTargeting will work correctly when a hook raises has modified flag in getHighestCpmBidsFromBidPool', function () { + let bidsReceived; + let amGetAdUnitsStub; + let amBidsReceivedStub; + let bidExpiryStub; + + beforeEach(function () { + bidsReceived = [bid2, bid1].map(deepClone); + + amBidsReceivedStub = sandbox.stub(auctionManager, 'getBidsReceived').callsFake(function() { + return bidsReceived; + }); + amGetAdUnitsStub = sandbox.stub(auctionManager, 'getAdUnitCodes').callsFake(function() { + return ['/123456/header-bid-tag-0']; + }); + bidExpiryStub = sandbox.stub(filters, 'isBidNotExpired').returns(true); + + setupBeforeHookFnOnce(getHighestCpmBidsFromBidPool, function (fn, bidsReceived, highestCpmCallback, adUnitBidLimit = 0, hasModified = false) { + fn.call(this, bidsReceived, highestCpmCallback, adUnitBidLimit, true); + }); + }); + + afterEach(function () { + getHighestCpmBidsFromBidPool.getHooks().remove(); + }) + + it('will apply correct targeting', function () { + let targeting = targetingInstance.getAllTargeting(['/123456/header-bid-tag-0']); + + expect(targeting['/123456/header-bid-tag-0']['hb_pb']).to.equal('0.53'); + expect(targeting['/123456/header-bid-tag-0']['hb_adid']).to.equal('148018fe5e'); + }) + }); + describe('getAllTargeting without bids return empty object', function () { let amBidsReceivedStub; let amGetAdUnitsStub;