github.com/prebid/prebid-server@v0.275.0/endpoints/openrtb2/test_utils.go (about)

     1  package openrtb2
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"net"
    10  	"net/http"
    11  	"net/http/httptest"
    12  	"os"
    13  	"strconv"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/buger/jsonparser"
    18  	"github.com/julienschmidt/httprouter"
    19  	"github.com/prebid/openrtb/v19/openrtb2"
    20  	"github.com/prebid/openrtb/v19/openrtb3"
    21  	"github.com/prebid/prebid-server/adapters"
    22  	"github.com/prebid/prebid-server/analytics"
    23  	analyticsConf "github.com/prebid/prebid-server/analytics/config"
    24  	"github.com/prebid/prebid-server/config"
    25  	"github.com/prebid/prebid-server/currency"
    26  	"github.com/prebid/prebid-server/errortypes"
    27  	"github.com/prebid/prebid-server/exchange"
    28  	"github.com/prebid/prebid-server/experiment/adscert"
    29  	"github.com/prebid/prebid-server/gdpr"
    30  	"github.com/prebid/prebid-server/hooks"
    31  	"github.com/prebid/prebid-server/hooks/hookexecution"
    32  	"github.com/prebid/prebid-server/hooks/hookstage"
    33  	"github.com/prebid/prebid-server/macros"
    34  	"github.com/prebid/prebid-server/metrics"
    35  	metricsConfig "github.com/prebid/prebid-server/metrics/config"
    36  	"github.com/prebid/prebid-server/openrtb_ext"
    37  	pbc "github.com/prebid/prebid-server/prebid_cache_client"
    38  	"github.com/prebid/prebid-server/stored_requests"
    39  	"github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher"
    40  	"github.com/prebid/prebid-server/util/iputil"
    41  	"github.com/prebid/prebid-server/util/uuidutil"
    42  	jsonpatch "gopkg.in/evanphx/json-patch.v4"
    43  )
    44  
    45  // In this file we define:
    46  //  - Auxiliary types
    47  //  - Unit test interface implementations such as mocks
    48  //  - Other auxiliary functions that don't make assertions and don't take t *testing.T as parameter
    49  //
    50  // All of the above are useful for this package's unit test framework.
    51  
    52  // ----------------------
    53  // test auxiliary types
    54  // ----------------------
    55  const maxSize = 1024 * 256
    56  
    57  const (
    58  	AMP_ENDPOINT = iota
    59  	OPENRTB_ENDPOINT
    60  	VIDEO_ENDPOINT
    61  )
    62  
    63  type testCase struct {
    64  	// Common
    65  	endpointType            int
    66  	Description             string            `json:"description"`
    67  	Config                  *testConfigValues `json:"config"`
    68  	BidRequest              json.RawMessage   `json:"mockBidRequest"`
    69  	ExpectedValidatedBidReq json.RawMessage   `json:"expectedValidatedBidRequest"`
    70  	ExpectedReturnCode      int               `json:"expectedReturnCode,omitempty"`
    71  	ExpectedErrorMessage    string            `json:"expectedErrorMessage"`
    72  	Query                   string            `json:"query"`
    73  	planBuilder             hooks.ExecutionPlanBuilder
    74  
    75  	// "/openrtb2/auction" endpoint JSON test info
    76  	ExpectedBidResponse json.RawMessage `json:"expectedBidResponse"`
    77  
    78  	// "/openrtb2/amp" endpoint JSON test info
    79  	StoredRequest       map[string]json.RawMessage `json:"mockAmpStoredRequest"`
    80  	StoredResponse      map[string]json.RawMessage `json:"mockAmpStoredResponse"`
    81  	ExpectedAmpResponse json.RawMessage            `json:"expectedAmpResponse"`
    82  }
    83  
    84  type testConfigValues struct {
    85  	AccountRequired     bool                          `json:"accountRequired"`
    86  	AliasJSON           string                        `json:"aliases"`
    87  	BlacklistedAccounts []string                      `json:"blacklistedAccts"`
    88  	BlacklistedApps     []string                      `json:"blacklistedApps"`
    89  	DisabledAdapters    []string                      `json:"disabledAdapters"`
    90  	CurrencyRates       map[string]map[string]float64 `json:"currencyRates"`
    91  	MockBidders         []mockBidderHandler           `json:"mockBidders"`
    92  	RealParamsValidator bool                          `json:"realParamsValidator"`
    93  }
    94  
    95  type brokenExchange struct{}
    96  
    97  func (e *brokenExchange) HoldAuction(ctx context.Context, r *exchange.AuctionRequest, debugLog *exchange.DebugLog) (*exchange.AuctionResponse, error) {
    98  	return nil, errors.New("Critical, unrecoverable error.")
    99  }
   100  
   101  // Stored Requests
   102  var testStoredRequestData = map[string]json.RawMessage{
   103  	// Valid JSON
   104  	"1": json.RawMessage(`{"id": "{{UUID}}"}`),
   105  	"2": json.RawMessage(`{
   106  		"id": "{{uuid}}",
   107  		"tmax": 500,
   108  		"ext": {
   109  			"prebid": {
   110  				"targeting": {
   111  					"pricegranularity": "low"
   112  				}
   113  			}
   114  		}
   115  	}`),
   116  	// Invalid JSON because it comes with an extra closing curly brace '}'
   117  	"3": json.RawMessage(`{
   118  		"tmax": 500,
   119  				"ext": {
   120  						"prebid": {
   121  								"targeting": {
   122  										"pricegranularity": "low"
   123  								}
   124  						}
   125  				}}
   126  		}`),
   127  	// Valid JSON
   128  	"4": json.RawMessage(`{"id": "ThisID", "cur": ["USD"]}`),
   129  
   130  	// Stored Request with Root Ext Passthrough
   131  	"5": json.RawMessage(`{
   132  		"ext": {
   133  			"prebid": {
   134  				"passthrough": {
   135  					"root_ext_passthrough": 20
   136  				}
   137  			}
   138  		}
   139  	}`),
   140  }
   141  
   142  // Stored Imp Requests
   143  var testStoredImpData = map[string]json.RawMessage{
   144  	// Has valid JSON and matches schema
   145  	"1": json.RawMessage(`{
   146  			"id": "adUnit1",
   147  			"ext": {
   148  				"appnexus": {
   149  					"placementId": "abc",
   150  					"position": "above",
   151  					"reserve": 0.35
   152  				},
   153  				"rubicon": {
   154  					"accountId": "abc"
   155  				}
   156  			},
   157  			"video":{
   158  				"w":200,
   159  				"h":300
   160  			}
   161  		}`),
   162  	// Has valid JSON, matches schema but is missing video object
   163  	"2": json.RawMessage(`{
   164  			"id": "adUnit1",
   165  			"ext": {
   166  				"appnexus": {
   167  					"placementId": "abc",
   168  					"position": "above",
   169  					"reserve": 0.35
   170  				},
   171  				"rubicon": {
   172  					"accountId": "abc"
   173  				}
   174  			}
   175  		}`),
   176  	// Invalid JSON, is missing a coma after the rubicon's "accountId" field
   177  	"7": json.RawMessage(`{
   178  			"id": "adUnit1",
   179  			"ext": {
   180  				"appnexus": {
   181  					"placementId": 12345678,
   182  					"position": "above",
   183  					"reserve": 0.35
   184  				},
   185  				"rubicon": {
   186  					"accountId": 23456789
   187  					"siteId": 113932,
   188  					"zoneId": 535510
   189  				}
   190  			}
   191  		}`),
   192  	// Valid JSON. Missing video object
   193  	"9": json.RawMessage(`{
   194  			"id": "adUnit1",
   195  			"ext": {
   196  				"appnexus": {
   197  					"placementId": 12345678,
   198  					"position": "above",
   199  					"reserve": 0.35
   200  				},
   201  				"rubicon": {
   202  					"accountId": 23456789,
   203  					"siteId": 113932,
   204  					"zoneId": 535510
   205  				}
   206  			}
   207  		}`),
   208  	// Valid JSON. Missing video object
   209  	"10": json.RawMessage(`{
   210  			"ext": {
   211  				"appnexus": {
   212  					"placementId": 12345678,
   213  					"position": "above",
   214  					"reserve": 0.35
   215  				}
   216  			}
   217  		}`),
   218  	// Stored Imp with Passthrough
   219  	"6": json.RawMessage(`{
   220  		"id": "my-imp-id",
   221  		"ext": {
   222  			"prebid": {
   223  				"passthrough": {
   224  					"imp_passthrough": 30
   225  				}
   226  			}
   227  		}
   228  	}`),
   229  }
   230  
   231  // Incoming requests with stored request IDs
   232  var testStoredRequests = []string{
   233  	`{
   234  		"id": "ThisID",
   235  		"imp": [
   236  			{
   237  				"video":{
   238  					"h":300,
   239  					"w":200
   240  				},
   241  				"ext": {
   242  					"prebid": {
   243  						"storedrequest": {
   244  							"id": "1"
   245  						},
   246  						"options": {
   247  							"echovideoattrs": true
   248  						}
   249  					}
   250  				}
   251  			}
   252  		],
   253  		"ext": {
   254  			"prebid": {
   255  				"cache": {
   256  					"markup": 1
   257  				},
   258  				"targeting": {
   259  				}
   260  			}
   261  		}
   262  	}`,
   263  	`{
   264  		"id": "ThisID",
   265  		"imp": [
   266  			{
   267  				"id": "adUnit2",
   268  				"ext": {
   269  					"prebid": {
   270  						"storedrequest": {
   271  							"id": "1"
   272  						},
   273  						"options": {
   274  							"echovideoattrs": true
   275  						}
   276  					},
   277  					"appnexus": {
   278  						"placementId": "def",
   279  						"trafficSourceCode": "mysite.com",
   280  						"reserve": null
   281  					},
   282  					"rubicon": null
   283  				}
   284  			}
   285  		],
   286  		"ext": {
   287  			"prebid": {
   288  				"cache": {
   289  					"markup": 1
   290  				},
   291  				"targeting": {
   292  				}
   293  			}
   294  		}
   295  	}`,
   296  	`{
   297  		"id": "ThisID",
   298  		"imp": [
   299  			{
   300  				"ext": {
   301  					"prebid": {
   302  						"storedrequest": {
   303  							"id": "2"
   304  						},
   305  						"options": {
   306  							"echovideoattrs": false
   307  						}
   308  					}
   309  				}
   310  			}
   311  		],
   312  		"ext": {
   313  			"prebid": {
   314  				"storedrequest": {
   315  					"id": "2"
   316  				}
   317  			}
   318  		}
   319  	}`,
   320  	`{
   321  		"id": "ThisID",
   322  		"imp": [
   323  			{
   324  				"id": "some-static-imp",
   325  				"video":{
   326  					"mimes":["video/mp4"]
   327  				},
   328  				"ext": {
   329  					"appnexus": {
   330  						"placementId": "abc",
   331  						"position": "below"
   332  					}
   333  				}
   334  			},
   335  			{
   336  				"ext": {
   337  					"prebid": {
   338  						"storedrequest": {
   339  							"id": "1"
   340  						}
   341  					}
   342  				}
   343  			}
   344  		],
   345  		"ext": {
   346  			"prebid": {
   347  				"cache": {
   348  					"markup": 1
   349  				},
   350  				"targeting": {
   351  				}
   352  			}
   353  		}
   354  	}`,
   355  	`{
   356  		"id": "ThisID",
   357  		"imp": [
   358  			{
   359  				"id": "my-imp-id",
   360  				"video":{
   361  					"h":300,
   362  					"w":200
   363  				},
   364  				"ext": {
   365  					"prebid": {
   366  						"storedrequest": {
   367  							"id": "6"
   368  						}
   369  					}
   370  				}
   371  			}
   372  		],
   373  		"ext": {
   374  			"prebid": {
   375  				"storedrequest": {
   376  					"id": "5"
   377  				}
   378  			}
   379  		}
   380  	}`,
   381  }
   382  
   383  // The expected requests after stored request processing
   384  var testFinalRequests = []string{
   385  	`{
   386  		"id": "ThisID",
   387  		"imp": [
   388  			{
   389  				"video":{
   390  					"h":300,
   391  					"w":200
   392  				},
   393  				"ext":{
   394  					"appnexus":{
   395  						"placementId":"abc",
   396  						"position":"above",
   397  						"reserve":0.35
   398  					},
   399  					"prebid":{
   400  						"storedrequest":{
   401  							"id":"1"
   402  						},
   403  					"options":{
   404  						"echovideoattrs":true
   405  					}
   406  				},
   407  				"rubicon":{
   408  					"accountId":"abc"
   409  				}
   410  			},
   411  			"id":"adUnit1"
   412  			}
   413  		],
   414  		"ext": {
   415  			"prebid": {
   416  				"cache": {
   417  					"markup": 1
   418  				},
   419  				"targeting": {
   420  			}
   421  		}
   422  }
   423  	}`,
   424  	`{
   425  		"id": "ThisID",
   426  		"imp": [
   427  			{
   428  				"video":{
   429  					"w":200,
   430  					"h":300
   431  				},
   432  				"ext":{
   433  					"appnexus":{
   434  						"placementId":"def",
   435  						"position":"above",
   436  						"trafficSourceCode":"mysite.com"
   437  					},
   438  					"prebid":{
   439  						"storedrequest":{
   440  							"id":"1"
   441  						},
   442  						"options":{
   443  							"echovideoattrs":true
   444  						}
   445  					}
   446  				},
   447  				"id":"adUnit2"
   448  			}
   449  		],
   450  		"ext": {
   451  			"prebid": {
   452  				"cache": {
   453  					"markup": 1
   454  				},
   455  				"targeting": {
   456  				}
   457  			}
   458  		}
   459  	}`,
   460  	`{
   461    		"ext": {
   462    		  "prebid": {
   463    		    "storedrequest": {
   464    		      "id": "2"
   465    		    },
   466    		    "targeting": {
   467    		      "pricegranularity": "low"
   468    		    }
   469    		  }
   470    		},
   471    		"id": "ThisID",
   472    		"imp": [
   473    		  {
   474    		    "ext": {
   475    		      "appnexus": {
   476    		        "placementId": "abc",
   477    		        "position": "above",
   478    		        "reserve": 0.35
   479    		      },
   480    		      "prebid": {
   481    		        "storedrequest": {
   482    		          "id": "2"
   483    		        },
   484    		        "options":{
   485  					"echovideoattrs":false
   486  				}
   487    		      },
   488    		      "rubicon": {
   489    		        "accountId": "abc"
   490    		      }
   491    		    },
   492    		    "id": "adUnit1"
   493    		  }
   494    		],
   495    		"tmax": 500
   496  	}`,
   497  	`{
   498  	"id": "ThisID",
   499  	"imp": [
   500  		{
   501      		"id": "some-static-imp",
   502      		"video": {
   503      		  "mimes": [
   504      		    "video/mp4"
   505      		  ]
   506      		},
   507      		"ext": {
   508      		  "appnexus": {
   509      		    "placementId": "abc",
   510      		    "position": "below"
   511      		  }
   512      		}
   513    		},
   514    		{
   515    		  "ext": {
   516    		    "appnexus": {
   517    		      "placementId": "abc",
   518    		      "position": "above",
   519    		      "reserve": 0.35
   520    		    },
   521    		    "prebid": {
   522    		      "storedrequest": {
   523    		        "id": "1"
   524    		      }
   525    		    },
   526    		    "rubicon": {
   527    		      "accountId": "abc"
   528    		    }
   529    		  },
   530    		  "id": "adUnit1",
   531  		  "video":{
   532  				"w":200,
   533  				"h":300
   534            }
   535    		}
   536  	],
   537  	"ext": {
   538  		"prebid": {
   539  			"cache": {
   540  				"markup": 1
   541  			},
   542  			"targeting": {
   543  			}
   544  		}
   545  	}
   546  }`,
   547  	`{
   548  	"id": "ThisID",
   549  	"imp": [
   550  		{
   551  			"ext":{
   552  			   "prebid":{
   553  				  "passthrough":{
   554  					 "imp_passthrough":30
   555  				  },
   556  				  "storedrequest":{
   557  					 "id":"6"
   558  				  }
   559  			   }
   560  			},
   561  			"id":"my-imp-id",
   562  			"video":{
   563  			   "h":300,
   564  			   "w":200
   565  			}
   566  		 }
   567  	],
   568  	"ext":{
   569  		"prebid":{
   570  		   "passthrough":{
   571  			  "root_ext_passthrough":20
   572  		   },
   573  		   "storedrequest":{
   574  			  "id":"5"
   575  		   }
   576  		}
   577  	 }
   578  }`,
   579  }
   580  
   581  var testStoredImpIds = []string{
   582  	"adUnit1", "adUnit2", "adUnit1", "some-static-imp", "my-imp-id",
   583  }
   584  
   585  var testStoredImps = []string{
   586  	`{
   587  		"id": "adUnit1",
   588          "ext": {
   589          	"appnexus": {
   590          		"placementId": "abc",
   591          		"position": "above",
   592          		"reserve": 0.35
   593          	},
   594          	"rubicon": {
   595          		"accountId": "abc"
   596          	}
   597          },
   598  		"video":{
   599          	"w":200,
   600          	"h":300
   601  		}
   602  	}`,
   603  	`{
   604  		"id": "adUnit1",
   605          "ext": {
   606          	"appnexus": {
   607          		"placementId": "abc",
   608          		"position": "above",
   609          		"reserve": 0.35
   610          	},
   611          	"rubicon": {
   612          		"accountId": "abc"
   613          	}
   614          },
   615  		"video":{
   616          	"w":200,
   617          	"h":300
   618  		}
   619  	}`,
   620  	`{
   621  			"id": "adUnit1",
   622  			"ext": {
   623  				"appnexus": {
   624  					"placementId": "abc",
   625  					"position": "above",
   626  					"reserve": 0.35
   627  				},
   628  				"rubicon": {
   629  					"accountId": "abc"
   630  				}
   631  			}
   632  		}`,
   633  	``,
   634  	`{
   635  		"id": "my-imp-id",
   636  		"ext": {
   637  			"prebid": {
   638  				"passthrough": {
   639  					"imp_passthrough": 30
   640  				}
   641  			}
   642  		}
   643  	}`,
   644  }
   645  
   646  var testBidRequests = []string{
   647  	`{
   648  		"id": "ThisID",
   649  		"app": {
   650  			"id": "123"
   651  		},
   652  		"imp": [
   653  			{
   654  				"video":{
   655  					"h":300,
   656  					"w":200
   657  				},
   658  				"ext": {
   659  					"prebid": {
   660  						"storedrequest": {
   661  							"id": "1"
   662  						},
   663  						"options": {
   664  							"echovideoattrs": true
   665  						}
   666  					}
   667  				}
   668  			}
   669  		],
   670  		"ext": {
   671  			"prebid": {
   672  				"cache": {
   673  					"markup": 1
   674  				},
   675  				"targeting": {
   676  				}
   677  			}
   678  		}
   679  	}`,
   680  	`{
   681  		"id": "ThisID",
   682  		"site": {
   683  			"page": "prebid.org"
   684  		},
   685  		"imp": [
   686  			{
   687  				"id": "adUnit2",
   688  				"ext": {
   689  					"prebid": {
   690  						"storedrequest": {
   691  							"id": "1"
   692  						},
   693  						"options": {
   694  							"echovideoattrs": true
   695  						}
   696  					},
   697  					"appnexus": {
   698  						"placementId": "def",
   699  						"trafficSourceCode": "mysite.com",
   700  						"reserve": null
   701  					},
   702  					"rubicon": null
   703  				}
   704  			}
   705  		],
   706  		"ext": {
   707  			"prebid": {
   708  				"storedrequest": {
   709  					"id": "1"
   710  				}
   711  			}
   712  		}
   713  	}`,
   714  	`{
   715  		"id": "ThisID",
   716  		"app": {
   717  			"id": "123"
   718  		},
   719  		"imp": [
   720  			{
   721  				"ext": {
   722  					"prebid": {
   723  						"storedrequest": {
   724  							"id": "2"
   725  						},
   726  						"options": {
   727  							"echovideoattrs": false
   728  						}
   729  					}
   730  				}
   731  			}
   732  		],
   733  		"ext": {
   734  			"prebid": {
   735  				"storedrequest": {
   736  					"id": "2"
   737  				}
   738  			}
   739  		}
   740  	}`,
   741  	`{
   742  		"id": "ThisID",
   743  		"site": {
   744  			"page": "prebid.org"
   745  		},
   746  		"imp": [
   747  			{
   748  				"ext": {
   749  					"prebid": {
   750  						"storedrequest": {
   751  							"id": "2"
   752  						},
   753  						"options": {
   754  							"echovideoattrs": false
   755  						}
   756  					}
   757  				}
   758  			}
   759  		],
   760  		"ext": {
   761  			"prebid": {
   762  				"storedrequest": {
   763  					"id": "2"
   764  				}
   765  			}
   766  		}
   767  	}`,
   768  	`{
   769  		"id": "ThisID",
   770  		"app": {
   771  			"id": "123"
   772  		},
   773  		"imp": [
   774  			{
   775  				"ext": {
   776  					"prebid": {
   777  						"storedrequest": {
   778  							"id": "1"
   779  						},
   780  						"options": {
   781  							"echovideoattrs": false
   782  						}
   783  					}
   784  				}
   785  			}
   786  		],
   787  		"ext": {
   788  			"prebid": {
   789  				"storedrequest": {
   790  					"id": "1"
   791  				}
   792  			}
   793  		}
   794  	}`,
   795  	`{
   796  		"id": "ThisID",
   797  		"imp": [{
   798  			"id": "some-impression-id",
   799  			"banner": {
   800  				"format": [{
   801  						"w": 600,
   802  						"h": 500
   803  					},
   804  					{
   805  						"w": 300,
   806  						"h": 600
   807  					}
   808  				]
   809  			},
   810  			"ext": {
   811  				"appnexus": {
   812  					"placementId": 12883451
   813  				}
   814  			}
   815  		}],
   816  		"ext": {
   817  			"prebid": {
   818  				"debug": true,
   819  				"storedrequest": {
   820  					"id": "4"
   821  				}
   822  			}
   823  		},
   824  	  "site": {
   825  		"page": "https://example.com"
   826  	  }
   827  	}`,
   828  }
   829  
   830  // ---------------------------------------------------------
   831  // Some interfaces implemented with the purspose of testing
   832  // ---------------------------------------------------------
   833  
   834  // mockStoredReqFetcher implements the Fetcher interface
   835  type mockStoredReqFetcher struct {
   836  }
   837  
   838  func (cf mockStoredReqFetcher) FetchRequests(ctx context.Context, requestIDs []string, impIDs []string) (requestData map[string]json.RawMessage, impData map[string]json.RawMessage, errs []error) {
   839  	return testStoredRequestData, testStoredImpData, nil
   840  }
   841  
   842  func (cf mockStoredReqFetcher) FetchResponses(ctx context.Context, ids []string) (data map[string]json.RawMessage, errs []error) {
   843  	return nil, nil
   844  }
   845  
   846  // mockExchange implements the Exchange interface
   847  type mockExchange struct {
   848  	lastRequest *openrtb2.BidRequest
   849  }
   850  
   851  func (m *mockExchange) HoldAuction(ctx context.Context, auctionRequest *exchange.AuctionRequest, debugLog *exchange.DebugLog) (*exchange.AuctionResponse, error) {
   852  	r := auctionRequest.BidRequestWrapper
   853  	m.lastRequest = r.BidRequest
   854  	return &exchange.AuctionResponse{
   855  		BidResponse: &openrtb2.BidResponse{
   856  			SeatBid: []openrtb2.SeatBid{{
   857  				Bid: []openrtb2.Bid{{
   858  					AdM: "<script></script>",
   859  				}},
   860  			}},
   861  		},
   862  	}, nil
   863  }
   864  
   865  // hardcodedResponseIPValidator implements the IPValidator interface.
   866  type hardcodedResponseIPValidator struct {
   867  	response bool
   868  }
   869  
   870  func (v hardcodedResponseIPValidator) IsValid(net.IP, iputil.IPVersion) bool {
   871  	return v.response
   872  }
   873  
   874  // fakeUUIDGenerator implements the UUIDGenerator interface
   875  type fakeUUIDGenerator struct {
   876  	id  string
   877  	err error
   878  }
   879  
   880  func (f fakeUUIDGenerator) Generate() (string, error) {
   881  	return f.id, f.err
   882  }
   883  
   884  // warningsCheckExchange is a well-behaved exchange which stores all incoming warnings.
   885  // implements the Exchange interface
   886  type warningsCheckExchange struct {
   887  	auctionRequest exchange.AuctionRequest
   888  }
   889  
   890  func (e *warningsCheckExchange) HoldAuction(ctx context.Context, r *exchange.AuctionRequest, debugLog *exchange.DebugLog) (*exchange.AuctionResponse, error) {
   891  	e.auctionRequest = *r
   892  	return nil, nil
   893  }
   894  
   895  // nobidExchange is a well-behaved exchange which always bids "no bid".
   896  // implements the Exchange interface
   897  type nobidExchange struct {
   898  	gotRequest *openrtb2.BidRequest
   899  }
   900  
   901  func (e *nobidExchange) HoldAuction(ctx context.Context, auctionRequest *exchange.AuctionRequest, debugLog *exchange.DebugLog) (*exchange.AuctionResponse, error) {
   902  	r := auctionRequest.BidRequestWrapper
   903  	e.gotRequest = r.BidRequest
   904  
   905  	return &exchange.AuctionResponse{
   906  		BidResponse: &openrtb2.BidResponse{
   907  			ID:    r.BidRequest.ID,
   908  			BidID: "test bid id",
   909  			NBR:   openrtb3.NoBidUnknownError.Ptr(),
   910  		},
   911  	}, nil
   912  }
   913  
   914  // mockCurrencyRatesClient is a mock currency rate server and the rates it returns
   915  // are set in the JSON test file
   916  type mockCurrencyRatesClient struct {
   917  	data currencyInfo
   918  }
   919  
   920  type currencyInfo struct {
   921  	Conversions map[string]map[string]float64 `json:"conversions"`
   922  	DataAsOfRaw string                        `json:"dataAsOf"`
   923  }
   924  
   925  func (s mockCurrencyRatesClient) handle(w http.ResponseWriter, req *http.Request) {
   926  	s.data.DataAsOfRaw = "2018-09-12"
   927  
   928  	// Marshal the response and http write it
   929  	currencyServerJsonResponse, err := json.Marshal(&s.data)
   930  	if err != nil {
   931  		http.Error(w, err.Error(), http.StatusInternalServerError)
   932  		return
   933  	}
   934  	w.Write(currencyServerJsonResponse)
   935  	return
   936  }
   937  
   938  // mockBidderHandler carries mock bidder server information that will be read from JSON test files
   939  // and defines a handle function of a a mock bidder service.
   940  type mockBidderHandler struct {
   941  	BidderName string  `json:"bidderName"`
   942  	Currency   string  `json:"currency"`
   943  	Price      float64 `json:"price"`
   944  	DealID     string  `json:"dealid"`
   945  }
   946  
   947  func (b mockBidderHandler) bid(w http.ResponseWriter, req *http.Request) {
   948  	// Read request Body
   949  	buf := new(bytes.Buffer)
   950  	buf.ReadFrom(req.Body)
   951  
   952  	// Unmarshal exit if error
   953  	var openrtb2Request openrtb2.BidRequest
   954  	if err := json.Unmarshal(buf.Bytes(), &openrtb2Request); err != nil {
   955  		http.Error(w, err.Error(), http.StatusBadRequest)
   956  		return
   957  	}
   958  
   959  	var openrtb2ImpExt map[string]json.RawMessage
   960  	if err := json.Unmarshal(openrtb2Request.Imp[0].Ext, &openrtb2ImpExt); err != nil {
   961  		http.Error(w, err.Error(), http.StatusBadRequest)
   962  		return
   963  	}
   964  
   965  	_, exists := openrtb2ImpExt["bidder"]
   966  	if !exists {
   967  		http.Error(w, "This request is not meant for this bidder", http.StatusBadRequest)
   968  		return
   969  	}
   970  
   971  	// Create bid service openrtb2.BidResponse with one bid according to JSON test file values
   972  	var serverResponseObject = openrtb2.BidResponse{
   973  		ID:  openrtb2Request.ID,
   974  		Cur: b.Currency,
   975  		SeatBid: []openrtb2.SeatBid{
   976  			{
   977  				Bid: []openrtb2.Bid{
   978  					{
   979  						ID:     b.BidderName + "-bid",
   980  						ImpID:  openrtb2Request.Imp[0].ID,
   981  						Price:  b.Price,
   982  						DealID: b.DealID,
   983  					},
   984  				},
   985  				Seat: b.BidderName,
   986  			},
   987  		},
   988  	}
   989  
   990  	// Marshal the response and http write it
   991  	serverJsonResponse, err := json.Marshal(&serverResponseObject)
   992  	if err != nil {
   993  		http.Error(w, err.Error(), http.StatusInternalServerError)
   994  		return
   995  	}
   996  	w.Write(serverJsonResponse)
   997  	return
   998  }
   999  
  1000  // mockAdapter is a mock impression-splitting adapter
  1001  type mockAdapter struct {
  1002  	mockServerURL string
  1003  	Server        config.Server
  1004  }
  1005  
  1006  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
  1007  	adapter := &mockAdapter{
  1008  		mockServerURL: config.Endpoint,
  1009  		Server:        server,
  1010  	}
  1011  	return adapter, nil
  1012  }
  1013  
  1014  func (a mockAdapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
  1015  	var requests []*adapters.RequestData
  1016  	var errors []error
  1017  
  1018  	requestCopy := *request
  1019  	for _, imp := range request.Imp {
  1020  		requestCopy.Imp = []openrtb2.Imp{imp}
  1021  
  1022  		requestJSON, err := json.Marshal(request)
  1023  		if err != nil {
  1024  			errors = append(errors, err)
  1025  			continue
  1026  		}
  1027  
  1028  		requestData := &adapters.RequestData{
  1029  			Method: "POST",
  1030  			Uri:    a.mockServerURL,
  1031  			Body:   requestJSON,
  1032  		}
  1033  		requests = append(requests, requestData)
  1034  	}
  1035  	return requests, errors
  1036  }
  1037  
  1038  func (a mockAdapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
  1039  	if responseData.StatusCode != http.StatusOK {
  1040  		switch responseData.StatusCode {
  1041  		case http.StatusNoContent:
  1042  			return nil, nil
  1043  		case http.StatusBadRequest:
  1044  			return nil, []error{&errortypes.BadInput{
  1045  				Message: "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.",
  1046  			}}
  1047  		default:
  1048  			return nil, []error{&errortypes.BadServerResponse{
  1049  				Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", responseData.StatusCode),
  1050  			}}
  1051  		}
  1052  	}
  1053  
  1054  	var publisherResponse openrtb2.BidResponse
  1055  	if err := json.Unmarshal(responseData.Body, &publisherResponse); err != nil {
  1056  		return nil, []error{err}
  1057  	}
  1058  
  1059  	rv := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
  1060  	rv.Currency = publisherResponse.Cur
  1061  	for _, seatBid := range publisherResponse.SeatBid {
  1062  		for i, bid := range seatBid.Bid {
  1063  			for _, imp := range request.Imp {
  1064  				if imp.ID == bid.ImpID {
  1065  					b := &adapters.TypedBid{
  1066  						Bid:     &seatBid.Bid[i],
  1067  						BidType: openrtb_ext.BidTypeBanner,
  1068  					}
  1069  					rv.Bids = append(rv.Bids, b)
  1070  				}
  1071  			}
  1072  		}
  1073  	}
  1074  	return rv, nil
  1075  }
  1076  
  1077  // ---------------------------------------------------------
  1078  // Auxiliary functions that don't make assertions and don't
  1079  // take t *testing.T as parameter
  1080  // ---------------------------------------------------------
  1081  func getBidderInfos(disabledAdapters []string, biddersNames []openrtb_ext.BidderName) config.BidderInfos {
  1082  	biddersInfos := make(config.BidderInfos)
  1083  	for _, name := range biddersNames {
  1084  		isDisabled := false
  1085  		for _, disabledAdapter := range disabledAdapters {
  1086  			if string(name) == disabledAdapter {
  1087  				isDisabled = true
  1088  				break
  1089  			}
  1090  		}
  1091  		biddersInfos[string(name)] = newBidderInfo(isDisabled)
  1092  	}
  1093  	return biddersInfos
  1094  }
  1095  
  1096  func newBidderInfo(isDisabled bool) config.BidderInfo {
  1097  	return config.BidderInfo{
  1098  		Disabled: isDisabled,
  1099  	}
  1100  }
  1101  
  1102  func parseTestData(fileData []byte, testFile string) (testCase, error) {
  1103  
  1104  	parsedTestData := testCase{}
  1105  	var err, errEm error
  1106  
  1107  	// Get testCase values
  1108  	parsedTestData.BidRequest, _, _, err = jsonparser.Get(fileData, "mockBidRequest")
  1109  	if err != nil {
  1110  		return parsedTestData, fmt.Errorf("Error jsonparsing root.mockBidRequest from file %s. Desc: %v.", testFile, err)
  1111  	}
  1112  
  1113  	// Get testCaseConfig values
  1114  	parsedTestData.Config = &testConfigValues{}
  1115  	var jsonTestConfig json.RawMessage
  1116  
  1117  	jsonTestConfig, _, _, err = jsonparser.Get(fileData, "config")
  1118  	if err == nil {
  1119  		if err = json.Unmarshal(jsonTestConfig, parsedTestData.Config); err != nil {
  1120  			return parsedTestData, fmt.Errorf("Error unmarshaling root.config from file %s. Desc: %v.", testFile, err)
  1121  		}
  1122  	}
  1123  
  1124  	// Get the return code we expect PBS to throw back given test's bidRequest and config
  1125  	parsedReturnCode, err := jsonparser.GetInt(fileData, "expectedReturnCode")
  1126  	if err != nil {
  1127  		return parsedTestData, fmt.Errorf("Error jsonparsing root.code from file %s. Desc: %v.", testFile, err)
  1128  	}
  1129  
  1130  	// Get both bid response and error message, if any
  1131  	parsedTestData.ExpectedBidResponse, _, _, err = jsonparser.Get(fileData, "expectedBidResponse")
  1132  	parsedTestData.ExpectedErrorMessage, errEm = jsonparser.GetString(fileData, "expectedErrorMessage")
  1133  
  1134  	if err == nil && errEm == nil {
  1135  		return parsedTestData, fmt.Errorf("Test case %s can't have both a valid expectedBidResponse and a valid expectedErrorMessage, fields are mutually exclusive", testFile)
  1136  	} else if err != nil && errEm != nil {
  1137  		return parsedTestData, fmt.Errorf("Test case %s should come with either a valid expectedBidResponse or a valid expectedErrorMessage, not both.", testFile)
  1138  	}
  1139  
  1140  	parsedTestData.ExpectedReturnCode = int(parsedReturnCode)
  1141  
  1142  	return parsedTestData, nil
  1143  }
  1144  
  1145  func (tc *testConfigValues) getBlacklistedAppMap() map[string]bool {
  1146  	var blacklistedAppMap map[string]bool
  1147  
  1148  	if len(tc.BlacklistedApps) > 0 {
  1149  		blacklistedAppMap = make(map[string]bool, len(tc.BlacklistedApps))
  1150  		for _, app := range tc.BlacklistedApps {
  1151  			blacklistedAppMap[app] = true
  1152  		}
  1153  	}
  1154  	return blacklistedAppMap
  1155  }
  1156  
  1157  func (tc *testConfigValues) getBlackListedAccountMap() map[string]bool {
  1158  	var blacklistedAccountMap map[string]bool
  1159  
  1160  	if len(tc.BlacklistedAccounts) > 0 {
  1161  		blacklistedAccountMap = make(map[string]bool, len(tc.BlacklistedAccounts))
  1162  		for _, account := range tc.BlacklistedAccounts {
  1163  			blacklistedAccountMap[account] = true
  1164  		}
  1165  	}
  1166  	return blacklistedAccountMap
  1167  }
  1168  
  1169  // exchangeTestWrapper is a wrapper that asserts the openrtb2 bid request just before the HoldAuction call
  1170  type exchangeTestWrapper struct {
  1171  	ex                    exchange.Exchange
  1172  	actualValidatedBidReq *openrtb2.BidRequest
  1173  }
  1174  
  1175  func (te *exchangeTestWrapper) HoldAuction(ctx context.Context, r *exchange.AuctionRequest, debugLog *exchange.DebugLog) (*exchange.AuctionResponse, error) {
  1176  
  1177  	// rebuild/resync the request in the request wrapper.
  1178  	if err := r.BidRequestWrapper.RebuildRequest(); err != nil {
  1179  		return nil, err
  1180  	}
  1181  
  1182  	// Save the validated bidRequest that we are about to feed HoldAuction
  1183  	te.actualValidatedBidReq = r.BidRequestWrapper.BidRequest
  1184  
  1185  	// Call HoldAuction() implementation as written in the exchange package
  1186  	return te.ex.HoldAuction(ctx, r, debugLog)
  1187  }
  1188  
  1189  // buildTestExchange returns an exchange with mock bidder servers and mock currency conversion server
  1190  func buildTestExchange(testCfg *testConfigValues, adapterMap map[openrtb_ext.BidderName]exchange.AdaptedBidder, mockBidServersArray []*httptest.Server, mockCurrencyRatesServer *httptest.Server, bidderInfos config.BidderInfos, cfg *config.Configuration, met metrics.MetricsEngine, mockFetcher stored_requests.CategoryFetcher) (exchange.Exchange, []*httptest.Server) {
  1191  	if len(testCfg.MockBidders) == 0 {
  1192  		testCfg.MockBidders = append(testCfg.MockBidders, mockBidderHandler{BidderName: "appnexus", Currency: "USD", Price: 0.00})
  1193  	}
  1194  	for _, mockBidder := range testCfg.MockBidders {
  1195  		bidServer := httptest.NewServer(http.HandlerFunc(mockBidder.bid))
  1196  		bidderAdapter := mockAdapter{mockServerURL: bidServer.URL}
  1197  		bidderName := openrtb_ext.BidderName(mockBidder.BidderName)
  1198  
  1199  		adapterMap[bidderName] = exchange.AdaptBidder(bidderAdapter, bidServer.Client(), &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, bidderName, nil, "")
  1200  		mockBidServersArray = append(mockBidServersArray, bidServer)
  1201  	}
  1202  
  1203  	mockCurrencyConverter := currency.NewRateConverter(mockCurrencyRatesServer.Client(), mockCurrencyRatesServer.URL, time.Second)
  1204  	mockCurrencyConverter.Run()
  1205  
  1206  	gdprPermsBuilder := fakePermissionsBuilder{
  1207  		permissions: &fakePermissions{},
  1208  	}.Builder
  1209  
  1210  	testExchange := exchange.NewExchange(adapterMap,
  1211  		&wellBehavedCache{},
  1212  		cfg,
  1213  		nil,
  1214  		met,
  1215  		bidderInfos,
  1216  		gdprPermsBuilder,
  1217  		mockCurrencyConverter,
  1218  		mockFetcher,
  1219  		&adscert.NilSigner{},
  1220  		macros.NewStringIndexBasedReplacer(),
  1221  	)
  1222  
  1223  	testExchange = &exchangeTestWrapper{
  1224  		ex: testExchange,
  1225  	}
  1226  
  1227  	return testExchange, mockBidServersArray
  1228  }
  1229  
  1230  // buildTestEndpoint instantiates an openrtb2 Auction endpoint designed to test endpoints/openrtb2/auction.go
  1231  func buildTestEndpoint(test testCase, cfg *config.Configuration) (httprouter.Handle, *exchangeTestWrapper, []*httptest.Server, *httptest.Server, error) {
  1232  	if test.Config == nil {
  1233  		test.Config = &testConfigValues{}
  1234  	}
  1235  
  1236  	var paramValidator openrtb_ext.BidderParamValidator
  1237  	if test.Config.RealParamsValidator {
  1238  		var err error
  1239  		paramValidator, err = openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
  1240  		if err != nil {
  1241  			return nil, nil, nil, nil, err
  1242  		}
  1243  	} else {
  1244  		paramValidator = mockBidderParamValidator{}
  1245  	}
  1246  
  1247  	bidderInfos := getBidderInfos(test.Config.DisabledAdapters, openrtb_ext.CoreBidderNames())
  1248  	bidderMap := exchange.GetActiveBidders(bidderInfos)
  1249  	disabledBidders := exchange.GetDisabledBidderWarningMessages(bidderInfos)
  1250  	met := &metricsConfig.NilMetricsEngine{}
  1251  	mockFetcher := empty_fetcher.EmptyFetcher{}
  1252  
  1253  	// Adapter map with mock adapters needed to run JSON test cases
  1254  	adapterMap := make(map[openrtb_ext.BidderName]exchange.AdaptedBidder, 0)
  1255  	mockBidServersArray := make([]*httptest.Server, 0, 3)
  1256  
  1257  	// Mock prebid Server's currency converter, instantiate and start
  1258  	mockCurrencyConversionService := mockCurrencyRatesClient{
  1259  		currencyInfo{
  1260  			Conversions: test.Config.CurrencyRates,
  1261  		},
  1262  	}
  1263  	mockCurrencyRatesServer := httptest.NewServer(http.HandlerFunc(mockCurrencyConversionService.handle))
  1264  
  1265  	testExchange, mockBidServersArray := buildTestExchange(test.Config, adapterMap, mockBidServersArray, mockCurrencyRatesServer, bidderInfos, cfg, met, mockFetcher)
  1266  
  1267  	var storedRequestFetcher stored_requests.Fetcher
  1268  	if len(test.StoredRequest) > 0 {
  1269  		storedRequestFetcher = &mockAmpStoredReqFetcher{test.StoredRequest}
  1270  	} else {
  1271  		storedRequestFetcher = &mockStoredReqFetcher{}
  1272  	}
  1273  
  1274  	var storedResponseFetcher stored_requests.Fetcher
  1275  	if len(test.StoredResponse) > 0 {
  1276  		storedResponseFetcher = &mockAmpStoredResponseFetcher{test.StoredResponse}
  1277  	} else {
  1278  		storedResponseFetcher = empty_fetcher.EmptyFetcher{}
  1279  	}
  1280  
  1281  	var accountFetcher stored_requests.AccountFetcher
  1282  	accountFetcher = &mockAccountFetcher{
  1283  		data: map[string]json.RawMessage{
  1284  			"malformed_acct": json.RawMessage(`{"disabled":"invalid type"}`),
  1285  		},
  1286  	}
  1287  
  1288  	planBuilder := test.planBuilder
  1289  	if planBuilder == nil {
  1290  		planBuilder = hooks.EmptyPlanBuilder{}
  1291  	}
  1292  
  1293  	var endpointBuilder func(uuidutil.UUIDGenerator, exchange.Exchange, openrtb_ext.BidderParamValidator, stored_requests.Fetcher, stored_requests.AccountFetcher, *config.Configuration, metrics.MetricsEngine, analytics.PBSAnalyticsModule, map[string]string, []byte, map[string]openrtb_ext.BidderName, stored_requests.Fetcher, hooks.ExecutionPlanBuilder, *exchange.TmaxAdjustmentsPreprocessed) (httprouter.Handle, error)
  1294  
  1295  	switch test.endpointType {
  1296  	case AMP_ENDPOINT:
  1297  		endpointBuilder = NewAmpEndpoint
  1298  	default: //case OPENRTB_ENDPOINT:
  1299  		endpointBuilder = NewEndpoint
  1300  	}
  1301  
  1302  	endpoint, err := endpointBuilder(
  1303  		fakeUUIDGenerator{},
  1304  		testExchange,
  1305  		paramValidator,
  1306  		storedRequestFetcher,
  1307  		accountFetcher,
  1308  		cfg,
  1309  		met,
  1310  		analyticsConf.NewPBSAnalytics(&config.Analytics{}),
  1311  		disabledBidders,
  1312  		[]byte(test.Config.AliasJSON),
  1313  		bidderMap,
  1314  		storedResponseFetcher,
  1315  		planBuilder,
  1316  		nil,
  1317  	)
  1318  
  1319  	return endpoint, testExchange.(*exchangeTestWrapper), mockBidServersArray, mockCurrencyRatesServer, err
  1320  }
  1321  
  1322  type mockBidderParamValidator struct{}
  1323  
  1324  func (v mockBidderParamValidator) Validate(name openrtb_ext.BidderName, ext json.RawMessage) error {
  1325  	return nil
  1326  }
  1327  func (v mockBidderParamValidator) Schema(name openrtb_ext.BidderName) string { return "" }
  1328  
  1329  type mockAccountFetcher struct {
  1330  	data map[string]json.RawMessage
  1331  }
  1332  
  1333  func (af *mockAccountFetcher) FetchAccount(ctx context.Context, defaultAccountJSON json.RawMessage, accountID string) (json.RawMessage, []error) {
  1334  	if account, ok := af.data[accountID]; ok {
  1335  		return account, nil
  1336  	}
  1337  	return nil, []error{stored_requests.NotFoundError{ID: accountID, DataType: "Account"}}
  1338  }
  1339  
  1340  type mockAmpStoredReqFetcher struct {
  1341  	data map[string]json.RawMessage
  1342  }
  1343  
  1344  func (cf *mockAmpStoredReqFetcher) FetchRequests(ctx context.Context, requestIDs []string, impIDs []string) (requestData map[string]json.RawMessage, impData map[string]json.RawMessage, errs []error) {
  1345  	return cf.data, nil, nil
  1346  }
  1347  
  1348  func (cf *mockAmpStoredReqFetcher) FetchResponses(ctx context.Context, ids []string) (data map[string]json.RawMessage, errs []error) {
  1349  	return nil, nil
  1350  }
  1351  
  1352  type mockAmpStoredResponseFetcher struct {
  1353  	data map[string]json.RawMessage
  1354  }
  1355  
  1356  func (cf *mockAmpStoredResponseFetcher) FetchRequests(ctx context.Context, requestIDs []string, impIDs []string) (requestData map[string]json.RawMessage, impData map[string]json.RawMessage, errs []error) {
  1357  	return nil, nil, nil
  1358  }
  1359  
  1360  func (cf *mockAmpStoredResponseFetcher) FetchResponses(ctx context.Context, ids []string) (data map[string]json.RawMessage, errs []error) {
  1361  	for _, storedResponseID := range ids {
  1362  		if storedResponse, exists := cf.data[storedResponseID]; exists {
  1363  			// Found. Unescape string before returning
  1364  			response, err := strconv.Unquote(string(storedResponse))
  1365  			if err != nil {
  1366  				return nil, append([]error{}, err)
  1367  			}
  1368  			cf.data[storedResponseID] = json.RawMessage(response)
  1369  			return cf.data, nil
  1370  		}
  1371  	}
  1372  	return nil, nil
  1373  }
  1374  
  1375  type wellBehavedCache struct{}
  1376  
  1377  func (c *wellBehavedCache) GetExtCacheData() (scheme string, host string, path string) {
  1378  	return "https", "www.pbcserver.com", "/pbcache/endpoint"
  1379  }
  1380  
  1381  func (c *wellBehavedCache) PutJson(ctx context.Context, values []pbc.Cacheable) ([]string, []error) {
  1382  	ids := make([]string, len(values))
  1383  	for i := 0; i < len(values); i++ {
  1384  		ids[i] = strconv.Itoa(i)
  1385  	}
  1386  	return ids, nil
  1387  }
  1388  
  1389  func readFile(t *testing.T, filename string) []byte {
  1390  	data, err := os.ReadFile(filename)
  1391  	if err != nil {
  1392  		t.Fatalf("Failed to read file %s: %v", filename, err)
  1393  	}
  1394  	return data
  1395  }
  1396  
  1397  type fakePermissionsBuilder struct {
  1398  	permissions gdpr.Permissions
  1399  }
  1400  
  1401  func (fpb fakePermissionsBuilder) Builder(gdpr.TCF2ConfigReader, gdpr.RequestInfo) gdpr.Permissions {
  1402  	return fpb.permissions
  1403  }
  1404  
  1405  type fakePermissions struct {
  1406  }
  1407  
  1408  func (p *fakePermissions) HostCookiesAllowed(ctx context.Context) (bool, error) {
  1409  	return true, nil
  1410  }
  1411  
  1412  func (p *fakePermissions) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName) (bool, error) {
  1413  	return true, nil
  1414  }
  1415  
  1416  func (p *fakePermissions) AuctionActivitiesAllowed(ctx context.Context, bidderCoreName openrtb_ext.BidderName, bidder openrtb_ext.BidderName) (permissions gdpr.AuctionPermissions, err error) {
  1417  	return gdpr.AuctionPermissions{
  1418  		AllowBidRequest: true,
  1419  	}, nil
  1420  }
  1421  
  1422  type mockPlanBuilder struct {
  1423  	entrypointPlan               hooks.Plan[hookstage.Entrypoint]
  1424  	rawAuctionPlan               hooks.Plan[hookstage.RawAuctionRequest]
  1425  	processedAuctionPlan         hooks.Plan[hookstage.ProcessedAuctionRequest]
  1426  	bidderRequestPlan            hooks.Plan[hookstage.BidderRequest]
  1427  	rawBidderResponsePlan        hooks.Plan[hookstage.RawBidderResponse]
  1428  	allProcessedBidResponsesPlan hooks.Plan[hookstage.AllProcessedBidResponses]
  1429  	auctionResponsePlan          hooks.Plan[hookstage.AuctionResponse]
  1430  }
  1431  
  1432  func (m mockPlanBuilder) PlanForEntrypointStage(_ string) hooks.Plan[hookstage.Entrypoint] {
  1433  	return m.entrypointPlan
  1434  }
  1435  
  1436  func (m mockPlanBuilder) PlanForRawAuctionStage(_ string, _ *config.Account) hooks.Plan[hookstage.RawAuctionRequest] {
  1437  	return m.rawAuctionPlan
  1438  }
  1439  
  1440  func (m mockPlanBuilder) PlanForProcessedAuctionStage(_ string, _ *config.Account) hooks.Plan[hookstage.ProcessedAuctionRequest] {
  1441  	return m.processedAuctionPlan
  1442  }
  1443  
  1444  func (m mockPlanBuilder) PlanForBidderRequestStage(_ string, _ *config.Account) hooks.Plan[hookstage.BidderRequest] {
  1445  	return m.bidderRequestPlan
  1446  }
  1447  
  1448  func (m mockPlanBuilder) PlanForRawBidderResponseStage(_ string, _ *config.Account) hooks.Plan[hookstage.RawBidderResponse] {
  1449  	return m.rawBidderResponsePlan
  1450  }
  1451  
  1452  func (m mockPlanBuilder) PlanForAllProcessedBidResponsesStage(_ string, _ *config.Account) hooks.Plan[hookstage.AllProcessedBidResponses] {
  1453  	return m.allProcessedBidResponsesPlan
  1454  }
  1455  
  1456  func (m mockPlanBuilder) PlanForAuctionResponseStage(_ string, _ *config.Account) hooks.Plan[hookstage.AuctionResponse] {
  1457  	return m.auctionResponsePlan
  1458  }
  1459  
  1460  func makePlan[H any](hook H) hooks.Plan[H] {
  1461  	return hooks.Plan[H]{
  1462  		{
  1463  			Timeout: 5 * time.Millisecond,
  1464  			Hooks: []hooks.HookWrapper[H]{
  1465  				{
  1466  					Module: "foobar",
  1467  					Code:   "foo",
  1468  					Hook:   hook,
  1469  				},
  1470  			},
  1471  		},
  1472  	}
  1473  }
  1474  
  1475  type mockRejectionHook struct {
  1476  	nbr int
  1477  	err error
  1478  }
  1479  
  1480  func (m mockRejectionHook) HandleEntrypointHook(
  1481  	_ context.Context,
  1482  	_ hookstage.ModuleInvocationContext,
  1483  	_ hookstage.EntrypointPayload,
  1484  ) (hookstage.HookResult[hookstage.EntrypointPayload], error) {
  1485  	return hookstage.HookResult[hookstage.EntrypointPayload]{Reject: true, NbrCode: m.nbr}, m.err
  1486  }
  1487  
  1488  func (m mockRejectionHook) HandleRawAuctionHook(
  1489  	_ context.Context,
  1490  	_ hookstage.ModuleInvocationContext,
  1491  	_ hookstage.RawAuctionRequestPayload,
  1492  ) (hookstage.HookResult[hookstage.RawAuctionRequestPayload], error) {
  1493  	return hookstage.HookResult[hookstage.RawAuctionRequestPayload]{Reject: true, NbrCode: m.nbr}, m.err
  1494  }
  1495  
  1496  func (m mockRejectionHook) HandleProcessedAuctionHook(
  1497  	_ context.Context,
  1498  	_ hookstage.ModuleInvocationContext,
  1499  	_ hookstage.ProcessedAuctionRequestPayload,
  1500  ) (hookstage.HookResult[hookstage.ProcessedAuctionRequestPayload], error) {
  1501  	return hookstage.HookResult[hookstage.ProcessedAuctionRequestPayload]{Reject: true, NbrCode: m.nbr}, m.err
  1502  }
  1503  
  1504  func (m mockRejectionHook) HandleBidderRequestHook(
  1505  	_ context.Context,
  1506  	_ hookstage.ModuleInvocationContext,
  1507  	payload hookstage.BidderRequestPayload,
  1508  ) (hookstage.HookResult[hookstage.BidderRequestPayload], error) {
  1509  	result := hookstage.HookResult[hookstage.BidderRequestPayload]{}
  1510  	if payload.Bidder == "appnexus" {
  1511  		result.Reject = true
  1512  		result.NbrCode = m.nbr
  1513  	}
  1514  
  1515  	return result, m.err
  1516  }
  1517  
  1518  func (m mockRejectionHook) HandleRawBidderResponseHook(
  1519  	_ context.Context,
  1520  	_ hookstage.ModuleInvocationContext,
  1521  	payload hookstage.RawBidderResponsePayload,
  1522  ) (hookstage.HookResult[hookstage.RawBidderResponsePayload], error) {
  1523  	result := hookstage.HookResult[hookstage.RawBidderResponsePayload]{}
  1524  	if payload.Bidder == "appnexus" {
  1525  		result.Reject = true
  1526  		result.NbrCode = m.nbr
  1527  	}
  1528  
  1529  	return result, nil
  1530  }
  1531  
  1532  var entryPointHookUpdateWithErrors = hooks.HookWrapper[hookstage.Entrypoint]{
  1533  	Module: "foobar",
  1534  	Code:   "foo",
  1535  	Hook: mockUpdateHook{
  1536  		entrypointHandler: func(
  1537  			_ hookstage.ModuleInvocationContext,
  1538  			payload hookstage.EntrypointPayload,
  1539  		) (hookstage.HookResult[hookstage.EntrypointPayload], error) {
  1540  			ch := hookstage.ChangeSet[hookstage.EntrypointPayload]{}
  1541  			ch.AddMutation(func(payload hookstage.EntrypointPayload) (hookstage.EntrypointPayload, error) {
  1542  				payload.Request.Header.Add("foo", "bar")
  1543  				return payload, nil
  1544  			}, hookstage.MutationUpdate, "header", "foo")
  1545  
  1546  			return hookstage.HookResult[hookstage.EntrypointPayload]{
  1547  				ChangeSet: ch,
  1548  				Errors:    []string{"error 1"},
  1549  			}, nil
  1550  		},
  1551  	},
  1552  }
  1553  
  1554  var entryPointHookUpdateWithErrorsAndWarnings = hooks.HookWrapper[hookstage.Entrypoint]{
  1555  	Module: "foobar",
  1556  	Code:   "bar",
  1557  	Hook: mockUpdateHook{
  1558  		entrypointHandler: func(
  1559  			_ hookstage.ModuleInvocationContext,
  1560  			payload hookstage.EntrypointPayload,
  1561  		) (hookstage.HookResult[hookstage.EntrypointPayload], error) {
  1562  			ch := hookstage.ChangeSet[hookstage.EntrypointPayload]{}
  1563  			ch.AddMutation(func(payload hookstage.EntrypointPayload) (hookstage.EntrypointPayload, error) {
  1564  				params := payload.Request.URL.Query()
  1565  				params.Add("foo", "baz")
  1566  				payload.Request.URL.RawQuery = params.Encode()
  1567  				return payload, nil
  1568  			}, hookstage.MutationUpdate, "param", "foo")
  1569  
  1570  			return hookstage.HookResult[hookstage.EntrypointPayload]{
  1571  				ChangeSet: ch,
  1572  				Errors:    []string{"error 1"},
  1573  				Warnings:  []string{"warning 1"},
  1574  			}, nil
  1575  		},
  1576  	},
  1577  }
  1578  
  1579  var entryPointHookUpdate = hooks.HookWrapper[hookstage.Entrypoint]{
  1580  	Module: "foobar",
  1581  	Code:   "baz",
  1582  	Hook: mockUpdateHook{
  1583  		entrypointHandler: func(
  1584  			ctx hookstage.ModuleInvocationContext,
  1585  			payload hookstage.EntrypointPayload,
  1586  		) (hookstage.HookResult[hookstage.EntrypointPayload], error) {
  1587  			result := hookstage.HookResult[hookstage.EntrypointPayload]{}
  1588  			if ctx.Endpoint != hookexecution.EndpointAuction {
  1589  				result.Warnings = []string{fmt.Sprintf("Endpoint %s is not supported by hook.", ctx.Endpoint)}
  1590  				return result, nil
  1591  			}
  1592  
  1593  			ch := hookstage.ChangeSet[hookstage.EntrypointPayload]{}
  1594  			ch.AddMutation(func(payload hookstage.EntrypointPayload) (hookstage.EntrypointPayload, error) {
  1595  				body, err := jsonpatch.MergePatch(payload.Body, []byte(`{"tmax":50}`))
  1596  				if err == nil {
  1597  					payload.Body = body
  1598  				}
  1599  				return payload, err
  1600  			}, hookstage.MutationUpdate, "body", "tmax")
  1601  			ch.AddMutation(func(payload hookstage.EntrypointPayload) (hookstage.EntrypointPayload, error) {
  1602  				body, err := jsonpatch.MergePatch(payload.Body, []byte(`{"regs": {"ext": {"gdpr": 1, "us_privacy": "1NYN"}}}`))
  1603  				if err == nil {
  1604  					payload.Body = body
  1605  				}
  1606  				return payload, err
  1607  			}, hookstage.MutationAdd, "body", "regs", "ext", "us_privacy")
  1608  			result.ChangeSet = ch
  1609  
  1610  			return result, nil
  1611  		},
  1612  	},
  1613  }
  1614  
  1615  var rawAuctionHookNone = hooks.HookWrapper[hookstage.RawAuctionRequest]{
  1616  	Module: "vendor.module",
  1617  	Code:   "foobar",
  1618  	Hook:   mockUpdateHook{},
  1619  }
  1620  
  1621  type mockUpdateHook struct {
  1622  	entrypointHandler func(
  1623  		hookstage.ModuleInvocationContext,
  1624  		hookstage.EntrypointPayload,
  1625  	) (hookstage.HookResult[hookstage.EntrypointPayload], error)
  1626  }
  1627  
  1628  func (m mockUpdateHook) HandleEntrypointHook(
  1629  	_ context.Context,
  1630  	miCtx hookstage.ModuleInvocationContext,
  1631  	payload hookstage.EntrypointPayload,
  1632  ) (hookstage.HookResult[hookstage.EntrypointPayload], error) {
  1633  	return m.entrypointHandler(miCtx, payload)
  1634  }
  1635  
  1636  func (m mockUpdateHook) HandleRawAuctionHook(
  1637  	_ context.Context,
  1638  	_ hookstage.ModuleInvocationContext,
  1639  	_ hookstage.RawAuctionRequestPayload,
  1640  ) (hookstage.HookResult[hookstage.RawAuctionRequestPayload], error) {
  1641  	return hookstage.HookResult[hookstage.RawAuctionRequestPayload]{}, nil
  1642  }