2424import org .prebid .server .proto .openrtb .ext .ExtPrebid ;
2525import org .prebid .server .proto .openrtb .ext .request .thetradedesk .ExtImpTheTradeDesk ;
2626
27+ import java .math .BigDecimal ;
2728import java .util .Arrays ;
2829import java .util .List ;
2930import java .util .Set ;
3334import static java .util .function .UnaryOperator .identity ;
3435import static org .assertj .core .api .Assertions .assertThat ;
3536import static org .assertj .core .api .Assertions .assertThatIllegalArgumentException ;
37+ import static org .assertj .core .api .Assertions .tuple ;
3638import static org .prebid .server .proto .openrtb .ext .response .BidType .banner ;
3739import static org .prebid .server .proto .openrtb .ext .response .BidType .video ;
3840import static org .prebid .server .proto .openrtb .ext .response .BidType .xNative ;
@@ -458,7 +460,7 @@ public void makeBidsShouldReturnVideoBid() throws JsonProcessingException {
458460 }
459461
460462 @ Test
461- public void makeBidsShouldThrowErrorWhenMediaTypeIsMissing () throws JsonProcessingException {
463+ public void makeBidsShouldReturnErrorWhenMediaTypeIsMissing () throws JsonProcessingException {
462464 // given
463465 final BidderCall <BidRequest > httpCall = givenHttpCall (
464466 givenBidResponse (bidBuilder -> bidBuilder .impid ("123" )));
@@ -469,7 +471,36 @@ public void makeBidsShouldThrowErrorWhenMediaTypeIsMissing() throws JsonProcessi
469471 // then
470472 assertThat (result .getValue ()).isEmpty ();
471473 assertThat (result .getErrors ()).hasSize (1 )
472- .containsOnly (BidderError .badServerResponse ("unsupported mtype: null" ));
474+ .containsOnly (BidderError .badServerResponse ("could not define media type for impression: 123" ));
475+ }
476+
477+ @ Test
478+ public void makeBidsShouldReturnValidBidsAndErrorsForMixedMediaTypes () throws JsonProcessingException {
479+ // given
480+ final BidderCall <BidRequest > httpCall = givenHttpCall (
481+ mapper .writeValueAsString (BidResponse .builder ()
482+ .cur ("USD" )
483+ .seatbid (singletonList (SeatBid .builder ()
484+ .bid (Arrays .asList (
485+ Bid .builder ().mtype (1 ).impid ("valid1" ).build (), // valid banner
486+ Bid .builder ().mtype (3 ).impid ("invalid1" ).build (), // invalid mtype
487+ Bid .builder ().mtype (2 ).impid ("valid2" ).build (), // valid video
488+ Bid .builder ().mtype (null ).impid ("invalid2" ).build () // null mtype
489+ ))
490+ .build ()))
491+ .build ()));
492+
493+ // when
494+ final Result <List <BidderBid >> result = target .makeBids (httpCall , null );
495+
496+ // then
497+ assertThat (result .getValue ()).hasSize (2 )
498+ .extracting (bidderBid -> bidderBid .getBid ().getImpid ())
499+ .containsExactly ("valid1" , "valid2" );
500+ assertThat (result .getErrors ()).hasSize (2 )
501+ .containsExactly (
502+ BidderError .badServerResponse ("could not define media type for impression: invalid1" ),
503+ BidderError .badServerResponse ("could not define media type for impression: invalid2" ));
473504 }
474505
475506 private String givenBidResponse (UnaryOperator <Bid .BidBuilder > bidCustomizer ) throws JsonProcessingException {
@@ -509,4 +540,202 @@ private static ObjectNode impExt(String publisherId, String supplySourceId) {
509540 return mapper .valueToTree (ExtPrebid .of (null , ExtImpTheTradeDesk .of (publisherId , supplySourceId )));
510541 }
511542
543+ @ Test
544+ public void makeBidsShouldReplacePriceMacroInNurlAndAdmWithBidPrice () throws JsonProcessingException {
545+ // given
546+ final BidderCall <BidRequest > httpCall = givenHttpCall (
547+ givenBidResponse (bidBuilder -> bidBuilder
548+ .mtype (1 )
549+ .impid ("123" )
550+ .price (BigDecimal .valueOf (1.23 ))
551+ .nurl ("http://example.com/nurl?price=${AUCTION_PRICE}" )
552+ .adm ("<div>Price: ${AUCTION_PRICE}</div>" )));
553+
554+ // when
555+ final Result <List <BidderBid >> result = target .makeBids (httpCall , null );
556+
557+ // then
558+ assertThat (result .getErrors ()).isEmpty ();
559+ assertThat (result .getValue ()).hasSize (1 )
560+ .extracting (BidderBid ::getBid )
561+ .extracting (Bid ::getNurl , Bid ::getAdm , Bid ::getPrice )
562+ .containsOnly (tuple ("http://example.com/nurl?price=1.23" , "<div>Price: 1.23</div>" , BigDecimal .valueOf (1.23 )));
563+ }
564+
565+ @ Test
566+ public void makeBidsShouldReplacePriceMacroWithZeroWhenBidPriceIsNull () throws JsonProcessingException {
567+ // given
568+ final BidderCall <BidRequest > httpCall = givenHttpCall (
569+ givenBidResponse (bidBuilder -> bidBuilder
570+ .mtype (1 )
571+ .impid ("123" )
572+ .price (null )
573+ .nurl ("http://example.com/nurl?price=${AUCTION_PRICE}" )
574+ .adm ("<div>Price: ${AUCTION_PRICE}</div>" )));
575+
576+ // when
577+ final Result <List <BidderBid >> result = target .makeBids (httpCall , null );
578+
579+ // then
580+ assertThat (result .getErrors ()).isEmpty ();
581+ assertThat (result .getValue ()).hasSize (1 )
582+ .extracting (BidderBid ::getBid )
583+ .extracting (Bid ::getNurl , Bid ::getAdm )
584+ .containsOnly (tuple ("http://example.com/nurl?price=0" , "<div>Price: 0</div>" ));
585+ }
586+
587+ @ Test
588+ public void makeBidsShouldReplacePriceMacroWithZeroWhenBidPriceIsZero () throws JsonProcessingException {
589+ // given
590+ final BidderCall <BidRequest > httpCall = givenHttpCall (
591+ givenBidResponse (bidBuilder -> bidBuilder
592+ .mtype (1 )
593+ .impid ("123" )
594+ .price (BigDecimal .ZERO )
595+ .nurl ("http://example.com/nurl?price=${AUCTION_PRICE}" )
596+ .adm ("<div>Price: ${AUCTION_PRICE}</div>" )));
597+
598+ // when
599+ final Result <List <BidderBid >> result = target .makeBids (httpCall , null );
600+
601+ // then
602+ assertThat (result .getErrors ()).isEmpty ();
603+ assertThat (result .getValue ()).hasSize (1 )
604+ .extracting (BidderBid ::getBid )
605+ .extracting (Bid ::getNurl , Bid ::getAdm )
606+ .containsOnly (tuple ("http://example.com/nurl?price=0" , "<div>Price: 0</div>" ));
607+ }
608+
609+ @ Test
610+ public void makeBidsShouldReplacePriceMacroInNurlOnlyWhenAdmDoesNotContainMacro () throws JsonProcessingException {
611+ // given
612+ final BidderCall <BidRequest > httpCall = givenHttpCall (
613+ givenBidResponse (bidBuilder -> bidBuilder
614+ .mtype (1 )
615+ .impid ("123" )
616+ .price (BigDecimal .valueOf (5.67 ))
617+ .nurl ("http://example.com/nurl?price=${AUCTION_PRICE}" )
618+ .adm ("<div>No macro here</div>" )));
619+
620+ // when
621+ final Result <List <BidderBid >> result = target .makeBids (httpCall , null );
622+
623+ // then
624+ assertThat (result .getErrors ()).isEmpty ();
625+ assertThat (result .getValue ()).hasSize (1 )
626+ .extracting (BidderBid ::getBid )
627+ .extracting (Bid ::getNurl , Bid ::getAdm )
628+ .containsOnly (tuple ("http://example.com/nurl?price=5.67" , "<div>No macro here</div>" ));
629+ }
630+
631+ @ Test
632+ public void makeBidsShouldReplacePriceMacroInAdmOnlyWhenNurlDoesNotContainMacro () throws JsonProcessingException {
633+ // given
634+ final BidderCall <BidRequest > httpCall = givenHttpCall (
635+ givenBidResponse (bidBuilder -> bidBuilder
636+ .mtype (1 )
637+ .impid ("123" )
638+ .price (BigDecimal .valueOf (8.90 ))
639+ .nurl ("http://example.com/nurl" )
640+ .adm ("<div>Price: ${AUCTION_PRICE}</div>" )));
641+
642+ // when
643+ final Result <List <BidderBid >> result = target .makeBids (httpCall , null );
644+
645+ // then
646+ assertThat (result .getErrors ()).isEmpty ();
647+ assertThat (result .getValue ()).hasSize (1 )
648+ .extracting (BidderBid ::getBid )
649+ .extracting (Bid ::getNurl , Bid ::getAdm )
650+ .containsOnly (tuple ("http://example.com/nurl" , "<div>Price: 8.9</div>" ));
651+ }
652+
653+ @ Test
654+ public void makeBidsShouldNotReplacePriceMacroWhenNurlAndAdmDoNotContainMacro () throws JsonProcessingException {
655+ // given
656+ final BidderCall <BidRequest > httpCall = givenHttpCall (
657+ givenBidResponse (bidBuilder -> bidBuilder
658+ .mtype (1 )
659+ .impid ("123" )
660+ .price (BigDecimal .valueOf (12.34 ))
661+ .nurl ("http://example.com/nurl" )
662+ .adm ("<div>No macro</div>" )));
663+
664+ // when
665+ final Result <List <BidderBid >> result = target .makeBids (httpCall , null );
666+
667+ // then
668+ assertThat (result .getErrors ()).isEmpty ();
669+ assertThat (result .getValue ()).hasSize (1 )
670+ .extracting (BidderBid ::getBid )
671+ .extracting (Bid ::getNurl , Bid ::getAdm )
672+ .containsOnly (tuple ("http://example.com/nurl" , "<div>No macro</div>" ));
673+ }
674+
675+ @ Test
676+ public void makeBidsShouldHandleNullNurlAndAdm () throws JsonProcessingException {
677+ // given
678+ final BidderCall <BidRequest > httpCall = givenHttpCall (
679+ givenBidResponse (bidBuilder -> bidBuilder
680+ .mtype (1 )
681+ .impid ("123" )
682+ .price (BigDecimal .valueOf (15.00 ))
683+ .nurl (null )
684+ .adm (null )));
685+
686+ // when
687+ final Result <List <BidderBid >> result = target .makeBids (httpCall , null );
688+
689+ // then
690+ assertThat (result .getErrors ()).isEmpty ();
691+ assertThat (result .getValue ()).hasSize (1 )
692+ .extracting (BidderBid ::getBid )
693+ .extracting (Bid ::getNurl , Bid ::getAdm )
694+ .containsOnly (tuple (null , null ));
695+ }
696+
697+ @ Test
698+ public void makeBidsShouldReplaceMultiplePriceMacrosInSameField () throws JsonProcessingException {
699+ // given
700+ final BidderCall <BidRequest > httpCall = givenHttpCall (
701+ givenBidResponse (bidBuilder -> bidBuilder
702+ .mtype (1 )
703+ .impid ("123" )
704+ .price (BigDecimal .valueOf (9.99 ))
705+ .nurl ("http://example.com/nurl?price=${AUCTION_PRICE}&backup_price=${AUCTION_PRICE}" )
706+ .adm ("<div>Price: ${AUCTION_PRICE}, Fallback: ${AUCTION_PRICE}</div>" )));
707+
708+ // when
709+ final Result <List <BidderBid >> result = target .makeBids (httpCall , null );
710+
711+ // then
712+ assertThat (result .getErrors ()).isEmpty ();
713+ assertThat (result .getValue ()).hasSize (1 )
714+ .extracting (BidderBid ::getBid )
715+ .extracting (Bid ::getNurl , Bid ::getAdm )
716+ .containsOnly (tuple ("http://example.com/nurl?price=9.99&backup_price=9.99" , "<div>Price: 9.99, Fallback: 9.99</div>" ));
717+ }
718+
719+ @ Test
720+ public void makeBidsShouldHandleLargeDecimalPrices () throws JsonProcessingException {
721+ // given
722+ final BidderCall <BidRequest > httpCall = givenHttpCall (
723+ givenBidResponse (bidBuilder -> bidBuilder
724+ .mtype (1 )
725+ .impid ("123" )
726+ .price (new BigDecimal ("123456789.123456789" ))
727+ .nurl ("http://example.com/nurl?price=${AUCTION_PRICE}" )
728+ .adm ("<div>Price: ${AUCTION_PRICE}</div>" )));
729+
730+ // when
731+ final Result <List <BidderBid >> result = target .makeBids (httpCall , null );
732+
733+ // then
734+ assertThat (result .getErrors ()).isEmpty ();
735+ assertThat (result .getValue ()).hasSize (1 )
736+ .extracting (BidderBid ::getBid )
737+ .extracting (Bid ::getNurl , Bid ::getAdm )
738+ .containsOnly (tuple ("http://example.com/nurl?price=123456789.123456789" , "<div>Price: 123456789.123456789</div>" ));
739+ }
740+
512741}
0 commit comments