github.com/prebid/prebid-server/v2@v2.18.0/stored_responses/stored_responses_test.go (about) 1 package stored_responses 2 3 import ( 4 "context" 5 "encoding/json" 6 "errors" 7 "testing" 8 9 "github.com/prebid/openrtb/v20/openrtb2" 10 "github.com/prebid/prebid-server/v2/openrtb_ext" 11 "github.com/stretchr/testify/assert" 12 ) 13 14 func TestRemoveImpsWithStoredResponses(t *testing.T) { 15 bidRespId1 := json.RawMessage(`{"id": "resp_id1"}`) 16 testCases := []struct { 17 description string 18 reqIn *openrtb2.BidRequest 19 storedBidResponses ImpBidderStoredResp 20 expectedImps []openrtb2.Imp 21 }{ 22 { 23 description: "request with imps and stored bid response for this imp", 24 reqIn: &openrtb2.BidRequest{Imp: []openrtb2.Imp{ 25 {ID: "imp-id1"}, 26 }}, 27 storedBidResponses: ImpBidderStoredResp{ 28 "imp-id1": {"appnexus": bidRespId1}, 29 }, 30 expectedImps: nil, 31 }, 32 { 33 description: "request with imps and stored bid response for one of these imp", 34 reqIn: &openrtb2.BidRequest{Imp: []openrtb2.Imp{ 35 {ID: "imp-id1"}, 36 {ID: "imp-id2"}, 37 }}, 38 storedBidResponses: ImpBidderStoredResp{ 39 "imp-id1": {"appnexus": bidRespId1}, 40 }, 41 expectedImps: []openrtb2.Imp{ 42 { 43 ID: "imp-id2", 44 }, 45 }, 46 }, 47 { 48 description: "request with imps and stored bid response for both of these imp", 49 reqIn: &openrtb2.BidRequest{Imp: []openrtb2.Imp{ 50 {ID: "imp-id1"}, 51 {ID: "imp-id2"}, 52 }}, 53 storedBidResponses: ImpBidderStoredResp{ 54 "imp-id1": {"appnexus": bidRespId1}, 55 "imp-id2": {"appnexus": bidRespId1}, 56 }, 57 expectedImps: nil, 58 }, 59 { 60 description: "request with imps and no stored bid responses", 61 reqIn: &openrtb2.BidRequest{Imp: []openrtb2.Imp{ 62 {ID: "imp-id1"}, 63 {ID: "imp-id2"}, 64 }}, 65 storedBidResponses: nil, 66 67 expectedImps: []openrtb2.Imp{ 68 {ID: "imp-id1"}, 69 {ID: "imp-id2"}, 70 }, 71 }, 72 } 73 for _, testCase := range testCases { 74 request := testCase.reqIn 75 removeImpsWithStoredResponses(request, testCase.storedBidResponses) 76 assert.Equal(t, testCase.expectedImps, request.Imp, "incorrect Impressions for testCase %s", testCase.description) 77 } 78 } 79 80 func TestBuildStoredBidResponses(t *testing.T) { 81 bidRespId1 := json.RawMessage(`{"id": "resp_id1"}`) 82 bidRespId2 := json.RawMessage(`{"id": "resp_id2"}`) 83 bidRespId3 := json.RawMessage(`{"id": "resp_id3"}`) 84 testCases := []struct { 85 description string 86 storedBidResponses ImpBidderStoredResp 87 expectedResult BidderImpsWithBidResponses 88 }{ 89 { 90 description: "one imp and stored response for this imp with one bidder", 91 storedBidResponses: ImpBidderStoredResp{ 92 "imp-id1": {"bidderA": bidRespId1}, 93 }, 94 expectedResult: BidderImpsWithBidResponses{ 95 "bidderA": { 96 "imp-id1": bidRespId1, 97 }, 98 }, 99 }, 100 { 101 description: "one imp and stored response for this imp with two bidders", 102 storedBidResponses: ImpBidderStoredResp{ 103 "imp-id1": {"bidderA": bidRespId1, "bidderB": bidRespId2}, 104 }, 105 106 expectedResult: BidderImpsWithBidResponses{ 107 "bidderA": { 108 "imp-id1": bidRespId1, 109 }, 110 "bidderB": { 111 "imp-id1": bidRespId2, 112 }, 113 }, 114 }, 115 { 116 description: "two imps and stored response for this imp with two bidders", 117 storedBidResponses: ImpBidderStoredResp{ 118 "imp-id1": {"bidderA": bidRespId1}, 119 "imp-id2": {"bidderB": bidRespId2}, 120 }, 121 122 expectedResult: BidderImpsWithBidResponses{ 123 "bidderA": { 124 "imp-id1": bidRespId1, 125 }, 126 "bidderB": { 127 "imp-id2": bidRespId2, 128 }, 129 }, 130 }, 131 { 132 description: "three imps and stored response for these imps with two bidders", 133 storedBidResponses: ImpBidderStoredResp{ 134 "imp-id1": {"bidderA": bidRespId1}, 135 "imp-id2": {"bidderB": bidRespId2}, 136 "imp-id3": {"bidderA": bidRespId3}, 137 }, 138 139 expectedResult: BidderImpsWithBidResponses{ 140 "bidderA": { 141 "imp-id1": bidRespId1, 142 "imp-id3": bidRespId3, 143 }, 144 "bidderB": { 145 "imp-id2": bidRespId2, 146 }, 147 }, 148 }, 149 { 150 description: "empty stored responses", 151 storedBidResponses: ImpBidderStoredResp{}, 152 expectedResult: BidderImpsWithBidResponses{}, 153 }, 154 } 155 156 for _, testCase := range testCases { 157 158 bidderToImpToResponses := buildStoredResp(testCase.storedBidResponses) 159 for expectedBidderName := range testCase.expectedResult { 160 assert.Equal(t, testCase.expectedResult[expectedBidderName], bidderToImpToResponses[expectedBidderName], "incorrect stored responses for testCase %s", testCase.description) 161 } 162 } 163 } 164 165 func TestProcessStoredAuctionAndBidResponsesErrors(t *testing.T) { 166 testCases := []struct { 167 description string 168 request openrtb2.BidRequest 169 expectedErrorList []error 170 }{ 171 { 172 description: "Invalid stored auction response format: empty stored Auction Response Id", 173 request: openrtb2.BidRequest{ 174 Imp: []openrtb2.Imp{ 175 {ID: "imp-id1", 176 Ext: json.RawMessage(`{ 177 "prebid": { 178 "storedauctionresponse": {} 179 }}`)}, 180 }, 181 }, 182 expectedErrorList: []error{errors.New("request.imp[0] has ext.prebid.storedauctionresponse specified, but \"id\" field is missing ")}, 183 }, 184 { 185 description: "Invalid stored bid response format: empty storedbidresponse.bidder", 186 request: openrtb2.BidRequest{ 187 Imp: []openrtb2.Imp{ 188 {ID: "imp-id1", 189 Ext: json.RawMessage(`{ 190 "prebid": { 191 "storedbidresponse": [ 192 { "id": "123abc"}] 193 }}`)}, 194 }, 195 }, 196 expectedErrorList: []error{errors.New("request.imp[0] has ext.prebid.storedbidresponse specified, but \"id\" or/and \"bidder\" fields are missing ")}, 197 }, 198 { 199 description: "Invalid stored bid response format: empty storedbidresponse.id", 200 request: openrtb2.BidRequest{ 201 Imp: []openrtb2.Imp{ 202 {ID: "imp-id1", 203 Ext: json.RawMessage(`{ 204 "prebid": { 205 "storedbidresponse": [ 206 { "bidder": "testbidder"}] 207 }}`)}, 208 }, 209 }, 210 expectedErrorList: []error{errors.New("request.imp[0] has ext.prebid.storedbidresponse specified, but \"id\" or/and \"bidder\" fields are missing ")}, 211 }, 212 { 213 description: "Invalid stored auction response format: empty stored Auction Response Id in second imp", 214 request: openrtb2.BidRequest{ 215 Imp: []openrtb2.Imp{ 216 {ID: "imp-id1", 217 Ext: json.RawMessage(`{ 218 "prebid": { 219 "storedauctionresponse": { 220 "id":"123" 221 } 222 }}`)}, 223 {ID: "imp-id2", 224 Ext: json.RawMessage(`{ 225 "prebid": { 226 "storedauctionresponse": { 227 "id":"" 228 } 229 }}`)}, 230 }, 231 }, 232 expectedErrorList: []error{errors.New("request.imp[1] has ext.prebid.storedauctionresponse specified, but \"id\" field is missing ")}, 233 }, 234 { 235 description: "Invalid stored bid response format: empty stored bid Response Id in second imp", 236 request: openrtb2.BidRequest{ 237 Imp: []openrtb2.Imp{ 238 {ID: "imp-id1", 239 Ext: json.RawMessage(`{ 240 "prebid": { 241 "storedbidresponse": [ 242 {"bidder":"testBidder", "id": "123abc"} 243 ] 244 }}`)}, 245 {ID: "imp-id2", 246 Ext: json.RawMessage(`{ 247 "prebid": { 248 "storedbidresponse": [ 249 {"bidder":"testBidder", "id": ""} 250 ] 251 }}`)}, 252 }, 253 }, 254 expectedErrorList: []error{errors.New("request.imp[1] has ext.prebid.storedbidresponse specified, but \"id\" or/and \"bidder\" fields are missing ")}, 255 }, 256 } 257 258 for _, test := range testCases { 259 t.Run(test.description, func(t *testing.T) { 260 rw := &openrtb_ext.RequestWrapper{BidRequest: &test.request} 261 _, _, _, errorList := ProcessStoredResponses(context.TODO(), rw, nil) 262 assert.Equalf(t, test.expectedErrorList, errorList, "Error doesn't match: %s\n", test.description) 263 }) 264 } 265 266 } 267 268 func TestProcessStoredAuctionAndBidResponses(t *testing.T) { 269 bidStoredResp1 := json.RawMessage(`[{"bid": [{"id": "bid_id1"],"seat": "bidderA"}]`) 270 bidStoredResp2 := json.RawMessage(`[{"bid": [{"id": "bid_id2"],"seat": "bidderB"}]`) 271 bidStoredResp3 := json.RawMessage(`[{"bid": [{"id": "bid_id3"],"seat": "bidderA"}]`) 272 mockStoredResponses := map[string]json.RawMessage{ 273 "1": bidStoredResp1, 274 "2": bidStoredResp2, 275 "3": bidStoredResp3, 276 } 277 fetcher := &mockStoredBidResponseFetcher{mockStoredResponses} 278 279 testCases := []struct { 280 description string 281 request openrtb2.BidRequest 282 expectedStoredAuctionResponses ImpsWithBidResponses 283 expectedStoredBidResponses ImpBidderStoredResp 284 expectedBidderImpReplaceImpID BidderImpReplaceImpID 285 }{ 286 { 287 description: "No stored responses", 288 request: openrtb2.BidRequest{ 289 Imp: []openrtb2.Imp{ 290 {ID: "imp-id1", 291 Ext: json.RawMessage(`{ 292 "prebid": {} 293 }`)}, 294 }, 295 }, 296 expectedStoredAuctionResponses: nil, 297 expectedStoredBidResponses: nil, 298 expectedBidderImpReplaceImpID: nil, 299 }, 300 { 301 description: "Stored auction response one imp", 302 request: openrtb2.BidRequest{ 303 Imp: []openrtb2.Imp{ 304 {ID: "imp-id1", 305 Ext: json.RawMessage(`{ 306 "appnexus": { 307 "placementId": 123 308 }, 309 "prebid": { 310 "storedauctionresponse": { 311 "id": "1" 312 } 313 } 314 }`)}, 315 }, 316 }, 317 expectedStoredAuctionResponses: ImpsWithBidResponses{ 318 "imp-id1": bidStoredResp1, 319 }, 320 expectedStoredBidResponses: ImpBidderStoredResp{}, 321 expectedBidderImpReplaceImpID: BidderImpReplaceImpID{}, 322 }, 323 { 324 description: "Stored bid response one imp", 325 request: openrtb2.BidRequest{ 326 Imp: []openrtb2.Imp{ 327 {ID: "imp-id1", 328 Ext: json.RawMessage(`{ 329 "bidderA": { 330 "placementId": 123 331 }, 332 "prebid": { 333 "storedbidresponse": [ 334 {"bidder":"bidderA", "id": "1"} 335 ] 336 } 337 }`)}, 338 }, 339 }, 340 expectedStoredAuctionResponses: ImpsWithBidResponses{}, 341 expectedStoredBidResponses: ImpBidderStoredResp{ 342 "imp-id1": {"bidderA": bidStoredResp1}, 343 }, 344 expectedBidderImpReplaceImpID: BidderImpReplaceImpID{ 345 "bidderA": map[string]bool{"imp-id1": true}, 346 }, 347 }, 348 { 349 description: "Stored bid responses two bidders one imp", 350 request: openrtb2.BidRequest{ 351 Imp: []openrtb2.Imp{ 352 {ID: "imp-id1", 353 Ext: json.RawMessage(`{ 354 "bidderA": { 355 "placementId": 123 356 }, 357 "bidderB": { 358 "placementId": 123 359 }, 360 "prebid": { 361 "storedbidresponse": [ 362 {"bidder":"bidderA", "id": "1", "replaceimpid": true}, 363 {"bidder":"bidderB", "id": "2", "replaceimpid": false} 364 ] 365 } 366 }`)}, 367 }, 368 }, 369 expectedStoredAuctionResponses: ImpsWithBidResponses{}, 370 expectedStoredBidResponses: ImpBidderStoredResp{ 371 "imp-id1": {"bidderA": bidStoredResp1, "bidderB": bidStoredResp2}, 372 }, 373 expectedBidderImpReplaceImpID: BidderImpReplaceImpID{ 374 "bidderA": map[string]bool{"imp-id1": true}, 375 "bidderB": map[string]bool{"imp-id1": false}, 376 }, 377 }, 378 { 379 description: "Stored bid responses two same mixed case bidders one imp", 380 request: openrtb2.BidRequest{ 381 Imp: []openrtb2.Imp{ 382 {ID: "imp-id1", 383 Ext: json.RawMessage(`{ 384 "bidderA": { 385 "placementId": 123 386 }, 387 "BIDDERa": { 388 "placementId": 123 389 }, 390 "prebid": { 391 "storedbidresponse": [ 392 {"bidder":"bidderA", "id": "1", "replaceimpid": true}, 393 {"bidder":"bidderB", "id": "2", "replaceimpid": false} 394 ] 395 } 396 }`)}, 397 }, 398 }, 399 expectedStoredAuctionResponses: ImpsWithBidResponses{}, 400 expectedStoredBidResponses: ImpBidderStoredResp{ 401 "imp-id1": {"bidderA": bidStoredResp1, "BIDDERa": bidStoredResp1}, 402 }, 403 expectedBidderImpReplaceImpID: BidderImpReplaceImpID{ 404 "BIDDERa": map[string]bool{"imp-id1": true}, 405 "bidderA": map[string]bool{"imp-id1": true}, 406 }, 407 }, 408 { 409 description: "Stored bid responses 3 same mixed case bidders in imp.ext and imp.ext.prebid.bidders one imp", 410 request: openrtb2.BidRequest{ 411 Imp: []openrtb2.Imp{ 412 {ID: "imp-id1", 413 Ext: json.RawMessage(`{ 414 "bidderA": { 415 "placementId": 123 416 }, 417 "BIDDERa": { 418 "placementId": 123 419 }, 420 "prebid": { 421 "bidder": { 422 "BiddeRa": { 423 "placementId": 12883451 424 } 425 }, 426 "storedbidresponse": [ 427 {"bidder":"bidderA", "id": "1", "replaceimpid": true}, 428 {"bidder":"bidderB", "id": "2", "replaceimpid": false} 429 ] 430 } 431 }`)}, 432 }, 433 }, 434 expectedStoredAuctionResponses: ImpsWithBidResponses{}, 435 expectedStoredBidResponses: ImpBidderStoredResp{ 436 "imp-id1": {"bidderA": bidStoredResp1, "BIDDERa": bidStoredResp1, "BiddeRa": bidStoredResp1}, 437 }, 438 expectedBidderImpReplaceImpID: BidderImpReplaceImpID{ 439 "BIDDERa": map[string]bool{"imp-id1": true}, 440 "bidderA": map[string]bool{"imp-id1": true}, 441 "BiddeRa": map[string]bool{"imp-id1": true}, 442 }, 443 }, 444 { 445 description: "Stored bid responses 3 same mixed case bidders in imp.ext and imp.ext.prebid.bidders one imp, duplicated stored response", 446 request: openrtb2.BidRequest{ 447 Imp: []openrtb2.Imp{ 448 {ID: "imp-id1", 449 Ext: json.RawMessage(`{ 450 "bidderA": { 451 "placementId": 123 452 }, 453 "BIDDERa": { 454 "placementId": 123 455 }, 456 "prebid": { 457 "bidder": { 458 "BiddeRa": { 459 "placementId": 12883451 460 } 461 }, 462 "storedbidresponse": [ 463 {"bidder":"bidderA", "id": "1", "replaceimpid": true}, 464 {"bidder":"bidderA", "id": "2", "replaceimpid": true}, 465 {"bidder":"bidderB", "id": "2", "replaceimpid": false} 466 ] 467 } 468 }`)}, 469 }, 470 }, 471 expectedStoredAuctionResponses: ImpsWithBidResponses{}, 472 expectedStoredBidResponses: ImpBidderStoredResp{ 473 "imp-id1": {"bidderA": bidStoredResp1, "BIDDERa": bidStoredResp1, "BiddeRa": bidStoredResp1}, 474 }, 475 expectedBidderImpReplaceImpID: BidderImpReplaceImpID{ 476 "BIDDERa": map[string]bool{"imp-id1": true}, 477 "bidderA": map[string]bool{"imp-id1": true}, 478 "BiddeRa": map[string]bool{"imp-id1": true}, 479 }, 480 }, 481 { 482 //This is not a valid scenario for real auction request, added for testing purposes 483 description: "Stored auction and bid responses one imp", 484 request: openrtb2.BidRequest{ 485 Imp: []openrtb2.Imp{ 486 {ID: "imp-id1", 487 Ext: json.RawMessage(`{ 488 "bidderA": { 489 "placementId": 123 490 }, 491 "bidderB": { 492 "placementId": 123 493 }, 494 "prebid": { 495 "storedauctionresponse": { 496 "id": "1" 497 }, 498 "storedbidresponse": [ 499 {"bidder":"bidderA", "id": "1"}, 500 {"bidder":"bidderB", "id": "2"} 501 ] 502 } 503 }`)}, 504 }, 505 }, 506 expectedStoredAuctionResponses: ImpsWithBidResponses{ 507 "imp-id1": bidStoredResp1, 508 }, 509 expectedStoredBidResponses: ImpBidderStoredResp{ 510 "imp-id1": {"bidderA": bidStoredResp1, "bidderB": bidStoredResp2}, 511 }, 512 expectedBidderImpReplaceImpID: BidderImpReplaceImpID{ 513 "bidderA": map[string]bool{"imp-id1": true}, 514 "bidderB": map[string]bool{"imp-id1": true}, 515 }, 516 }, 517 { 518 description: "Stored auction response three imps", 519 request: openrtb2.BidRequest{ 520 Imp: []openrtb2.Imp{ 521 {ID: "imp-id1", 522 Ext: json.RawMessage(`{ 523 "appnexus": { 524 "placementId": 123 525 }, 526 "prebid": { 527 "storedauctionresponse": { 528 "id": "1" 529 } 530 } 531 }`)}, 532 {ID: "imp-id2", 533 Ext: json.RawMessage(`{ 534 "appnexus": { 535 "placementId": 123 536 }, 537 "prebid": { 538 "storedauctionresponse": { 539 "id": "2" 540 } 541 } 542 }`)}, 543 { 544 ID: "imp-id3", 545 Ext: json.RawMessage(`{ 546 "appnexus": { 547 "placementId": 123 548 }, 549 "prebid": { 550 "storedauctionresponse": { 551 "id": "3" 552 } 553 } 554 }`), 555 }, 556 }, 557 }, 558 expectedStoredAuctionResponses: ImpsWithBidResponses{ 559 "imp-id1": bidStoredResp1, 560 "imp-id2": bidStoredResp2, 561 "imp-id3": bidStoredResp3, 562 }, 563 expectedStoredBidResponses: ImpBidderStoredResp{}, 564 expectedBidderImpReplaceImpID: BidderImpReplaceImpID{}, 565 }, 566 { 567 description: "Stored auction response three imps duplicated stored auction response", 568 request: openrtb2.BidRequest{ 569 Imp: []openrtb2.Imp{ 570 {ID: "imp-id1", 571 Ext: json.RawMessage(`{ 572 "appnexus": { 573 "placementId": 123 574 }, 575 "prebid": { 576 "storedauctionresponse": { 577 "id": "1" 578 } 579 } 580 }`)}, 581 {ID: "imp-id2", 582 Ext: json.RawMessage(`{ 583 "appnexus": { 584 "placementId": 123 585 }, 586 "prebid": { 587 "storedauctionresponse": { 588 "id": "2" 589 } 590 } 591 }`)}, 592 { 593 ID: "imp-id3", 594 Ext: json.RawMessage(`{ 595 "appnexus": { 596 "placementId": 123 597 }, 598 "prebid": { 599 "storedauctionresponse": { 600 "id": "2" 601 } 602 } 603 }`), 604 }, 605 }, 606 }, 607 expectedStoredAuctionResponses: ImpsWithBidResponses{ 608 "imp-id1": bidStoredResp1, 609 "imp-id2": bidStoredResp2, 610 "imp-id3": bidStoredResp2, 611 }, 612 expectedStoredBidResponses: ImpBidderStoredResp{}, 613 expectedBidderImpReplaceImpID: BidderImpReplaceImpID{}, 614 }, 615 { 616 description: "Stored bid responses two bidders two imp", 617 request: openrtb2.BidRequest{ 618 Imp: []openrtb2.Imp{ 619 {ID: "imp-id1", 620 Ext: json.RawMessage(`{ 621 "bidderA": { 622 "placementId": 123 623 }, 624 "bidderB": { 625 "placementId": 123 626 }, 627 "prebid": { 628 "storedbidresponse": [ 629 {"bidder":"bidderA", "id": "1", "replaceimpid": false}, 630 {"bidder":"bidderB", "id": "2"} 631 ] 632 } 633 }`)}, 634 {ID: "imp-id2", 635 Ext: json.RawMessage(`{ 636 "bidderA": { 637 "placementId": 123 638 }, 639 "bidderB": { 640 "placementId": 123 641 }, 642 "prebid": { 643 "storedbidresponse": [ 644 {"bidder":"bidderA", "id": "3"}, 645 {"bidder":"bidderB", "id": "2", "replaceimpid": false} 646 ] 647 } 648 }`)}, 649 }, 650 }, 651 expectedStoredAuctionResponses: ImpsWithBidResponses{}, 652 expectedStoredBidResponses: ImpBidderStoredResp{ 653 "imp-id1": {"bidderA": bidStoredResp1, "bidderB": bidStoredResp2}, 654 "imp-id2": {"bidderA": bidStoredResp3, "bidderB": bidStoredResp2}, 655 }, 656 expectedBidderImpReplaceImpID: BidderImpReplaceImpID{ 657 "bidderA": map[string]bool{"imp-id1": false, "imp-id2": true}, 658 "bidderB": map[string]bool{"imp-id1": true, "imp-id2": false}, 659 }, 660 }, 661 } 662 663 for _, test := range testCases { 664 t.Run(test.description, func(t *testing.T) { 665 rw := openrtb_ext.RequestWrapper{BidRequest: &test.request} 666 storedAuctionResponses, storedBidResponses, bidderImpReplaceImpId, errorList := ProcessStoredResponses(context.TODO(), &rw, fetcher) 667 assert.Equal(t, test.expectedStoredAuctionResponses, storedAuctionResponses) 668 assert.Equal(t, test.expectedStoredBidResponses, storedBidResponses) 669 assert.Equal(t, test.expectedBidderImpReplaceImpID, bidderImpReplaceImpId) 670 assert.Nil(t, errorList, "Error should be nil") 671 }) 672 } 673 674 } 675 676 func TestProcessStoredResponsesNotFoundResponse(t *testing.T) { 677 bidStoredResp1 := json.RawMessage(`[{"bid": [{"id": "bid_id1"],"seat": "bidderA"}]`) 678 bidStoredResp2 := json.RawMessage(`[{"bid": [{"id": "bid_id2"],"seat": "bidderB"}]`) 679 mockStoredResponses := map[string]json.RawMessage{ 680 "1": bidStoredResp1, 681 "2": bidStoredResp2, 682 "3": nil, 683 "4": nil, 684 } 685 fetcher := &mockStoredBidResponseFetcher{mockStoredResponses} 686 687 testCases := []struct { 688 description string 689 request openrtb2.BidRequest 690 expectedErrors []error 691 }{ 692 { 693 description: "Stored bid response with nil data, one bidder one imp", 694 request: openrtb2.BidRequest{ 695 Imp: []openrtb2.Imp{ 696 {ID: "imp-id1", 697 Ext: json.RawMessage(`{ 698 "bidderB": { 699 "placementId": 123 700 }, 701 "prebid": { 702 "storedbidresponse": [ 703 {"bidder":"bidderB", "id": "3"} 704 ] 705 } 706 }`)}, 707 }, 708 }, 709 expectedErrors: []error{ 710 errors.New("failed to fetch stored bid response for impId = imp-id1, bidder = bidderB and storedBidResponse id = 3"), 711 }, 712 }, 713 { 714 description: "Stored bid response with nil data, one bidder, two imps, one with correct stored response", 715 request: openrtb2.BidRequest{ 716 Imp: []openrtb2.Imp{ 717 {ID: "imp-id1", 718 Ext: json.RawMessage(`{ 719 "bidderB": { 720 "placementId": 123 721 }, 722 "prebid": { 723 "storedbidresponse": [ 724 {"bidder":"bidderB", "id": "1"} 725 ] 726 } 727 }`)}, 728 {ID: "imp-id2", 729 Ext: json.RawMessage(`{ 730 "bidderB": { 731 "placementId": 123 732 }, 733 "prebid": { 734 "storedbidresponse": [ 735 {"bidder":"bidderB", "id": "3"} 736 ] 737 } 738 }`)}, 739 }, 740 }, 741 expectedErrors: []error{ 742 errors.New("failed to fetch stored bid response for impId = imp-id2, bidder = bidderB and storedBidResponse id = 3"), 743 }, 744 }, 745 { 746 description: "Stored bid response with nil data, one bidder, two imps, both with correct stored response", 747 request: openrtb2.BidRequest{ 748 Imp: []openrtb2.Imp{ 749 {ID: "imp-id1", 750 Ext: json.RawMessage(`{ 751 "bidderB": { 752 "placementId": 123 753 }, 754 "prebid": { 755 "storedbidresponse": [ 756 {"bidder":"bidderB", "id": "4"} 757 ] 758 } 759 }`)}, 760 {ID: "imp-id2", 761 Ext: json.RawMessage(`{ 762 "bidderB": { 763 "placementId": 123 764 }, 765 "prebid": { 766 "storedbidresponse": [ 767 {"bidder":"bidderB", "id": "3"} 768 ] 769 } 770 }`)}, 771 }, 772 }, 773 expectedErrors: []error{ 774 errors.New("failed to fetch stored bid response for impId = imp-id1, bidder = bidderB and storedBidResponse id = 4"), 775 errors.New("failed to fetch stored bid response for impId = imp-id2, bidder = bidderB and storedBidResponse id = 3"), 776 }, 777 }, 778 { 779 description: "Stored auction response with nil data and one imp", 780 request: openrtb2.BidRequest{ 781 Imp: []openrtb2.Imp{ 782 {ID: "imp-id1", 783 Ext: json.RawMessage(`{ 784 "appnexus": { 785 "placementId": 123 786 }, 787 "prebid": { 788 "storedauctionresponse": { 789 "id": "4" 790 } 791 } 792 }`)}, 793 }, 794 }, 795 expectedErrors: []error{ 796 errors.New("failed to fetch stored auction response for impId = imp-id1 and storedAuctionResponse id = 4"), 797 }, 798 }, 799 { 800 description: "Stored auction response with nil data, and two imps with nil responses", 801 request: openrtb2.BidRequest{ 802 Imp: []openrtb2.Imp{ 803 {ID: "imp-id1", 804 Ext: json.RawMessage(`{ 805 "appnexus": { 806 "placementId": 123 807 }, 808 "prebid": { 809 "storedauctionresponse": { 810 "id": "4" 811 } 812 } 813 }`)}, 814 {ID: "imp-id2", 815 Ext: json.RawMessage(`{ 816 "appnexus": { 817 "placementId": 123 818 }, 819 "prebid": { 820 "storedauctionresponse": { 821 "id": "3" 822 } 823 } 824 }`)}, 825 }, 826 }, 827 expectedErrors: []error{ 828 errors.New("failed to fetch stored auction response for impId = imp-id1 and storedAuctionResponse id = 4"), 829 errors.New("failed to fetch stored auction response for impId = imp-id2 and storedAuctionResponse id = 3"), 830 }, 831 }, 832 { 833 description: "Stored auction response with nil data, two imps, one with nil responses", 834 request: openrtb2.BidRequest{ 835 Imp: []openrtb2.Imp{ 836 {ID: "imp-id1", 837 Ext: json.RawMessage(`{ 838 "appnexus": { 839 "placementId": 123 840 }, 841 "prebid": { 842 "storedauctionresponse": { 843 "id": "2" 844 } 845 } 846 }`)}, 847 {ID: "imp-id2", 848 Ext: json.RawMessage(`{ 849 "appnexus": { 850 "placementId": 123 851 }, 852 "prebid": { 853 "storedauctionresponse": { 854 "id": "3" 855 } 856 } 857 }`)}, 858 }, 859 }, 860 expectedErrors: []error{ 861 errors.New("failed to fetch stored auction response for impId = imp-id2 and storedAuctionResponse id = 3"), 862 }, 863 }, 864 } 865 866 for _, test := range testCases { 867 t.Run(test.description, func(t *testing.T) { 868 rw := openrtb_ext.RequestWrapper{BidRequest: &test.request} 869 _, _, _, errorList := ProcessStoredResponses(context.TODO(), &rw, fetcher) 870 for _, err := range test.expectedErrors { 871 assert.Contains(t, errorList, err) 872 } 873 }) 874 } 875 } 876 877 func TestFlipMap(t *testing.T) { 878 testCases := []struct { 879 description string 880 inImpBidderReplaceImpID ImpBidderReplaceImpID 881 outBidderImpReplaceImpID BidderImpReplaceImpID 882 }{ 883 { 884 description: "Empty ImpBidderReplaceImpID", 885 inImpBidderReplaceImpID: ImpBidderReplaceImpID{}, 886 outBidderImpReplaceImpID: BidderImpReplaceImpID{}, 887 }, 888 { 889 description: "Nil ImpBidderReplaceImpID", 890 inImpBidderReplaceImpID: nil, 891 outBidderImpReplaceImpID: BidderImpReplaceImpID{}, 892 }, 893 { 894 description: "ImpBidderReplaceImpID has a one element map with single element", 895 inImpBidderReplaceImpID: ImpBidderReplaceImpID{"imp-id": {"bidderA": true}}, 896 outBidderImpReplaceImpID: BidderImpReplaceImpID{"bidderA": {"imp-id": true}}, 897 }, 898 { 899 description: "ImpBidderReplaceImpID has a one element map with multiple elements", 900 inImpBidderReplaceImpID: ImpBidderReplaceImpID{"imp-id": {"bidderA": true, "bidderB": false}}, 901 outBidderImpReplaceImpID: BidderImpReplaceImpID{"bidderA": {"imp-id": true}, "bidderB": {"imp-id": false}}, 902 }, 903 { 904 description: "ImpBidderReplaceImpID has multiple elements map with single element", 905 inImpBidderReplaceImpID: ImpBidderReplaceImpID{ 906 "imp-id1": {"bidderA": true}, 907 "imp-id2": {"bidderB": false}}, 908 outBidderImpReplaceImpID: BidderImpReplaceImpID{ 909 "bidderA": {"imp-id1": true}, 910 "bidderB": {"imp-id2": false}}, 911 }, 912 { 913 description: "ImpBidderReplaceImpID has multiple elements map with multiple elements", 914 inImpBidderReplaceImpID: ImpBidderReplaceImpID{ 915 "imp-id1": {"bidderA": true, "bidderB": false, "bidderC": false, "bidderD": true}, 916 "imp-id2": {"bidderA": false, "bidderB": false, "bidderC": true, "bidderD": true}, 917 "imp-id3": {"bidderA": false, "bidderB": true, "bidderC": true, "bidderD": false}}, 918 outBidderImpReplaceImpID: BidderImpReplaceImpID{ 919 "bidderA": {"imp-id1": true, "imp-id2": false, "imp-id3": false}, 920 "bidderB": {"imp-id1": false, "imp-id2": false, "imp-id3": true}, 921 "bidderC": {"imp-id1": false, "imp-id2": true, "imp-id3": true}, 922 "bidderD": {"imp-id1": true, "imp-id2": true, "imp-id3": false}}, 923 }, 924 } 925 926 for _, test := range testCases { 927 t.Run(test.description, func(t *testing.T) { 928 actualResult := flipMap(test.inImpBidderReplaceImpID) 929 assert.Equal(t, test.outBidderImpReplaceImpID, actualResult) 930 }) 931 } 932 } 933 934 type mockStoredBidResponseFetcher struct { 935 data map[string]json.RawMessage 936 } 937 938 func (cf *mockStoredBidResponseFetcher) FetchRequests(ctx context.Context, requestIDs []string, impIDs []string) (requestData map[string]json.RawMessage, impData map[string]json.RawMessage, errs []error) { 939 return nil, nil, nil 940 } 941 942 func (cf *mockStoredBidResponseFetcher) FetchResponses(ctx context.Context, ids []string) (data map[string]json.RawMessage, errs []error) { 943 return cf.data, nil 944 }