diff --git a/adapters/adform/adform_test.go b/adapters/adform/adform_test.go index e59bd951858..63646f5f7f5 100644 --- a/adapters/adform/adform_test.go +++ b/adapters/adform/adform_test.go @@ -21,6 +21,8 @@ import ( "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/config" "github.com/prebid/prebid-server/openrtb_ext" + + "github.com/stretchr/testify/assert" ) func TestJsonSamples(t *testing.T) { @@ -597,3 +599,77 @@ func TestPriceTypeUrlParameterCreation(t *testing.T) { } } } + +// Asserts that toOpenRtbBidResponse() creates a *adapters.BidderResponse with +// the currency of the last valid []*adformBid element and the expected number of bids +func TestToOpenRtbBidResponse(t *testing.T) { + expectedBids := 3 + lastCurrency, anotherCurrency, emptyCurrency := "EUR", "USD", "" + + request := &openrtb.BidRequest{ + ID: "test-request-id", + Imp: []openrtb.Imp{ + { + ID: "banner-imp-no1", + Ext: json.RawMessage(`{"bidder1": { "mid": "32341" }}`), + Banner: &openrtb.Banner{}, + }, + { + ID: "banner-imp-no2", + Ext: json.RawMessage(`{"bidder1": { "mid": "32342" }}`), + Banner: &openrtb.Banner{}, + }, + { + ID: "banner-imp-no3", + Ext: json.RawMessage(`{"bidder1": { "mid": "32343" }}`), + Banner: &openrtb.Banner{}, + }, + { + ID: "banner-imp-no4", + Ext: json.RawMessage(`{"bidder1": { "mid": "32344" }}`), + Banner: &openrtb.Banner{}, + }, + }, + Device: &openrtb.Device{UA: "ua", IP: "ip"}, + User: &openrtb.User{BuyerUID: "buyerUID"}, + } + + testAdformBids := []*adformBid{ + { + ResponseType: "banner", + Banner: "banner-content1", + Price: 1.23, + Currency: anotherCurrency, + Width: 300, + Height: 200, + DealId: "dealId1", + CreativeId: "creativeId1", + }, + {}, + { + ResponseType: "banner", + Banner: "banner-content3", + Price: 1.24, + Currency: emptyCurrency, + Width: 300, + Height: 200, + DealId: "dealId3", + CreativeId: "creativeId3", + }, + { + ResponseType: "banner", + Banner: "banner-content4", + Price: 1.25, + Currency: lastCurrency, + Width: 300, + Height: 200, + DealId: "dealId4", + CreativeId: "creativeId4", + }, + } + + actualBidResponse := toOpenRtbBidResponse(testAdformBids, request) + + assert.Equalf(t, expectedBids, len(actualBidResponse.Bids), "bid count") + assert.Equalf(t, lastCurrency, actualBidResponse.Currency, "currency") +} diff --git a/currencies/rate_converter_test.go b/currencies/rate_converter_test.go index a1807a63ffb..f9a14895347 100644 --- a/currencies/rate_converter_test.go +++ b/currencies/rate_converter_test.go @@ -379,6 +379,7 @@ func TestRates(t *testing.T) { {from: "", to: "EUR", expectedRate: 0, hasError: true}, {from: "CNY", to: "", expectedRate: 0, hasError: true}, {from: "", to: "", expectedRate: 0, hasError: true}, + {from: "USD", to: "USD", expectedRate: 1, hasError: false}, } mockedHttpServer := httptest.NewServer(http.HandlerFunc( diff --git a/exchange/exchange.go b/exchange/exchange.go index 071b4f8d771..3d9055ca8a6 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -324,6 +324,7 @@ func (e *exchange) buildBidResponse(ctx context.Context, liveAdapters []openrtb_ if adapterBids[a] != nil && len(adapterBids[a].bids) > 0 { sb := e.makeSeatBid(adapterBids[a], a, adapterExtra, auc) seatBids = append(seatBids, *sb) + bidResponse.Cur = adapterBids[a].currency } } diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index dc4d7d980e9..31dddae4c74 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -314,51 +314,145 @@ func TestGetBidCacheInfo(t *testing.T) { assert.Equal(t, expCacheURL, cacheURL, "[TestGetBidCacheInfo] cacheId field in ext should equal \"%s\" \n", expCacheURL) } -func buildBidResponseParams(bidderName openrtb_ext.BidderName, bidRequest *openrtb.BidRequest) ([]openrtb_ext.BidderName, map[openrtb_ext.BidderName]*pbsOrtbSeatBid, json.RawMessage, map[openrtb_ext.BidderName]*seatResponseExtra) { - //liveAdapters []openrtb_ext.BidderName, - liveAdapters := []openrtb_ext.BidderName{bidderName} +func TestBidResponseCurrency(t *testing.T) { + // Init objects + cfg := &config.Configuration{Adapters: make(map[string]config.Adapter, 1)} + cfg.Adapters["appnexus"] = config.Adapter{Endpoint: "http://ib.adnxs.com"} - //adapterBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, - adapterBids := map[openrtb_ext.BidderName]*pbsOrtbSeatBid{ - bidderName: { - bids: []*pbsOrtbBid{ - { - bid: &openrtb.Bid{ - ID: "some-imp-id", - Price: 9.517803, - W: 300, - H: 250, - }, - bidType: openrtb_ext.BidTypeBanner, - bidTargets: map[string]string{ - "pricegranularity": "med", - "includewinners": "true", - "includebidderkeys": "false", - }, - }, - }, - currency: "USD", - //ext: bidRequest.Ext, - }, + handlerNoBidServer := func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(204) } + server := httptest.NewServer(http.HandlerFunc(handlerNoBidServer)) + defer server.Close() + + e := NewExchange(server.Client(), nil, cfg, pbsmetrics.NewMetrics(metrics.NewRegistry(), openrtb_ext.BidderList(), config.DisabledMetrics{}), adapters.ParseBidderInfos(cfg.Adapters, "../static/bidder-info", openrtb_ext.BidderList()), gdpr.AlwaysAllow{}, currencies.NewRateConverterDefault()).(*exchange) + + liveAdapters := make([]openrtb_ext.BidderName, 1) + liveAdapters[0] = "appnexus" + + bidRequest := &openrtb.BidRequest{ + ID: "some-request-id", + Imp: []openrtb.Imp{{ + ID: "some-impression-id", + Banner: &openrtb.Banner{Format: []openrtb.Format{{W: 300, H: 250}, {W: 300, H: 600}}}, + Ext: json.RawMessage(`{"appnexus": {"placementId": 10433394}}`), + }}, + Site: &openrtb.Site{Page: "prebid.org", Ext: json.RawMessage(`{"amp":0}`)}, + Device: &openrtb.Device{UA: "curl/7.54.0", IP: "::1"}, + AT: 1, + TMax: 500, + Ext: json.RawMessage(`{"id": "some-request-id","site": {"page": "prebid.org"},"imp": [{"id": "some-impression-id","banner": {"format": [{"w": 300,"h": 250},{"w": 300,"h": 600}]},"ext": {"appnexus": {"placementId": 10433394}}}],"tmax": 500}`), } - //resolvedRequest json.RawMessage resolvedRequest := json.RawMessage(`{"id": "some-request-id","site": {"page": "prebid.org"},"imp": [{"id": "some-impression-id","banner": {"format": [{"w": 300,"h": 250},{"w": 300,"h": 600}]},"ext": {"appnexus": {"placementId": 1}}}],"tmax": 500}`) - //adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, adapterExtra := map[openrtb_ext.BidderName]*seatResponseExtra{ - bidderName: { - ResponseTimeMillis: 5, - Errors: []openrtb_ext.ExtBidderError{ + "appnexus": {ResponseTimeMillis: 5}, + } + + var errList []error + + sampleBid := &openrtb.Bid{ + ID: "some-imp-id", + Price: 9.517803, + W: 300, + H: 250, + Ext: nil, + } + aPbsOrtbBidArr := []*pbsOrtbBid{{bid: sampleBid, bidType: openrtb_ext.BidTypeBanner}} + sampleSeatBid := []openrtb.SeatBid{ + { + Seat: "appnexus", + Bid: []openrtb.Bid{ { - Code: 999, - Message: "Post ib.adnxs.com/openrtb2?query1&query2: unsupported protocol scheme \"\"", + ID: "some-imp-id", + Price: 9.517803, + W: 300, + H: 250, + Ext: json.RawMessage(`{"prebid":{"type":"banner"}}`), + }, + }, + }, + } + emptySeatBid := []openrtb.SeatBid{} + + // Test cases + type aTest struct { + description string + adapterBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid + expectedBidResponse *openrtb.BidResponse + } + testCases := []aTest{ + { + description: "1) Adapter to bids map comes with a non-empty currency field and non-empty bid array", + adapterBids: map[openrtb_ext.BidderName]*pbsOrtbSeatBid{ + openrtb_ext.BidderName("appnexus"): { + bids: aPbsOrtbBidArr, + currency: "USD", }, }, + expectedBidResponse: &openrtb.BidResponse{ + ID: "some-request-id", + SeatBid: sampleSeatBid, + Cur: "USD", + Ext: json.RawMessage(`{"responsetimemillis":{"appnexus":5},"tmaxrequest":500} +`), + }, + }, + { + description: "2) Adapter to bids map comes with a non-empty currency field but an empty bid array", + adapterBids: map[openrtb_ext.BidderName]*pbsOrtbSeatBid{ + openrtb_ext.BidderName("appnexus"): { + bids: nil, + currency: "USD", + }, + }, + expectedBidResponse: &openrtb.BidResponse{ + ID: "some-request-id", + SeatBid: emptySeatBid, + Cur: "", + Ext: json.RawMessage(`{"responsetimemillis":{"appnexus":5},"tmaxrequest":500} +`), + }, + }, + { + description: "3) Adapter to bids map comes with an empty currency string and a non-empty bid array", + adapterBids: map[openrtb_ext.BidderName]*pbsOrtbSeatBid{ + openrtb_ext.BidderName("appnexus"): { + bids: aPbsOrtbBidArr, + currency: "", + }, + }, + expectedBidResponse: &openrtb.BidResponse{ + ID: "some-request-id", + SeatBid: sampleSeatBid, + Cur: "", + Ext: json.RawMessage(`{"responsetimemillis":{"appnexus":5},"tmaxrequest":500} +`), + }, + }, + { + description: "4) Adapter to bids map comes with an empty currency string and an empty bid array", + adapterBids: map[openrtb_ext.BidderName]*pbsOrtbSeatBid{ + openrtb_ext.BidderName("appnexus"): { + bids: nil, + currency: "", + }, + }, + expectedBidResponse: &openrtb.BidResponse{ + ID: "some-request-id", + SeatBid: emptySeatBid, + Cur: "", + Ext: json.RawMessage(`{"responsetimemillis":{"appnexus":5},"tmaxrequest":500} +`), + }, }, } - return liveAdapters, adapterBids, resolvedRequest, adapterExtra + // Run tests + for i := range testCases { + actualBidResp, err := e.buildBidResponse(context.Background(), liveAdapters, testCases[i].adapterBids, bidRequest, resolvedRequest, adapterExtra, nil, errList) + assert.NoError(t, err, fmt.Sprintf("[TEST_FAILED] e.buildBidResponse resturns error in test: %s Error message: %s \n", testCases[i].description, err)) + assert.Equalf(t, testCases[i].expectedBidResponse, actualBidResp, fmt.Sprintf("[TEST_FAILED] Objects must be equal for test: %s \n Expected: >>%s<< \n Actual: >>%s<< ", testCases[i].description, testCases[i].expectedBidResponse.Ext, actualBidResp.Ext)) + } } // TestRaceIntegration runs an integration test using all the sample params from