Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.prebid.server.functional.model.request.auction

import com.fasterxml.jackson.annotation.JsonValue
import org.prebid.server.functional.util.PBSUtils

enum AuctionEnvironment {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ import org.prebid.server.functional.model.config.AccountConfig
import org.prebid.server.functional.model.config.AlternateBidderCodes
import org.prebid.server.functional.model.config.BidderConfig
import org.prebid.server.functional.model.db.Account

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also pls add smoke case for stored request, response, imp request

import org.prebid.server.functional.model.db.StoredImp
import org.prebid.server.functional.model.db.StoredResponse
import org.prebid.server.functional.model.request.auction.Amx
import org.prebid.server.functional.model.request.auction.BidRequest
import org.prebid.server.functional.model.request.auction.Imp
import org.prebid.server.functional.model.request.auction.PrebidStoredRequest
import org.prebid.server.functional.model.request.auction.StoredBidResponse
import org.prebid.server.functional.model.request.auction.Targeting
import org.prebid.server.functional.model.response.auction.BidExt
import org.prebid.server.functional.model.response.auction.BidResponse
Expand Down Expand Up @@ -269,7 +274,7 @@ class AlternateBidderCodeSpec extends BaseSpec {
assert response.ext.seatnonbid.size() == 1

def seatNonBid = response.ext.seatnonbid[0]
assert seatNonBid.seat == AMX.value
assert seatNonBid.seat == bidderName.value
assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id
assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL

Expand Down Expand Up @@ -324,7 +329,7 @@ class AlternateBidderCodeSpec extends BaseSpec {
assert response.ext.seatnonbid.size() == 1

def seatNonBid = response.ext.seatnonbid[0]
assert seatNonBid.seat == ALIAS.value
assert seatNonBid.seat == bidderName.value
assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id
assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL

Expand Down Expand Up @@ -782,7 +787,7 @@ class AlternateBidderCodeSpec extends BaseSpec {
assert response.ext.seatnonbid.size() == 1

def seatNonBid = response.ext.seatnonbid[0]
assert seatNonBid.seat == AMX.value
assert seatNonBid.seat == UNKNOWN.value
assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id
assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL

Expand Down Expand Up @@ -838,7 +843,7 @@ class AlternateBidderCodeSpec extends BaseSpec {
assert response.ext.seatnonbid.size() == 1

def seatNonBid = response.ext.seatnonbid[0]
assert seatNonBid.seat == AMX.value
assert seatNonBid.seat == UNKNOWN.value
assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id
assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL

Expand Down Expand Up @@ -1182,7 +1187,7 @@ class AlternateBidderCodeSpec extends BaseSpec {
assert response.ext.seatnonbid.size() == 1

def seatNonBid = response.ext.seatnonbid[0]
assert seatNonBid.seat == AMX.value
assert seatNonBid.seat == requestedAllowedBidderCode.value
assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id
assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL

Expand Down Expand Up @@ -1237,7 +1242,7 @@ class AlternateBidderCodeSpec extends BaseSpec {
assert response.ext.seatnonbid.size() == 1

def seatNonBid = response.ext.seatnonbid[0]
assert seatNonBid.seat == AMX.value
assert seatNonBid.seat == requestedAllowedBidderCode.value
assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id
assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL

Expand Down Expand Up @@ -1298,7 +1303,7 @@ class AlternateBidderCodeSpec extends BaseSpec {
assert response.ext.seatnonbid.size() == 1

def seatNonBid = response.ext.seatnonbid[0]
assert seatNonBid.seat == AMX.value
assert seatNonBid.seat == allowedBidderCodes.value
assert seatNonBid.nonBid[0].impId == bidRequest.imp[0].id
assert seatNonBid.nonBid[0].statusCode == RESPONSE_REJECTED_GENERAL

Expand Down Expand Up @@ -1572,6 +1577,200 @@ class AlternateBidderCodeSpec extends BaseSpec {
pbsServiceFactory.removeContainer(pbsConfig)
}

def "PBS should throw error when different bidder response with same seat"() {
given: "Default bid request with amx and generic bidder"
def bidRequest = getBidRequestWithAmxBidderAndAlternateBidderCode().tap {
imp[0].ext.prebid.bidder.generic = new Generic()
ext.prebid.alternateBidderCodes.bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [GENERIC])]
}

and: "Bid response with bidder code"
def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap {
it.seatbid[0].bid[0].ext = new BidExt(bidderCode: GENERIC)
}
bidder.setResponse(bidRequest.id, bidResponse)

and: "Flash metrics"
flushMetrics(pbsServiceWithAmxBidder)

when: "PBS processes auction request"
pbsServiceWithAmxBidder.sendAuctionRequest(bidRequest)

then: "Request should fail with error"
def exception = thrown(PrebidServerException)
assert exception.statusCode == 500
assert exception.responseBody.contains("Duplicate key generic")
}

def "PBS should return two seat when same bidder response with different bidder code"() {
given: "Default bid request with amx and generic bidder"
def bidRequest = getBidRequestWithAmxBidderAndAlternateBidderCode().tap {
imp[0].ext.prebid.bidder.amx = new Amx()
Comment thread
osulzhenko marked this conversation as resolved.
Outdated
imp.add(Imp.getDefaultImpression())
imp[1].ext.prebid.bidder.amx = new Amx()
imp[1].ext.prebid.bidder.generic = null
ext.prebid.alternateBidderCodes.bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [GENERIC, AMX])]
}

and: "Bid response with bidder code"
def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap {
it.seatbid[0].bid[0].ext = new BidExt(bidderCode: GENERIC)
it.seatbid[0].bid[1].ext = new BidExt(bidderCode: AMX)
}
bidder.setResponse(bidRequest.id, bidResponse)

and: "Flash metrics"
flushMetrics(pbsServiceWithAmxBidder)

when: "PBS processes auction request"
def response = pbsServiceWithAmxBidder.sendAuctionRequest(bidRequest)
Comment thread
osulzhenko marked this conversation as resolved.

then: "Bid response should contain seat"
assert response.seatbid.seat.sort() == [GENERIC, AMX].sort()

and: "Response should contain adapter code"
assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX, AMX]

and: "Response should contain bidder amx targeting"
def targeting = response.seatbid.bid.ext.prebid.targeting.flatten().collectEntries()
assert targeting["hb_pb_${AMX}"]
assert targeting["hb_size_${AMX}"]
assert targeting["hb_bidder_${AMX}"] == AMX.value

and: 'Response targeting should contain generic'
assert targeting["hb_pb_${GENERIC}"]
assert targeting["hb_size_${GENERIC}"]
assert targeting["hb_bidder_${GENERIC}"] == GENERIC.value

and: "Response should contain repose millis with corresponding bidder"
assert response.ext.responsetimemillis.containsKey(GENERIC.value)
assert response.ext.responsetimemillis.containsKey(AMX.value)

and: "Bidder request should be valid"
assert bidder.getBidderRequests(bidRequest.id)

and: "Response shouldn't contain warnings and error and seatNonBid"
assert !response.ext?.warnings
assert !response.ext?.errors
assert !response.ext?.seatnonbid

and: "Response shouldn't contain demand source"
assert !response.seatbid.first.bid.first.ext.prebid.meta.demandSource

and: "PBS shouldn't emit validation metrics"
def metrics = pbsServiceWithAmxBidder.sendCollectedMetricsRequest()
assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(AMX)]
assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(GENERIC)]
}

def "PBS should populate seat bid from stored bid response when stored bid response and alternate bidder code specified"() {
given: "Default bid request with amx bidder"
def storedResponseId = PBSUtils.randomNumber
def bidRequest = getBidRequestWithAmxBidderAndAlternateBidderCode().tap {
imp[0].ext.prebid.storedBidResponse = [new StoredBidResponse(id: storedResponseId, bidder: AMX)]
ext.prebid.alternateBidderCodes.bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [GENERIC])]
}

