diff --git a/src/adapters/bidderFactory.ts b/src/adapters/bidderFactory.ts index 60f21964f88..75ded1c00b0 100644 --- a/src/adapters/bidderFactory.ts +++ b/src/adapters/bidderFactory.ts @@ -686,6 +686,16 @@ export function isValid(adUnitCode: string, bid: Bid, {index = auctionManager.in return false; } + if (bid.mediaType) { + const mediaTypes = index.getMediaTypes(bid); + if (mediaTypes && Object.keys(mediaTypes).length > 0) { + if (!mediaTypes.hasOwnProperty(bid.mediaType)) { + logError(errorMessage(`Bid mediaType '${bid.mediaType}' is not supported by the ad unit. Allowed: ${Object.keys(mediaTypes).join(', ')}`)); + return false; + } + } + } + if (FEATURES.NATIVE && bid.mediaType === 'native' && !nativeBidIsValid(bid, {index})) { logError(errorMessage('Native bid missing some required properties.')); return false; diff --git a/test/spec/unit/core/bidderFactory_spec.js b/test/spec/unit/core/bidderFactory_spec.js index 79b09cfbe11..ea73fc30188 100644 --- a/test/spec/unit/core/bidderFactory_spec.js +++ b/test/spec/unit/core/bidderFactory_spec.js @@ -1744,6 +1744,72 @@ describe('bidderFactory', () => { }); }); }) + + describe('media type validation', () => { + let req; + + function mkResponse(props) { + return Object.assign({ + requestId: req.bidId, + cpm: 1, + ttl: 60, + creativeId: '123', + netRevenue: true, + currency: 'USD', + width: 1, + height: 2, + }, props); + } + + function checkValid(bid) { + return isValid('au', bid, {index: stubAuctionIndex({bidRequests: [req]})}); + } + + beforeEach(() => { + req = { + ...MOCK_BIDS_REQUEST.bids[0], + mediaTypes: { + banner: { + sizes: [[1, 2]] + } + } + }; + }); + + it('should reject video bid when ad unit only has banner', () => { + expect(checkValid(mkResponse({mediaType: 'video'}))).to.be.false; + }); + + it('should reject banner bid when ad unit only has video', () => { + req.mediaTypes = {video: {context: 'instream'}}; + expect(checkValid(mkResponse({mediaType: 'banner'}))).to.be.false; + }); + + it('should accept banner bid when ad unit has banner', () => { + expect(checkValid(mkResponse({mediaType: 'banner'}))).to.be.true; + }); + + it('should accept video bid when ad unit has both banner and video', () => { + req.mediaTypes = { + banner: {sizes: [[1, 2]]}, + video: {context: 'instream'} + }; + expect(checkValid(mkResponse({mediaType: 'video', vastUrl: 'http://vast.xml'}))).to.be.true; + }); + + it('should skip check when bid.mediaType is not set', () => { + expect(checkValid(mkResponse({mediaType: undefined}))).to.be.true; + }); + + it('should skip check when mediaTypes is unavailable from index', () => { + delete req.mediaTypes; + expect(checkValid(mkResponse({mediaType: 'banner'}))).to.be.true; + }); + + it('should reject native bid when ad unit only has banner', () => { + expect(checkValid(mkResponse({mediaType: 'native'}))).to.be.false; + }); + }); }); describe('gzip compression', () => {