github.com/prebid/prebid-server/v2@v2.18.0/adapters/rubicon/rubicon_test.go (about)

     1  package rubicon
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"net/http"
     8  	"strconv"
     9  	"testing"
    10  
    11  	"github.com/prebid/prebid-server/v2/adapters"
    12  	"github.com/prebid/prebid-server/v2/adapters/adapterstest"
    13  	"github.com/prebid/prebid-server/v2/config"
    14  	"github.com/prebid/prebid-server/v2/errortypes"
    15  	"github.com/prebid/prebid-server/v2/openrtb_ext"
    16  	"github.com/prebid/prebid-server/v2/util/ptrutil"
    17  
    18  	"github.com/buger/jsonparser"
    19  	"github.com/prebid/openrtb/v20/adcom1"
    20  	"github.com/prebid/openrtb/v20/openrtb2"
    21  	"github.com/stretchr/testify/assert"
    22  	"github.com/stretchr/testify/mock"
    23  )
    24  
    25  type rubiAppendTrackerUrlTestScenario struct {
    26  	source   string
    27  	tracker  string
    28  	expected string
    29  }
    30  
    31  type rubiPopulateFpdAttributesScenario struct {
    32  	source json.RawMessage
    33  	target map[string]interface{}
    34  	result map[string]interface{}
    35  }
    36  
    37  type rubiSetNetworkIdTestScenario struct {
    38  	bidExt            *openrtb_ext.ExtBidPrebid
    39  	buyer             string
    40  	expectedNetworkId int64
    41  	isNetworkIdSet    bool
    42  }
    43  
    44  type rubiBidInfo struct {
    45  	domain        string
    46  	page          string
    47  	deviceIP      string
    48  	deviceUA      string
    49  	buyerUID      string
    50  	devicePxRatio float64
    51  }
    52  
    53  var rubidata rubiBidInfo
    54  
    55  func TestAppendTracker(t *testing.T) {
    56  	testScenarios := []rubiAppendTrackerUrlTestScenario{
    57  		{
    58  			source:   "http://test.url/",
    59  			tracker:  "prebid",
    60  			expected: "http://test.url/?tk_xint=prebid",
    61  		},
    62  		{
    63  			source:   "http://test.url/?hello=true",
    64  			tracker:  "prebid",
    65  			expected: "http://test.url/?hello=true&tk_xint=prebid",
    66  		},
    67  	}
    68  
    69  	for _, scenario := range testScenarios {
    70  		res := appendTrackerToUrl(scenario.source, scenario.tracker)
    71  		assert.Equal(t, scenario.expected, res, "Failed to convert '%s' to '%s'", res, scenario.expected)
    72  	}
    73  }
    74  
    75  func TestSetImpNative(t *testing.T) {
    76  	testScenarios := []struct {
    77  		request       string
    78  		impNative     map[string]interface{}
    79  		expectedError error
    80  	}{
    81  		{
    82  			request:       "{}",
    83  			impNative:     map[string]interface{}{"somekey": "someValue"},
    84  			expectedError: fmt.Errorf("unable to find imp in json data"),
    85  		},
    86  		{
    87  			request:       "{\"imp\":[]}",
    88  			impNative:     map[string]interface{}{"somekey": "someValue"},
    89  			expectedError: fmt.Errorf("unable to find imp[0] in json data"),
    90  		},
    91  		{
    92  			request:       "{\"imp\":[{}]}",
    93  			impNative:     map[string]interface{}{"somekey": "someValue"},
    94  			expectedError: fmt.Errorf("unable to find imp[0].native in json data"),
    95  		},
    96  	}
    97  	for _, scenario := range testScenarios {
    98  		_, err := setImpNative([]byte(scenario.request), scenario.impNative)
    99  		assert.Equal(t, scenario.expectedError, err)
   100  	}
   101  }
   102  
   103  func TestResolveNativeObject(t *testing.T) {
   104  	testScenarios := []struct {
   105  		nativeObject  openrtb2.Native
   106  		target        map[string]interface{}
   107  		expectedError error
   108  	}{
   109  		{
   110  			nativeObject:  openrtb2.Native{Ver: "1.0", Request: "{\"eventtrackers\": \"someWrongValue\"}"},
   111  			target:        map[string]interface{}{},
   112  			expectedError: nil,
   113  		},
   114  		{
   115  			nativeObject:  openrtb2.Native{Ver: "1.1", Request: "{\"eventtrackers\": \"someWrongValue\"}"},
   116  			target:        map[string]interface{}{},
   117  			expectedError: nil,
   118  		},
   119  		{
   120  			nativeObject:  openrtb2.Native{Ver: "1", Request: "{\"eventtrackers\": \"someWrongValue\"}"},
   121  			target:        map[string]interface{}{},
   122  			expectedError: fmt.Errorf("Eventtrackers are not present or not of array type"),
   123  		},
   124  		{
   125  			nativeObject:  openrtb2.Native{Ver: "1", Request: "{\"eventtrackers\": [], \"context\": \"someWrongValue\"}"},
   126  			target:        map[string]interface{}{},
   127  			expectedError: fmt.Errorf("Context is not of int type"),
   128  		},
   129  		{
   130  			nativeObject:  openrtb2.Native{Ver: "1", Request: "{\"eventtrackers\": [], \"plcmttype\": 2}"},
   131  			target:        map[string]interface{}{},
   132  			expectedError: nil,
   133  		},
   134  		{
   135  			nativeObject:  openrtb2.Native{Ver: "1", Request: "{\"eventtrackers\": [], \"context\": 1}"},
   136  			target:        map[string]interface{}{},
   137  			expectedError: fmt.Errorf("Plcmttype is not present or not of int type"),
   138  		},
   139  	}
   140  	for _, scenario := range testScenarios {
   141  		_, err := resolveNativeObject(&scenario.nativeObject, scenario.target)
   142  		assert.Equal(t, scenario.expectedError, err)
   143  	}
   144  }
   145  
   146  func TestResolveVideoSizeId(t *testing.T) {
   147  	testScenarios := []struct {
   148  		placement   adcom1.VideoPlacementSubtype
   149  		instl       int8
   150  		impId       string
   151  		expected    int
   152  		expectedErr error
   153  	}{
   154  		{
   155  			placement:   1,
   156  			instl:       1,
   157  			impId:       "impId",
   158  			expected:    201,
   159  			expectedErr: nil,
   160  		},
   161  		{
   162  			placement:   3,
   163  			instl:       1,
   164  			impId:       "impId",
   165  			expected:    203,
   166  			expectedErr: nil,
   167  		},
   168  		{
   169  			placement:   4,
   170  			instl:       1,
   171  			impId:       "impId",
   172  			expected:    202,
   173  			expectedErr: nil,
   174  		},
   175  		{
   176  			placement: 4,
   177  			instl:     3,
   178  			impId:     "impId",
   179  			expectedErr: &errortypes.BadInput{
   180  				Message: "video.size_id can not be resolved in impression with id : impId",
   181  			},
   182  		},
   183  	}
   184  
   185  	for _, scenario := range testScenarios {
   186  		res, err := resolveVideoSizeId(scenario.placement, scenario.instl, scenario.impId)
   187  		assert.Equal(t, scenario.expected, res)
   188  		assert.Equal(t, scenario.expectedErr, err)
   189  	}
   190  }
   191  
   192  func TestOpenRTBRequestWithDifferentBidFloorAttributes(t *testing.T) {
   193  	testScenarios := []struct {
   194  		bidFloor         float64
   195  		bidFloorCur      string
   196  		setMock          func(m *mock.Mock)
   197  		expectedBidFloor float64
   198  		expectedBidCur   string
   199  		expectedErrors   []error
   200  	}{
   201  		{
   202  			bidFloor:         1,
   203  			bidFloorCur:      "WRONG",
   204  			setMock:          func(m *mock.Mock) { m.On("GetRate", "WRONG", "USD").Return(2.5, errors.New("some error")) },
   205  			expectedBidFloor: 0,
   206  			expectedBidCur:   "",
   207  			expectedErrors: []error{
   208  				&errortypes.BadInput{Message: "Unable to convert provided bid floor currency from WRONG to USD"},
   209  			},
   210  		},
   211  		{
   212  			bidFloor:         1,
   213  			bidFloorCur:      "USD",
   214  			setMock:          func(m *mock.Mock) {},
   215  			expectedBidFloor: 1,
   216  			expectedBidCur:   "USD",
   217  			expectedErrors:   nil,
   218  		},
   219  		{
   220  			bidFloor:         1,
   221  			bidFloorCur:      "EUR",
   222  			setMock:          func(m *mock.Mock) { m.On("GetRate", "EUR", "USD").Return(1.2, nil) },
   223  			expectedBidFloor: 1.2,
   224  			expectedBidCur:   "USD",
   225  			expectedErrors:   nil,
   226  		},
   227  		{
   228  			bidFloor:         0,
   229  			bidFloorCur:      "",
   230  			setMock:          func(m *mock.Mock) {},
   231  			expectedBidFloor: 0,
   232  			expectedBidCur:   "",
   233  			expectedErrors:   nil,
   234  		},
   235  		{
   236  			bidFloor:         -1,
   237  			bidFloorCur:      "CZK",
   238  			setMock:          func(m *mock.Mock) {},
   239  			expectedBidFloor: -1,
   240  			expectedBidCur:   "CZK",
   241  			expectedErrors:   nil,
   242  		},
   243  	}
   244  
   245  	for _, scenario := range testScenarios {
   246  		mockConversions := &mockCurrencyConversion{}
   247  		scenario.setMock(&mockConversions.Mock)
   248  
   249  		extraRequestInfo := adapters.ExtraRequestInfo{
   250  			CurrencyConversions: mockConversions,
   251  		}
   252  
   253  		bidder := new(RubiconAdapter)
   254  
   255  		request := &openrtb2.BidRequest{
   256  			ID: "test-request-id",
   257  			Imp: []openrtb2.Imp{{
   258  				ID:          "test-imp-id",
   259  				BidFloorCur: scenario.bidFloorCur,
   260  				BidFloor:    scenario.bidFloor,
   261  				Banner: &openrtb2.Banner{
   262  					Format: []openrtb2.Format{
   263  						{W: 300, H: 250},
   264  					},
   265  				},
   266  				Ext: json.RawMessage(`{"bidder": {
   267  										"zoneId": 8394,
   268  										"siteId": 283282,
   269  										"accountId": 7891
   270                                        }}`),
   271  			}},
   272  			App: &openrtb2.App{
   273  				ID:   "com.test",
   274  				Name: "testApp",
   275  			},
   276  		}
   277  
   278  		reqs, errs := bidder.MakeRequests(request, &extraRequestInfo)
   279  
   280  		mockConversions.AssertExpectations(t)
   281  
   282  		if scenario.expectedErrors == nil {
   283  			rubiconReq := &openrtb2.BidRequest{}
   284  			if err := json.Unmarshal(reqs[0].Body, rubiconReq); err != nil {
   285  				t.Fatalf("Unexpected error while decoding request: %s", err)
   286  			}
   287  			assert.Equal(t, scenario.expectedBidFloor, rubiconReq.Imp[0].BidFloor)
   288  			assert.Equal(t, scenario.expectedBidCur, rubiconReq.Imp[0].BidFloorCur)
   289  		} else {
   290  			assert.Equal(t, scenario.expectedErrors, errs)
   291  		}
   292  	}
   293  }
   294  
   295  type mockCurrencyConversion struct {
   296  	mock.Mock
   297  }
   298  
   299  func (m *mockCurrencyConversion) GetRate(from string, to string) (float64, error) {
   300  	args := m.Called(from, to)
   301  	return args.Get(0).(float64), args.Error(1)
   302  }
   303  
   304  func (m *mockCurrencyConversion) GetRates() *map[string]map[string]float64 {
   305  	args := m.Called()
   306  	return args.Get(0).(*map[string]map[string]float64)
   307  }
   308  
   309  func TestOpenRTBRequest(t *testing.T) {
   310  	bidder := new(RubiconAdapter)
   311  
   312  	rubidata = rubiBidInfo{
   313  		domain:        "nytimes.com",
   314  		page:          "https://www.nytimes.com/2017/05/04/movies/guardians-of-the-galaxy-2-review-chris-pratt.html?hpw&rref=movies&action=click&pgtype=Homepage&module=well-region&region=bottom-well&WT.nav=bottom-well&_r=0",
   315  		deviceIP:      "25.91.96.36",
   316  		deviceUA:      "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30",
   317  		buyerUID:      "need-an-actual-rp-id",
   318  		devicePxRatio: 4.0,
   319  	}
   320  
   321  	request := &openrtb2.BidRequest{
   322  		ID: "test-request-id",
   323  		Imp: []openrtb2.Imp{{
   324  			ID: "test-imp-banner-id",
   325  			Banner: &openrtb2.Banner{
   326  				Format: []openrtb2.Format{
   327  					{W: 300, H: 250},
   328  					{W: 300, H: 600},
   329  				},
   330  			},
   331  			Ext: json.RawMessage(`{"bidder": {
   332  				"zoneId": 8394,
   333  				"siteId": 283282,
   334  				"accountId": 7891,
   335  				"inventory": {"key1" : "val1"},
   336  				"visitor": {"key2" : "val2"}
   337  			}}`),
   338  		}, {
   339  			ID: "test-imp-video-id",
   340  			Video: &openrtb2.Video{
   341  				W:           ptrutil.ToPtr[int64](640),
   342  				H:           ptrutil.ToPtr[int64](360),
   343  				MIMEs:       []string{"video/mp4"},
   344  				MinDuration: 15,
   345  				MaxDuration: 30,
   346  			},
   347  			Ext: json.RawMessage(`{"bidder": {
   348  				"zoneId": 7780,
   349  				"siteId": 283282,
   350  				"accountId": 7891,
   351  				"inventory": {"key1" : "val1"},
   352  				"visitor": {"key2" : "val2"},
   353  				"video": {
   354  					"language": "en",
   355  					"playerHeight": 360,
   356  					"playerWidth": 640,
   357  					"size_id": 203,
   358  					"skip": 1,
   359  					"skipdelay": 5
   360  				}
   361  			}}`),
   362  		}},
   363  		App: &openrtb2.App{
   364  			ID:   "com.test",
   365  			Name: "testApp",
   366  		},
   367  		Device: &openrtb2.Device{
   368  			PxRatio: rubidata.devicePxRatio,
   369  		},
   370  		User: &openrtb2.User{
   371  			Ext: json.RawMessage(`{
   372  				"eids": [{
   373                      "source": "pubcid",
   374  					"uids": [{"id": "2402fc76-7b39-4f0e-bfc2-060ef7693648"}]
   375  				}]
   376              }`),
   377  		},
   378  		Ext: json.RawMessage(`{"prebid": {}}`),
   379  	}
   380  
   381  	reqs, errs := bidder.MakeRequests(request, &adapters.ExtraRequestInfo{})
   382  
   383  	assert.Empty(t, errs, "Got unexpected errors while building HTTP requests: %v", errs)
   384  	assert.Equal(t, 2, len(reqs), "Unexpected number of HTTP requests. Got %d. Expected %d", len(reqs), 2)
   385  
   386  	for i := 0; i < len(reqs); i++ {
   387  		httpReq := reqs[i]
   388  		assert.Equal(t, "POST", httpReq.Method, "Expected a POST message. Got %s", httpReq.Method)
   389  
   390  		var rpRequest openrtb2.BidRequest
   391  		if err := json.Unmarshal(httpReq.Body, &rpRequest); err != nil {
   392  			t.Fatalf("Failed to unmarshal HTTP request: %v", rpRequest)
   393  		}
   394  
   395  		assert.Equal(t, request.ID, rpRequest.ID, "Bad Request ID. Expected %s, Got %s", request.ID, rpRequest.ID)
   396  		assert.Equal(t, 1, len(rpRequest.Imp), "Wrong len(request.Imp). Expected %d, Got %d", len(request.Imp), len(rpRequest.Imp))
   397  		assert.Nil(t, rpRequest.Cur, "Wrong request.Cur. Expected nil, Got %s", rpRequest.Cur)
   398  		assert.Nil(t, rpRequest.Ext, "Wrong request.ext. Expected nil, Got %v", rpRequest.Ext)
   399  
   400  		if rpRequest.Imp[0].ID == "test-imp-banner-id" {
   401  			var rpExt rubiconBannerExt
   402  			if err := json.Unmarshal(rpRequest.Imp[0].Ext, &rpExt); err != nil {
   403  				t.Fatal("Error unmarshalling request from the outgoing request.")
   404  			}
   405  
   406  			assert.Equal(t, int64(300), rpRequest.Imp[0].Banner.Format[0].W,
   407  				"Banner width does not match. Expected %d, Got %d", 300, rpRequest.Imp[0].Banner.Format[0].W)
   408  
   409  			assert.Equal(t, int64(250), rpRequest.Imp[0].Banner.Format[0].H,
   410  				"Banner height does not match. Expected %d, Got %d", 250, rpRequest.Imp[0].Banner.Format[0].H)
   411  
   412  			assert.Equal(t, int64(300), rpRequest.Imp[0].Banner.Format[1].W,
   413  				"Banner width does not match. Expected %d, Got %d", 300, rpRequest.Imp[0].Banner.Format[1].W)
   414  
   415  			assert.Equal(t, int64(600), rpRequest.Imp[0].Banner.Format[1].H,
   416  				"Banner height does not match. Expected %d, Got %d", 600, rpRequest.Imp[0].Banner.Format[1].H)
   417  		} else if rpRequest.Imp[0].ID == "test-imp-video-id" {
   418  			var rpExt rubiconVideoExt
   419  			if err := json.Unmarshal(rpRequest.Imp[0].Ext, &rpExt); err != nil {
   420  				t.Fatal("Error unmarshalling request from the outgoing request.")
   421  			}
   422  
   423  			assert.Equal(t, ptrutil.ToPtr[int64](640), rpRequest.Imp[0].Video.W,
   424  				"Video width does not match. Expected %d, Got %d", 640, rpRequest.Imp[0].Video.W)
   425  
   426  			assert.Equal(t, ptrutil.ToPtr[int64](360), rpRequest.Imp[0].Video.H,
   427  				"Video height does not match. Expected %d, Got %d", 360, rpRequest.Imp[0].Video.H)
   428  
   429  			assert.Equal(t, "video/mp4", rpRequest.Imp[0].Video.MIMEs[0], "Video MIMEs do not match. Expected %s, Got %s", "video/mp4", rpRequest.Imp[0].Video.MIMEs[0])
   430  
   431  			assert.Equal(t, int64(15), rpRequest.Imp[0].Video.MinDuration,
   432  				"Video min duration does not match. Expected %d, Got %d", 15, rpRequest.Imp[0].Video.MinDuration)
   433  
   434  			assert.Equal(t, int64(30), rpRequest.Imp[0].Video.MaxDuration,
   435  				"Video max duration does not match. Expected %d, Got %d", 30, rpRequest.Imp[0].Video.MaxDuration)
   436  		}
   437  
   438  		assert.NotNil(t, rpRequest.User.Ext, "User.Ext object should not be nil.")
   439  
   440  		var userExt rubiconUserExt
   441  		if err := json.Unmarshal(rpRequest.User.Ext, &userExt); err != nil {
   442  			t.Fatal("Error unmarshalling request.user.ext object.")
   443  		}
   444  
   445  		assert.NotNil(t, userExt.Eids)
   446  		assert.Equal(t, 1, len(userExt.Eids), "Eids values are not as expected!")
   447  		assert.Contains(t, userExt.Eids, openrtb2.EID{Source: "pubcid", UIDs: []openrtb2.UID{{ID: "2402fc76-7b39-4f0e-bfc2-060ef7693648"}}})
   448  	}
   449  }
   450  
   451  func TestOpenRTBRequestWithBannerImpEvenIfImpHasVideo(t *testing.T) {
   452  	bidder := new(RubiconAdapter)
   453  
   454  	request := &openrtb2.BidRequest{
   455  		ID: "test-request-id",
   456  		Imp: []openrtb2.Imp{{
   457  			ID: "test-imp-id",
   458  			Banner: &openrtb2.Banner{
   459  				Format: []openrtb2.Format{
   460  					{W: 300, H: 250},
   461  				},
   462  			},
   463  			Video: &openrtb2.Video{
   464  				W:     ptrutil.ToPtr[int64](640),
   465  				H:     ptrutil.ToPtr[int64](360),
   466  				MIMEs: []string{"video/mp4"},
   467  			},
   468  			Ext: json.RawMessage(`{"bidder": {
   469  				"zoneId": 8394,
   470  				"siteId": 283282,
   471  				"accountId": 7891,
   472  				"inventory": {"key1" : "val1"},
   473  				"visitor": {"key2" : "val2"}
   474  			}}`),
   475  		}},
   476  		App: &openrtb2.App{
   477  			ID:   "com.test",
   478  			Name: "testApp",
   479  		},
   480  	}
   481  
   482  	reqs, errs := bidder.MakeRequests(request, &adapters.ExtraRequestInfo{})
   483  
   484  	assert.Empty(t, errs, "Got unexpected errors while building HTTP requests: %v", errs)
   485  
   486  	assert.Equal(t, 1, len(reqs), "Unexpected number of HTTP requests. Got %d. Expected %d", len(reqs), 1)
   487  
   488  	rubiconReq := &openrtb2.BidRequest{}
   489  	if err := json.Unmarshal(reqs[0].Body, rubiconReq); err != nil {
   490  		t.Fatalf("Unexpected error while decoding request: %s", err)
   491  	}
   492  
   493  	assert.Equal(t, 1, len(rubiconReq.Imp), "Unexpected number of request impressions. Got %d. Expected %d", len(rubiconReq.Imp), 1)
   494  
   495  	assert.Nil(t, rubiconReq.Imp[0].Video, "Unexpected video object in request impression")
   496  
   497  	assert.NotNil(t, rubiconReq.Imp[0].Banner, "Banner object must be in request impression")
   498  }
   499  
   500  func TestOpenRTBRequestWithImpAndAdSlotIncluded(t *testing.T) {
   501  	bidder := new(RubiconAdapter)
   502  
   503  	request := &openrtb2.BidRequest{
   504  		ID: "test-request-id",
   505  		Imp: []openrtb2.Imp{{
   506  			ID: "test-imp-id",
   507  			Banner: &openrtb2.Banner{
   508  				Format: []openrtb2.Format{
   509  					{W: 300, H: 250},
   510  				},
   511  			},
   512  			Ext: json.RawMessage(`{
   513  				"bidder": {
   514  					"zoneId": 8394,
   515  					"siteId": 283282,
   516  					"accountId": 7891,
   517  					"inventory": {"key1" : "val1"},
   518  					"visitor": {"key2" : "val2"}
   519  				},
   520  				"context": {
   521  					"data": {
   522                          "adserver": {
   523                               "adslot": "/test-adslot",
   524                               "name": "gam"
   525                          }
   526  					}
   527  				}
   528  			}`),
   529  		}},
   530  		App: &openrtb2.App{
   531  			ID:   "com.test",
   532  			Name: "testApp",
   533  		},
   534  	}
   535  
   536  	reqs, _ := bidder.MakeRequests(request, &adapters.ExtraRequestInfo{})
   537  
   538  	rubiconReq := &openrtb2.BidRequest{}
   539  	if err := json.Unmarshal(reqs[0].Body, rubiconReq); err != nil {
   540  		t.Fatalf("Unexpected error while decoding request: %s", err)
   541  	}
   542  
   543  	assert.Equal(t, 1, len(rubiconReq.Imp),
   544  		"Unexpected number of request impressions. Got %d. Expected %d", len(rubiconReq.Imp), 1)
   545  
   546  	var rpImpExt rubiconImpExt
   547  	if err := json.Unmarshal(rubiconReq.Imp[0].Ext, &rpImpExt); err != nil {
   548  		t.Fatal("Error unmarshalling imp.ext")
   549  	}
   550  
   551  	dfpAdUnitCode, err := jsonparser.GetString(rpImpExt.RP.Target, "dfp_ad_unit_code")
   552  	if err != nil {
   553  		t.Fatal("Error extracting dfp_ad_unit_code")
   554  	}
   555  	assert.Equal(t, dfpAdUnitCode, "/test-adslot")
   556  }
   557  
   558  func TestOpenRTBFirstPartyDataPopulating(t *testing.T) {
   559  	testScenarios := []rubiPopulateFpdAttributesScenario{
   560  		{
   561  			source: json.RawMessage(`{"sourceKey": ["sourceValue", "sourceValue2"]}`),
   562  			target: map[string]interface{}{"targetKey": []interface{}{"targetValue"}},
   563  			result: map[string]interface{}{"targetKey": []interface{}{"targetValue"}, "sourceKey": []interface{}{"sourceValue", "sourceValue2"}},
   564  		},
   565  		{
   566  			source: json.RawMessage(`{"sourceKey": ["sourceValue", "sourceValue2"]}`),
   567  			target: make(map[string]interface{}),
   568  			result: map[string]interface{}{"sourceKey": []interface{}{"sourceValue", "sourceValue2"}},
   569  		},
   570  		{
   571  			source: json.RawMessage(`{"sourceKey": "sourceValue"}`),
   572  			target: make(map[string]interface{}),
   573  			result: map[string]interface{}{"sourceKey": [1]string{"sourceValue"}},
   574  		},
   575  		{
   576  			source: json.RawMessage(`{"sourceKey": true, "sourceKey2": [true, false, true]}`),
   577  			target: make(map[string]interface{}),
   578  			result: map[string]interface{}{"sourceKey": [1]string{"true"}, "sourceKey2": []string{"true", "false", "true"}},
   579  		},
   580  		{
   581  			source: json.RawMessage(`{"sourceKey": 1, "sourceKey2": [1, 2, 3]}`),
   582  			target: make(map[string]interface{}),
   583  			result: map[string]interface{}{"sourceKey": [1]string{"1"}},
   584  		},
   585  		{
   586  			source: json.RawMessage(`{"sourceKey": 1, "sourceKey2": 3.23}`),
   587  			target: make(map[string]interface{}),
   588  			result: map[string]interface{}{"sourceKey": [1]string{"1"}},
   589  		},
   590  		{
   591  			source: json.RawMessage(`{"sourceKey": {}}`),
   592  			target: make(map[string]interface{}),
   593  			result: make(map[string]interface{}),
   594  		},
   595  	}
   596  
   597  	for _, scenario := range testScenarios {
   598  		populateFirstPartyDataAttributes(scenario.source, scenario.target)
   599  		assert.Equal(t, scenario.result, scenario.target)
   600  	}
   601  }
   602  
   603  func TestOpenRTBRequestWithBadvOverflowed(t *testing.T) {
   604  	bidder := new(RubiconAdapter)
   605  
   606  	badvOverflowed := make([]string, 100)
   607  	for i := range badvOverflowed {
   608  		badvOverflowed[i] = strconv.Itoa(i)
   609  	}
   610  
   611  	request := &openrtb2.BidRequest{
   612  		ID:   "test-request-id",
   613  		BAdv: badvOverflowed,
   614  		Imp: []openrtb2.Imp{{
   615  			ID: "test-imp-id",
   616  			Banner: &openrtb2.Banner{
   617  				Format: []openrtb2.Format{
   618  					{W: 300, H: 250},
   619  				},
   620  			},
   621  			Ext: json.RawMessage(`{
   622  				"bidder": {
   623  					"zoneId": 8394,
   624  					"siteId": 283282,
   625  					"accountId": 7891,
   626  					"inventory": {"key1" : "val1"},
   627  					"visitor": {"key2" : "val2"}
   628  				}
   629  			}`),
   630  		}},
   631  		App: &openrtb2.App{
   632  			ID:   "com.test",
   633  			Name: "testApp",
   634  		},
   635  	}
   636  
   637  	reqs, _ := bidder.MakeRequests(request, &adapters.ExtraRequestInfo{})
   638  
   639  	rubiconReq := &openrtb2.BidRequest{}
   640  	if err := json.Unmarshal(reqs[0].Body, rubiconReq); err != nil {
   641  		t.Fatalf("Unexpected error while decoding request: %s", err)
   642  	}
   643  
   644  	badvRequest := rubiconReq.BAdv
   645  	assert.Equal(t, badvOverflowed[:50], badvRequest, "Unexpected dfp_ad_unit_code: %s")
   646  }
   647  
   648  func TestOpenRTBRequestWithVideoImpEvenIfImpHasBannerButAllRequiredVideoFields(t *testing.T) {
   649  	bidder := new(RubiconAdapter)
   650  
   651  	request := &openrtb2.BidRequest{
   652  		ID: "test-request-id",
   653  		Imp: []openrtb2.Imp{{
   654  			ID: "test-imp-id",
   655  			Banner: &openrtb2.Banner{
   656  				Format: []openrtb2.Format{
   657  					{W: 300, H: 250},
   658  				},
   659  			},
   660  			Video: &openrtb2.Video{
   661  				W:           ptrutil.ToPtr[int64](640),
   662  				H:           ptrutil.ToPtr[int64](360),
   663  				MIMEs:       []string{"video/mp4"},
   664  				Protocols:   []adcom1.MediaCreativeSubtype{adcom1.CreativeVAST10},
   665  				MaxDuration: 30,
   666  				Linearity:   1,
   667  				API:         []adcom1.APIFramework{},
   668  			},
   669  			Ext: json.RawMessage(`{"bidder": {
   670  				"zoneId": 8394,
   671  				"siteId": 283282,
   672  				"accountId": 7891,
   673  				"inventory": {"key1": "val1"},
   674  				"visitor": {"key2": "val2"},
   675  				"video": {"size_id": 1}
   676  			}}`),
   677  		}},
   678  		App: &openrtb2.App{
   679  			ID:   "com.test",
   680  			Name: "testApp",
   681  		},
   682  	}
   683  
   684  	reqs, errs := bidder.MakeRequests(request, &adapters.ExtraRequestInfo{})
   685  
   686  	assert.Empty(t, errs, "Got unexpected errors while building HTTP requests: %v", errs)
   687  
   688  	assert.Equal(t, 1, len(reqs), "Unexpected number of HTTP requests. Got %d. Expected %d", len(reqs), 1)
   689  
   690  	rubiconReq := &openrtb2.BidRequest{}
   691  	if err := json.Unmarshal(reqs[0].Body, rubiconReq); err != nil {
   692  		t.Fatalf("Unexpected error while decoding request: %s", err)
   693  	}
   694  
   695  	assert.Equal(t, 1, len(rubiconReq.Imp),
   696  		"Unexpected number of request impressions. Got %d. Expected %d", len(rubiconReq.Imp), 1)
   697  
   698  	assert.Nil(t, rubiconReq.Imp[0].Banner, "Unexpected banner object in request impression")
   699  
   700  	assert.NotNil(t, rubiconReq.Imp[0].Video, "Video object must be in request impression")
   701  }
   702  
   703  func TestOpenRTBRequestWithVideoImpAndEnabledRewardedInventoryFlag(t *testing.T) {
   704  	bidder := new(RubiconAdapter)
   705  
   706  	request := &openrtb2.BidRequest{
   707  		ID: "test-request-id",
   708  		Imp: []openrtb2.Imp{{
   709  			ID: "test-imp-id",
   710  			Video: &openrtb2.Video{
   711  				W:           ptrutil.ToPtr[int64](640),
   712  				H:           ptrutil.ToPtr[int64](360),
   713  				MIMEs:       []string{"video/mp4"},
   714  				Protocols:   []adcom1.MediaCreativeSubtype{adcom1.CreativeVAST10},
   715  				MaxDuration: 30,
   716  				Linearity:   1,
   717  				API:         []adcom1.APIFramework{},
   718  			},
   719  			Ext: json.RawMessage(`{
   720  			"prebid":{
   721  				"is_rewarded_inventory": 1
   722  			},
   723  			"bidder": {
   724  				"video": {"size_id": 1},
   725  				"zoneId": "123",
   726  				"siteId": 1234,
   727  				"accountId": "444"
   728  			}}`),
   729  		}},
   730  		App: &openrtb2.App{
   731  			ID:   "com.test",
   732  			Name: "testApp",
   733  		},
   734  	}
   735  
   736  	reqs, _ := bidder.MakeRequests(request, &adapters.ExtraRequestInfo{})
   737  
   738  	rubiconReq := &openrtb2.BidRequest{}
   739  	if err := json.Unmarshal(reqs[0].Body, rubiconReq); err != nil {
   740  		t.Fatalf("Unexpected error while decoding request: %s", err)
   741  	}
   742  
   743  	videoExt := &rubiconVideoExt{}
   744  	if err := json.Unmarshal(rubiconReq.Imp[0].Video.Ext, &videoExt); err != nil {
   745  		t.Fatal("Error unmarshalling request.imp[i].video.ext object.")
   746  	}
   747  
   748  	assert.Equal(t, "rewarded", videoExt.VideoType,
   749  		"Unexpected VideoType. Got %s. Expected %s", videoExt.VideoType, "rewarded")
   750  }
   751  
   752  func TestOpenRTBEmptyResponse(t *testing.T) {
   753  	httpResp := &adapters.ResponseData{
   754  		StatusCode: http.StatusNoContent,
   755  	}
   756  	bidder := new(RubiconAdapter)
   757  	bidResponse, errs := bidder.MakeBids(nil, nil, httpResp)
   758  
   759  	assert.Nil(t, bidResponse, "Expected empty response")
   760  	assert.Empty(t, errs, "Expected 0 errors. Got %d", len(errs))
   761  }
   762  
   763  func TestOpenRTBSurpriseResponse(t *testing.T) {
   764  	httpResp := &adapters.ResponseData{
   765  		StatusCode: http.StatusAccepted,
   766  	}
   767  	bidder := new(RubiconAdapter)
   768  	bidResponse, errs := bidder.MakeBids(nil, nil, httpResp)
   769  
   770  	assert.Nil(t, bidResponse, "Expected empty response")
   771  
   772  	assert.Equal(t, 1, len(errs), "Expected 1 error. Got %d", len(errs))
   773  }
   774  
   775  func TestOpenRTBStandardResponse(t *testing.T) {
   776  	request := &openrtb2.BidRequest{
   777  		ID: "test-request-id",
   778  		Imp: []openrtb2.Imp{{
   779  			ID: "test-imp-id",
   780  			Banner: &openrtb2.Banner{
   781  				Format: []openrtb2.Format{{
   782  					W: 320,
   783  					H: 50,
   784  				}},
   785  			},
   786  			Ext: json.RawMessage(`{"bidder": {
   787  				"accountId": 2763,
   788  				"siteId": 68780,
   789  				"zoneId": 327642
   790  			}}`),
   791  		}},
   792  	}
   793  
   794  	requestJson, _ := json.Marshal(request)
   795  	reqData := &adapters.RequestData{
   796  		Method:  "POST",
   797  		Uri:     "test-uri",
   798  		Body:    requestJson,
   799  		Headers: nil,
   800  	}
   801  
   802  	httpResp := &adapters.ResponseData{
   803  		StatusCode: http.StatusOK,
   804  		Body:       []byte(`{"id":"test-request-id","seatbid":[{"bid":[{"id":"1234567890","impid":"test-imp-id","price": 2,"crid":"4122982","adm":"some ad","h": 50,"w": 320,"ext":{"bidder":{"rp":{"targeting": {"key": "rpfl_2763", "values":["43_tier0100"]},"mime": "text/html","size_id": 43}}}}]}]}`),
   805  	}
   806  
   807  	bidder := new(RubiconAdapter)
   808  	bidResponse, errs := bidder.MakeBids(request, reqData, httpResp)
   809  
   810  	assert.NotNil(t, bidResponse, "Expected not empty response")
   811  	assert.Equal(t, 1, len(bidResponse.Bids), "Expected 1 bid. Got %d", len(bidResponse.Bids))
   812  
   813  	assert.Empty(t, errs, "Expected 0 errors. Got %d", len(errs))
   814  
   815  	assert.Equal(t, openrtb_ext.BidTypeBanner, bidResponse.Bids[0].BidType,
   816  		"Expected a banner bid. Got: %s", bidResponse.Bids[0].BidType)
   817  
   818  	theBid := bidResponse.Bids[0].Bid
   819  	assert.Equal(t, "1234567890", theBid.ID, "Bad bid ID. Expected %s, got %s", "1234567890", theBid.ID)
   820  }
   821  
   822  func TestOpenRTBResponseOverridePriceFromBidRequest(t *testing.T) {
   823  	request := &openrtb2.BidRequest{
   824  		ID: "test-request-id",
   825  		Imp: []openrtb2.Imp{{
   826  			ID: "test-imp-id",
   827  			Banner: &openrtb2.Banner{
   828  				Format: []openrtb2.Format{{
   829  					W: 320,
   830  					H: 50,
   831  				}},
   832  			},
   833  			Ext: json.RawMessage(`{"bidder": {
   834  				"accountId": 2763,
   835  				"siteId": 68780,
   836  				"zoneId": 327642
   837  			}}`),
   838  		}},
   839  		Ext: json.RawMessage(`{"prebid": {
   840  			"bidders": {
   841  				"rubicon": {
   842  					"debug": {
   843  						"cpmoverride": 10
   844  			}}}}}`),
   845  	}
   846  
   847  	requestJson, _ := json.Marshal(request)
   848  	reqData := &adapters.RequestData{
   849  		Method:  "POST",
   850  		Uri:     "test-uri",
   851  		Body:    requestJson,
   852  		Headers: nil,
   853  	}
   854  
   855  	httpResp := &adapters.ResponseData{
   856  		StatusCode: http.StatusOK,
   857  		Body:       []byte(`{"id":"test-request-id","seatbid":[{"bid":[{"id":"1234567890","impid":"test-imp-id","price": 2,"crid":"4122982","adm":"some ad","h": 50,"w": 320,"ext":{"bidder":{"rp":{"targeting": {"key": "rpfl_2763", "values":["43_tier0100"]},"mime": "text/html","size_id": 43}}}}]}]}`),
   858  	}
   859  
   860  	bidder := new(RubiconAdapter)
   861  	bidResponse, errs := bidder.MakeBids(request, reqData, httpResp)
   862  
   863  	assert.Empty(t, errs, "Expected 0 errors. Got %d", len(errs))
   864  
   865  	assert.Equal(t, float64(10), bidResponse.Bids[0].Bid.Price,
   866  		"Expected Price 10. Got: %s", bidResponse.Bids[0].Bid.Price)
   867  }
   868  
   869  func TestOpenRTBResponseSettingOfNetworkId(t *testing.T) {
   870  	testScenarios := []rubiSetNetworkIdTestScenario{
   871  		{
   872  			bidExt:            nil,
   873  			buyer:             "1",
   874  			expectedNetworkId: 1,
   875  			isNetworkIdSet:    true,
   876  		},
   877  		{
   878  			bidExt:            nil,
   879  			buyer:             "0",
   880  			expectedNetworkId: 0,
   881  			isNetworkIdSet:    false,
   882  		},
   883  		{
   884  			bidExt:            nil,
   885  			buyer:             "-1",
   886  			expectedNetworkId: 0,
   887  			isNetworkIdSet:    false,
   888  		},
   889  		{
   890  			bidExt:            nil,
   891  			buyer:             "1.1",
   892  			expectedNetworkId: 0,
   893  			isNetworkIdSet:    false,
   894  		},
   895  		{
   896  			bidExt:            &openrtb_ext.ExtBidPrebid{},
   897  			buyer:             "2",
   898  			expectedNetworkId: 2,
   899  			isNetworkIdSet:    true,
   900  		},
   901  		{
   902  			bidExt:            &openrtb_ext.ExtBidPrebid{Meta: &openrtb_ext.ExtBidPrebidMeta{}},
   903  			buyer:             "3",
   904  			expectedNetworkId: 3,
   905  			isNetworkIdSet:    true,
   906  		},
   907  		{
   908  			bidExt:            &openrtb_ext.ExtBidPrebid{Meta: &openrtb_ext.ExtBidPrebidMeta{NetworkID: 5}},
   909  			buyer:             "4",
   910  			expectedNetworkId: 4,
   911  			isNetworkIdSet:    true,
   912  		},
   913  		{
   914  			bidExt:            &openrtb_ext.ExtBidPrebid{Meta: &openrtb_ext.ExtBidPrebidMeta{NetworkID: 5}},
   915  			buyer:             "-1",
   916  			expectedNetworkId: 5,
   917  			isNetworkIdSet:    false,
   918  		},
   919  	}
   920  
   921  	for _, scenario := range testScenarios {
   922  		request := &openrtb2.BidRequest{
   923  			Imp: []openrtb2.Imp{{
   924  				ID:     "test-imp-id",
   925  				Banner: &openrtb2.Banner{},
   926  			}},
   927  		}
   928  
   929  		requestJson, _ := json.Marshal(request)
   930  		reqData := &adapters.RequestData{
   931  			Method:  "POST",
   932  			Uri:     "test-uri",
   933  			Body:    requestJson,
   934  			Headers: nil,
   935  		}
   936  
   937  		var givenBidExt json.RawMessage
   938  		if scenario.bidExt != nil {
   939  			marshalledExt, _ := json.Marshal(scenario.bidExt)
   940  			givenBidExt = marshalledExt
   941  		} else {
   942  			givenBidExt = nil
   943  		}
   944  
   945  		givenBidResponse := rubiconBidResponse{
   946  			SeatBid: []rubiconSeatBid{{Buyer: scenario.buyer,
   947  				Bid: []rubiconBid{{
   948  					Bid: openrtb2.Bid{Price: 123.2, ImpID: "test-imp-id", Ext: givenBidExt}}}}},
   949  		}
   950  		body, _ := json.Marshal(&givenBidResponse)
   951  		httpResp := &adapters.ResponseData{
   952  			StatusCode: http.StatusOK,
   953  			Body:       body,
   954  		}
   955  
   956  		bidder := new(RubiconAdapter)
   957  		bidResponse, errs := bidder.MakeBids(request, reqData, httpResp)
   958  		assert.Empty(t, errs)
   959  		if scenario.isNetworkIdSet {
   960  			networkdId, err := jsonparser.GetInt(bidResponse.Bids[0].Bid.Ext, "prebid", "meta", "networkId")
   961  			assert.NoError(t, err)
   962  			assert.Equal(t, scenario.expectedNetworkId, networkdId)
   963  		} else {
   964  			assert.Equal(t, bidResponse.Bids[0].Bid.Ext, givenBidExt)
   965  		}
   966  	}
   967  }
   968  
   969  func TestOpenRTBResponseOverridePriceFromCorrespondingImp(t *testing.T) {
   970  	request := &openrtb2.BidRequest{
   971  		ID: "test-request-id",
   972  		Imp: []openrtb2.Imp{{
   973  			ID: "test-imp-id",
   974  			Banner: &openrtb2.Banner{
   975  				Format: []openrtb2.Format{{
   976  					W: 320,
   977  					H: 50,
   978  				}},
   979  			},
   980  			Ext: json.RawMessage(`{"bidder": {
   981  				"accountId": 2763,
   982  				"siteId": 68780,
   983  				"zoneId": 327642,
   984  				"debug": {
   985  					"cpmoverride" : 20 
   986  				}
   987  			}}`),
   988  		}},
   989  		Ext: json.RawMessage(`{"prebid": {
   990  			"bidders": {
   991  				"rubicon": {
   992  					"debug": {
   993  						"cpmoverride": 10
   994  			}}}}}`),
   995  	}
   996  
   997  	requestJson, _ := json.Marshal(request)
   998  	reqData := &adapters.RequestData{
   999  		Method:  "POST",
  1000  		Uri:     "test-uri",
  1001  		Body:    requestJson,
  1002  		Headers: nil,
  1003  	}
  1004  
  1005  	httpResp := &adapters.ResponseData{
  1006  		StatusCode: http.StatusOK,
  1007  		Body:       []byte(`{"id":"test-request-id","seatbid":[{"bid":[{"id":"1234567890","impid":"test-imp-id","price": 2,"crid":"4122982","adm":"some ad","h": 50,"w": 320,"ext":{"bidder":{"rp":{"targeting": {"key": "rpfl_2763", "values":["43_tier0100"]},"mime": "text/html","size_id": 43}}}}]}]}`),
  1008  	}
  1009  
  1010  	bidder := new(RubiconAdapter)
  1011  	bidResponse, errs := bidder.MakeBids(request, reqData, httpResp)
  1012  
  1013  	assert.Empty(t, errs, "Expected 0 errors. Got %d", len(errs))
  1014  
  1015  	assert.Equal(t, float64(20), bidResponse.Bids[0].Bid.Price,
  1016  		"Expected Price 20. Got: %s", bidResponse.Bids[0].Bid.Price)
  1017  }
  1018  
  1019  func TestOpenRTBCopyBidIdFromResponseIfZero(t *testing.T) {
  1020  	request := &openrtb2.BidRequest{
  1021  		ID:  "test-request-id",
  1022  		Imp: []openrtb2.Imp{{}},
  1023  	}
  1024  
  1025  	requestJson, _ := json.Marshal(request)
  1026  	reqData := &adapters.RequestData{Body: requestJson}
  1027  
  1028  	httpResp := &adapters.ResponseData{
  1029  		StatusCode: http.StatusOK,
  1030  		Body:       []byte(`{"id":"test-request-id","bidid":"1234567890","seatbid":[{"bid":[{"id":"0","price": 1}]}]}`),
  1031  	}
  1032  
  1033  	bidder := new(RubiconAdapter)
  1034  	bidResponse, _ := bidder.MakeBids(request, reqData, httpResp)
  1035  
  1036  	theBid := bidResponse.Bids[0].Bid
  1037  	assert.Equal(t, "1234567890", theBid.ID, "Bad bid ID. Expected %s, got %s", "1234567890", theBid.ID)
  1038  }
  1039  
  1040  func TestJsonSamples(t *testing.T) {
  1041  	bidder, buildErr := Builder(openrtb_ext.BidderRubicon, config.Adapter{
  1042  		Endpoint: "uri",
  1043  		XAPI: config.AdapterXAPI{
  1044  			Username: "xuser",
  1045  			Password: "xpass",
  1046  			Tracker:  "pbs-test-tracker",
  1047  		}}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
  1048  
  1049  	if buildErr != nil {
  1050  		t.Fatalf("Builder returned unexpected error %v", buildErr)
  1051  	}
  1052  
  1053  	adapterstest.RunJSONBidderTest(t, "rubicontest", bidder)
  1054  }