and: "Stored bid response in DB"
def storedBidResponse = BidResponse.getDefaultBidResponse(bidRequest)
def storedResponse = new StoredResponse(responseId: storedResponseId, storedBidResponse: storedBidResponse)
storedResponseDao.save(storedResponse)

and: "Bid response with bidder code"
def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap {
it.seatbid[0].bid[0].ext = new BidExt(bidderCode: GENERIC)
}
bidder.setResponse(bidRequest.id, bidResponse)

and: "Flash metrics"
flushMetrics(pbsServiceWithAmxBidder)

when: "PBS processes auction request"
def response = pbsServiceWithAmxBidder.sendAuctionRequest(bidRequest)

then: "Bid response should contain seat"
assert response.seatbid.seat.sort() == [AMX].sort()
Comment thread
osulzhenko marked this conversation as resolved.
Outdated

and: "Response should contain adapter code"
assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX]

and: "Response should contain bidder generic targeting"
def targeting = response.seatbid.bid.ext.prebid.targeting.flatten().collectEntries()
assert targeting["hb_pb_${AMX}"]
assert targeting["hb_size_${AMX}"]
assert targeting["hb_bidder_${AMX}"] == AMX.value

and: "Response should contain repose millis with corresponding bidder"
assert response.ext.responsetimemillis.containsKey(AMX.value)

