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®ion=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 }