From ac7ac6714dc8b907f4288693526e3272473df413 Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Fri, 28 Mar 2025 17:52:09 +0200 Subject: [PATCH 1/4] Tests: Enforce Random Bid Id Feature --- .../functional/tests/AuctionSpec.groovy | 100 +++++++++++++++++- .../server/functional/util/PBSUtils.groovy | 12 +++ 2 files changed, 110 insertions(+), 2 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/AuctionSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/AuctionSpec.groovy index f9b9688d4da..2f4861627d7 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/AuctionSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/AuctionSpec.groovy @@ -42,14 +42,17 @@ import static org.prebid.server.functional.util.SystemProperties.PBS_VERSION class AuctionSpec extends BaseSpec { private static final String USER_SYNC_URL = "$networkServiceContainer.rootUri/generic-usersync" - private static final boolean CORS_SUPPORT = false + private static final Boolean CORS_SUPPORT = false private static final UserSyncInfo.Type USER_SYNC_TYPE = REDIRECT - private static final int DEFAULT_TIMEOUT = getRandomTimeout() + private static final Integer DEFAULT_TIMEOUT = getRandomTimeout() + private static final Integer MIN_BID_ID_LENGTH = 17 + private static final Integer DEFAULT_UUID_LENGTH = 36 private static final Map PBS_CONFIG = ["auction.biddertmax.max" : MAX_TIMEOUT as String, "auction.default-timeout-ms": DEFAULT_TIMEOUT as String] private static final Map GENERIC_CONFIG = [ "adapters.${GENERIC.value}.usersync.${USER_SYNC_TYPE.value}.url" : USER_SYNC_URL, "adapters.${GENERIC.value}.usersync.${USER_SYNC_TYPE.value}.support-cors": CORS_SUPPORT.toString()] + @Shared PrebidServerService prebidServerService = pbsServiceFactory.getService(PBS_CONFIG) @@ -591,4 +594,97 @@ class AuctionSpec extends BaseSpec { def bidderRequest = bidder.getBidderRequest(bidRequest.id) assert !bidderRequest?.device?.ext?.cdep } + + def "PBS should override short bid.id with random uuid when enforce-random-bid-id is enabled"() { + given: "PBS with disabled generate-bid-id" + def pbsConfig = ['auction.enforce-random-bid-id': 'true'] + def pbsService = pbsServiceFactory.getService(pbsConfig) + + and: "Default big request" + def bidRequest = BidRequest.defaultBidRequest + + and: "Default bid response" + def originalBidId = PBSUtils.getRandomString(PBSUtils.getRandomNumber(0, MIN_BID_ID_LENGTH)) + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { + seatbid.first.bid.first.id = originalBidId + } + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "Should include imp from original request" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.imp.id.sort() == bidRequest.imp.id.sort() + + and: "BidResponse should contain bid.id equals to generated pr-bid-id" + assert response.seatbid.bid.id.flatten().sort() != [originalBidId] + + and: "Bidder request should contain generated pb-bid-id" + assert PBSUtils.isUUID(response.seatbid.first.bid.first.id) + + cleanup: "Stop and remove pbs container" + pbsServiceFactory.removeContainer(pbsConfig) + } + + def "PBS shouldn't override short bid.id when enforce-random-bid-id in default or disabled"() { + given: "PBS with disabled generate-bid-id" + def pbsConfig = ["auction.enforce-random-bid-id": enforceRandomBidId] + def pbsService = pbsServiceFactory.getService(pbsConfig) + + and: "Default big request" + def bidRequest = BidRequest.defaultBidRequest + + and: "Default bid response" + def originalBidId = PBSUtils.getRandomString(PBSUtils.getRandomNumber(0, MIN_BID_ID_LENGTH)) + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { + seatbid.first.bid.first.id = originalBidId + } + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "Should include imp from original request" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.imp.id.sort() == bidRequest.imp.id.sort() + + and: "BidResponse should contain bid.id equals to generated pr-bid-id" + assert response.seatbid.bid.id.flatten().sort() == [originalBidId] + + cleanup: "Stop and remove pbs container" + pbsServiceFactory.removeContainer(pbsConfig) + + where: + enforceRandomBidId << [null, 'false'] + } + + def "PBS shouldn't override long enough bid.id with random uuid when enforce-random-bid-id is enabled"() { + given: "PBS with disabled generate-bid-id" + def pbsConfig = ['auction.enforce-random-bid-id': 'true'] + def pbsService = pbsServiceFactory.getService(pbsConfig) + + and: "Default big request" + def bidRequest = BidRequest.defaultBidRequest + + and: "Default bid response" + def originalBidId = PBSUtils.getRandomString(PBSUtils.getRandomNumber(MIN_BID_ID_LENGTH, DEFAULT_UUID_LENGTH)) + def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { + seatbid.first.bid.first.id = originalBidId + } + bidder.setResponse(bidRequest.id, bidResponse) + + when: "PBS processes auction request" + def response = pbsService.sendAuctionRequest(bidRequest) + + then: "Should include imp from original request" + def bidderRequest = bidder.getBidderRequest(bidRequest.id) + assert bidderRequest.imp.id.sort() == bidRequest.imp.id.sort() + + and: "BidResponse should contain bid.id equals to generated pr-bid-id" + assert response.seatbid.bid.id.flatten().sort() == [originalBidId] + + cleanup: "Stop and remove pbs container" + pbsServiceFactory.removeContainer(pbsConfig) + } } diff --git a/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy b/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy index c82e96268c6..e1e7750ea05 100644 --- a/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy +++ b/src/test/groovy/org/prebid/server/functional/util/PBSUtils.groovy @@ -155,4 +155,16 @@ class PBSUtils implements ObjectMapperWrapper { def version = versionParts.join('.') return (version >= minVersion && version <= maxVersion) ? version : getRandomVersion(minVersion, maxVersion) } + + static Boolean isUUID(String str) { + if (str == null) { + return false + } + try { + UUID.fromString(str) + return true + } catch (IllegalArgumentException e) { + return false + } + } } From 681a54e85afb274bcbc79fd5279373822280d696 Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Mon, 31 Mar 2025 13:47:56 +0300 Subject: [PATCH 2/4] update after review --- .../server/functional/tests/AuctionSpec.groovy | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/AuctionSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/AuctionSpec.groovy index 2f4861627d7..cefc7346a1d 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/AuctionSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/AuctionSpec.groovy @@ -596,11 +596,11 @@ class AuctionSpec extends BaseSpec { } def "PBS should override short bid.id with random uuid when enforce-random-bid-id is enabled"() { - given: "PBS with disabled generate-bid-id" + given: "PBS with enabled generate-bid-id" def pbsConfig = ['auction.enforce-random-bid-id': 'true'] def pbsService = pbsServiceFactory.getService(pbsConfig) - and: "Default big request" + and: "Default bid request" def bidRequest = BidRequest.defaultBidRequest and: "Default bid response" @@ -617,10 +617,10 @@ class AuctionSpec extends BaseSpec { def bidderRequest = bidder.getBidderRequest(bidRequest.id) assert bidderRequest.imp.id.sort() == bidRequest.imp.id.sort() - and: "BidResponse should contain bid.id equals to generated pr-bid-id" + and: "BidResponse should contain different bid.id" assert response.seatbid.bid.id.flatten().sort() != [originalBidId] - and: "Bidder request should contain generated pb-bid-id" + and: "BidResponse should contain generated UUID" assert PBSUtils.isUUID(response.seatbid.first.bid.first.id) cleanup: "Stop and remove pbs container" @@ -632,7 +632,7 @@ class AuctionSpec extends BaseSpec { def pbsConfig = ["auction.enforce-random-bid-id": enforceRandomBidId] def pbsService = pbsServiceFactory.getService(pbsConfig) - and: "Default big request" + and: "Default bid request" def bidRequest = BidRequest.defaultBidRequest and: "Default bid response" @@ -660,11 +660,11 @@ class AuctionSpec extends BaseSpec { } def "PBS shouldn't override long enough bid.id with random uuid when enforce-random-bid-id is enabled"() { - given: "PBS with disabled generate-bid-id" + given: "PBS with enabled generate-bid-id" def pbsConfig = ['auction.enforce-random-bid-id': 'true'] def pbsService = pbsServiceFactory.getService(pbsConfig) - and: "Default big request" + and: "Default bid request" def bidRequest = BidRequest.defaultBidRequest and: "Default bid response" From ba0956c11dfdda2f476c2ae89c0e9c752df6ac8d Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Mon, 31 Mar 2025 15:16:44 +0300 Subject: [PATCH 3/4] update after review --- .../functional/tests/AuctionSpec.groovy | 52 +++++++++++++++---- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/AuctionSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/AuctionSpec.groovy index cefc7346a1d..5d2a67dc6e6 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/AuctionSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/AuctionSpec.groovy @@ -601,15 +601,21 @@ class AuctionSpec extends BaseSpec { def pbsService = pbsServiceFactory.getService(pbsConfig) and: "Default bid request" - def bidRequest = BidRequest.defaultBidRequest + def bidRequest = BidRequest.defaultBidRequest.tap { + enableEvents() + } and: "Default bid response" - def originalBidId = PBSUtils.getRandomString(PBSUtils.getRandomNumber(0, MIN_BID_ID_LENGTH)) + def originalBidId = PBSUtils.getRandomString(PBSUtils.getRandomNumber(1, MIN_BID_ID_LENGTH - 1)) def bidResponse = BidResponse.getDefaultBidResponse(bidRequest).tap { seatbid.first.bid.first.id = originalBidId } bidder.setResponse(bidRequest.id, bidResponse) + and: "Save account in DB" + def account = new Account(uuid: bidRequest.accountId, eventsEnabled: true) + accountDao.save(account) + when: "PBS processes auction request" def response = pbsService.sendAuctionRequest(bidRequest) @@ -617,8 +623,14 @@ class AuctionSpec extends BaseSpec { def bidderRequest = bidder.getBidderRequest(bidRequest.id) assert bidderRequest.imp.id.sort() == bidRequest.imp.id.sort() + and: "Bid response should contain changed bid.id for wins event" + def bidIds = response.seatbid.bid.id.flatten() + def bidResponseEvents = response.seatbid.first.bid.first.ext.prebid.events + assert bidResponseEvents.win.contains("win&b=${bidIds.first}") + assert bidResponseEvents.imp.contains("imp&b=${bidIds.first}") + and: "BidResponse should contain different bid.id" - assert response.seatbid.bid.id.flatten().sort() != [originalBidId] + assert bidIds.sort() != [originalBidId] and: "BidResponse should contain generated UUID" assert PBSUtils.isUUID(response.seatbid.first.bid.first.id) @@ -633,7 +645,9 @@ class AuctionSpec extends BaseSpec { def pbsService = pbsServiceFactory.getService(pbsConfig) and: "Default bid request" - def bidRequest = BidRequest.defaultBidRequest + def bidRequest = BidRequest.defaultBidRequest.tap { + enableEvents() + } and: "Default bid response" def originalBidId = PBSUtils.getRandomString(PBSUtils.getRandomNumber(0, MIN_BID_ID_LENGTH)) @@ -642,6 +656,10 @@ class AuctionSpec extends BaseSpec { } bidder.setResponse(bidRequest.id, bidResponse) + and: "Save account in DB" + def account = new Account(uuid: bidRequest.accountId, eventsEnabled: true) + accountDao.save(account) + when: "PBS processes auction request" def response = pbsService.sendAuctionRequest(bidRequest) @@ -649,11 +667,16 @@ class AuctionSpec extends BaseSpec { def bidderRequest = bidder.getBidderRequest(bidRequest.id) assert bidderRequest.imp.id.sort() == bidRequest.imp.id.sort() - and: "BidResponse should contain bid.id equals to generated pr-bid-id" + and: "Bid response should contain changed bid.id for wins event" + def bidResponseEvents = response.seatbid.first.bid.first.ext.prebid.events + assert bidResponseEvents.win.contains("win&b=${originalBidId}") + assert bidResponseEvents.imp.contains("imp&b=${originalBidId}") + + and: "BidResponse should contain original bid.id" assert response.seatbid.bid.id.flatten().sort() == [originalBidId] - cleanup: "Stop and remove pbs container" - pbsServiceFactory.removeContainer(pbsConfig) +// cleanup: "Stop and remove pbs container" +// pbsServiceFactory.removeContainer(pbsConfig) where: enforceRandomBidId << [null, 'false'] @@ -665,7 +688,9 @@ class AuctionSpec extends BaseSpec { def pbsService = pbsServiceFactory.getService(pbsConfig) and: "Default bid request" - def bidRequest = BidRequest.defaultBidRequest + def bidRequest = BidRequest.defaultBidRequest.tap { + enableEvents() + } and: "Default bid response" def originalBidId = PBSUtils.getRandomString(PBSUtils.getRandomNumber(MIN_BID_ID_LENGTH, DEFAULT_UUID_LENGTH)) @@ -674,6 +699,10 @@ class AuctionSpec extends BaseSpec { } bidder.setResponse(bidRequest.id, bidResponse) + and: "Save account in DB" + def account = new Account(uuid: bidRequest.accountId, eventsEnabled: true) + accountDao.save(account) + when: "PBS processes auction request" def response = pbsService.sendAuctionRequest(bidRequest) @@ -681,7 +710,12 @@ class AuctionSpec extends BaseSpec { def bidderRequest = bidder.getBidderRequest(bidRequest.id) assert bidderRequest.imp.id.sort() == bidRequest.imp.id.sort() - and: "BidResponse should contain bid.id equals to generated pr-bid-id" + and: "Bid response should contain changed bid.id for wins event" + def bidResponseEvents = response.seatbid.first.bid.first.ext.prebid.events + assert bidResponseEvents.win.contains("win&b=${originalBidId}") + assert bidResponseEvents.imp.contains("imp&b=${originalBidId}") + + and: "BidResponse should contain original bid.id" assert response.seatbid.bid.id.flatten().sort() == [originalBidId] cleanup: "Stop and remove pbs container" From 7348499ac981f5f028f465664ca54cb14de69078 Mon Sep 17 00:00:00 2001 From: osulzhenko Date: Mon, 31 Mar 2025 15:46:35 +0300 Subject: [PATCH 4/4] update after review --- .../org/prebid/server/functional/tests/AuctionSpec.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/groovy/org/prebid/server/functional/tests/AuctionSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/AuctionSpec.groovy index 5d2a67dc6e6..e5e9649d82b 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/AuctionSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/AuctionSpec.groovy @@ -675,8 +675,8 @@ class AuctionSpec extends BaseSpec { and: "BidResponse should contain original bid.id" assert response.seatbid.bid.id.flatten().sort() == [originalBidId] -// cleanup: "Stop and remove pbs container" -// pbsServiceFactory.removeContainer(pbsConfig) + cleanup: "Stop and remove pbs container" + pbsServiceFactory.removeContainer(pbsConfig) where: enforceRandomBidId << [null, 'false']