and: "Bidder request shouldn't be called due to storedBidResponse"
assert !bidder.getBidderRequests(bidRequest.id)

and: "Response shouldn't contain warnings and error and seatNonBid"
assert !response.ext?.warnings
assert !response.ext?.errors
assert !response.ext?.seatnonbid

and: "Response shouldn't contain demand source"
assert !response.seatbid.first.bid.first.ext.prebid.meta.demandSource

and: "PBS shouldn't emit validation metrics"
def metrics = pbsServiceWithAmxBidder.sendCollectedMetricsRequest()
assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(AMX)]
}

def "PBS auction allow bidder code when imp stored request and allowed bidder code present"() {
given: "Default bid request"
def bidRequest = getBidRequestWithAmxBidderAndAlternateBidderCode().tap {
imp[0].ext.prebid.storedRequest = new PrebidStoredRequest(id: PBSUtils.randomString)
ext.prebid.alternateBidderCodes.bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [GENERIC])]
}

and: "Save storedImp into DB"
def storedImp = StoredImp.getStoredImp(bidRequest)
storedImpDao.save(storedImp)

and: "Bid response with bidder code"
def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap {
it.seatbid[0].bid[0].ext = new BidExt(bidderCode: GENERIC)
}
bidder.setResponse(bidRequest.id, bidResponse)

when: "Requesting PBS auction"
def response = pbsServiceWithAmxBidder.sendAuctionRequest(bidRequest)

then: "Bid response should contain seat"
assert response.seatbid.seat.sort() == [GENERIC].sort()

and: "Response should contain adapter code"
assert response.seatbid.bid.ext.prebid.meta.adapterCode.flatten() == [AMX]

and: "Response should contain bidder generic targeting"
def targeting = response.seatbid.bid.ext.prebid.targeting.flatten().collectEntries()
assert targeting["hb_pb_${GENERIC}"]
assert targeting["hb_size_${GENERIC}"]
assert targeting["hb_bidder_${GENERIC}"] == GENERIC.value

and: "Response should contain repose millis with corresponding bidder"
assert response.ext.responsetimemillis.containsKey(GENERIC.value)

and: "Bidder request should be called"
assert bidder.getBidderRequests(bidRequest.id)

and: "Response shouldn't contain warnings and error and seatNonBid"
assert !response.ext?.warnings
assert !response.ext?.errors
assert !response.ext?.seatnonbid

and: "Response shouldn't contain demand source"
assert !response.seatbid.first.bid.first.ext.prebid.meta.demandSource

and: "PBS shouldn't emit validation metrics"
def metrics = pbsServiceWithAmxBidder.sendCollectedMetricsRequest()
assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(GENERIC)]
assert !metrics[ADAPTER_RESPONSE_VALIDATION_METRICS.formatted(AMX)]
}

