22
33import com .fasterxml .jackson .core .JsonProcessingException ;
44import com .fasterxml .jackson .databind .node .ObjectNode ;
5+ import java .math .BigDecimal ;
56import com .iab .openrtb .request .App ;
67import com .iab .openrtb .request .Banner ;
78import com .iab .openrtb .request .BidRequest ;
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,203 @@ private static ObjectNode impExt(String publisherId, String supplySourceId) {
509540 return mapper .valueToTree (ExtPrebid .of (null , ExtImpTheTradeDesk .of (publisherId , supplySourceId )));
510541 }
511542
543+
544+ @ Test
545+ public void makeBidsShouldReplacePriceMacroInNurlAndAdmWithBidPrice () throws JsonProcessingException {
546+ // given
547+ final BidderCall <BidRequest > httpCall = givenHttpCall (
548+ givenBidResponse (bidBuilder -> bidBuilder
549+ .mtype (1 )
550+ .impid ("123" )
551+ .price (BigDecimal .valueOf (1.23 ))
552+ .nurl ("http://example.com/nurl?price=${AUCTION_PRICE}" )
553+ .adm ("<div>Price: ${AUCTION_PRICE}</div>" )));
554+
555+ // when
556+ final Result <List <BidderBid >> result = target .makeBids (httpCall , null );
557+
558+ // then
559+ assertThat (result .getErrors ()).isEmpty ();
560+ assertThat (result .getValue ()).hasSize (1 )
561+ .extracting (BidderBid ::getBid )
562+ .extracting (Bid ::getNurl , Bid ::getAdm , Bid ::getPrice )
563+ .containsOnly (tuple ("http://example.com/nurl?price=1.23" , "<div>Price: 1.23</div>" , BigDecimal .valueOf (1.23 )));
564+ }
565+
566+ @ Test
567+ public void makeBidsShouldReplacePriceMacroWithZeroWhenBidPriceIsNull () throws JsonProcessingException {
568+ // given
569+ final BidderCall <BidRequest > httpCall = givenHttpCall (
570+ givenBidResponse (bidBuilder -> bidBuilder
571+ .mtype (1 )
572+ .impid ("123" )
573+ .price (null )
574+ .nurl ("http://example.com/nurl?price=${AUCTION_PRICE}" )
575+ .adm ("<div>Price: ${AUCTION_PRICE}</div>" )));
576+
577+ // when
578+ final Result <List <BidderBid >> result = target .makeBids (httpCall , null );
579+
580+ // then
581+ assertThat (result .getErrors ()).isEmpty ();
582+ assertThat (result .getValue ()).hasSize (1 )
583+ .extracting (BidderBid ::getBid )
584+ .extracting (Bid ::getNurl , Bid ::getAdm )
585+ .containsOnly (tuple ("http://example.com/nurl?price=0" , "<div>Price: 0</div>" ));
586+ }
587+
588+ @ Test
589+ public void makeBidsShouldReplacePriceMacroWithZeroWhenBidPriceIsZero () throws JsonProcessingException {
590+ // given
591+ final BidderCall <BidRequest > httpCall = givenHttpCall (
592+ givenBidResponse (bidBuilder -> bidBuilder
593+ .mtype (1 )
594+ .impid ("123" )
595+ .price (BigDecimal .ZERO )
596+ .nurl ("http://example.com/nurl?price=${AUCTION_PRICE}" )
597+ .adm ("<div>Price: ${AUCTION_PRICE}</div>" )));
598+
599+ // when
600+ final Result <List <BidderBid >> result = target .makeBids (httpCall , null );
601+
602+ // then
603+ assertThat (result .getErrors ()).isEmpty ();
604+ assertThat (result .getValue ()).hasSize (1 )
605+ .extracting (BidderBid ::getBid )
606+ .extracting (Bid ::getNurl , Bid ::getAdm )
607+ .containsOnly (tuple ("http://example.com/nurl?price=0" , "<div>Price: 0</div>" ));
608+ }
609+
610+ @ Test
611+ public void makeBidsShouldReplacePriceMacroInNurlOnlyWhenAdmDoesNotContainMacro () throws JsonProcessingException {
612+ // given
613+ final BidderCall <BidRequest > httpCall = givenHttpCall (
614+ givenBidResponse (bidBuilder -> bidBuilder
615+ .mtype (1 )
616+ .impid ("123" )
617+ .price (BigDecimal .valueOf (5.67 ))
618+ .nurl ("http://example.com/nurl?price=${AUCTION_PRICE}" )
619+ .adm ("<div>No macro here</div>" )));
620+
621+ // when
622+ final Result <List <BidderBid >> result = target .makeBids (httpCall , null );
623+
624+ // then
625+ assertThat (result .getErrors ()).isEmpty ();
626+ assertThat (result .getValue ()).hasSize (1 )
627+ .extracting (BidderBid ::getBid )
628+ .extracting (Bid ::getNurl , Bid ::getAdm )
629+ .containsOnly (tuple ("http://example.com/nurl?price=5.67" , "<div>No macro here</div>" ));
630+ }
631+
632+ @ Test
633+ public void makeBidsShouldReplacePriceMacroInAdmOnlyWhenNurlDoesNotContainMacro () throws JsonProcessingException {
634+ // given
635+ final BidderCall <BidRequest > httpCall = givenHttpCall (
636+ givenBidResponse (bidBuilder -> bidBuilder
637+ .mtype (1 )
638+ .impid ("123" )
639+ .price (BigDecimal .valueOf (8.90 ))
640+ .nurl ("http://example.com/nurl" )
641+ .adm ("<div>Price: ${AUCTION_PRICE}</div>" )));
642+
643+ // when
644+ final Result <List <BidderBid >> result = target .makeBids (httpCall , null );
645+
646+ // then
647+ assertThat (result .getErrors ()).isEmpty ();
648+ assertThat (result .getValue ()).hasSize (1 )
649+ .extracting (BidderBid ::getBid )
650+ .extracting (Bid ::getNurl , Bid ::getAdm )
651+ .containsOnly (tuple ("http://example.com/nurl" , "<div>Price: 8.90</div>" ));
652+ }
653+
654+ @ Test
655+ public void makeBidsShouldNotReplacePriceMacroWhenNurlAndAdmDoNotContainMacro () throws JsonProcessingException {
656+ // given
657+ final BidderCall <BidRequest > httpCall = givenHttpCall (
658+ givenBidResponse (bidBuilder -> bidBuilder
659+ .mtype (1 )
660+ .impid ("123" )
661+ .price (BigDecimal .valueOf (12.34 ))
662+ .nurl ("http://example.com/nurl" )
663+ .adm ("<div>No macro</div>" )));
664+
665+ // when
666+ final Result <List <BidderBid >> result = target .makeBids (httpCall , null );
667+
668+ // then
669+ assertThat (result .getErrors ()).isEmpty ();
670+ assertThat (result .getValue ()).hasSize (1 )
671+ .extracting (BidderBid ::getBid )
672+ .extracting (Bid ::getNurl , Bid ::getAdm )
673+ .containsOnly (tuple ("http://example.com/nurl" , "<div>No macro</div>" ));
674+ }
675+
676+ @ Test
677+ public void makeBidsShouldHandleNullNurlAndAdm () throws JsonProcessingException {
678+ // given
679+ final BidderCall <BidRequest > httpCall = givenHttpCall (
680+ givenBidResponse (bidBuilder -> bidBuilder
681+ .mtype (1 )
682+ .impid ("123" )
683+ .price (BigDecimal .valueOf (15.00 ))
684+ .nurl (null )
685+ .adm (null )));
686+
687+ // when
688+ final Result <List <BidderBid >> result = target .makeBids (httpCall , null );
689+
690+ // then
691+ assertThat (result .getErrors ()).isEmpty ();
692+ assertThat (result .getValue ()).hasSize (1 )
693+ .extracting (BidderBid ::getBid )
694+ .extracting (Bid ::getNurl , Bid ::getAdm )
695+ .containsOnly (tuple (null , null ));
696+ }
697+
698+ @ Test
699+ public void makeBidsShouldReplaceMultiplePriceMacrosInSameField () throws JsonProcessingException {
700+ // given
701+ final BidderCall <BidRequest > httpCall = givenHttpCall (
702+ givenBidResponse (bidBuilder -> bidBuilder
703+ .mtype (1 )
704+ .impid ("123" )
705+ .price (BigDecimal .valueOf (9.99 ))
706+ .nurl ("http://example.com/nurl?price=${AUCTION_PRICE}&backup_price=${AUCTION_PRICE}" )
707+ .adm ("<div>Price: ${AUCTION_PRICE}, Fallback: ${AUCTION_PRICE}</div>" )));
708+
709+ // when
710+ final Result <List <BidderBid >> result = target .makeBids (httpCall , null );
711+
712+ // then
713+ assertThat (result .getErrors ()).isEmpty ();
714+ assertThat (result .getValue ()).hasSize (1 )
715+ .extracting (BidderBid ::getBid )
716+ .extracting (Bid ::getNurl , Bid ::getAdm )
717+ .containsOnly (tuple ("http://example.com/nurl?price=9.99&backup_price=9.99" , "<div>Price: 9.99, Fallback: 9.99</div>" ));
718+ }
719+
720+ @ Test
721+ public void makeBidsShouldHandleLargeDecimalPrices () throws JsonProcessingException {
722+ // given
723+ final BidderCall <BidRequest > httpCall = givenHttpCall (
724+ givenBidResponse (bidBuilder -> bidBuilder
725+ .mtype (1 )
726+ .impid ("123" )
727+ .price (new BigDecimal ("123456789.123456789" ))
728+ .nurl ("http://example.com/nurl?price=${AUCTION_PRICE}" )
729+ .adm ("<div>Price: ${AUCTION_PRICE}</div>" )));
730+
731+ // when
732+ final Result <List <BidderBid >> result = target .makeBids (httpCall , null );
733+
734+ // then
735+ assertThat (result .getErrors ()).isEmpty ();
736+ assertThat (result .getValue ()).hasSize (1 )
737+ .extracting (BidderBid ::getBid )
738+ .extracting (Bid ::getNurl , Bid ::getAdm )
739+ .containsOnly (tuple ("http://example.com/nurl?price=123456789.123456789" , "<div>Price: 123456789.123456789</div>" ));
740+ }
741+
512742}
0 commit comments