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  }