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