github.com/prebid/prebid-server@v0.275.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/v19/openrtb2"
    10  	"github.com/prebid/prebid-server/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  	bidderMap := map[string]openrtb_ext.BidderName{"testBidder": "testBidder"}
   167  
   168  	testCases := []struct {
   169  		description       string
   170  		requestJson       []byte
   171  		expectedErrorList []error
   172  	}{
   173  		{
   174  			description: "Invalid stored auction response format: empty stored Auction Response Id",
   175  			requestJson: []byte(`{"imp": [
   176      			  {
   177      			    "id": "imp-id1",
   178      			    "ext": {
   179      			      "prebid": {
   180      			        "storedauctionresponse": {
   181      			        }
   182      			      }
   183      			    }
   184      			  }
   185      			]}`),
   186  			expectedErrorList: []error{errors.New("request.imp[0] has ext.prebid.storedauctionresponse specified, but \"id\" field is missing ")},
   187  		},
   188  		{
   189  			description: "Invalid stored bid response format: empty storedbidresponse.bidder",
   190  			requestJson: []byte(`{"imp": [
   191      			  {
   192      			    "id": "imp-id1",
   193      			    "ext": {
   194      			      "prebid": {
   195      			        "storedbidresponse": [
   196  							{ "id": "123abc"}]
   197      			      }
   198      			    }
   199      			  }
   200      			]}`),
   201  			expectedErrorList: []error{errors.New("request.imp[0] has ext.prebid.storedbidresponse specified, but \"id\" or/and \"bidder\" fields are missing ")},
   202  		},
   203  		{
   204  			description: "Invalid stored bid response format: empty storedbidresponse.id",
   205  			requestJson: []byte(`{"imp": [
   206      			  {
   207      			    "id": "imp-id1",
   208      			    "ext": {
   209      			      "prebid": {
   210      			        "storedbidresponse": [
   211  							{ "bidder": "testbidder"}]
   212      			      }
   213      			    }
   214      			  }
   215      			]}`),
   216  			expectedErrorList: []error{errors.New("request.imp[0] has ext.prebid.storedbidresponse specified, but \"id\" or/and \"bidder\" fields are missing ")},
   217  		},
   218  		{
   219  			description: "Invalid stored bid response: storedbidresponse.bidder not found",
   220  			requestJson: []byte(`{"imp": [
   221      			  {
   222      			    "id": "imp-id1",
   223      			    "ext": {
   224      			      "prebid": {
   225      			        "storedbidresponse": [
   226  							{ "bidder": "testBidder123", "id": "123abc"}]
   227      			      }
   228      			    }
   229      			  }
   230      			]}`),
   231  			expectedErrorList: []error{errors.New("request.imp[impId: imp-id1].ext.prebid.bidder contains unknown bidder: testBidder123. Did you forget an alias in request.ext.prebid.aliases?")},
   232  		},
   233  		{
   234  			description: "Invalid stored auction response format: empty stored Auction Response Id in second imp",
   235  			requestJson: []byte(`{"imp": [
   236      			  {
   237      			    "id": "imp-id1",
   238      			    "ext": {
   239      			      "prebid": {
   240      			        "storedauctionresponse": {
   241  							"id":"123"
   242      			        }
   243      			      }
   244      			    }
   245      			  },
   246  			      {
   247      			    "id": "imp-id2",
   248      			    "ext": {
   249      			      "prebid": {
   250      			        "storedauctionresponse": {
   251  							"id":""
   252      			        }
   253      			      }
   254      			    }
   255      			  }
   256      			]}`),
   257  			expectedErrorList: []error{errors.New("request.imp[1] has ext.prebid.storedauctionresponse specified, but \"id\" field is missing ")},
   258  		},
   259  		{
   260  			description: "Invalid stored bid response format: empty stored bid Response Id in second imp",
   261  			requestJson: []byte(`{"imp": [
   262      			  {
   263      			    "id": "imp-id1",
   264      			    "ext": {
   265      			      "prebid": {
   266      			        "storedbidresponse": [
   267                               {"bidder":"testBidder", "id": "123abc"}
   268                          ]
   269      			      }
   270      			    }
   271      			  },
   272  			      {
   273      			    "id": "imp-id2",
   274      			    "ext": {
   275      			      "prebid": {
   276      			        "storedbidresponse": [
   277                               {"bidder":"testBidder", "id": ""}
   278                          ]
   279      			      }
   280      			    }
   281      			  }
   282      			]}`),
   283  			expectedErrorList: []error{errors.New("request.imp[1] has ext.prebid.storedbidresponse specified, but \"id\" or/and \"bidder\" fields are missing ")},
   284  		},
   285  	}
   286  
   287  	for _, test := range testCases {
   288  		_, _, _, errorList := ProcessStoredResponses(nil, test.requestJson, nil, bidderMap)
   289  		assert.Equalf(t, test.expectedErrorList, errorList, "Error doesn't match: %s\n", test.description)
   290  	}
   291  
   292  }
   293  
   294  func TestProcessStoredAuctionAndBidResponses(t *testing.T) {
   295  	bidderMap := map[string]openrtb_ext.BidderName{"bidderA": "bidderA", "bidderB": "bidderB"}
   296  	bidStoredResp1 := json.RawMessage(`[{"bid": [{"id": "bid_id1"],"seat": "bidderA"}]`)
   297  	bidStoredResp2 := json.RawMessage(`[{"bid": [{"id": "bid_id2"],"seat": "bidderB"}]`)
   298  	bidStoredResp3 := json.RawMessage(`[{"bid": [{"id": "bid_id3"],"seat": "bidderA"}]`)
   299  	mockStoredResponses := map[string]json.RawMessage{
   300  		"1": bidStoredResp1,
   301  		"2": bidStoredResp2,
   302  		"3": bidStoredResp3,
   303  	}
   304  	fetcher := &mockStoredBidResponseFetcher{mockStoredResponses}
   305  
   306  	testCases := []struct {
   307  		description                    string
   308  		requestJson                    []byte
   309  		expectedStoredAuctionResponses ImpsWithBidResponses
   310  		expectedStoredBidResponses     ImpBidderStoredResp
   311  		expectedBidderImpReplaceImpID  BidderImpReplaceImpID
   312  	}{
   313  		{
   314  			description: "No stored responses",
   315  			requestJson: []byte(`{"imp": [
   316      			  {
   317      			    "id": "imp-id1",
   318      			    "ext": {
   319      			      "prebid": {
   320      			        
   321      			      }
   322      			    }
   323      			  }
   324      			]}`),
   325  			expectedStoredAuctionResponses: nil,
   326  			expectedStoredBidResponses:     nil,
   327  			expectedBidderImpReplaceImpID:  nil,
   328  		},
   329  		{
   330  			description: "Stored auction response one imp",
   331  			requestJson: []byte(`{"imp": [
   332      			  {
   333      			    "id": "imp-id1",
   334      			    "ext": {
   335                  		"appnexus": {
   336  							"placementId": 123
   337                  		},
   338                  		"prebid": {
   339                      		"storedauctionresponse": {
   340                          		"id": "1"
   341                      		}
   342                  		}
   343              		}
   344      			  }
   345      			]}`),
   346  			expectedStoredAuctionResponses: ImpsWithBidResponses{
   347  				"imp-id1": bidStoredResp1,
   348  			},
   349  			expectedStoredBidResponses:    ImpBidderStoredResp{},
   350  			expectedBidderImpReplaceImpID: BidderImpReplaceImpID{},
   351  		},
   352  		{
   353  			description: "Stored bid response one imp",
   354  			requestJson: []byte(`{"imp": [
   355      			  {
   356      			    "id": "imp-id1",
   357      			    "ext": {
   358                  		"appnexus": {
   359  							"placementId": 123
   360                  		},
   361                  		"prebid": {
   362                      		"storedbidresponse": [
   363                          		{"bidder":"bidderA", "id": "1"}
   364                      		]
   365                  		}
   366              		}
   367      			  }
   368      			]}`),
   369  			expectedStoredAuctionResponses: ImpsWithBidResponses{},
   370  			expectedStoredBidResponses: ImpBidderStoredResp{
   371  				"imp-id1": {"bidderA": bidStoredResp1},
   372  			},
   373  			expectedBidderImpReplaceImpID: BidderImpReplaceImpID{
   374  				"bidderA": map[string]bool{"imp-id1": true},
   375  			},
   376  		},
   377  		{
   378  			description: "Stored bid responses two bidders one imp",
   379  			requestJson: []byte(`{"imp": [
   380      			  {
   381      			    "id": "imp-id1",
   382      			    "ext": {
   383                  		"appnexus": {
   384  							"placementId": 123
   385                  		},
   386                  		"prebid": {
   387                      		"storedbidresponse": [
   388                          		{"bidder":"bidderA", "id": "1", "replaceimpid": true},
   389                          		{"bidder":"bidderB", "id": "2", "replaceimpid": false}
   390                      		]
   391                  		}
   392              		}
   393      			  }
   394      			]}`),
   395  			expectedStoredAuctionResponses: ImpsWithBidResponses{},
   396  			expectedStoredBidResponses: ImpBidderStoredResp{
   397  				"imp-id1": {"bidderA": bidStoredResp1, "bidderB": bidStoredResp2},
   398  			},
   399  			expectedBidderImpReplaceImpID: BidderImpReplaceImpID{
   400  				"bidderA": map[string]bool{"imp-id1": true},
   401  				"bidderB": map[string]bool{"imp-id1": false},
   402  			},
   403  		},
   404  		{
   405  			//This is not a valid scenario for real auction request, added for testing purposes
   406  			description: "Stored auction and bid responses one imp",
   407  			requestJson: []byte(`{"imp": [
   408      			  {
   409      			    "id": "imp-id1",
   410      			    "ext": {
   411                  		"appnexus": {
   412  							"placementId": 123
   413                  		},
   414                  		"prebid": {
   415  							"storedauctionresponse": {
   416                          		"id": "1"
   417                      		},
   418                      		"storedbidresponse": [
   419                          		{"bidder":"bidderA", "id": "1"},
   420                          		{"bidder":"bidderB", "id": "2"}
   421                      		]
   422                  		}
   423              		}
   424      			  }
   425      			]}`),
   426  			expectedStoredAuctionResponses: ImpsWithBidResponses{
   427  				"imp-id1": bidStoredResp1,
   428  			},
   429  			expectedStoredBidResponses: ImpBidderStoredResp{
   430  				"imp-id1": {"bidderA": bidStoredResp1, "bidderB": bidStoredResp2},
   431  			},
   432  			expectedBidderImpReplaceImpID: BidderImpReplaceImpID{
   433  				"bidderA": map[string]bool{"imp-id1": true},
   434  				"bidderB": map[string]bool{"imp-id1": true},
   435  			},
   436  		},
   437  		{
   438  			description: "Stored auction response three imps",
   439  			requestJson: []byte(`{"imp": [
   440      			  {
   441      			    "id": "imp-id1",
   442      			    "ext": {
   443                  		"appnexus": {
   444  							"placementId": 123
   445                  		},
   446                  		"prebid": {
   447                      		"storedauctionresponse": {
   448                          		"id": "1"
   449                      		}
   450                  		}
   451              		}
   452      			  },
   453  					{
   454      			    "id": "imp-id2",
   455      			    "ext": {
   456                  		"appnexus": {
   457  							"placementId": 123
   458                  		},
   459                  		"prebid": {
   460                      		"storedauctionresponse": {
   461                          		"id": "2"
   462                      		}
   463                  		}
   464              		}
   465      			  },
   466  					{
   467      			    "id": "imp-id3",
   468      			    "ext": {
   469                  		"appnexus": {
   470  							"placementId": 123
   471                  		},
   472                  		"prebid": {
   473                      		"storedauctionresponse": {
   474                          		"id": "3"
   475                      		}
   476                  		}
   477              		}
   478      			  }
   479      			]}`),
   480  			expectedStoredAuctionResponses: ImpsWithBidResponses{
   481  				"imp-id1": bidStoredResp1,
   482  				"imp-id2": bidStoredResp2,
   483  				"imp-id3": bidStoredResp3,
   484  			},
   485  			expectedStoredBidResponses:    ImpBidderStoredResp{},
   486  			expectedBidderImpReplaceImpID: BidderImpReplaceImpID{},
   487  		},
   488  		{
   489  			description: "Stored auction response three imps duplicated stored auction response",
   490  			requestJson: []byte(`{"imp": [
   491      			  {
   492      			    "id": "imp-id1",
   493      			    "ext": {
   494                  		"appnexus": {
   495  							"placementId": 123
   496                  		},
   497                  		"prebid": {
   498                      		"storedauctionresponse": {
   499                          		"id": "1"
   500                      		}
   501                  		}
   502              		}
   503      			  },
   504  					{
   505      			    "id": "imp-id2",
   506      			    "ext": {
   507                  		"appnexus": {
   508  							"placementId": 123
   509                  		},
   510                  		"prebid": {
   511                      		"storedauctionresponse": {
   512                          		"id": "2"
   513                      		}
   514                  		}
   515              		}
   516      			  },
   517  					{
   518      			    "id": "imp-id3",
   519      			    "ext": {
   520                  		"appnexus": {
   521  							"placementId": 123
   522                  		},
   523                  		"prebid": {
   524                      		"storedauctionresponse": {
   525                          		"id": "2"
   526                      		}
   527                  		}
   528              		}
   529      			  }
   530      			]}`),
   531  			expectedStoredAuctionResponses: ImpsWithBidResponses{
   532  				"imp-id1": bidStoredResp1,
   533  				"imp-id2": bidStoredResp2,
   534  				"imp-id3": bidStoredResp2,
   535  			},
   536  			expectedStoredBidResponses:    ImpBidderStoredResp{},
   537  			expectedBidderImpReplaceImpID: BidderImpReplaceImpID{},
   538  		},
   539  		{
   540  			description: "Stored bid responses two bidders two imp",
   541  			requestJson: []byte(`{"imp": [
   542      			  {
   543      			    "id": "imp-id1",
   544      			    "ext": {
   545                  		"appnexus": {
   546  							"placementId": 123
   547                  		},
   548                  		"prebid": {
   549                      		"storedbidresponse": [
   550                          		{"bidder":"bidderA", "id": "1", "replaceimpid": false},
   551                          		{"bidder":"bidderB", "id": "2"}
   552                      		]
   553                  		}
   554              		}
   555      			  },
   556  					{
   557      			    "id": "imp-id2",
   558      			    "ext": {
   559                  		"appnexus": {
   560  							"placementId": 123
   561                  		},
   562                  		"prebid": {
   563                      		"storedbidresponse": [
   564                          		{"bidder":"bidderA", "id": "3"},
   565                          		{"bidder":"bidderB", "id": "2", "replaceimpid": false}
   566                      		]
   567                  		}
   568              		}
   569      			  }
   570      			]}`),
   571  			expectedStoredAuctionResponses: ImpsWithBidResponses{},
   572  			expectedStoredBidResponses: ImpBidderStoredResp{
   573  				"imp-id1": {"bidderA": bidStoredResp1, "bidderB": bidStoredResp2},
   574  				"imp-id2": {"bidderA": bidStoredResp3, "bidderB": bidStoredResp2},
   575  			},
   576  			expectedBidderImpReplaceImpID: BidderImpReplaceImpID{
   577  				"bidderA": map[string]bool{"imp-id1": false, "imp-id2": true},
   578  				"bidderB": map[string]bool{"imp-id1": true, "imp-id2": false},
   579  			},
   580  		},
   581  	}
   582  
   583  	for _, test := range testCases {
   584  		storedAuctionResponses, storedBidResponses, bidderImpReplaceImpId, errorList := ProcessStoredResponses(nil, test.requestJson, fetcher, bidderMap)
   585  		assert.Equal(t, test.expectedStoredAuctionResponses, storedAuctionResponses, "storedAuctionResponses doesn't match: %s\n", test.description)
   586  		assert.Equalf(t, test.expectedStoredBidResponses, storedBidResponses, "storedBidResponses doesn't match: %s\n", test.description)
   587  		assert.Equal(t, test.expectedBidderImpReplaceImpID, bidderImpReplaceImpId, "bidderImpReplaceImpId doesn't match: %s\n", test.description)
   588  		assert.Nil(t, errorList, "Error should be nil")
   589  	}
   590  
   591  }
   592  
   593  func TestProcessStoredResponsesNotFoundResponse(t *testing.T) {
   594  	bidderMap := map[string]openrtb_ext.BidderName{"bidderA": "bidderA", "bidderB": "bidderB"}
   595  	bidStoredResp1 := json.RawMessage(`[{"bid": [{"id": "bid_id1"],"seat": "bidderA"}]`)
   596  	bidStoredResp2 := json.RawMessage(`[{"bid": [{"id": "bid_id2"],"seat": "bidderB"}]`)
   597  	mockStoredResponses := map[string]json.RawMessage{
   598  		"1": bidStoredResp1,
   599  		"2": bidStoredResp2,
   600  		"3": nil,
   601  		"4": nil,
   602  	}
   603  	fetcher := &mockStoredBidResponseFetcher{mockStoredResponses}
   604  
   605  	testCases := []struct {
   606  		description    string
   607  		requestJson    []byte
   608  		expectedErrors []error
   609  	}{
   610  		{
   611  			description: "Stored bid response with nil data, one bidder one imp",
   612  			requestJson: []byte(`{"imp": [
   613      			  {
   614      			    "id": "imp-id1",
   615      			    "ext": {
   616                  		"appnexus": {
   617  							"placementId": 123
   618                  		},
   619                  		"prebid": {
   620                      		"storedbidresponse": [
   621                          		{"bidder":"bidderB", "id": "3"}
   622                      		]
   623                  		}
   624              		}
   625      			  }
   626      			]}`),
   627  			expectedErrors: []error{
   628  				errors.New("failed to fetch stored bid response for impId = imp-id1, bidder = bidderB and storedBidResponse id = 3"),
   629  			},
   630  		},
   631  		{
   632  			description: "Stored bid response with nil data, one bidder, two imps, one with correct stored response",
   633  			requestJson: []byte(`{"imp": [
   634      			  {
   635      			    "id": "imp-id1",
   636      			    "ext": {
   637                  		"appnexus": {
   638  							"placementId": 123
   639                  		},
   640                  		"prebid": {
   641                      		"storedbidresponse": [
   642                          		{"bidder":"bidderB", "id": "1"}
   643                      		]
   644                  		}
   645              		}
   646      			  },
   647                    {
   648      			    "id": "imp-id2",
   649      			    "ext": {
   650                  		"appnexus": {
   651  							"placementId": 123
   652                  		},
   653                  		"prebid": {
   654                      		"storedbidresponse": [
   655                          		{"bidder":"bidderB", "id": "3"}
   656                      		]
   657                  		}
   658              		}
   659      			  }
   660      			]}`),
   661  			expectedErrors: []error{
   662  				errors.New("failed to fetch stored bid response for impId = imp-id2, bidder = bidderB and storedBidResponse id = 3"),
   663  			},
   664  		},
   665  		{
   666  			description: "Stored bid response with nil data, one bidder, two imps, both with correct stored response",
   667  			requestJson: []byte(`{"imp": [
   668      			  {
   669      			    "id": "imp-id1",
   670      			    "ext": {
   671                  		"appnexus": {
   672  							"placementId": 123
   673                  		},
   674                  		"prebid": {
   675                      		"storedbidresponse": [
   676                          		{"bidder":"bidderB", "id": "4"}
   677                      		]
   678                  		}
   679              		}
   680      			  },
   681                    {
   682      			    "id": "imp-id2",
   683      			    "ext": {
   684                  		"appnexus": {
   685  							"placementId": 123
   686                  		},
   687                  		"prebid": {
   688                      		"storedbidresponse": [
   689                          		{"bidder":"bidderB", "id": "3"}
   690                      		]
   691                  		}
   692              		}
   693      			  }
   694      			]}`),
   695  			expectedErrors: []error{
   696  				errors.New("failed to fetch stored bid response for impId = imp-id1, bidder = bidderB and storedBidResponse id = 4"),
   697  				errors.New("failed to fetch stored bid response for impId = imp-id2, bidder = bidderB and storedBidResponse id = 3"),
   698  			},
   699  		},
   700  		{
   701  			description: "Stored auction response with nil data and one imp",
   702  			requestJson: []byte(`{"imp": [
   703      			  {
   704      			    "id": "imp-id1",
   705      			    "ext": {
   706                  		"appnexus": {
   707  							"placementId": 123
   708                  		},
   709                  		"prebid": {
   710                      		"storedauctionresponse": {
   711                          		"id": "4"
   712                      		}
   713                  		}
   714              		}
   715      			  }
   716      			]}`),
   717  			expectedErrors: []error{
   718  				errors.New("failed to fetch stored auction response for impId = imp-id1 and storedAuctionResponse id = 4"),
   719  			},
   720  		},
   721  		{
   722  			description: "Stored auction response with nil data, and two imps with nil responses",
   723  			requestJson: []byte(`{"imp": [
   724      			  {
   725      			    "id": "imp-id1",
   726      			    "ext": {
   727                  		"appnexus": {
   728  							"placementId": 123
   729                  		},
   730                  		"prebid": {
   731                      		"storedauctionresponse": {
   732                          		"id": "4"
   733                      		}
   734                  		}
   735              		}
   736      			  },
   737                    {
   738      			    "id": "imp-id2",
   739      			    "ext": {
   740                  		"appnexus": {
   741  							"placementId": 123
   742                  		},
   743                  		"prebid": {
   744                      		"storedauctionresponse": {
   745                          		"id": "3"
   746                      		}
   747                  		}
   748              		}
   749      			  }
   750      			]}`),
   751  			expectedErrors: []error{
   752  				errors.New("failed to fetch stored auction response for impId = imp-id1 and storedAuctionResponse id = 4"),
   753  				errors.New("failed to fetch stored auction response for impId = imp-id2 and storedAuctionResponse id = 3"),
   754  			},
   755  		},
   756  		{
   757  			description: "Stored auction response with nil data, two imps, one with nil responses",
   758  			requestJson: []byte(`{"imp": [
   759      			  {
   760      			    "id": "imp-id1",
   761      			    "ext": {
   762                  		"appnexus": {
   763  							"placementId": 123
   764                  		},
   765                  		"prebid": {
   766                      		"storedauctionresponse": {
   767                          		"id": "2"
   768                      		}
   769                  		}
   770              		}
   771      			  },
   772                    {
   773      			    "id": "imp-id2",
   774      			    "ext": {
   775                  		"appnexus": {
   776  							"placementId": 123
   777                  		},
   778                  		"prebid": {
   779                      		"storedauctionresponse": {
   780                          		"id": "3"
   781                      		}
   782                  		}
   783              		}
   784      			  }
   785      			]}`),
   786  			expectedErrors: []error{
   787  				errors.New("failed to fetch stored auction response for impId = imp-id2 and storedAuctionResponse id = 3"),
   788  			},
   789  		},
   790  	}
   791  
   792  	for _, test := range testCases {
   793  		_, _, _, errorList := ProcessStoredResponses(nil, test.requestJson, fetcher, bidderMap)
   794  		for _, err := range test.expectedErrors {
   795  			assert.Contains(t, errorList, err, "incorrect errors returned: %s", test.description)
   796  		}
   797  	}
   798  }
   799  
   800  func TestFlipMap(t *testing.T) {
   801  	testCases := []struct {
   802  		description              string
   803  		inImpBidderReplaceImpID  ImpBidderReplaceImpID
   804  		outBidderImpReplaceImpID BidderImpReplaceImpID
   805  	}{
   806  		{
   807  			description:              "Empty ImpBidderReplaceImpID",
   808  			inImpBidderReplaceImpID:  ImpBidderReplaceImpID{},
   809  			outBidderImpReplaceImpID: BidderImpReplaceImpID{},
   810  		},
   811  		{
   812  			description:              "Nil ImpBidderReplaceImpID",
   813  			inImpBidderReplaceImpID:  nil,
   814  			outBidderImpReplaceImpID: BidderImpReplaceImpID{},
   815  		},
   816  		{
   817  			description:              "ImpBidderReplaceImpID has a one element map with single element",
   818  			inImpBidderReplaceImpID:  ImpBidderReplaceImpID{"imp-id": {"bidderA": true}},
   819  			outBidderImpReplaceImpID: BidderImpReplaceImpID{"bidderA": {"imp-id": true}},
   820  		},
   821  		{
   822  			description:              "ImpBidderReplaceImpID has a one element map with multiple elements",
   823  			inImpBidderReplaceImpID:  ImpBidderReplaceImpID{"imp-id": {"bidderA": true, "bidderB": false}},
   824  			outBidderImpReplaceImpID: BidderImpReplaceImpID{"bidderA": {"imp-id": true}, "bidderB": {"imp-id": false}},
   825  		},
   826  		{
   827  			description: "ImpBidderReplaceImpID has multiple elements map with single element",
   828  			inImpBidderReplaceImpID: ImpBidderReplaceImpID{
   829  				"imp-id1": {"bidderA": true},
   830  				"imp-id2": {"bidderB": false}},
   831  			outBidderImpReplaceImpID: BidderImpReplaceImpID{
   832  				"bidderA": {"imp-id1": true},
   833  				"bidderB": {"imp-id2": false}},
   834  		},
   835  		{
   836  			description: "ImpBidderReplaceImpID has multiple elements map with multiple elements",
   837  			inImpBidderReplaceImpID: ImpBidderReplaceImpID{
   838  				"imp-id1": {"bidderA": true, "bidderB": false, "bidderC": false, "bidderD": true},
   839  				"imp-id2": {"bidderA": false, "bidderB": false, "bidderC": true, "bidderD": true},
   840  				"imp-id3": {"bidderA": false, "bidderB": true, "bidderC": true, "bidderD": false}},
   841  			outBidderImpReplaceImpID: BidderImpReplaceImpID{
   842  				"bidderA": {"imp-id1": true, "imp-id2": false, "imp-id3": false},
   843  				"bidderB": {"imp-id1": false, "imp-id2": false, "imp-id3": true},
   844  				"bidderC": {"imp-id1": false, "imp-id2": true, "imp-id3": true},
   845  				"bidderD": {"imp-id1": true, "imp-id2": true, "imp-id3": false}},
   846  		},
   847  	}
   848  
   849  	for _, test := range testCases {
   850  		actualResult := flipMap(test.inImpBidderReplaceImpID)
   851  		assert.Equal(t, test.outBidderImpReplaceImpID, actualResult, "Incorrect flipped map for test case %s\n", test.description)
   852  	}
   853  }
   854  
   855  type mockStoredBidResponseFetcher struct {
   856  	data map[string]json.RawMessage
   857  }
   858  
   859  func (cf *mockStoredBidResponseFetcher) FetchRequests(ctx context.Context, requestIDs []string, impIDs []string) (requestData map[string]json.RawMessage, impData map[string]json.RawMessage, errs []error) {
   860  	return nil, nil, nil
   861  }
   862  
   863  func (cf *mockStoredBidResponseFetcher) FetchResponses(ctx context.Context, ids []string) (data map[string]json.RawMessage, errs []error) {
   864  	return cf.data, nil
   865  }