private static Account getAccountWithAlternateBidderCode(BidRequest bidRequest) {
new Account().tap {
it.uuid = bidRequest.accountId
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.prebid.server.functional.tests

import org.prebid.server.functional.model.Currency
import org.prebid.server.functional.model.bidder.Generic
import org.prebid.server.functional.model.config.AccountAuctionConfig
import org.prebid.server.functional.model.config.AccountConfig
import org.prebid.server.functional.model.config.AlternateBidderCodes
Expand Down Expand Up @@ -31,6 +32,7 @@ import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST
import static org.prebid.server.functional.model.Currency.EUR
import static org.prebid.server.functional.model.Currency.GBP
import static org.prebid.server.functional.model.Currency.USD
import static org.prebid.server.functional.model.bidder.BidderName.ALIAS
import static org.prebid.server.functional.model.bidder.BidderName.AMX
import static org.prebid.server.functional.model.bidder.BidderName.APPNEXUS
import static org.prebid.server.functional.model.bidder.BidderName.GENERIC
Expand Down Expand Up @@ -1152,6 +1154,45 @@ class BidAdjustmentSpec extends BaseSpec {
bidAdjustmentFactor << [0.9, 1.1]
}

def "PBS should prefer bid price adjustment based on media type and alternate bidder code when request has per-media-type bid adjustment factors with soft alias"() {
given: "Default bid request with bid adjustment"
def bidRequest = BidRequest.getDefaultBidRequest(SITE).tap {
ext.prebid.aliases = [(ALIAS.value): AMX]
imp[0].ext.prebid.bidder.generic = null
imp[0].ext.prebid.bidder.amx = null
imp[0].ext.prebid.bidder.alias = new Generic()
ext.prebid.tap {
bidAdjustmentFactors = new BidAdjustmentFactors().tap {
adjustments = [(GENERIC): randomDecimal]
mediaTypes = [(BANNER): [(GENERIC): bidAdjustmentFactor]]
}
alternateBidderCodes = new AlternateBidderCodes().tap {
enabled = true
bidders = [(AMX): new BidderConfig(enabled: true, allowedBidderCodes: [GENERIC])]
}
}
}

and: "Bid response with bidder code"
def bidResponse = BidResponse.getDefaultBidResponse(bidRequest, AMX).tap {
it.seatbid[0].bid[0].ext = new BidExt(bidderCode: GENERIC)
}
bidder.setResponse(bidRequest.id, bidResponse)

when: "PBS processes auction request"
def response = pbsService.sendAuctionRequest(bidRequest)

then: "Final bid price should be adjusted"
assert response?.seatbid?.first?.bid?.first?.price == bidResponse.seatbid.first.bid.first.price *
bidAdjustmentFactor

and: "Response should contain repose millis with corresponding bidder"
assert response.ext.responsetimemillis.containsKey(GENERIC.value)

where:
bidAdjustmentFactor << [0.9, 1.1]
}

private static Map<String, String> getExternalCurrencyConverterConfig() {
["auction.ad-server-currency" : DEFAULT_CURRENCY as String,
"currency-converter.external-rates.enabled" : "true",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ import java.time.Instant
import static org.prebid.server.functional.model.bidder.BidderName.OPENX
import static org.prebid.server.functional.model.bidder.BidderName.OPENX_ALIAS
import static org.prebid.server.functional.model.bidder.BidderName.WILDCARD
import static org.prebid.server.functional.model.request.auction.AuctionEnvironment.*
import static org.prebid.server.functional.model.request.auction.AuctionEnvironment.DEVICE_ORCHESTRATED
import static org.prebid.server.functional.model.request.auction.AuctionEnvironment.NOT_SUPPORTED
import static org.prebid.server.functional.model.request.auction.AuctionEnvironment.SERVER_ORCHESTRATED
import static org.prebid.server.functional.model.request.auction.AuctionEnvironment.UNKNOWN
import static org.prebid.server.functional.model.request.auction.PaaFormat.IAB
import static org.prebid.server.functional.model.request.auction.PaaFormat.ORIGINAL
import static org.prebid.server.functional.model.response.auction.ErrorType.PREBID
Expand Down Expand Up @@ -199,7 +200,7 @@ class OpenxSpec extends BaseSpec {
assert interestGroupAuctionSeller.impId == impId
assert interestGroupAuctionSeller.config
assert interestGroupAuctionSeller.ext.bidder == bidResponse.seatbid[0].seat.value
assert interestGroupAuctionSeller.ext.adapter == OPENX.value
assert interestGroupAuctionSeller.ext.adapter == bidResponse.seatbid[0].seat.value
}

def "PBS shouldn't populate fledge config when bid response didn't return fledge config"() {
Expand Down Expand Up @@ -307,7 +308,7 @@ class OpenxSpec extends BaseSpec {
assert auctionConfigs?.size() == 1
assert auctionConfigs[0].impId == impId
assert auctionConfigs[0].bidder == OPENX_ALIAS.value
assert auctionConfigs[0].adapter == OPENX.value
assert auctionConfigs[0].adapter == OPENX_ALIAS.value
assert auctionConfigs[0].config == fledgeConfig

and: "PBS response shouldn't contain igi config"
Expand Down Expand Up @@ -350,7 +351,13 @@ class OpenxSpec extends BaseSpec {
assert interestGroupAuctionSeller.impId == impId
assert interestGroupAuctionSeller.config == fledgeConfig
assert interestGroupAuctionSeller.ext.bidder == OPENX_ALIAS.value
assert interestGroupAuctionSeller.ext.adapter == OPENX.value
assert interestGroupAuctionSeller.ext.adapter == OPENX_ALIAS.value
Comment thread
osulzhenko marked this conversation as resolved.

and: "Response should contain seat"
assert response.seatbid[0].seat == OPENX_ALIAS

and: "Response should contain seat"
assert response.seatbid[0].bid[0].ext.prebid.meta.adapterCode == OPENX_ALIAS

and: "PBS response shouldn't contain igi config"
assert !response.ext?.interestGroupAuctionIntent?[0].interestGroupAuctionBuyer
Expand Down
Loading