github.com/prebid/prebid-server@v0.275.0/exchange/bidder_validate_bids_test.go (about)

     1  package exchange
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/prebid/openrtb/v19/openrtb2"
     8  	"github.com/prebid/prebid-server/adapters"
     9  	"github.com/prebid/prebid-server/currency"
    10  	"github.com/prebid/prebid-server/exchange/entities"
    11  	"github.com/prebid/prebid-server/experiment/adscert"
    12  	"github.com/prebid/prebid-server/hooks/hookexecution"
    13  	"github.com/prebid/prebid-server/openrtb_ext"
    14  	"github.com/stretchr/testify/assert"
    15  )
    16  
    17  func TestAllValidBids(t *testing.T) {
    18  	var bidder AdaptedBidder = addValidatedBidderMiddleware(&mockAdaptedBidder{
    19  		bidResponse: []*entities.PbsOrtbSeatBid{{
    20  			Bids: []*entities.PbsOrtbBid{
    21  				{
    22  					Bid: &openrtb2.Bid{
    23  						ID:    "one-bid",
    24  						ImpID: "thisImp",
    25  						Price: 0.45,
    26  						CrID:  "thisCreative",
    27  					},
    28  				},
    29  				{
    30  					Bid: &openrtb2.Bid{
    31  						ID:    "thatBid",
    32  						ImpID: "thatImp",
    33  						Price: 0.40,
    34  						CrID:  "thatCreative",
    35  					},
    36  				},
    37  				{
    38  					Bid: &openrtb2.Bid{
    39  						ID:    "123",
    40  						ImpID: "456",
    41  						Price: 0.44,
    42  						CrID:  "789",
    43  					},
    44  				},
    45  				{
    46  					Bid: &openrtb2.Bid{
    47  						ID:     "zeroPriceBid",
    48  						ImpID:  "444",
    49  						Price:  0.00,
    50  						CrID:   "555",
    51  						DealID: "777",
    52  					},
    53  				},
    54  			},
    55  		},
    56  		}})
    57  	bidderReq := BidderRequest{
    58  		BidRequest: &openrtb2.BidRequest{},
    59  		BidderName: openrtb_ext.BidderAppnexus,
    60  	}
    61  	bidAdjustments := map[string]float64{string(openrtb_ext.BidderAppnexus): 1.0}
    62  	bidReqOptions := bidRequestOptions{
    63  		accountDebugAllowed: true,
    64  		headerDebugAllowed:  false,
    65  		addCallSignHeader:   false,
    66  		bidAdjustments:      bidAdjustments,
    67  	}
    68  	seatBids, _, errs := bidder.requestBid(context.Background(), bidderReq, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, &adscert.NilSigner{}, bidReqOptions, openrtb_ext.ExtAlternateBidderCodes{}, &hookexecution.EmptyHookExecutor{}, nil)
    69  	assert.Len(t, seatBids, 1)
    70  	assert.Len(t, seatBids[0].Bids, 4)
    71  	assert.Len(t, errs, 0)
    72  }
    73  
    74  func TestAllBadBids(t *testing.T) {
    75  	bidder := addValidatedBidderMiddleware(&mockAdaptedBidder{
    76  		bidResponse: []*entities.PbsOrtbSeatBid{{
    77  			Bids: []*entities.PbsOrtbBid{
    78  				{
    79  					Bid: &openrtb2.Bid{
    80  						ID:    "one-bid",
    81  						Price: 0.45,
    82  						CrID:  "thisCreative",
    83  					},
    84  				},
    85  				{
    86  					Bid: &openrtb2.Bid{
    87  						ID:    "thatBid",
    88  						ImpID: "thatImp",
    89  						CrID:  "thatCreative",
    90  					},
    91  				},
    92  				{
    93  					Bid: &openrtb2.Bid{
    94  						ID:    "123",
    95  						ImpID: "456",
    96  						Price: 0.44,
    97  					},
    98  				},
    99  				{
   100  					Bid: &openrtb2.Bid{
   101  						ImpID: "456",
   102  						Price: 0.44,
   103  						CrID:  "blah",
   104  					},
   105  				},
   106  				{
   107  					Bid: &openrtb2.Bid{
   108  						ID:     "zeroPriceBidNoDeal",
   109  						ImpID:  "444",
   110  						Price:  0.00,
   111  						CrID:   "555",
   112  						DealID: "",
   113  					},
   114  				},
   115  				{
   116  					Bid: &openrtb2.Bid{
   117  						ID:    "negativePrice",
   118  						ImpID: "999",
   119  						Price: -0.10,
   120  						CrID:  "888",
   121  					},
   122  				},
   123  				{},
   124  			},
   125  		},
   126  		}})
   127  	bidderReq := BidderRequest{
   128  		BidRequest: &openrtb2.BidRequest{},
   129  		BidderName: openrtb_ext.BidderAppnexus,
   130  	}
   131  	bidAdjustments := map[string]float64{string(openrtb_ext.BidderAppnexus): 1.0}
   132  	bidReqOptions := bidRequestOptions{
   133  		accountDebugAllowed: true,
   134  		headerDebugAllowed:  false,
   135  		addCallSignHeader:   false,
   136  		bidAdjustments:      bidAdjustments,
   137  	}
   138  	seatBids, _, errs := bidder.requestBid(context.Background(), bidderReq, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, &adscert.NilSigner{}, bidReqOptions, openrtb_ext.ExtAlternateBidderCodes{}, &hookexecution.EmptyHookExecutor{}, nil)
   139  	assert.Len(t, seatBids, 1)
   140  	assert.Len(t, seatBids[0].Bids, 0)
   141  	assert.Len(t, errs, 7)
   142  }
   143  
   144  func TestMixedBids(t *testing.T) {
   145  	bidder := addValidatedBidderMiddleware(&mockAdaptedBidder{
   146  		bidResponse: []*entities.PbsOrtbSeatBid{{
   147  			Bids: []*entities.PbsOrtbBid{
   148  				{
   149  					Bid: &openrtb2.Bid{
   150  						ID:    "one-bid",
   151  						ImpID: "thisImp",
   152  						Price: 0.45,
   153  						CrID:  "thisCreative",
   154  					},
   155  				},
   156  				{
   157  					Bid: &openrtb2.Bid{
   158  						ID:    "thatBid",
   159  						ImpID: "thatImp",
   160  						CrID:  "thatCreative",
   161  					},
   162  				},
   163  				{
   164  					Bid: &openrtb2.Bid{
   165  						ID:    "123",
   166  						ImpID: "456",
   167  						Price: 0.44,
   168  						CrID:  "789",
   169  					},
   170  				},
   171  				{
   172  					Bid: &openrtb2.Bid{
   173  						ImpID: "456",
   174  						Price: 0.44,
   175  						CrID:  "blah",
   176  					},
   177  				},
   178  				{
   179  					Bid: &openrtb2.Bid{
   180  						ID:     "zeroPriceBid",
   181  						ImpID:  "444",
   182  						Price:  0.00,
   183  						CrID:   "555",
   184  						DealID: "777",
   185  					},
   186  				},
   187  				{
   188  					Bid: &openrtb2.Bid{
   189  						ID:     "zeroPriceBidNoDeal",
   190  						ImpID:  "444",
   191  						Price:  0.00,
   192  						CrID:   "555",
   193  						DealID: "",
   194  					},
   195  				},
   196  				{
   197  					Bid: &openrtb2.Bid{
   198  						ID:    "negativePrice",
   199  						ImpID: "999",
   200  						Price: -0.10,
   201  						CrID:  "888",
   202  					},
   203  				},
   204  				{},
   205  			},
   206  		},
   207  		}})
   208  	bidderReq := BidderRequest{
   209  		BidRequest: &openrtb2.BidRequest{},
   210  		BidderName: openrtb_ext.BidderAppnexus,
   211  	}
   212  	bidAdjustments := map[string]float64{string(openrtb_ext.BidderAppnexus): 1.0}
   213  	bidReqOptions := bidRequestOptions{
   214  		accountDebugAllowed: true,
   215  		headerDebugAllowed:  false,
   216  		addCallSignHeader:   false,
   217  		bidAdjustments:      bidAdjustments,
   218  	}
   219  	seatBids, _, errs := bidder.requestBid(context.Background(), bidderReq, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, &adscert.NilSigner{}, bidReqOptions, openrtb_ext.ExtAlternateBidderCodes{}, &hookexecution.EmptyHookExecutor{}, nil)
   220  	assert.Len(t, seatBids, 1)
   221  	assert.Len(t, seatBids[0].Bids, 3)
   222  	assert.Len(t, errs, 5)
   223  }
   224  
   225  func TestCurrencyBids(t *testing.T) {
   226  	currencyTestCases := []struct {
   227  		brqCur           []string
   228  		brpCur           string
   229  		defaultCur       string
   230  		expectedValidBid bool
   231  	}{
   232  		// Case bid request and bid response don't specify any currencies.
   233  		// Expected to be valid since both bid request / response will be overridden with default currency (USD).
   234  		{
   235  			brqCur:           []string{},
   236  			brpCur:           "",
   237  			expectedValidBid: true,
   238  		},
   239  		// Case bid request specifies a currency (default one) but bid response doesn't.
   240  		// Expected to be valid since bid response will be overridden with default currency (USD).
   241  		{
   242  			brqCur:           []string{"USD"},
   243  			brpCur:           "",
   244  			expectedValidBid: true,
   245  		},
   246  		// Case bid request specifies more than 1 currency (default one and another one) but bid response doesn't.
   247  		// Expected to be valid since bid response will be overridden with default currency (USD).
   248  		{
   249  			brqCur:           []string{"USD", "EUR"},
   250  			brpCur:           "",
   251  			expectedValidBid: true,
   252  		},
   253  		// Case bid request specifies more than 1 currency (default one and another one) and bid response specifies default currency (USD).
   254  		// Expected to be valid.
   255  		{
   256  			brqCur:           []string{"USD", "EUR"},
   257  			brpCur:           "USD",
   258  			expectedValidBid: true,
   259  		},
   260  		// Case bid request specifies more than 1 currency (default one and another one) and bid response specifies the second currency allowed (not USD).
   261  		// Expected to be valid.
   262  		{
   263  			brqCur:           []string{"USD", "EUR"},
   264  			brpCur:           "EUR",
   265  			expectedValidBid: true,
   266  		},
   267  		// Case bid request specifies only 1 currency which is not the default one.
   268  		// Bid response doesn't specify any currency.
   269  		// Expected to be invalid.
   270  		{
   271  			brqCur:           []string{"JPY"},
   272  			brpCur:           "",
   273  			expectedValidBid: false,
   274  		},
   275  		// Case bid request doesn't specify any currencies.
   276  		// Bid response specifies a currency which is not the default one.
   277  		// Expected to be invalid.
   278  		{
   279  			brqCur:           []string{},
   280  			brpCur:           "JPY",
   281  			expectedValidBid: false,
   282  		},
   283  		// Case bid request specifies a currency.
   284  		// Bid response specifies a currency which is not the one specified in bid request.
   285  		// Expected to be invalid.
   286  		{
   287  			brqCur:           []string{"USD"},
   288  			brpCur:           "EUR",
   289  			expectedValidBid: false,
   290  		},
   291  		// Case bid request specifies several currencies.
   292  		// Bid response specifies a currency which is not the one specified in bid request.
   293  		// Expected to be invalid.
   294  		{
   295  			brqCur:           []string{"USD", "EUR"},
   296  			brpCur:           "JPY",
   297  			expectedValidBid: false,
   298  		},
   299  	}
   300  
   301  	for _, tc := range currencyTestCases {
   302  		bids := []*entities.PbsOrtbBid{
   303  			{
   304  				Bid: &openrtb2.Bid{
   305  					ID:    "one-bid",
   306  					ImpID: "thisImp",
   307  					Price: 0.45,
   308  					CrID:  "thisCreative",
   309  				},
   310  			},
   311  			{
   312  				Bid: &openrtb2.Bid{
   313  					ID:    "thatBid",
   314  					ImpID: "thatImp",
   315  					Price: 0.44,
   316  					CrID:  "thatCreative",
   317  				},
   318  			},
   319  		}
   320  		bidder := addValidatedBidderMiddleware(&mockAdaptedBidder{
   321  			bidResponse: []*entities.PbsOrtbSeatBid{{
   322  				Currency: tc.brpCur,
   323  				Bids:     bids,
   324  			},
   325  			}})
   326  
   327  		expectedValidBids := len(bids)
   328  		expectedErrs := 0
   329  
   330  		if tc.expectedValidBid != true {
   331  			// If currency mistmatch, we should have one error
   332  			expectedErrs = 1
   333  			expectedValidBids = 0
   334  		}
   335  
   336  		request := &openrtb2.BidRequest{
   337  			Cur: tc.brqCur,
   338  		}
   339  		bidderRequest := BidderRequest{BidRequest: request, BidderName: openrtb_ext.BidderAppnexus}
   340  
   341  		bidAdjustments := map[string]float64{string(openrtb_ext.BidderAppnexus): 1.0}
   342  		bidReqOptions := bidRequestOptions{
   343  			accountDebugAllowed: true,
   344  			headerDebugAllowed:  false,
   345  			addCallSignHeader:   false,
   346  			bidAdjustments:      bidAdjustments,
   347  		}
   348  		seatBids, _, errs := bidder.requestBid(context.Background(), bidderRequest, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, &adscert.NilSigner{}, bidReqOptions, openrtb_ext.ExtAlternateBidderCodes{}, &hookexecution.EmptyHookExecutor{}, nil)
   349  		assert.Len(t, seatBids, 1)
   350  		assert.Len(t, seatBids[0].Bids, expectedValidBids)
   351  		assert.Len(t, errs, expectedErrs)
   352  	}
   353  }
   354  
   355  type mockAdaptedBidder struct {
   356  	bidResponse   []*entities.PbsOrtbSeatBid
   357  	extraRespInfo extraBidderRespInfo
   358  	errorResponse []error
   359  }
   360  
   361  func (b *mockAdaptedBidder) requestBid(ctx context.Context, bidderRequest BidderRequest, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, adsCertSigner adscert.Signer, bidRequestMetadata bidRequestOptions, alternateBidderCodes openrtb_ext.ExtAlternateBidderCodes, executor hookexecution.StageExecutor, ruleToAdjustments openrtb_ext.AdjustmentsByDealID) ([]*entities.PbsOrtbSeatBid, extraBidderRespInfo, []error) {
   362  	return b.bidResponse, b.extraRespInfo, b.errorResponse
   363  }