github.com/prebid/prebid-server/v2@v2.18.0/firstpartydata/first_party_data_test.go (about) 1 package firstpartydata 2 3 import ( 4 "encoding/json" 5 "os" 6 "path/filepath" 7 "testing" 8 9 "github.com/prebid/openrtb/v20/openrtb2" 10 "github.com/prebid/prebid-server/v2/errortypes" 11 "github.com/prebid/prebid-server/v2/openrtb_ext" 12 "github.com/prebid/prebid-server/v2/util/jsonutil" 13 "github.com/stretchr/testify/assert" 14 "github.com/stretchr/testify/require" 15 ) 16 17 func TestExtractGlobalFPD(t *testing.T) { 18 testCases := []struct { 19 description string 20 input openrtb_ext.RequestWrapper 21 expectedReq openrtb_ext.RequestWrapper 22 expectedFpd map[string][]byte 23 }{ 24 { 25 description: "Site, app and user data present", 26 input: openrtb_ext.RequestWrapper{ 27 BidRequest: &openrtb2.BidRequest{ 28 ID: "bid_id", 29 Site: &openrtb2.Site{ 30 ID: "reqSiteId", 31 Page: "http://www.foobar.com/1234.html", 32 Publisher: &openrtb2.Publisher{ 33 ID: "1", 34 }, 35 Ext: json.RawMessage(`{"data":{"somesitefpd":"sitefpdDataTest"}}`), 36 }, 37 User: &openrtb2.User{ 38 ID: "reqUserID", 39 Yob: 1982, 40 Gender: "M", 41 Ext: json.RawMessage(`{"data":{"someuserfpd":"userfpdDataTest"}}`), 42 }, 43 App: &openrtb2.App{ 44 ID: "appId", 45 Ext: json.RawMessage(`{"data":{"someappfpd":"appfpdDataTest"}}`), 46 }, 47 }, 48 }, 49 expectedReq: openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ 50 ID: "bid_id", 51 Site: &openrtb2.Site{ 52 ID: "reqSiteId", 53 Page: "http://www.foobar.com/1234.html", 54 Publisher: &openrtb2.Publisher{ 55 ID: "1", 56 }, 57 }, 58 User: &openrtb2.User{ 59 ID: "reqUserID", 60 Yob: 1982, 61 Gender: "M", 62 }, 63 App: &openrtb2.App{ 64 ID: "appId", 65 }, 66 }}, 67 expectedFpd: map[string][]byte{ 68 "site": []byte(`{"somesitefpd":"sitefpdDataTest"}`), 69 "user": []byte(`{"someuserfpd":"userfpdDataTest"}`), 70 "app": []byte(`{"someappfpd":"appfpdDataTest"}`), 71 }, 72 }, 73 { 74 description: "App FPD only present", 75 input: openrtb_ext.RequestWrapper{ 76 BidRequest: &openrtb2.BidRequest{ 77 ID: "bid_id", 78 Site: &openrtb2.Site{ 79 ID: "reqSiteId", 80 Page: "http://www.foobar.com/1234.html", 81 Publisher: &openrtb2.Publisher{ 82 ID: "1", 83 }, 84 }, 85 App: &openrtb2.App{ 86 ID: "appId", 87 Ext: json.RawMessage(`{"data":{"someappfpd":"appfpdDataTest"}}`), 88 }, 89 }, 90 }, 91 expectedReq: openrtb_ext.RequestWrapper{ 92 BidRequest: &openrtb2.BidRequest{ 93 ID: "bid_id", 94 Site: &openrtb2.Site{ 95 ID: "reqSiteId", 96 Page: "http://www.foobar.com/1234.html", 97 Publisher: &openrtb2.Publisher{ 98 ID: "1", 99 }, 100 }, 101 App: &openrtb2.App{ 102 ID: "appId", 103 }, 104 }, 105 }, 106 expectedFpd: map[string][]byte{ 107 "app": []byte(`{"someappfpd":"appfpdDataTest"}`), 108 "user": nil, 109 "site": nil, 110 }, 111 }, 112 { 113 description: "User FPD only present", 114 input: openrtb_ext.RequestWrapper{ 115 BidRequest: &openrtb2.BidRequest{ 116 ID: "bid_id", 117 Site: &openrtb2.Site{ 118 ID: "reqSiteId", 119 Page: "http://www.foobar.com/1234.html", 120 Publisher: &openrtb2.Publisher{ 121 ID: "1", 122 }, 123 }, 124 User: &openrtb2.User{ 125 ID: "reqUserID", 126 Yob: 1982, 127 Gender: "M", 128 Ext: json.RawMessage(`{"data":{"someuserfpd":"userfpdDataTest"}}`), 129 }, 130 }, 131 }, 132 expectedReq: openrtb_ext.RequestWrapper{ 133 BidRequest: &openrtb2.BidRequest{ 134 ID: "bid_id", 135 Site: &openrtb2.Site{ 136 ID: "reqSiteId", 137 Page: "http://www.foobar.com/1234.html", 138 Publisher: &openrtb2.Publisher{ 139 ID: "1", 140 }, 141 }, 142 User: &openrtb2.User{ 143 ID: "reqUserID", 144 Yob: 1982, 145 Gender: "M", 146 }, 147 }, 148 }, 149 expectedFpd: map[string][]byte{ 150 "app": nil, 151 "user": []byte(`{"someuserfpd":"userfpdDataTest"}`), 152 "site": nil, 153 }, 154 }, 155 { 156 description: "No FPD present in req", 157 input: openrtb_ext.RequestWrapper{ 158 BidRequest: &openrtb2.BidRequest{ 159 ID: "bid_id", 160 Site: &openrtb2.Site{ 161 ID: "reqSiteId", 162 Page: "http://www.foobar.com/1234.html", 163 Publisher: &openrtb2.Publisher{ 164 ID: "1", 165 }, 166 }, 167 User: &openrtb2.User{ 168 ID: "reqUserID", 169 Yob: 1982, 170 Gender: "M", 171 }, 172 App: &openrtb2.App{ 173 ID: "appId", 174 }, 175 }, 176 }, 177 expectedReq: openrtb_ext.RequestWrapper{ 178 BidRequest: &openrtb2.BidRequest{ 179 ID: "bid_id", 180 Site: &openrtb2.Site{ 181 ID: "reqSiteId", 182 Page: "http://www.foobar.com/1234.html", 183 Publisher: &openrtb2.Publisher{ 184 ID: "1", 185 }, 186 }, 187 User: &openrtb2.User{ 188 ID: "reqUserID", 189 Yob: 1982, 190 Gender: "M", 191 }, 192 App: &openrtb2.App{ 193 ID: "appId", 194 }, 195 }, 196 }, 197 expectedFpd: map[string][]byte{ 198 "app": nil, 199 "user": nil, 200 "site": nil, 201 }, 202 }, 203 { 204 description: "Site FPD only present", 205 input: openrtb_ext.RequestWrapper{ 206 BidRequest: &openrtb2.BidRequest{ 207 ID: "bid_id", 208 Site: &openrtb2.Site{ 209 ID: "reqSiteId", 210 Page: "http://www.foobar.com/1234.html", 211 Publisher: &openrtb2.Publisher{ 212 ID: "1", 213 }, 214 Ext: json.RawMessage(`{"data":{"someappfpd":true}}`), 215 }, 216 App: &openrtb2.App{ 217 ID: "appId", 218 }, 219 }, 220 }, 221 expectedReq: openrtb_ext.RequestWrapper{ 222 BidRequest: &openrtb2.BidRequest{ 223 ID: "bid_id", 224 Site: &openrtb2.Site{ 225 ID: "reqSiteId", 226 Page: "http://www.foobar.com/1234.html", 227 Publisher: &openrtb2.Publisher{ 228 ID: "1", 229 }, 230 }, 231 App: &openrtb2.App{ 232 ID: "appId", 233 }, 234 }, 235 }, 236 expectedFpd: map[string][]byte{ 237 "app": nil, 238 "user": nil, 239 "site": []byte(`{"someappfpd":true}`), 240 }, 241 }, 242 } 243 for _, test := range testCases { 244 245 inputReq := &test.input 246 fpd, err := ExtractGlobalFPD(inputReq) 247 assert.NoError(t, err, "Error should be nil") 248 err = inputReq.RebuildRequest() 249 assert.NoError(t, err, "Error should be nil") 250 251 assert.Equal(t, test.expectedReq.BidRequest, inputReq.BidRequest, "Incorrect input request after global fpd extraction") 252 253 assert.Equal(t, test.expectedFpd[userKey], fpd[userKey], "Incorrect User FPD") 254 assert.Equal(t, test.expectedFpd[appKey], fpd[appKey], "Incorrect App FPD") 255 assert.Equal(t, test.expectedFpd[siteKey], fpd[siteKey], "Incorrect Site FPDt") 256 } 257 } 258 259 func TestExtractOpenRtbGlobalFPD(t *testing.T) { 260 testCases := []struct { 261 description string 262 input openrtb2.BidRequest 263 output openrtb2.BidRequest 264 expectedFpdData map[string][]openrtb2.Data 265 }{ 266 { 267 description: "Site, app and user data present", 268 input: openrtb2.BidRequest{ 269 ID: "bid_id", 270 Imp: []openrtb2.Imp{ 271 {ID: "impid"}, 272 }, 273 Site: &openrtb2.Site{ 274 ID: "reqSiteId", 275 Content: &openrtb2.Content{ 276 Data: []openrtb2.Data{ 277 {ID: "siteDataId1", Name: "siteDataName1"}, 278 {ID: "siteDataId2", Name: "siteDataName2"}, 279 }, 280 }, 281 }, 282 User: &openrtb2.User{ 283 ID: "reqUserID", 284 Yob: 1982, 285 Gender: "M", 286 Data: []openrtb2.Data{ 287 {ID: "userDataId1", Name: "userDataName1"}, 288 }, 289 }, 290 App: &openrtb2.App{ 291 ID: "appId", 292 Content: &openrtb2.Content{ 293 Data: []openrtb2.Data{ 294 {ID: "appDataId1", Name: "appDataName1"}, 295 }, 296 }, 297 }, 298 }, 299 output: openrtb2.BidRequest{ 300 ID: "bid_id", 301 Imp: []openrtb2.Imp{ 302 {ID: "impid"}, 303 }, 304 Site: &openrtb2.Site{ 305 ID: "reqSiteId", 306 Content: &openrtb2.Content{}, 307 }, 308 User: &openrtb2.User{ 309 ID: "reqUserID", 310 Yob: 1982, 311 Gender: "M", 312 }, 313 App: &openrtb2.App{ 314 ID: "appId", 315 Content: &openrtb2.Content{}, 316 }, 317 }, 318 expectedFpdData: map[string][]openrtb2.Data{ 319 siteContentDataKey: {{ID: "siteDataId1", Name: "siteDataName1"}, {ID: "siteDataId2", Name: "siteDataName2"}}, 320 userDataKey: {{ID: "userDataId1", Name: "userDataName1"}}, 321 appContentDataKey: {{ID: "appDataId1", Name: "appDataName1"}}, 322 }, 323 }, 324 { 325 description: "No Site, app or user data present", 326 input: openrtb2.BidRequest{ 327 ID: "bid_id", 328 Imp: []openrtb2.Imp{ 329 {ID: "impid"}, 330 }, 331 }, 332 output: openrtb2.BidRequest{ 333 ID: "bid_id", 334 Imp: []openrtb2.Imp{ 335 {ID: "impid"}, 336 }, 337 }, 338 expectedFpdData: map[string][]openrtb2.Data{ 339 siteContentDataKey: nil, 340 userDataKey: nil, 341 appContentDataKey: nil, 342 }, 343 }, 344 { 345 description: "Site only data present", 346 input: openrtb2.BidRequest{ 347 ID: "bid_id", 348 Imp: []openrtb2.Imp{ 349 {ID: "impid"}, 350 }, 351 Site: &openrtb2.Site{ 352 ID: "reqSiteId", 353 Page: "test/page", 354 Content: &openrtb2.Content{ 355 Data: []openrtb2.Data{ 356 {ID: "siteDataId1", Name: "siteDataName1"}, 357 }, 358 }, 359 }, 360 }, 361 output: openrtb2.BidRequest{ 362 ID: "bid_id", 363 Imp: []openrtb2.Imp{ 364 {ID: "impid"}, 365 }, 366 Site: &openrtb2.Site{ 367 ID: "reqSiteId", 368 Page: "test/page", 369 Content: &openrtb2.Content{}, 370 }, 371 }, 372 expectedFpdData: map[string][]openrtb2.Data{ 373 siteContentDataKey: {{ID: "siteDataId1", Name: "siteDataName1"}}, 374 userDataKey: nil, 375 appContentDataKey: nil, 376 }, 377 }, 378 { 379 description: "App only data present", 380 input: openrtb2.BidRequest{ 381 ID: "bid_id", 382 Imp: []openrtb2.Imp{ 383 {ID: "impid"}, 384 }, 385 App: &openrtb2.App{ 386 ID: "reqAppId", 387 Content: &openrtb2.Content{ 388 Data: []openrtb2.Data{ 389 {ID: "appDataId1", Name: "appDataName1"}, 390 }, 391 }, 392 }, 393 }, 394 output: openrtb2.BidRequest{ 395 ID: "bid_id", 396 Imp: []openrtb2.Imp{ 397 {ID: "impid"}, 398 }, 399 App: &openrtb2.App{ 400 ID: "reqAppId", 401 Content: &openrtb2.Content{}, 402 }, 403 }, 404 expectedFpdData: map[string][]openrtb2.Data{ 405 siteContentDataKey: nil, 406 userDataKey: nil, 407 appContentDataKey: {{ID: "appDataId1", Name: "appDataName1"}}, 408 }, 409 }, 410 { 411 description: "User only data present", 412 input: openrtb2.BidRequest{ 413 ID: "bid_id", 414 Imp: []openrtb2.Imp{ 415 {ID: "impid"}, 416 }, 417 Site: &openrtb2.Site{ 418 ID: "reqSiteId", 419 }, 420 App: &openrtb2.App{ 421 ID: "reqAppId", 422 }, 423 User: &openrtb2.User{ 424 ID: "reqUserId", 425 Yob: 1982, 426 Gender: "M", 427 Data: []openrtb2.Data{ 428 {ID: "userDataId1", Name: "userDataName1"}, 429 }, 430 }, 431 }, 432 output: openrtb2.BidRequest{ 433 ID: "bid_id", 434 Imp: []openrtb2.Imp{ 435 {ID: "impid"}, 436 }, 437 Site: &openrtb2.Site{ 438 ID: "reqSiteId", 439 }, 440 App: &openrtb2.App{ 441 ID: "reqAppId", 442 }, 443 User: &openrtb2.User{ 444 ID: "reqUserId", 445 Yob: 1982, 446 Gender: "M", 447 }, 448 }, 449 expectedFpdData: map[string][]openrtb2.Data{ 450 siteContentDataKey: nil, 451 userDataKey: {{ID: "userDataId1", Name: "userDataName1"}}, 452 appContentDataKey: nil, 453 }, 454 }, 455 } 456 for _, test := range testCases { 457 458 inputReq := &test.input 459 460 res := ExtractOpenRtbGlobalFPD(inputReq) 461 462 assert.Equal(t, &test.output, inputReq, "Result request is incorrect") 463 assert.Equal(t, test.expectedFpdData[siteContentDataKey], res[siteContentDataKey], "siteContentData data is incorrect") 464 assert.Equal(t, test.expectedFpdData[userDataKey], res[userDataKey], "userData is incorrect") 465 assert.Equal(t, test.expectedFpdData[appContentDataKey], res[appContentDataKey], "appContentData is incorrect") 466 467 } 468 } 469 470 func TestExtractBidderConfigFPD(t *testing.T) { 471 testPath := "tests/extractbidderconfigfpd" 472 473 tests, err := os.ReadDir(testPath) 474 require.NoError(t, err, "Cannot Discover Tests") 475 476 for _, test := range tests { 477 t.Run(test.Name(), func(t *testing.T) { 478 path := filepath.Join(testPath, test.Name()) 479 480 testFile, err := loadTestFile[fpdFile](path) 481 require.NoError(t, err, "Load Test File") 482 483 givenRequestExtPrebid := &openrtb_ext.ExtRequestPrebid{} 484 err = jsonutil.UnmarshalValid(testFile.InputRequestData, givenRequestExtPrebid) 485 require.NoError(t, err, "Cannot Load Test Conditions") 486 487 testRequest := &openrtb_ext.RequestExt{} 488 testRequest.SetPrebid(givenRequestExtPrebid) 489 490 // run test 491 results, err := ExtractBidderConfigFPD(testRequest) 492 493 // assert errors 494 if len(testFile.ValidationErrors) > 0 { 495 require.EqualError(t, err, testFile.ValidationErrors[0].Message, "Expected Error Not Received") 496 } else { 497 require.NoError(t, err, "Error Not Expected") 498 assert.Nil(t, testRequest.GetPrebid().BidderConfigs, "Bidder specific FPD config should be removed from request") 499 } 500 501 // assert fpd (with normalization for nicer looking tests) 502 for bidderName, expectedFPD := range testFile.BidderConfigFPD { 503 require.Contains(t, results, bidderName) 504 505 if expectedFPD.App != nil { 506 assert.JSONEq(t, string(expectedFPD.App), string(results[bidderName].App), "app is incorrect") 507 } else { 508 assert.Nil(t, results[bidderName].App, "app expected to be nil") 509 } 510 511 if expectedFPD.Site != nil { 512 assert.JSONEq(t, string(expectedFPD.Site), string(results[bidderName].Site), "site is incorrect") 513 } else { 514 assert.Nil(t, results[bidderName].Site, "site expected to be nil") 515 } 516 517 if expectedFPD.User != nil { 518 assert.JSONEq(t, string(expectedFPD.User), string(results[bidderName].User), "user is incorrect") 519 } else { 520 assert.Nil(t, results[bidderName].User, "user expected to be nil") 521 } 522 } 523 }) 524 } 525 } 526 527 func TestResolveFPD(t *testing.T) { 528 testPath := "tests/resolvefpd" 529 530 tests, err := os.ReadDir(testPath) 531 require.NoError(t, err, "Cannot Discover Tests") 532 533 for _, test := range tests { 534 t.Run(test.Name(), func(t *testing.T) { 535 path := filepath.Join(testPath, test.Name()) 536 537 testFile, err := loadTestFile[fpdFileForResolveFPD](path) 538 require.NoError(t, err, "Load Test File") 539 540 request := &openrtb2.BidRequest{} 541 err = jsonutil.UnmarshalValid(testFile.InputRequestData, &request) 542 require.NoError(t, err, "Cannot Load Request") 543 544 originalRequest := &openrtb2.BidRequest{} 545 err = jsonutil.UnmarshalValid(testFile.InputRequestData, &originalRequest) 546 require.NoError(t, err, "Cannot Load Request") 547 548 reqExtFPD := make(map[string][]byte) 549 reqExtFPD["site"] = testFile.GlobalFPD["site"] 550 reqExtFPD["app"] = testFile.GlobalFPD["app"] 551 reqExtFPD["user"] = testFile.GlobalFPD["user"] 552 553 reqFPD := make(map[string][]openrtb2.Data, 3) 554 555 reqFPDSiteContentData := testFile.GlobalFPD[siteContentDataKey] 556 if len(reqFPDSiteContentData) > 0 { 557 var siteConData []openrtb2.Data 558 err = jsonutil.UnmarshalValid(reqFPDSiteContentData, &siteConData) 559 if err != nil { 560 t.Errorf("Unable to unmarshal site.content.data:") 561 } 562 reqFPD[siteContentDataKey] = siteConData 563 } 564 565 reqFPDAppContentData := testFile.GlobalFPD[appContentDataKey] 566 if len(reqFPDAppContentData) > 0 { 567 var appConData []openrtb2.Data 568 err = jsonutil.UnmarshalValid(reqFPDAppContentData, &appConData) 569 if err != nil { 570 t.Errorf("Unable to unmarshal app.content.data: ") 571 } 572 reqFPD[appContentDataKey] = appConData 573 } 574 575 reqFPDUserData := testFile.GlobalFPD[userDataKey] 576 if len(reqFPDUserData) > 0 { 577 var userData []openrtb2.Data 578 err = jsonutil.UnmarshalValid(reqFPDUserData, &userData) 579 if err != nil { 580 t.Errorf("Unable to unmarshal app.content.data: ") 581 } 582 reqFPD[userDataKey] = userData 583 } 584 585 // run test 586 resultFPD, errL := ResolveFPD(request, testFile.BidderConfigFPD, reqExtFPD, reqFPD, testFile.BiddersWithGlobalFPD) 587 588 if len(errL) == 0 { 589 assert.Equal(t, request, originalRequest, "Original request should not be modified") 590 591 expectedResultKeys := []string{} 592 for k := range testFile.OutputRequestData { 593 expectedResultKeys = append(expectedResultKeys, k.String()) 594 } 595 actualResultKeys := []string{} 596 for k := range resultFPD { 597 actualResultKeys = append(actualResultKeys, k.String()) 598 } 599 require.ElementsMatch(t, expectedResultKeys, actualResultKeys) 600 601 for k, outputReq := range testFile.OutputRequestData { 602 bidderFPD := resultFPD[k] 603 604 if outputReq.Site != nil && len(outputReq.Site.Ext) > 0 { 605 resSiteExt := bidderFPD.Site.Ext 606 expectedSiteExt := outputReq.Site.Ext 607 bidderFPD.Site.Ext = nil 608 outputReq.Site.Ext = nil 609 assert.JSONEq(t, string(expectedSiteExt), string(resSiteExt), "site.ext is incorrect") 610 assert.Equal(t, outputReq.Site, bidderFPD.Site, "Site is incorrect") 611 } 612 if outputReq.App != nil && len(outputReq.App.Ext) > 0 { 613 resAppExt := bidderFPD.App.Ext 614 expectedAppExt := outputReq.App.Ext 615 bidderFPD.App.Ext = nil 616 outputReq.App.Ext = nil 617 assert.JSONEq(t, string(expectedAppExt), string(resAppExt), "app.ext is incorrect") 618 assert.Equal(t, outputReq.App, bidderFPD.App, "App is incorrect") 619 } 620 if outputReq.User != nil && len(outputReq.User.Ext) > 0 { 621 resUserExt := bidderFPD.User.Ext 622 expectedUserExt := outputReq.User.Ext 623 bidderFPD.User.Ext = nil 624 outputReq.User.Ext = nil 625 assert.JSONEq(t, string(expectedUserExt), string(resUserExt), "user.ext is incorrect") 626 assert.Equal(t, outputReq.User, bidderFPD.User, "User is incorrect") 627 } 628 } 629 } else { 630 assert.ElementsMatch(t, errL, testFile.ValidationErrors, "Incorrect first party data warning message") 631 } 632 }) 633 } 634 } 635 636 func TestExtractFPDForBidders(t *testing.T) { 637 if specFiles, err := os.ReadDir("./tests/extractfpdforbidders"); err == nil { 638 for _, specFile := range specFiles { 639 path := filepath.Join("./tests/extractfpdforbidders/", specFile.Name()) 640 641 testFile, err := loadTestFile[fpdFile](path) 642 require.NoError(t, err, "Load Test File") 643 644 var expectedRequest openrtb2.BidRequest 645 err = jsonutil.UnmarshalValid(testFile.OutputRequestData, &expectedRequest) 646 if err != nil { 647 t.Errorf("Unable to unmarshal input request: %s", path) 648 } 649 650 resultRequest := &openrtb_ext.RequestWrapper{} 651 resultRequest.BidRequest = &openrtb2.BidRequest{} 652 err = jsonutil.UnmarshalValid(testFile.InputRequestData, resultRequest.BidRequest) 653 assert.NoError(t, err, "Error should be nil") 654 655 resultFPD, errL := ExtractFPDForBidders(resultRequest) 656 657 if len(testFile.ValidationErrors) > 0 { 658 assert.Equal(t, len(testFile.ValidationErrors), len(errL), "Incorrect number of errors was returned") 659 assert.ElementsMatch(t, errL, testFile.ValidationErrors, "Incorrect errors were returned") 660 //in case or error no further assertions needed 661 continue 662 } 663 assert.Empty(t, errL, "Error should be empty") 664 assert.Equal(t, len(resultFPD), len(testFile.BiddersFPDResolved)) 665 666 for bidderName, expectedValue := range testFile.BiddersFPDResolved { 667 actualValue := resultFPD[bidderName] 668 if expectedValue.Site != nil { 669 if len(expectedValue.Site.Ext) > 0 { 670 assert.JSONEq(t, string(expectedValue.Site.Ext), string(actualValue.Site.Ext), "Incorrect first party data") 671 expectedValue.Site.Ext = nil 672 actualValue.Site.Ext = nil 673 } 674 assert.Equal(t, expectedValue.Site, actualValue.Site, "Incorrect first party data") 675 } 676 if expectedValue.App != nil { 677 if len(expectedValue.App.Ext) > 0 { 678 assert.JSONEq(t, string(expectedValue.App.Ext), string(actualValue.App.Ext), "Incorrect first party data") 679 expectedValue.App.Ext = nil 680 actualValue.App.Ext = nil 681 } 682 assert.Equal(t, expectedValue.App, actualValue.App, "Incorrect first party data") 683 } 684 if expectedValue.User != nil { 685 if len(expectedValue.User.Ext) > 0 { 686 assert.JSONEq(t, string(expectedValue.User.Ext), string(actualValue.User.Ext), "Incorrect first party data") 687 expectedValue.User.Ext = nil 688 actualValue.User.Ext = nil 689 } 690 assert.Equal(t, expectedValue.User, actualValue.User, "Incorrect first party data") 691 } 692 } 693 694 if expectedRequest.Site != nil { 695 if len(expectedRequest.Site.Ext) > 0 { 696 assert.JSONEq(t, string(expectedRequest.Site.Ext), string(resultRequest.BidRequest.Site.Ext), "Incorrect site in request") 697 expectedRequest.Site.Ext = nil 698 resultRequest.BidRequest.Site.Ext = nil 699 } 700 assert.Equal(t, expectedRequest.Site, resultRequest.BidRequest.Site, "Incorrect site in request") 701 } 702 if expectedRequest.App != nil { 703 if len(expectedRequest.App.Ext) > 0 { 704 assert.JSONEq(t, string(expectedRequest.App.Ext), string(resultRequest.BidRequest.App.Ext), "Incorrect app in request") 705 expectedRequest.App.Ext = nil 706 resultRequest.BidRequest.App.Ext = nil 707 } 708 assert.Equal(t, expectedRequest.App, resultRequest.BidRequest.App, "Incorrect app in request") 709 } 710 if expectedRequest.User != nil { 711 if len(expectedRequest.User.Ext) > 0 { 712 assert.JSONEq(t, string(expectedRequest.User.Ext), string(resultRequest.BidRequest.User.Ext), "Incorrect user in request") 713 expectedRequest.User.Ext = nil 714 resultRequest.BidRequest.User.Ext = nil 715 } 716 assert.Equal(t, expectedRequest.User, resultRequest.BidRequest.User, "Incorrect user in request") 717 } 718 } 719 } 720 } 721 722 func TestResolveUser(t *testing.T) { 723 testCases := []struct { 724 description string 725 fpdConfig *openrtb_ext.ORTB2 726 bidRequestUser *openrtb2.User 727 globalFPD map[string][]byte 728 openRtbGlobalFPD map[string][]openrtb2.Data 729 expectedUser *openrtb2.User 730 expectError string 731 }{ 732 { 733 description: "FPD config and bid request user are not specified", 734 expectedUser: nil, 735 }, 736 { 737 description: "FPD config user only is specified", 738 fpdConfig: &openrtb_ext.ORTB2{User: json.RawMessage(`{"id":"test"}`)}, 739 expectedUser: &openrtb2.User{ID: "test"}, 740 }, 741 { 742 description: "FPD config and bid request user are specified", 743 fpdConfig: &openrtb_ext.ORTB2{User: json.RawMessage(`{"id":"test1"}`)}, 744 bidRequestUser: &openrtb2.User{ID: "test2"}, 745 expectedUser: &openrtb2.User{ID: "test1"}, 746 }, 747 { 748 description: "FPD config, bid request and global fpd user are specified, no input user ext", 749 fpdConfig: &openrtb_ext.ORTB2{User: json.RawMessage(`{"id":"test1"}`)}, 750 bidRequestUser: &openrtb2.User{ID: "test2"}, 751 globalFPD: map[string][]byte{userKey: []byte(`{"globalFPDUserData":"globalFPDUserDataValue"}`)}, 752 expectedUser: &openrtb2.User{ID: "test1", Ext: json.RawMessage(`{"data":{"globalFPDUserData":"globalFPDUserDataValue"}}`)}, 753 }, 754 { 755 description: "FPD config, bid request user with ext and global fpd user are specified, no input user ext", 756 fpdConfig: &openrtb_ext.ORTB2{User: json.RawMessage(`{"id":"test1"}`)}, 757 bidRequestUser: &openrtb2.User{ID: "test2", Ext: json.RawMessage(`{"test":{"inputFPDUserData":"inputFPDUserDataValue"}}`)}, 758 globalFPD: map[string][]byte{userKey: []byte(`{"globalFPDUserData":"globalFPDUserDataValue"}`)}, 759 expectedUser: &openrtb2.User{ID: "test1", Ext: json.RawMessage(`{"data":{"globalFPDUserData":"globalFPDUserDataValue"},"test":{"inputFPDUserData":"inputFPDUserDataValue"}}`)}, 760 }, 761 { 762 description: "FPD config, bid request and global fpd user are specified, with input user ext.data", 763 fpdConfig: &openrtb_ext.ORTB2{User: json.RawMessage(`{"id": "test1"}`)}, 764 bidRequestUser: &openrtb2.User{ID: "test2", Ext: json.RawMessage(`{"data":{"inputFPDUserData":"inputFPDUserDataValue"}}`)}, 765 globalFPD: map[string][]byte{userKey: []byte(`{"globalFPDUserData":"globalFPDUserDataValue"}`)}, 766 expectedUser: &openrtb2.User{ID: "test1", Ext: json.RawMessage(`{"data":{"globalFPDUserData":"globalFPDUserDataValue","inputFPDUserData":"inputFPDUserDataValue"}}`)}, 767 }, 768 { 769 description: "FPD config, bid request and global fpd user are specified, with input user ext.data malformed", 770 fpdConfig: &openrtb_ext.ORTB2{User: json.RawMessage(`{"id":"test1"}`)}, 771 bidRequestUser: &openrtb2.User{ID: "test2", Ext: json.RawMessage(`{"data":{"inputFPDUserData":"inputFPDUserDataValue"}}`)}, 772 globalFPD: map[string][]byte{userKey: []byte(`malformed`)}, 773 expectError: "invalid first party data ext", 774 }, 775 { 776 description: "bid request and openrtb global fpd user are specified, no input user ext", 777 bidRequestUser: &openrtb2.User{ID: "test2"}, 778 openRtbGlobalFPD: map[string][]openrtb2.Data{userDataKey: { 779 {ID: "DataId1", Name: "Name1"}, 780 {ID: "DataId2", Name: "Name2"}, 781 }}, 782 expectedUser: &openrtb2.User{ID: "test2", Data: []openrtb2.Data{ 783 {ID: "DataId1", Name: "Name1"}, 784 {ID: "DataId2", Name: "Name2"}, 785 }}, 786 }, 787 { 788 description: "fpd config user, bid request and openrtb global fpd user are specified, no input user ext", 789 fpdConfig: &openrtb_ext.ORTB2{User: json.RawMessage(`{"id":"test1"}`)}, 790 bidRequestUser: &openrtb2.User{ID: "test2"}, 791 openRtbGlobalFPD: map[string][]openrtb2.Data{userDataKey: { 792 {ID: "DataId1", Name: "Name1"}, 793 {ID: "DataId2", Name: "Name2"}, 794 }}, 795 expectedUser: &openrtb2.User{ID: "test1", Data: []openrtb2.Data{ 796 {ID: "DataId1", Name: "Name1"}, 797 {ID: "DataId2", Name: "Name2"}, 798 }}, 799 }, 800 { 801 description: "fpd config user with ext, bid request and openrtb global fpd user are specified, no input user ext", 802 fpdConfig: &openrtb_ext.ORTB2{User: json.RawMessage(`{"id":"test1","ext":{"test":1}}`)}, 803 bidRequestUser: &openrtb2.User{ID: "test2"}, 804 openRtbGlobalFPD: map[string][]openrtb2.Data{userDataKey: { 805 {ID: "DataId1", Name: "Name1"}, 806 {ID: "DataId2", Name: "Name2"}, 807 }}, 808 expectedUser: &openrtb2.User{ID: "test1", Data: []openrtb2.Data{ 809 {ID: "DataId1", Name: "Name1"}, 810 {ID: "DataId2", Name: "Name2"}, 811 }, 812 Ext: json.RawMessage(`{"test":1}`)}, 813 }, 814 { 815 description: "fpd config user with ext, bid requestuser with ext and openrtb global fpd user are specified, no input user ext", 816 fpdConfig: &openrtb_ext.ORTB2{User: json.RawMessage(`{"id":"test1","ext":{"test":1}}`)}, 817 bidRequestUser: &openrtb2.User{ID: "test2", Ext: json.RawMessage(`{"test":2,"key":"value"}`)}, 818 openRtbGlobalFPD: map[string][]openrtb2.Data{userDataKey: { 819 {ID: "DataId1", Name: "Name1"}, 820 {ID: "DataId2", Name: "Name2"}, 821 }}, 822 expectedUser: &openrtb2.User{ID: "test1", Data: []openrtb2.Data{ 823 {ID: "DataId1", Name: "Name1"}, 824 {ID: "DataId2", Name: "Name2"}, 825 }, 826 Ext: json.RawMessage(`{"key":"value","test":1}`)}, 827 }, 828 { 829 description: "fpd config user with malformed ext, bid requestuser with ext and openrtb global fpd user are specified, no input user ext", 830 fpdConfig: &openrtb_ext.ORTB2{User: json.RawMessage(`{"id": "test1","ext":{malformed}}`)}, 831 bidRequestUser: &openrtb2.User{ID: "test2", Ext: json.RawMessage(`{"test":2,"key":"value"}`)}, 832 openRtbGlobalFPD: map[string][]openrtb2.Data{userDataKey: { 833 {ID: "DataId1", Name: "Name1"}, 834 {ID: "DataId2", Name: "Name2"}, 835 }}, 836 expectedUser: &openrtb2.User{ID: "test1", Data: []openrtb2.Data{ 837 {ID: "DataId1", Name: "Name1"}, 838 {ID: "DataId2", Name: "Name2"}, 839 }, 840 Ext: json.RawMessage(`{"key":"value","test":1}`), 841 }, 842 expectError: "invalid first party data ext", 843 }, 844 } 845 for _, test := range testCases { 846 t.Run(test.description, func(t *testing.T) { 847 resultUser, err := resolveUser(test.fpdConfig, test.bidRequestUser, test.globalFPD, test.openRtbGlobalFPD, "bidderA") 848 849 if len(test.expectError) > 0 { 850 assert.EqualError(t, err, test.expectError) 851 } else { 852 assert.NoError(t, err, "unexpected error returned") 853 assert.Equal(t, test.expectedUser, resultUser, "Result user is incorrect") 854 } 855 }) 856 } 857 } 858 859 func TestResolveSite(t *testing.T) { 860 testCases := []struct { 861 description string 862 fpdConfig *openrtb_ext.ORTB2 863 bidRequestSite *openrtb2.Site 864 globalFPD map[string][]byte 865 openRtbGlobalFPD map[string][]openrtb2.Data 866 expectedSite *openrtb2.Site 867 expectError string 868 }{ 869 { 870 description: "FPD config and bid request site are not specified", 871 expectedSite: nil, 872 }, 873 { 874 description: "FPD config site only is specified", 875 fpdConfig: &openrtb_ext.ORTB2{Site: json.RawMessage(`{"id":"test"}`)}, 876 expectError: "incorrect First Party Data for bidder bidderA: Site object is not defined in request, but defined in FPD config", 877 }, 878 { 879 description: "FPD config and bid request site are specified", 880 fpdConfig: &openrtb_ext.ORTB2{Site: json.RawMessage(`{"id":"test1"}`)}, 881 bidRequestSite: &openrtb2.Site{ID: "test2"}, 882 expectedSite: &openrtb2.Site{ID: "test1"}, 883 }, 884 { 885 description: "FPD config, bid request and global fpd site are specified, no input site ext", 886 fpdConfig: &openrtb_ext.ORTB2{Site: json.RawMessage(`{"id":"test1"}`)}, 887 bidRequestSite: &openrtb2.Site{ID: "test2"}, 888 globalFPD: map[string][]byte{siteKey: []byte(`{"globalFPDSiteData":"globalFPDSiteDataValue"}`)}, 889 expectedSite: &openrtb2.Site{ID: "test1", Ext: json.RawMessage(`{"data":{"globalFPDSiteData":"globalFPDSiteDataValue"}}`)}, 890 }, 891 { 892 description: "FPD config, bid request site with ext and global fpd site are specified, no input site ext", 893 fpdConfig: &openrtb_ext.ORTB2{Site: json.RawMessage(`{"id":"test1"}`)}, 894 bidRequestSite: &openrtb2.Site{ID: "test2", Ext: json.RawMessage(`{"test":{"inputFPDSiteData":"inputFPDSiteDataValue"}}`)}, 895 globalFPD: map[string][]byte{siteKey: []byte(`{"globalFPDSiteData":"globalFPDSiteDataValue"}`)}, 896 expectedSite: &openrtb2.Site{ID: "test1", Ext: json.RawMessage(`{"data":{"globalFPDSiteData":"globalFPDSiteDataValue"},"test":{"inputFPDSiteData":"inputFPDSiteDataValue"}}`)}, 897 }, 898 { 899 description: "FPD config, bid request and global fpd site are specified, with input site ext.data", 900 fpdConfig: &openrtb_ext.ORTB2{Site: json.RawMessage(`{"id":"test1"}`)}, 901 bidRequestSite: &openrtb2.Site{ID: "test2", Ext: json.RawMessage(`{"data":{"inputFPDSiteData":"inputFPDSiteDataValue"}}`)}, 902 globalFPD: map[string][]byte{siteKey: []byte(`{"globalFPDSiteData":"globalFPDSiteDataValue"}`)}, 903 expectedSite: &openrtb2.Site{ID: "test1", Ext: json.RawMessage(`{"data":{"globalFPDSiteData":"globalFPDSiteDataValue","inputFPDSiteData":"inputFPDSiteDataValue"}}`)}, 904 }, 905 { 906 description: "FPD config, bid request and global fpd site are specified, with input site ext.data malformed", 907 fpdConfig: &openrtb_ext.ORTB2{Site: json.RawMessage(`{"id":"test1"}`)}, 908 bidRequestSite: &openrtb2.Site{ID: "test2", Ext: json.RawMessage(`{"data":{"inputFPDSiteData":"inputFPDSiteDataValue"}}`)}, 909 globalFPD: map[string][]byte{siteKey: []byte(`malformed`)}, 910 expectError: "invalid first party data ext", 911 }, 912 { 913 description: "bid request and openrtb global fpd site are specified, no input site ext", 914 bidRequestSite: &openrtb2.Site{ID: "test2"}, 915 openRtbGlobalFPD: map[string][]openrtb2.Data{siteContentDataKey: { 916 {ID: "DataId1", Name: "Name1"}, 917 {ID: "DataId2", Name: "Name2"}, 918 }}, 919 expectedSite: &openrtb2.Site{ID: "test2", Content: &openrtb2.Content{Data: []openrtb2.Data{ 920 {ID: "DataId1", Name: "Name1"}, 921 {ID: "DataId2", Name: "Name2"}, 922 }}}, 923 }, 924 { 925 description: "bid request with content and openrtb global fpd site are specified, no input site ext", 926 bidRequestSite: &openrtb2.Site{ID: "test2", Content: &openrtb2.Content{ 927 ID: "InputSiteContentId", 928 Data: []openrtb2.Data{ 929 {ID: "1", Name: "N1"}, 930 {ID: "2", Name: "N2"}, 931 }, 932 Ext: json.RawMessage(`{"contentPresent":true}`), 933 }}, 934 openRtbGlobalFPD: map[string][]openrtb2.Data{siteContentDataKey: { 935 {ID: "DataId1", Name: "Name1"}, 936 {ID: "DataId2", Name: "Name2"}, 937 }}, 938 expectedSite: &openrtb2.Site{ID: "test2", Content: &openrtb2.Content{ 939 ID: "InputSiteContentId", 940 Data: []openrtb2.Data{ 941 {ID: "DataId1", Name: "Name1"}, 942 {ID: "DataId2", Name: "Name2"}, 943 }, 944 Ext: json.RawMessage(`{"contentPresent":true}`), 945 }}, 946 }, 947 { 948 description: "fpd config site, bid request and openrtb global fpd site are specified, no input site ext", 949 fpdConfig: &openrtb_ext.ORTB2{Site: json.RawMessage(`{"id":"test1"}`)}, 950 bidRequestSite: &openrtb2.Site{ID: "test2"}, 951 openRtbGlobalFPD: map[string][]openrtb2.Data{siteContentDataKey: { 952 {ID: "DataId1", Name: "Name1"}, 953 {ID: "DataId2", Name: "Name2"}, 954 }}, 955 expectedSite: &openrtb2.Site{ID: "test1", Content: &openrtb2.Content{Data: []openrtb2.Data{ 956 {ID: "DataId1", Name: "Name1"}, 957 {ID: "DataId2", Name: "Name2"}, 958 }}}, 959 }, 960 { 961 description: "fpd config site with ext, bid request and openrtb global fpd site are specified, no input site ext", 962 fpdConfig: &openrtb_ext.ORTB2{Site: json.RawMessage(`{"id":"test1","ext":{"test":1}}`)}, 963 bidRequestSite: &openrtb2.Site{ID: "test2"}, 964 openRtbGlobalFPD: map[string][]openrtb2.Data{siteContentDataKey: { 965 {ID: "DataId1", Name: "Name1"}, 966 {ID: "DataId2", Name: "Name2"}, 967 }}, 968 expectedSite: &openrtb2.Site{ID: "test1", Content: &openrtb2.Content{Data: []openrtb2.Data{ 969 {ID: "DataId1", Name: "Name1"}, 970 {ID: "DataId2", Name: "Name2"}, 971 }}, 972 Ext: json.RawMessage(`{"test":1}`)}, 973 }, 974 { 975 description: "fpd config site with ext, bid request site with ext and openrtb global fpd site are specified, no input site ext", 976 fpdConfig: &openrtb_ext.ORTB2{Site: json.RawMessage(`{"id":"test1","ext":{"test":1}}`)}, 977 bidRequestSite: &openrtb2.Site{ID: "test2", Ext: json.RawMessage(`{"test":2,"key":"value"}`)}, 978 openRtbGlobalFPD: map[string][]openrtb2.Data{siteContentDataKey: { 979 {ID: "DataId1", Name: "Name1"}, 980 {ID: "DataId2", Name: "Name2"}, 981 }}, 982 expectedSite: &openrtb2.Site{ID: "test1", Content: &openrtb2.Content{Data: []openrtb2.Data{ 983 {ID: "DataId1", Name: "Name1"}, 984 {ID: "DataId2", Name: "Name2"}, 985 }}, 986 Ext: json.RawMessage(`{"key":"value","test":1}`)}, 987 }, 988 { 989 description: "fpd config site with malformed ext, bid request site with ext and openrtb global fpd site are specified, no input site ext", 990 fpdConfig: &openrtb_ext.ORTB2{Site: json.RawMessage(`{"id":"test1","ext":{malformed}}`)}, 991 bidRequestSite: &openrtb2.Site{ID: "test2", Ext: json.RawMessage(`{"test":2,"key":"value"}`)}, 992 openRtbGlobalFPD: map[string][]openrtb2.Data{siteContentDataKey: { 993 {ID: "DataId1", Name: "Name1"}, 994 {ID: "DataId2", Name: "Name2"}, 995 }}, 996 expectedSite: &openrtb2.Site{ID: "test1", Content: &openrtb2.Content{Data: []openrtb2.Data{ 997 {ID: "DataId1", Name: "Name1"}, 998 {ID: "DataId2", Name: "Name2"}, 999 }}, 1000 Ext: json.RawMessage(`{"key":"value","test":1}`), 1001 }, 1002 expectError: "invalid first party data ext", 1003 }, 1004 { 1005 description: "valid-id", 1006 bidRequestSite: &openrtb2.Site{ID: "1"}, 1007 fpdConfig: &openrtb_ext.ORTB2{Site: json.RawMessage(`{"id":"2"}`)}, 1008 expectedSite: &openrtb2.Site{ID: "2"}, 1009 }, 1010 { 1011 description: "valid-page", 1012 bidRequestSite: &openrtb2.Site{Page: "1"}, 1013 fpdConfig: &openrtb_ext.ORTB2{Site: json.RawMessage(`{"page":"2"}`)}, 1014 expectedSite: &openrtb2.Site{Page: "2"}, 1015 }, 1016 { 1017 description: "invalid-id", 1018 bidRequestSite: &openrtb2.Site{ID: "1"}, 1019 fpdConfig: &openrtb_ext.ORTB2{Site: json.RawMessage(`{"id":null}`)}, 1020 expectError: "incorrect First Party Data for bidder bidderA: Site object cannot set empty page if req.site.id is empty", 1021 }, 1022 { 1023 description: "invalid-page", 1024 bidRequestSite: &openrtb2.Site{Page: "1"}, 1025 fpdConfig: &openrtb_ext.ORTB2{Site: json.RawMessage(`{"page":null}`)}, 1026 expectError: "incorrect First Party Data for bidder bidderA: Site object cannot set empty page if req.site.id is empty", 1027 }, 1028 { 1029 description: "existing-err", 1030 bidRequestSite: &openrtb2.Site{ID: "1", Ext: []byte(`malformed`)}, 1031 fpdConfig: &openrtb_ext.ORTB2{Site: json.RawMessage(`{"ext":{"a":1}}`)}, 1032 expectError: "invalid request ext", 1033 }, 1034 { 1035 description: "fpd-err", 1036 bidRequestSite: &openrtb2.Site{ID: "1", Ext: []byte(`{"a":1}`)}, 1037 fpdConfig: &openrtb_ext.ORTB2{Site: json.RawMessage(`malformed`)}, 1038 expectError: "invalid first party data ext", 1039 }, 1040 } 1041 for _, test := range testCases { 1042 t.Run(test.description, func(t *testing.T) { 1043 resultSite, err := resolveSite(test.fpdConfig, test.bidRequestSite, test.globalFPD, test.openRtbGlobalFPD, "bidderA") 1044 1045 if len(test.expectError) > 0 { 1046 assert.EqualError(t, err, test.expectError) 1047 } else { 1048 require.NoError(t, err, "unexpected error returned") 1049 assert.Equal(t, test.expectedSite, resultSite, "Result site is incorrect") 1050 } 1051 }) 1052 } 1053 } 1054 1055 func TestResolveApp(t *testing.T) { 1056 testCases := []struct { 1057 description string 1058 fpdConfig *openrtb_ext.ORTB2 1059 bidRequestApp *openrtb2.App 1060 globalFPD map[string][]byte 1061 openRtbGlobalFPD map[string][]openrtb2.Data 1062 expectedApp *openrtb2.App 1063 expectError string 1064 }{ 1065 { 1066 description: "FPD config and bid request app are not specified", 1067 expectedApp: nil, 1068 }, 1069 { 1070 description: "FPD config app only is specified", 1071 fpdConfig: &openrtb_ext.ORTB2{App: json.RawMessage(`{"id":"test"}`)}, 1072 expectError: "incorrect First Party Data for bidder bidderA: App object is not defined in request, but defined in FPD config", 1073 }, 1074 { 1075 description: "FPD config and bid request app are specified", 1076 fpdConfig: &openrtb_ext.ORTB2{App: json.RawMessage(`{"id":"test1"}`)}, 1077 bidRequestApp: &openrtb2.App{ID: "test2"}, 1078 expectedApp: &openrtb2.App{ID: "test1"}, 1079 }, 1080 { 1081 description: "FPD config, bid request and global fpd app are specified, no input app ext", 1082 fpdConfig: &openrtb_ext.ORTB2{App: json.RawMessage(`{"id":"test1"}`)}, 1083 bidRequestApp: &openrtb2.App{ID: "test2"}, 1084 globalFPD: map[string][]byte{appKey: []byte(`{"globalFPDAppData":"globalFPDAppDataValue"}`)}, 1085 expectedApp: &openrtb2.App{ID: "test1", Ext: json.RawMessage(`{"data":{"globalFPDAppData":"globalFPDAppDataValue"}}`)}, 1086 }, 1087 { 1088 description: "FPD config, bid request app with ext and global fpd app are specified, no input app ext", 1089 fpdConfig: &openrtb_ext.ORTB2{App: json.RawMessage(`{"id":"test1"}`)}, 1090 bidRequestApp: &openrtb2.App{ID: "test2", Ext: json.RawMessage(`{"test":{"inputFPDAppData":"inputFPDAppDataValue"}}`)}, 1091 globalFPD: map[string][]byte{appKey: []byte(`{"globalFPDAppData":"globalFPDAppDataValue"}`)}, 1092 expectedApp: &openrtb2.App{ID: "test1", Ext: json.RawMessage(`{"data":{"globalFPDAppData":"globalFPDAppDataValue"},"test":{"inputFPDAppData":"inputFPDAppDataValue"}}`)}, 1093 }, 1094 { 1095 description: "FPD config, bid request and global fpd app are specified, with input app ext.data", 1096 fpdConfig: &openrtb_ext.ORTB2{App: json.RawMessage(`{"id":"test1"}`)}, 1097 bidRequestApp: &openrtb2.App{ID: "test2", Ext: json.RawMessage(`{"data":{"inputFPDAppData":"inputFPDAppDataValue"}}`)}, 1098 globalFPD: map[string][]byte{appKey: []byte(`{"globalFPDAppData":"globalFPDAppDataValue"}`)}, 1099 expectedApp: &openrtb2.App{ID: "test1", Ext: json.RawMessage(`{"data":{"globalFPDAppData":"globalFPDAppDataValue","inputFPDAppData":"inputFPDAppDataValue"}}`)}, 1100 }, 1101 { 1102 description: "FPD config, bid request and global fpd app are specified, with input app ext.data malformed", 1103 fpdConfig: &openrtb_ext.ORTB2{App: json.RawMessage(`{"id":"test1"}`)}, 1104 bidRequestApp: &openrtb2.App{ID: "test2", Ext: json.RawMessage(`{"data":{"inputFPDAppData":"inputFPDAppDataValue"}}`)}, 1105 globalFPD: map[string][]byte{appKey: []byte(`malformed`)}, 1106 expectError: "invalid first party data ext", 1107 }, 1108 { 1109 description: "bid request and openrtb global fpd app are specified, no input app ext", 1110 bidRequestApp: &openrtb2.App{ID: "test2"}, 1111 openRtbGlobalFPD: map[string][]openrtb2.Data{appContentDataKey: { 1112 {ID: "DataId1", Name: "Name1"}, 1113 {ID: "DataId2", Name: "Name2"}, 1114 }}, 1115 expectedApp: &openrtb2.App{ID: "test2", Content: &openrtb2.Content{Data: []openrtb2.Data{ 1116 {ID: "DataId1", Name: "Name1"}, 1117 {ID: "DataId2", Name: "Name2"}, 1118 }}}, 1119 }, 1120 { 1121 description: "bid request with content and openrtb global fpd app are specified, no input app ext", 1122 bidRequestApp: &openrtb2.App{ID: "test2", Content: &openrtb2.Content{ 1123 ID: "InputAppContentId", 1124 Data: []openrtb2.Data{ 1125 {ID: "1", Name: "N1"}, 1126 {ID: "2", Name: "N2"}, 1127 }, 1128 Ext: json.RawMessage(`{"contentPresent":true}`), 1129 }}, 1130 openRtbGlobalFPD: map[string][]openrtb2.Data{appContentDataKey: { 1131 {ID: "DataId1", Name: "Name1"}, 1132 {ID: "DataId2", Name: "Name2"}, 1133 }}, 1134 expectedApp: &openrtb2.App{ID: "test2", Content: &openrtb2.Content{ 1135 ID: "InputAppContentId", 1136 Data: []openrtb2.Data{ 1137 {ID: "DataId1", Name: "Name1"}, 1138 {ID: "DataId2", Name: "Name2"}, 1139 }, 1140 Ext: json.RawMessage(`{"contentPresent":true}`), 1141 }}, 1142 }, 1143 { 1144 description: "fpd config app, bid request and openrtb global fpd app are specified, no input app ext", 1145 fpdConfig: &openrtb_ext.ORTB2{App: json.RawMessage(`{"id":"test1"}`)}, 1146 bidRequestApp: &openrtb2.App{ID: "test2"}, 1147 openRtbGlobalFPD: map[string][]openrtb2.Data{appContentDataKey: { 1148 {ID: "DataId1", Name: "Name1"}, 1149 {ID: "DataId2", Name: "Name2"}, 1150 }}, 1151 expectedApp: &openrtb2.App{ID: "test1", Content: &openrtb2.Content{Data: []openrtb2.Data{ 1152 {ID: "DataId1", Name: "Name1"}, 1153 {ID: "DataId2", Name: "Name2"}, 1154 }}}, 1155 }, 1156 { 1157 description: "fpd config app with ext, bid request and openrtb global fpd app are specified, no input app ext", 1158 fpdConfig: &openrtb_ext.ORTB2{App: json.RawMessage(`{"id":"test1","ext":{"test":1}}`)}, 1159 bidRequestApp: &openrtb2.App{ID: "test2"}, 1160 openRtbGlobalFPD: map[string][]openrtb2.Data{appContentDataKey: { 1161 {ID: "DataId1", Name: "Name1"}, 1162 {ID: "DataId2", Name: "Name2"}, 1163 }}, 1164 expectedApp: &openrtb2.App{ID: "test1", Content: &openrtb2.Content{Data: []openrtb2.Data{ 1165 {ID: "DataId1", Name: "Name1"}, 1166 {ID: "DataId2", Name: "Name2"}, 1167 }}, 1168 Ext: json.RawMessage(`{"test":1}`)}, 1169 }, 1170 { 1171 description: "fpd config app with ext, bid request app with ext and openrtb global fpd app are specified, no input app ext", 1172 fpdConfig: &openrtb_ext.ORTB2{App: json.RawMessage(`{"id":"test1","ext":{"test":1}}`)}, 1173 bidRequestApp: &openrtb2.App{ID: "test2", Ext: json.RawMessage(`{"test":2,"key":"value"}`)}, 1174 openRtbGlobalFPD: map[string][]openrtb2.Data{appContentDataKey: { 1175 {ID: "DataId1", Name: "Name1"}, 1176 {ID: "DataId2", Name: "Name2"}, 1177 }}, 1178 expectedApp: &openrtb2.App{ID: "test1", Content: &openrtb2.Content{Data: []openrtb2.Data{ 1179 {ID: "DataId1", Name: "Name1"}, 1180 {ID: "DataId2", Name: "Name2"}, 1181 }}, 1182 Ext: json.RawMessage(`{"key":"value","test":1}`)}, 1183 }, 1184 { 1185 description: "fpd config app with malformed ext, bid request app with ext and openrtb global fpd app are specified, no input app ext", 1186 fpdConfig: &openrtb_ext.ORTB2{App: json.RawMessage(`{"id":"test1","ext":{malformed}}`)}, 1187 bidRequestApp: &openrtb2.App{ID: "test2", Ext: json.RawMessage(`{"test":2,"key":"value"}`)}, 1188 openRtbGlobalFPD: map[string][]openrtb2.Data{appContentDataKey: { 1189 {ID: "DataId1", Name: "Name1"}, 1190 {ID: "DataId2", Name: "Name2"}, 1191 }}, 1192 expectedApp: &openrtb2.App{ID: "test1", Content: &openrtb2.Content{Data: []openrtb2.Data{ 1193 {ID: "DataId1", Name: "Name1"}, 1194 {ID: "DataId2", Name: "Name2"}, 1195 }}, 1196 Ext: json.RawMessage(`{"key":"value","test":1}`), 1197 }, 1198 expectError: "invalid first party data ext", 1199 }, 1200 } 1201 for _, test := range testCases { 1202 t.Run(test.description, func(t *testing.T) { 1203 resultApp, err := resolveApp(test.fpdConfig, test.bidRequestApp, test.globalFPD, test.openRtbGlobalFPD, "bidderA") 1204 1205 if len(test.expectError) > 0 { 1206 assert.EqualError(t, err, test.expectError) 1207 } else { 1208 assert.NoError(t, err) 1209 assert.Equal(t, test.expectedApp, resultApp, "Result app is incorrect") 1210 } 1211 }) 1212 } 1213 } 1214 1215 func TestBuildExtData(t *testing.T) { 1216 testCases := []struct { 1217 description string 1218 input []byte 1219 expectedRes string 1220 }{ 1221 { 1222 description: "Input object with int value", 1223 input: []byte(`{"someData":123}`), 1224 expectedRes: `{"data":{"someData":123}}`, 1225 }, 1226 { 1227 description: "Input object with bool value", 1228 input: []byte(`{"someData":true}`), 1229 expectedRes: `{"data":{"someData":true}}`, 1230 }, 1231 { 1232 description: "Input object with string value", 1233 input: []byte(`{"someData":"true"}`), 1234 expectedRes: `{"data":{"someData":"true"}}`, 1235 }, 1236 { 1237 description: "No input object", 1238 input: []byte(`{}`), 1239 expectedRes: `{"data":{}}`, 1240 }, 1241 { 1242 description: "Input object with object value", 1243 input: []byte(`{"someData":{"moreFpdData":"fpddata"}}`), 1244 expectedRes: `{"data":{"someData":{"moreFpdData":"fpddata"}}}`, 1245 }, 1246 } 1247 1248 for _, test := range testCases { 1249 actualRes := buildExtData(test.input) 1250 assert.JSONEq(t, test.expectedRes, string(actualRes), "Incorrect result data") 1251 } 1252 } 1253 1254 func loadTestFile[T any](filename string) (T, error) { 1255 var testFile T 1256 1257 b, err := os.ReadFile(filename) 1258 if err != nil { 1259 return testFile, err 1260 } 1261 1262 err = json.Unmarshal(b, &testFile) 1263 if err != nil { 1264 return testFile, err 1265 } 1266 1267 return testFile, nil 1268 } 1269 1270 type fpdFile struct { 1271 InputRequestData json.RawMessage `json:"inputRequestData,omitempty"` 1272 OutputRequestData json.RawMessage `json:"outputRequestData,omitempty"` 1273 BidderConfigFPD map[openrtb_ext.BidderName]*openrtb_ext.ORTB2 `json:"bidderConfigFPD,omitempty"` 1274 BiddersFPDResolved map[openrtb_ext.BidderName]*ResolvedFirstPartyData `json:"biddersFPDResolved,omitempty"` 1275 GlobalFPD map[string]json.RawMessage `json:"globalFPD,omitempty"` 1276 ValidationErrors []*errortypes.BadInput `json:"validationErrors,omitempty"` 1277 } 1278 1279 type fpdFileForResolveFPD struct { 1280 InputRequestData json.RawMessage `json:"inputRequestData,omitempty"` 1281 OutputRequestData map[openrtb_ext.BidderName]openrtb2.BidRequest `json:"outputRequestData,omitempty"` 1282 BiddersWithGlobalFPD []string `json:"biddersWithGlobalFPD,omitempty"` 1283 BidderConfigFPD map[openrtb_ext.BidderName]*openrtb_ext.ORTB2 `json:"bidderConfigFPD,omitempty"` 1284 GlobalFPD map[string]json.RawMessage `json:"globalFPD,omitempty"` 1285 ValidationErrors []*errortypes.BadInput `json:"validationErrors,omitempty"` 1286 }